在日常开发中尽量不要用反射,如果需要,先考虑通过复制原始类的形式来避免反射,还不行再考虑通过反射。
反射能帮我们做什么?
- 反射构建出无法直接访问的类
- set或get到无法访问的类变量
- 调用不可访问的方法
每个包装器原始数据类型类具有名为 TYPE 的静态字段
int.class 和 Integer.TYPE 指的是同一个类对象。
上一篇我在Java反射札记里说了如何反射一个类,代参实例对象,反射属性和方法。这次我们来看一个开源类库
jOOR 传送门 (https://github.com/jOOQ/jOOR)
我们一起学习下这个库的代码
这个开源库适配了Java6,Java8 和2017年11月份已经发布的Java9,作者在开发这个类库使,借鉴了已有的项目,做了调研和测试。
github上给出的Demo代码段:
String world = on("java.lang.String") // Like Class.forName()
.create("Hello World") // Call most specific matching constructor
.call("substring", 6) // Call most specific matching substring() method
.call("toString") // Call toString()
.get();
上述代码采用链式调用,on方法 create方法,call方法内部都是调用的on的重载方法,返回Reflection类,最后的get()内部采用了泛型返回,如下:
/**
* Get the wrapped object
*
* @param <T> A convenience generic parameter for automatic unsafe casting
*/
@SuppressWarnings("unchecked")
public <T> T get() {
return (T) object;
}
关于泛型的使用,可以查阅之前的两篇:
- 重识Java泛型 上
- 重识Java泛型 下
在Android项目中,findViewById经过这样封装后,再也不用强转类型了。
类加载机制
JVM里java的类的加载流程图
这部分内容已在:Java反射札记 这篇讲解了,这里不重复了
Loading -> Linking -> Initialization
类一定会初始化的五大情况
-
使用new字节码指令创建类的实例,或者使用getstatic、putstatic读取或设置一个静态字段的值(放入常量池中的常量除外),或者调用一个静态方法的时候,对应类必须进行过初始化。
-
通过java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则要首先进行初始化。
-
当初始化一个类的时候,如果发现其父类没有进行过初始化,则首先触发父类初始化。
-
当虚拟机启动时,用户需要指定一个主类(包含main()方法的类),虚拟机会首先初始化这个类。
-
使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、RE_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。
关于第五点,没有接触过
现在通过代码实践来试试看吧
我定义了SuperClass类和SubClass类
SuperClass类
public class SuperClass {
public static String sValue = "666";
public static final String HELLO_WORLD = "hello world!";
static {
Out.println("SuperClass init");
}
public String getContent(){
return "This is String result";
}
public static void setsValue(String value){
sValue = value;
}
}
SubClass
public class SubClass extends SuperClass {
public static String sTemp = "";
static {
sTemp = "sub class temp";
Out.println("Subclass init");
}
public void instanceTest(){
Out.println("instanceTest");
}
}
测试下第一点,new的时候
/**
* 类初始化
*/
private static void testVersion1_2() {
SubClass subClass = new SubClass();
}
输出:
SuperClass init
Subclass init
子类SubClass和SuperClass都经历了类初始化阶段
测试下第一点里提到的引用静态字段时
/**
* 类初始化3
*/
private static void testVersion1_3() {
Out.println(SubClass.sTemp);
}
输出:
SuperClass init
Subclass init
sub class temp
再看下这段代码
/**
* 类初始化1 主动引用/被动引用
*/
private static void testVersion1_1() {
Out.println(SubClass.sValue);
}
你觉得SubClass类会调用static静态代码块么?
输出:
SuperClass init
666
答案是不会,神奇的一幕,SubClass没有经过类初始化
Java“相等”判定相关方法
我们最熟悉的是instanceof方法,这个是实例对象的方法,判断当前对象是否是某个类的实例。
如果我要比较class类与类之间是否子类关系呢?要比较class对象和一个实例对象呢?
Java反射为我们提供了解决方法
/**
* 相等判断 除了 instanceof
*/
private static void testVersion2_1() {
Out.println(SuperClass.HELLO_WORLD);
}
private static void testVersion3_1() {
boolean isAssignable = SuperClass.class.isAssignableFrom(SuperClass.class);
Out.println("isAssignable = " + isAssignable);
}
private static void testVersion3_2() {
SubClass subClass = new SubClass();
boolean isInstance = SuperClass.class.isInstance(subClass);
Out.println("isInstance = " + isInstance);
}
说明:使用isInstance,isAssignableFrom是需要注意判断的逻辑关系。比如拿testVersion3_1方法举例,boolean isAssignable = SuperClass.class.isAssignableFrom(SuperClass.class);说的是SuperClass是否是SuperClass的子类啊?isAssignable的布尔值就是答案
网友评论