什么是反射?
在程序的运行过程中,可以动态的创建对象。
反射的基石是什么?
字节码对象是反射的基石。字节码对象:Java类文件通过javac进行编译后生成的xxx.class文件,此文件由jvm加载至内存中,字节码文件就由此变成了字节码文件对象。
字节码对象的类型是什么?
字节码对象的类型为Class,此处Class并非是我们平时创建Java文件时所生成的class,这里专指Java文件的一种类型即引用数据类型。
如何获得对象的字节码文件对象?
1、通过Object类的getClass()方法。由于所有的类是继承自Object类,所以所有的引用类都可以使用此方法。
Class clazz = testController.getClass();
2、通过类型的class属性。
Class clazz = String.class;
3、Class类的forName()方法。注意forName中的类名需要全类名即:包名+类名
Class clazz2 = Class.forName("java.lang.String");
同一个类的不同对象获取到的字节码对象是否是同一个?
TestController testController = new TestController();
TestController testController2 = new TestController();
Class clazz = testController.getClass();
Class clazz2 = testController2.getClass();
System.out.println("testController-->" + clazz);
System.out.println("testController2-->" + clazz2);
System.out.println(clazz.equals(clazz2));
通过测试得到的答案是相同的字节码对象。因为在字节码文件被加载到内存中后,被jvm进行编译加载会根据对应类的class创建的文件对象。所以当我们每new一次,JVM不会再帮我们再重新加载一次。
字节码文件什么时候被加载?
1、new一个类的时候会被加载。相同的类型在进行第二次new的时候将不再加载。
2、访问一个类的静态成员的时候。
3、调用一个类的静态方法的时候。
4、调用发射的方式创建一个类的字节码对象的时候。
5、创建一个子类对象的时候。
6、Java命令执行一个字节码的时候。
字节码文件对象的组成
字节码文件对象由class类生成,则类中又包含了构造方法、成员方法、成员变量。因此字节码文件对象会包括:构造方法对象Constructor、成员方法对象Method、成员变量对象Field。
如何获取构造方法对象、成员方法对象、成员变量对象?
public class Test {
private int a = 2;
private String name;
private String passWord;
private String mobile;
public Test(){}
public Test(String name){
this.name = name;
}
private Test(String name, String passWord){
this.name = name;
this.passWord = passWord;
}
public Test(String name, String passWord, String mobile) {
this.name = name;
this.passWord = passWord;
this.mobile = mobile;
}
public static void main(String[] args) {
Test test = new Test();
Class<? extends Test> clazz = test.getClass();
System.out.println("----------------------Constructor--------------------------");
// 得到使用public修饰的构造方法对象
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("----------------------Method--------------------------");
// 得到使用public修饰的成员方法对象
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("----------------------Field--------------------------");
// 得到所有的成员变量对象
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
}
}
由执行结果我们可以清晰的看到,在进行对构造方法对象进行获取的时候,使用getConstructors(),并没由获取到private修饰的构造方法,但是我们可以使用getDeclaredConstructors()方法获取所有的构造方法。
可根据名称获取具体的构造方法对象,如:
Test test = new Test();
Class<? extends Test> clazz = test.getClass();
System.out.println("----------------------Constructor--------------------------");
Constructor<? extends Test> declaredConstructor = clazz.getDeclaredConstructor(String.class, String.class);
System.out.println(declaredConstructor);
由此我们可以确定是用getDeclaredConstructors()方法获取到了private修饰的构造方法,对于像成员方法即成员变量对象都是相同的用法。
如何使用反射创建一个对象?
Test test = new Test();
Class<? extends Test> clazz = test.getClass();
System.out.println("----------------------Constructor--------------------------");
// 得到使用public修饰的构造方法对象
Constructor<? extends Test> declaredConstructor = clazz.getDeclaredConstructor(String.class, String.class);
//暴力破解,设置其他类可以正常访问Test类的private方法
declaredConstructor.setAccessible(true);
Test instance = declaredConstructor.newInstance("张三", "123456");
System.out.println(instance);
此处一定要注意,当我们在其他类中要使用反射去创建对应的类时一定需要指定访问权限,不然无法访问。
由此可以看出,我们成功的通过反射通过Test的私有构造方法创建了一个Test实例。
如果觉着不错,可以关注公众号:Java秃头猿,定期更新只分享对我们日常开发有帮助的东西。