反射的功能虽然不常用,但是也会出现 “书到用时方恨少” 的情况,索性今天来将他搞扎实些,同时希望能帮助到有同样需求的同学~
反射的两个重要作用
- 反编译:.class -> .java
- 通过反射访问 Java 对象的 属性、 方法 、构造方法等 (最常用的)
反射机制需要使用的类
- java.lang.Class
第一步 拿到Class 对象(起源)
水是有源的,树是有根的,使用反射就要先拿到 ‘根’ —— > Class 对象
献上zhang san
public class ZhangSan {
private String name;
private int age;
public ZhangSan() {
}
public ZhangSan(String name, int age) {
this.name = name;
this.age = age;
}
private String quickRun(String cause){
System.out.println(cause);
return cause;
}
@Override
public String toString() {
return "ZhangSan{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Person {
public String type = "人类";
}
举例 获取 ZhangSan 的 Class 对象
方式 1: 会让ClassLoader 装载类,并‘进行初始化’
Class<?> zhang3Class = Class.forName("reflectTest.ZhangSan"); //传入包名+类名路径
System.out.println(zhang3Class); //打印:class reflectTest.ZhangSan
方式 2: ClassLoader 装在入内存,但‘不对类进行初始化’
Class c2 = ZhangSan.class;
System.out.println(c2); //打印:class reflectTest.ZhangSan
方式 3: 返回类对象运行时真正的所指对象 的Class对象 (对于反射而言没什么用)
Class c3 = new ZhangSan("Zhang3", 18).getClass();
System.out.println(c3); //打印:class reflectTest.ZhangSan
重点是记住前两中方式的区别就好了(是/否 对类进行初始化)
获取 Object
方式 1: 获取无参构造对象
// 获取无参构造对象
Class<?> zhang3Class = Class.forName("reflectTest.ZhangSan");
Object z3Obj =zhang3Class.getDeclaredConstructor().newInstance();//Java 9 开始使用
//Object z3Obj = zhang3Class.newInstance(); //Java 8 使用
方式 2: 获取有参构造对象
Class<?> zhang3Class = Class.forName("reflectTest.ZhangSan");
Object z3Obj = zhang3Class.getDeclaredConstructor(String.class,int.class).newInstance("zhang33", 19); //Java 9 开始使用
// Object z3Obj = zhang3Class.getConstructor(String.class,int.class); //Java 8 使用
// Object z3Obj = zhang3Class.newInstance("zhang33", 19); //Java 8 使用
反射类中的属性
反射私有的 ‘name’ 属性
打印 ZhangSan{name=‘li si’, age=19} 修改私有属性成功!
Class<?> zhang3Class = Class.forName("reflectTest.ZhangSan");
Object z3Obj = zhang3Class.getDeclaredConstructor(String.class,int.class).newInstance("zhang33", 19); //Java 9 开始使用
System.out.println(z3Obj); // ZhangSan{name='zhang33', age=19}
Field nameField = zhang3Class.getDeclaredField("name");
nameField.setAccessible(true); //改变属性访问限制 (可以访问私有属性了)
nameField.set(z3Obj,"li si");
System.out.println(z3Obj); // ZhangSan{name='li si', age=19}
Field 描述的是属性对象,其中包含很多信息 比如:属性名字 属性类型 属性注解(使用这些可以做很多事情~ ~ 后面会说)
反射类中的方法
Class<?> zhang3Class = Class.forName("reflectTest.ZhangSan");
Object z3Obj = zhang3Class.getDeclaredConstructor(String.class,int.class).newInstance("zhang33", 19); //Java 9 开始使用
Method quickRun = zhang3Class.getDeclaredMethod("quickRun",String.class); //方法名称 + 方法入参的类型 有几个入参填几个
quickRun.setAccessible(true); //改变属性访问限制 (可以访问私有方法了)
quickRun.invoke(z3Obj, "Lun Xiang 来了!!");
获取父类Class
getSuperclass
Class<?> zhang3Class = Class.forName("reflectTest.ZhangSan");
Class<?> superclass = zhang3Class.getSuperclass();
反射进阶
进阶(1) Api 作用域
上面叙述了,获取 属性 和 方法 的Api ,但是在有些场景会拿不到对应的对象,实际上是每个Api 的作用域不同导致的。
获取属性作用域
方法 | 本Class | SuperClass |
---|---|---|
getField | public | public |
getDeclareField | public 、procted 、private | no |
getFields | public | public |
getDeclareFields | public 、procted 、private | no |
获取方法作用域
方法 | 本Class | SuperClass |
---|---|---|
getMethod | public | public |
getDeclareMethod | public 、procted 、private | no |
getMethods | public | public |
getDeclareMethods | public 、procted 、private | no |
获取构造作用域
方法 | 本Class | SuperClass |
---|---|---|
getConstructor | public | no |
getDeclareConstructor | public 、procted 、private | no |
getConstructor | public | no |
getDeclareConstructor | public 、procted 、private | no |
反射创建一个对象, java 9 已经废弃了 class.newInstance();
所以直接用 class.getConstructor().newInstance() 吧;
小结:这一节介绍了反射当中常用的基础知识,
1、 获取 Class Object ;
2、 获取 属性 方法 ;
3、 Api 的作用域
如果想了解 反射实现动态代理 的同学,请看 下一节 Java 反射 “实现动态代理” —— 进阶使用(二))