Class类的使用
- 所有类都是
Class
类的对象,这种对象有三种表示方式
// c1/c2/c3是Foo的类类型
// 一个类的类类型只有一个,即 c1==c2==c3
Class c1 = Foo.class;
Class c2 = foo.getClass();
Class c3 = Class.forName("Foo");
- 可以通过类类型创建对象
Foo foo = (Foo)c1.newInstance(); // Foo需要有无参构造方法
- 基本数据类型、
void
关键字都有类类型
方法的反射
-
Method
类:一个方法就是一个Method
对象。 - 根据类类型获取方法
// 获取public方法,包括父类方法
Method[] methods = c.getMethods();
// 获取当前类声明的方法,不论访问权限
Method[] methods = c.getDeclaredMethods();
- 根据方法获取返回值类类型
Class returnClassType = method.getReturnType();
- 获取方法名称
String methodName = method.getName();
- 根据方法获取方法参数类类型数据
Class[] paramClassTypes = method.getParameterTypes();
- 获取某一方法
// 获取带两个int类型参数的,名字为print的方法
Method method = c.getMethod("print",int.class,int.class)
- 方法的操作
格式:method.invoke(obj,params[])
//调用a对象的print方法,传入两个参数
method.invoke(a,10,20);
//和直接调用方法效果一样
a.print(10,20);
成员变量的反射
-
Field
类:一个成员变量就是一个Field
对象。 - 根据类类型获取成员变量
// 获取public成员变量,包括父类成员变量
Field[] fields = c.getFields();
// 获取当前类声明的成员变量,不论访问权限
Field[] fields = c.getDeclaredFields();
- 例子
// 修改字符串的内容而不修改地址
//获取之前的值
String s = new String("abc");
System.out.println(s);
String s1 = s;
//利用反射属性修改值
Field field = null;
try {
field = s.getClass().getDeclaredField("value");
field.setAccessible(true);
field.set(s,"abcd".toCharArray());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//修改后的值
System.out.println(s);
System.out.println(s1 == s);
- 获取成员变量的类类型
Class fieldClassType = field.getType();
- 获取成员变量名称
String fieldName = field.getName();
构造函数的反射
-
Constructor
类:一个构造函数就是一个Constructor
对象。 - 根据类类型获取构造函数
// 获取public构造函数,包括父类构造函数
Constructor[] constructors = c.getConstructors();
// 获取当前类声明的构造函数,不论访问权限
//建议使用,因为构造方法都需要自己声明
Constructor[] constructors = c.getDeclaredConstructors();
- 获取构造函数名称
String methodName = constructor.getName();
- 根据方法获取构造函数参数类类型数据
Class[] paramClassTypes = constructor.getParameterTypes();
Java类加载机制
- 静态加载类:编译时加载类
new
对象是静态加载类,在编译时加载可能用到的类 - 动态加载类:运行时加载类
Class.forName
得到类类型,通过类类型创建对象是动态加载
需求实例:程序中如果是
World
调用World
的启动方法,如果是Excel
调用Excel
的启动方法,等等。
思路:如果直接在这个类中使用World、Excel
等类,当某一个可能不会用到的类不存在时会报错,所以需要动态加载
1.抽象出公共功能:
public interface OfficeAble {
void start();
}
2.实际类拥有功能:
public class World implements OfficeAble {
@Override
public void start() {
System.out.println("World start...");
}
}
public class Excel implements OfficeAble {
@Override
public void start() {
System.out.println("Excel start...");
}
}
3.实际使用:
public class OfficeTest {
public static void main(String[] args) {
try {
// Class c = Class.forName("com.zp.zptest.reflect.World");
Class c = Class.forName("com.zp.zptest.reflect.Excel");
OfficeAble officeAble = (OfficeAble) c.newInstance();
officeAble.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
用反射认识泛型的本质
- 反射是编译后的操作
- 编译后的集合是去泛型化的
- Java泛型是为了防止错误输入,只在编译时生效
- 反射是去泛型化的:
ArrayList list1 = new ArrayList();
ArrayList<String> list2 = new ArrayList<String>();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2); // 结果为true
- 使用反射给c2添加整型元素:
list2.add("hello");
Method method = c2.getMethod("add",Object.class);
method.invoke(list2,10);
System.out.println(list2); // [hello,10]