Class类
JVM为每个加载的class及interface创建了对应的Class实例来保存class及interface的所有信息;
获取一个class对应的Class实例后,就可以获取该class的所有信息;
通过Class实例获取class信息的方法称为反射(Reflection);
JVM总是动态加载class,可以在运行期根据条件来控制加载class。
/*如何获取一个class的Class实例?有三个方法*/
//方法一:直接通过一个class的静态变量class获取:
Class cls1 = String.class;
System.out.println(cls1);
//方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:
String s = "Hello";
Class cls2 = s.getClass();
System.out.println(cls2);
//方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:
Class cls3 = Class.forName("java.lang.String");
访问字段
对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。
我们先看看如何通过Class实例获取字段信息。Class类提供了以下几个方法来获取字段:
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取
当前类
的某个field(不包括父类) - Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
Java的反射API提供的Field类封装了字段的所有信息,一个Field对象包含了一个字段的所有信息:
- getName():返回字段名称,例如,“name”;
- getType():返回字段类型,也是一个Class实例,例如,String.class;
- getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。
通过反射读写字段是一种非常规方法,它会破坏对象的封装。
调用方法
Java的反射API提供的Method对象封装了方法的所有信息:
通过Class实例的方法可以获取Method实例:
- Method
getMethod(name, Class...)
:获取某个public的Method(包括父类) - Method
getDeclaredMethod(name, Class...)
:获取当前类
的某个Method(不包括父类) - Method[] getMethods():获取所有public的Method(包括父类)
- Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
通过Method实例可以获取方法信息
- getName():返回方法名称,例如:“getScore”;
- getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
- getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
- getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
通过Method实例可以调用某个对象的方法
- Object
invoke
(Object instance, Object… parameters);(如果无需指定实例对象,invoke方法传入的第一个参数为null)
通过设置setAccessible(true)来访问非public方法;
通过反射调用方法时,仍然遵循多态原则,如下例:
// 获取Person的run方法:
Method h = Person.class.getMethod("run");
// 对Student实例调用hello方法:
h.invoke(new Student("agile", 50)); //student.run
调用构造方法
我们通常使用new操作符创建新的实例:
Person p = new Person();
如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法:
Person p = Person.class.newInstance();
调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法
。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。
为了调用任意的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor对象和Method非常类似,不同之处仅在于它是一个构造方法,并且,调用结果总是返回实例:
public class Main {
public static void main(String[] args) throws Exception {
// 获取构造方法Integer(int):
Constructor cons1 = Integer.class.getConstructor(int.class);
// 调用构造方法:
Integer n1 = (Integer) cons1.newInstance(123);
System.out.println(n1);
// 获取构造方法Integer(String)
Constructor cons2 = Integer.class.getConstructor(String.class);
Integer n2 = (Integer) cons2.newInstance("456");
System.out.println(n2);
}
}
通过Class实例获取Constructor的方法如下:
- getConstructor(Class…):获取某个public的Constructor;
- getDeclaredConstructor(Class…):获取某个Constructor;
- getConstructors():获取所有public的Constructor;
- getDeclaredConstructors():获取所有Constructor。
注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。
获取继承关系
通过Class对象可以获取继承关系:
- Class getSuperclass():获取父类类型;
- Class[] getInterfaces():获取当前类实现的所有接口。
public class Main {
public static void main(String[] args) throws Exception {
Class s = Integer.class;
Class[] is = s.getInterfaces();
for (Class i : is) {
System.out.println(i);
}
}
}
运行上述代码可知,Integer实现的接口有:
- java.lang.Comparable
- java.lang.constant.Constable
- java.lang.constant.ConstantDesc
public class Main {
public static void main(String[] args) throws Exception {
Class i = Integer.class;
Class n = i.getSuperclass();
System.out.println(n);//class java.lang.Number
Class o = n.getSuperclass();
System.out.println(o);//class java.lang.Object
System.out.println(o.getSuperclass());//null
}
}
运行上述代码,可以看到,Integer的父类类型是Number,Number的父类是Object,Object的父类是null。除Object外,其他任何非interface的Class都必定存在一个父类类型。
如果是两个Class实例,要判断一个向上转型是否成立,可以调用isAssignableFrom():
// Integer i = ?
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
文章来源
廖雪峰老师:https://www.liaoxuefeng.com/wiki/1252599548343744/1264803033837024