除了int等基本类型外,Java的其他类型全部都是class(包括interface)
JVM为每个加载的class及interface创建了对应的Class实例来保存class及interface的所有信息;
获取一个class对应的Class实例后,就可以获取该class的所有信息;
通过Class实例获取class信息的方法称为反射(Reflection);
JVM总是动态加载class,可以在运行期根据条件来控制加载class。
1、访问字段
1、对于任意Object实例,我们只要获取到了它的class,我们就能获取它所有的信息。
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
2、拿到class的field之后,可以知道这个field叫什么名字。
- getName():返回字段名称,例如,"name";
- getType():返回字段类型,也是一个Class实例,例如,String.class;
- getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
当用 getModifiers 获取到字段的修饰符之后,可以在进行类型判断。
3、获取 field 的值
当获取到 field 之后,只是第一步,然后我们可以再获取这个field的值
Class cls = d.getClass(); // 这个d是实例化对象
Field[] fields = cls.getDeclaredFields(); // 获取实例对象d的所有字段信息。
Object value = field.get(d) // 我们再拿到具体的field之后,再利用get获取到值。
注意:调用Field.setAccessible(true)的意思是,别管这个字段是不是public,一律允许访问。这个要写在前面。
4、修改 field 的值
以上面为例,field.set(d,"xxx") 第一个参数是实例的对象,第二个参数是修改后的内容。
2、调用方法
1、获取所有方法
- 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表示不同的含义。
2、调用方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class diaoyongfangfa {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String name = "hello world!";
String r = name.substring(2,5); // llo
System.out.println(r);
// 利用反射来调用 substring 方法
Class cls = name.getClass();
Method m = cls.getMethod("substring", int.class,int.class);
String r2 = (String)m.invoke(name,2,5); // llo
System.out.println(r2);
/*
* 1、invoke(调用),上面是调用 String 的substring方法,该方法有多个版本,可以在 cls.getMethod("substring", int.class,int.class);
* 定义 int 的数量来调用不同的版本。
* 2、对 Method 实例调用 invoke 就相当于调用该方法,invoke 的一个参数是对象实例,后面的参数与 cls.getMethod() 除了第一个参数之外的参数相同即可。
* */
}
}
3、调用非public方法
和 filed 类似,通过 Method.setAccessible(true) 允许其调用
可能会失败。
4、多态
使用反射调用方法时,仍然遵循多态原则
即总是调用实际类型的覆写方法。
5、调用构造方法
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class diaoyonggouzaofangfa {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Demo d = new Demo();
// 通过反射来创建新实例
Demo d2 = Demo.class.newInstance();
d2.setName("chen");
System.out.println(d2.name);
/*
* 1、上面的只能调用 public 的无参构造方法,有参或者不是public,就无法直接通过newInstance 来调用。
* 2、为了调用任意的构造方法,提供了 Constructor 对象,
* 3、下面的时利用反射,调用了有参构造方法,再利用反射,获取getName方法,然后在调用invoke传入利用反射构造的有参实例,执行。
* */
// 通过反射来创建新实例并给name字段命名
Class<Demo> d3 = Demo.class;
// 获取带有参数的构造方法
Constructor<?> constructor = d3.getConstructor(String.class);
// 调用构造方法,创建对象
Object d3Object = constructor.newInstance("King");
System.out.println(d3Object);
Method d3Method = d3.getMethod("getName");
System.out.println(d3Method.invoke(d3Object));
}
}
通过Class实例获取Constructor的方法如下:
- getConstructor(Class...):获取某个public的Constructor;
- getDeclaredConstructor(Class...):获取某个Constructor;
- getConstructors():获取所有public的Constructor;
- getDeclaredConstructors():获取所有Constructor。
注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。