反射机制基础介绍
2023-07-14 18:24:15 130浏览
反射机制基础介绍,01.Java反射机制原理1.1反射原理是什么反射是为了能够动态的加载一个类动态加载类,动态的调用一个方法,动态的访问一个属性等动态要求而设计的。它的出发点就在于JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后通过使用java.lang.reflect包下得API以达到各种动态需求。1.2运行时执行反射反射机制是在运行状态中对于任意一个类,都
01.Java反射机制原理
1.1 反射原理是什么
- 反射是为了能够动态的加载一个类
- 动态加载类,动态的调用一个方法,动态的访问一个属性等动态要求而设计的。
- 它的出发点就在于JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后通过使用java.lang.reflect包下得API以达到各种动态需求。
1.2 运行时执行反射
- 反射机制是在运行状态中
- 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1.3 反射有哪些功能
- Java反射机制的功能有哪些?
- 1.在运行时判断任意一个对象所属的类。
- 2.在运行时构造任意一个类的对象。
- 3.在运行时判断任意一个类所具有的成员变量和方法。
- 4.在运行时调用任意一个对象的方法。
- 5.生成动态代理。
- 反射的组成
- 由于反射最终也必须有类参与,因此反射的组成一般有下面几个方面组成:
- 1.java.lang.Class.java:类对象;
- 2.java.lang.reflect.Constructor.java:类的构造器对象;
- 3.java.lang.reflect.Method.java:类的方法对象;
- 4.java.lang.reflect.Field.java:类的属性对象;
- 反射中类的加载过程
- 根据虚拟机的工作原理,一般情况下,类需要经过:加载->验证->准备->解析->初始化->使用->卸载这个过程,如果需要反射的类没有在内存中,那么首先会经过加载这个过程,并在在内存中生成一个class对象,有了这个class对象的引用,就可以发挥开发者的想象力,做自己想做的事情了。
- 反射是一种具有与类进行动态交互能力的一种机制,为什么要强调动态交互呢
- 动态加载,也就是在运行的时候才会加载,而不是在编译的时候,在需要的时候才进行加载获取,或者说你可以在任何时候加载一个不存在的类到内存中,然后进行各种交互,或者获取一个没有公开的类的所有信息,换句话说,开发者可以随时随意的利用反射的这种机制动态进行一些特殊的事情。
1.4 反射的初衷和场景
- 使用反射的初衷是什么
- 反射的初衷不是方便你去创建一个对象,而是让你在写代码的时候可以更加灵活,降低耦合,提高代码的自适应能力。
- Java反射的应用
- 1.逆向代码 ,例如反编译
- 2.与注解相结合的框架 例如Retrofit
- 3.单纯的反射机制应用框架 例如EventBus
- 4.动态生成类框架 例如Gson
- 反射的作用有哪些
- 前面只是说了反射是一种具有与Java类进行动态交互能力的一种机制,在Java和Android开发中,一般情况下下面几种场景会用到反射机制.
- 需要访问隐藏属性或者调用方法改变程序原来的逻辑,这个在开发中很常见的,由于一些原因,系统并没有开放一些接口出来,这个时候利用反射是一个有效的解决方法
- 自定义注解,注解就是在运行时利用反射机制来获取的。
- 在开发中动态加载类,比如在Android中的动态加载解决65k问题等等,模块化和插件化都离不开反射,离开了反射寸步难行。
- 反射的用途
- 官方解释:反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。这是一个相对高级的特性,只有那些语言基础非常扎实的开发者才应该使用它。如果能把这句警示时刻放在心里,那么反射机制就会成为一项强大的技术,可以让应用程序做一些几乎不可能做到的事情。
02.Class与.class文档
2.1 两者有何区别
- Java 在真正需要某个类时才会加载对应的.class文档
- 而非在程序启动时就加载所有类,因为大部分时候我们只需要用到应用程序部分资源,有选择地加载可以节省系统资源
- java.lang.Class 的实例是什么
- 代表 Java 应用程序运行时加载的 .class 文档,类、接口、Enum等编译过后,都会生成 .class 文档,所以 Class可以用来包含类、接口、Enum等信息
- Class 类没有公开的构造函数,实例是由 JVM 自动产生,每个 .class 文档加载时, JVM 会自动生成对应的 Class 对象
- 可以通过 Object 的 getClass() 方法或者通过 .class 常量取得每个对象对应的 Class 对象。如果是基本类型,可以使用对象的包装类加载 .TYPE 取得 Class 对象
- 例如,使用 Integer.TYPE 可取得代表 int 基本类型的 Class,通过 Integer.class 取得代表 Integer.class 文档的 Class
2.2 通过Class获取信息
- 在取得 Class 对象后,就可以操作 Class 对象的公开方法取得类基本信息
package com.yc.demo;
public class Student {
public static void main(String[] args) {
Class cl = Student.class;
System.out.println("类名称:"+cl.getName());
System.out.println("简单类名称:"+cl.getSimpleName());
System.out.println("包名:"+cl.getPackage());
System.out.println("是否为接口:"+cl.isInterface());
System.out.println("是否为基本类型:"+cl.isPrimitive());
System.out.println("是否为数组对象:"+cl.isArray());
System.out.println("父类名称:"+cl.getSuperclass().getName());
}
}
- 输出结果为
类名称:com.yc.demo.Student
简单类名称:Student
包名:package com.yc.demo
是否为接口:false
是否为基本类型:false
是否为数组对象:false
父类名称:java.lang.Object
Java 在真正需要类时才会加载.class文档,即在生成对象时才会加载.class文档。如果只是使用类声明了一个变量,此时并不会加载.class文档,而只是让编译程序检查对应的 .class 文档是否存在。
- 例如,在 Stduent 类中定义了 static 静态区域块,在首次加载 .class 文档时会被执行(这是默认情况下,也可以指定不执行)
public class Student {
static {
System.out.println("载入了 Student.class 文档");
}
}
- 再来测试加载顺序
package com.yc.demo;
public class Main {
public static void main(String[] args) {
Student student;
System.out.println("声明了 Student 变量");
student=new Student();
System.out.println("生成了 Student 实例");
}
}
- 输出结果为
声明了 Student 变量
载入了 Student.class 文档
生成了 Student 实例
03.如何避免反射
3.1 防止反射单例
- 枚举单例
public enum Singleton {
INSTANCE {
@Override
protected void read() {
System.out.println("read");
}
@Override
protected void write() {
System.out.println("write");
}
};
protected abstract void read();
protected abstract void write();
}
- class文件:
public abstract class Singleton extends Enum{
private Singleton(String s, int i){
super(s, i);
}
protected abstract void read();
protected abstract void write();
public static Singleton[] values(){
Singleton asingleton[];
int i;
Singleton asingleton1[];
System.arraycopy(asingleton = ENUM$VALUES, 0, asingleton1 = new Singleton[i = asingleton.length], 0, i);
return asingleton1;
}
public static Singleton valueOf(String s) {
return (Singleton)Enum.valueOf(singleton/Singleton, s);
}
Singleton(String s, int i, Singleton singleton){
this(s, i);
}
public static final Singleton INSTANCE;
private static final Singleton ENUM$VALUES[];
static {
INSTANCE = new Singleton("INSTANCE", 0){
protected void read(){
System.out.println("read");
}
protected void write(){
System.out.println("write");
}
};
ENUM$VALUES = (new Singleton[] {
INSTANCE
});
}
}
- 类的修饰abstract,所以没法实例化,反射也无能为力。
- 关于线程安全的保证,其实是通过类加载机制来保证的,我们看看INSTANCE的实例化时机,是在static块中,JVM加载类的过程显然是线程安全的。对于防止反序列化生成新实例的问题还不是很明白,一般的方法我们会在该类中添加上如下方法,不过枚举中也没有显示的写明该方法。
//readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
3.2 将类抽象也无法利用反射
- 为了避免反射,可以将类抽象化。因为抽象类无法被实例化的……
3.3 将类设置成final不可变
- 将一个类设置成final不可变,也可以避免反射改变属性!这个在第三方库中特别常见……
- 这个存在疑惑,待完善
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
暂无评论,快来写一下吧
展开评论