一、简单理解
在java字节码文件(.class文件)中,存放了一个Class类的实例,通过这个实例,可以拿到字节码文件里面的信息:包括构造器、字段、方法等。
二、经典面试题
1、创建Person的对象,使用new Person()还是使用反射?
回答:得看情况,一般情况下需要使用类的对象时,使用new创建;
如果出现多个类或者不确定数量的类调用同一个方法时,推荐使用反射创建,提高代码复用性,降低耦合;
2、反射是否破坏了面向对象的封装性?
回答:没破坏;存在即合理,封装象征规定,反射象征灵活;只是不同的业务场景使用不同的方式,使用封装提高代码的整洁性和安全性,反射提高了编写代码的灵活性,【我的理解是反射对多个类可以使用批处理,不再需要一个类一个类的去处理】;如果出现多个类或者不确定数量的类调用同一个方法时,推荐使用反射创建,提高代码复用性,降低耦合;
好比是规定男厕所只有男生能进入,女厕所只有女生能进入;特殊情况下也能不按规定进入;
三、反射的使用
1、获取字节码信息方式
// 1
Person person = new Person();
Class clazz1 = person.getClass();
// 2
Class clazz2 = Person.class;
// 3 最常用,调用 Class.forName()
Class clazz3 = Class.forName("allwe.pojo.Person");
// 4 使用类加载器
ClassLoader loder = this.getClass().getClassLoader();
Class clazz4 = loder.loadClass("allwe.pojo.Person");
2、获取构造器
//获取字节码信息
Class clazz = Class.forName("allwe.pojo.Student");
//通过字节码信息获取构造器数组//获取公开构造器
publicConstructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("===================");
//获取全部构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("===================");
//获取指定且开放的构造器 public
//不传参数获取空参构造器
//传入构造器的参数列表的类型
Constructor constructor = clazz.getConstructor(int.class,double.class);
System.out.println(constructor);
System.out.println("===================");
//获取任意权限修饰符的构造器
Constructor declaredConstructor = clazz.getDeclaredConstructor(int.class);
System.out.println(declaredConstructor);
3、使用构造器创建对象
//使用构造器创建对象,参数列表与使用的构造器相同即可
Object object = constructor.newInstance(2,3.0);
System.out.println(object);
System.out.println("+++++++++++++++++++++++");
//使用private修饰的构造器,需要关闭权限安全检查 true-关闭 默认开启
declaredConstructor.setAccessible(true);
Object object1 = declaredConstructor.newInstance(2);
System.out.println(object1);
System.out.println("+++++++++++++++++++++++");
//字节码文件直接调用newInstance,使用的是类的空参构造器
Object object2 = clazz.newInstance();
System.out.println(object2);
System.out.println("+++++++++++++++++++++++");
Constructor constructor1 = clazz.getConstructor();
Object object3 = constructor1.newInstance();
System.out.println(object3);
4、获取字节码文件的属性
//获取字节码信息
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass("allwe.pojo.Student");
//获取公开属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("======================");
//获取全部属性,不包含父类的属性
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("======================");
//获取指定的属性,公开
Field name = clazz.getField("name");
System.out.println(name);
System.out.println("======================");
//获取指定的属性,全部修饰符
Field studentNo = clazz.getDeclaredField("studentNo");
System.out.println(studentNo);
5、获取属性的属性
System.out.println("属性的属性======================");
//属性的具体结构
System.out.println("======================");
//获取修饰符
int modifiers = studentNo.getModifiers(); //返回int类型
System.out.println(Modifier.toString(modifiers));
System.out.println("======================");
//获取属性的数据类型
System.out.println(studentNo.getType().getName());
System.out.println("======================");
//获取属性的名字
System.out.println(studentNo.getName());
6、给对象属性赋值
//获取字节码信息
ClassLoader classLoader = this.getClass().getClassLoader();
//获取字节码文件
Class clazz = classLoader.loadClass("allwe.pojo.Student");
//创建对象
Object object = clazz.newInstance();
//给属性设置值,必须要先有对象
Field studentNo = clazz.getDeclaredField("studentNo");
studentNo.setAccessible(true);
studentNo.set(object,2018);
System.out.println(object);
7、调用对象内部方法
//获取字节码文件
Class clazz = this.getClass().getClassLoader().loadClass("allwe.pojo.Student");
//创建对象
Object object = clazz.newInstance();
//获取指定的方法
Method getStudentNo = clazz.getDeclaredMethod("getStudentNo",int.class);
getStudentNo.setAccessible(true);
//调用方法
//需要传入对象
getStudentNo.invoke(object,2);