java反射-基础反射知识
文章目录
反射
反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
主要用在企业级框架底层,可以动态根据 IO 读到的一些类、接口、枚举、注解等的信息,进而让其中的成员变量、方法、构造方法运行起来
- 目的:不使用 new 关键字创建对象,还能让其中的变量、方法执行
- 核心:1. 获取到某个 class 文件;2. 动态分析 class 中的内容
1. 反射示意图
任何 class文件 都是一个个体。
以下图为例,我们写了几个自定义类,它们编译后生成的 class文件, 本质上都是 Class类的一个实例。
也即如果程序中可得到 Class对象,也即一定获取到一个具体的 class文件。
这也是 java OO思想的体现,任何事物都是一个对象,包括编译生成的 class文件
2. 获取 Class对象 的方法
2.1 getClass方法
在 Object类 中定义了getClass方法。
Person p = new Person();
Class clazz = p.getClass();// class reflection.Person
int[] arr = new int[3];
Class clazz2 = arr.getClass();// class [I
2.2 class属性
任何类型,java 均配了一个class属性
Class clazz = Person.class;// class reflection.Person
Class clazz2 = int[].class;// class [I
2.3 forName方法
常用方法,前两种方法实际上已经知道是什么类了,有些多此一举。使用 Class类 的静态方法可以得到 Class对象
Class.forName("com.mysql.jdbc.Driver")
获取到Driver的class类对象,参数必须写全路径。
3. CLass对象的创建时间
Class对象(.class文件) 是 JVM 将 class文件加载至方法区时,会自动在堆中创建出当前这个class文件对应的唯一对象,实际上 new对象时,不是通过方法区的class文件创建的,而是根据堆中这个 class对象创建的。
4. 使用Class对象创建类的对象
newInstance方法要求被反射的class文件中,一定有默认的公开构造,即无参数公开构造
CLass clazz = CLass.forName("reflection.Person");
Object obj = clazz.newInstance();// 注意该方法在JDK9后弃用
// newInstance方法要求被反射的class文件中,一定有默认的公开构造,即无参数公开构造
System.out.println(obj);//reflection.Person@6833ce2c
但是 newInstance 方法只能构造公开无参的,不方便,所以还是获取到其中的构造方法,再去使用会更加方便
5. 获取CLass对象的内容
对于一个类而言,重要的就是 成员变量、构造方法、成员方法。反射解析出这些成员后,再封装成不同的对象。
- 成员变量: Field
- 构造方法: Constructor
- 成员方法: Method
以Person为例来获取其中内容
public class Person {
private int id;
public String name;
public Person(){
}
public Person(int id){
this.id = id;
}
private Person(int id, String name){
this.id = id;
this.name = name;
}
private void say(){
System.out.println("say");
}
public int hi(String name){
System.out.println("hi" + name);
return 0;
}
}
5.1 获取构造方法
getConstructor(参数类型)
获取某个特定的构造函数
Class clazz = Class.forName("reflection.Person");
Constructor cons = clazz.getConstructor(int.class);
System.out.println(cons); // public reflection.Person(int)
Object obj = cons.newInstance(123);
// obj就是创建的Person对象,依据的构造是仅有一个int参数的构造
getConstructores()
获取所有的公开的构造方法
Class clazz = Class.forName("reflection.Person");
Constructor[] cons = clazz.getConstructors(int.class);
for(Constructor c:cons){
System.out.println(c);
}
/*
---------------演示getConstructors--------------
public reflection.Person(int)
public reflection.Person()
*/
getDeclaredConstructor()
获取所有构造函数,包括私有构造。仅仅获取私有构造是不能用的,必须修改权限。Constructor继承了AccessibleObject,可以修改权限。
Class clazz = Class.forName("reflection.Person");
Constructor cons = clazz.getDeclaredConstructor(int.class, String.class);
cons.setAccessible(true); // 修改权限
System.out.println(cons); // private reflection.Person(int,java.lang.String)
Object obj1 = cons.newInstance(123, "321");
System.out.println(obj1); // reflection.Person@6833ce2c
面试小技巧
面试如果问到 private、public等的时候,说private是私有的,不能让外界访问的,可以说但是通过反射技术可以取消访问控制,然后将话题转向反射
5.2 获取成员变量
使用的场景较少, 使用 getField 获取成员变量,然后利用 set 方法设置某个对象该成员变量的值, 也可以利用 get 方法获取值
小细节如果是静态成员变量,那么传入的参数是null就可以正常获取了
Field field = clazz.getField("name");
field.set(obj, "jack");
System.out.println(obj); // reflection.Person@2aaf7cc2
System.out.println(field.get(obj));// jack
5.3 获取方法
- getMethod:获取特定方法,
- getDeclaredMethod:获取包含私有的方法,使用时需要取消权限检查
- getMethods:获取所有public方法,包含它所有父类的方法
- getDeclaredMethods: 获取当前类的所有方法,含私有,不含父类的
- 如果反射静态方法,传入的对象写为null即可
Class clazz = Class.forName("...");
// 依靠方法名与参数列表来反射方法
Method method = clazz.getMethod("say", String.class);
Object obj = clazz.newInstance();
// invoke方法是是method方法运行,参数:运行该方法的对象,该方法的参数,返回值:被反射的方法的返回值
Object ret = method.invoke(obj);
Method method = clazz.getMethod("hi", null);
method.setAccessible(true);
method.invoke(obj);