论文部分内容阅读
摘要:sun.misc.Unsafe类属于Java语言中基础类库的一部分,可以实现常规Java语言编程难以完成的任务,但它没有在JDK文档中公开,而且被限制使用。文中通过分析该类的设计模式,指出起限制作用的代码,并使用Java语言的反射机制获取到实例,同时给出具体代码以及使用条件。
0 引言
Java语言中,sun.misc.Unsafe类(下文简称Unsafe类)在基础类库中被大量使用,“扮演”着重要的角色。例如,java.math.BigInteger类在反序列化的过程中使用Unsafe类设置两个常量域,这两个常量域分别表示前者的符号和大小;java.util.concurrent.atomic包下的类使用Unsafe类实现原子操作。
Unsafe类的主要功能是执行底层调用,以本地代码的形式实现常规Java语言编程难以完成的任务。典型应用包括分配内存并以指针的形式进行操作、以偏移量的形式访问成员域、设置或取消线程的等待状态以及调用基于硬件的比较并交换指令等。可见,在编程过程中使用Unsafe类,有助于开发功能强大的应用。但是,Sun Micro System公司并沒有开放相应的API,而且限制开发人员获取实例。在此,笔者从该类的设计模式入手,介绍实例化过程中面临的问题,并以反射机制获取其实例。
1 Unsafe类的设计模式
面向对象的编程语言中,最重要的方法莫过于类的构造方法,它决定了实例的初始状态。Unsafe类在声明构造方法时,只使用如下一行代码:
private Unsafe() {}
由于构造方法只有一个,类在实例化时就必须执行。但是,该构造方法被声明为私有型,外界访问者不能调用,Unsafe类就必须自己调用构造方法完成初始化。代码如下:
private static final Unsafe theUnsafe = new Unsafe();
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
从代码中可以明显得出,Unsafe类采用了单例设计模式,以确保最多只有一个实例,而且外界必须使用getUnsafe方法才能得到。
2 不开放的getUnsafe方法
上文代码中,getUnsafe内部使用到sun.reflect.Reflection类的getCallerClass方法,后者也没有被公开,其返回值是Class类型,可以反映出当前虚拟机中类的调用关系,参数用于指定返回值距离调用栈顶端的距离。例如,参数值为0时返回sun.reflect.Reflection.Class,参数值为1返回Unsafe.Class,参数值为2时返回调用getUnsafe方法的类的Class实例,即引用cc。
根据JDK文档中的描述,只有在被引导类加载器加载的类、基本数据类型以及void类型的Class实例上调用getClassLoader方法,返回值才能为null。当开发人员直接调用getUnsafe方法时,cc所代表的类必然由开发人员编写,不会被引导类加载器加载,cc.getClassLoader()的返回结果必然不为null,则if语句条件成立,抛出异常,调用失败。
由此可见,getUnsafe方法只能被Java基础类库中已有的类调用,开发人员不能直接使用。
3 使用反射机制获取Unsafe实例
Java虚拟机在启动时,BootStrap加载器负责加载dt.jar中的类,Unsafe类就在其中,当开发人员的代码执行时,Unsafe类中的私有静态最终成员域theUnsafe已经被初始化,此时可以使用反射机制获取后者的引用,变相地获得Unsafe实例。需要说明的是,通过反射机制访问私有成员,需要取消Java语言访问检查。代码如下:
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class UnsafeSupport {
private static Unsafe unsafe;
static {
try{
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch(Exception e) {
System.out.println("访问失败");
}
}
public static Unsafe getInstance() {
return unsafe;
}
}
以上代码中,UnsafeSupport类封装了获取Unsafe实例的代码,通过公共静态方法getInstance返回结果,同样采用单一设计模式。在try代码块中,首先获取Unsafe类中theUnsafe成员域的Field对象;其次通过field.setAccessible(true)取消Java语言访问检查;最终,通过反射取得目标成员域的引用。
4 总结
Unsafe类封装了强大的功能,其中很多方法涉及到指针等底层操作,就像它的名称一样,会带来一定的不安全因素,这也是它不被公开的原因之一,在使用前要对代码做仔细的检查和测试。文中给出的方法需要取消Java语言访问检查,在不被允许的应用环境下会导致异常,使用前还需要进行全面的分析和评估。
参考文献:
[1]马朝晖,陈美红.Java语言导学.北京:机械工业出版社.2003,1.
[2]陈昊鹏.Java编程思想.北京:机械工业出版社.2007,6.
[3]陈昊鹏.JAVA核心技术卷II:高级特性.北京:机械工业出版社.2008,12.
[4]阎宏.Java与模式.北京:电子工业出版社.2002,10.
[5]耿祥义,张跃平.Java设计模式.北京:清华大学出版社.2009,5.
[6]O’Reilly Taiwan公司.JAVA技术手册.南京:东南大学出版社.2006,10.
0 引言
Java语言中,sun.misc.Unsafe类(下文简称Unsafe类)在基础类库中被大量使用,“扮演”着重要的角色。例如,java.math.BigInteger类在反序列化的过程中使用Unsafe类设置两个常量域,这两个常量域分别表示前者的符号和大小;java.util.concurrent.atomic包下的类使用Unsafe类实现原子操作。
Unsafe类的主要功能是执行底层调用,以本地代码的形式实现常规Java语言编程难以完成的任务。典型应用包括分配内存并以指针的形式进行操作、以偏移量的形式访问成员域、设置或取消线程的等待状态以及调用基于硬件的比较并交换指令等。可见,在编程过程中使用Unsafe类,有助于开发功能强大的应用。但是,Sun Micro System公司并沒有开放相应的API,而且限制开发人员获取实例。在此,笔者从该类的设计模式入手,介绍实例化过程中面临的问题,并以反射机制获取其实例。
1 Unsafe类的设计模式
面向对象的编程语言中,最重要的方法莫过于类的构造方法,它决定了实例的初始状态。Unsafe类在声明构造方法时,只使用如下一行代码:
private Unsafe() {}
由于构造方法只有一个,类在实例化时就必须执行。但是,该构造方法被声明为私有型,外界访问者不能调用,Unsafe类就必须自己调用构造方法完成初始化。代码如下:
private static final Unsafe theUnsafe = new Unsafe();
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
从代码中可以明显得出,Unsafe类采用了单例设计模式,以确保最多只有一个实例,而且外界必须使用getUnsafe方法才能得到。
2 不开放的getUnsafe方法
上文代码中,getUnsafe内部使用到sun.reflect.Reflection类的getCallerClass方法,后者也没有被公开,其返回值是Class类型,可以反映出当前虚拟机中类的调用关系,参数用于指定返回值距离调用栈顶端的距离。例如,参数值为0时返回sun.reflect.Reflection.Class,参数值为1返回Unsafe.Class,参数值为2时返回调用getUnsafe方法的类的Class实例,即引用cc。
根据JDK文档中的描述,只有在被引导类加载器加载的类、基本数据类型以及void类型的Class实例上调用getClassLoader方法,返回值才能为null。当开发人员直接调用getUnsafe方法时,cc所代表的类必然由开发人员编写,不会被引导类加载器加载,cc.getClassLoader()的返回结果必然不为null,则if语句条件成立,抛出异常,调用失败。
由此可见,getUnsafe方法只能被Java基础类库中已有的类调用,开发人员不能直接使用。
3 使用反射机制获取Unsafe实例
Java虚拟机在启动时,BootStrap加载器负责加载dt.jar中的类,Unsafe类就在其中,当开发人员的代码执行时,Unsafe类中的私有静态最终成员域theUnsafe已经被初始化,此时可以使用反射机制获取后者的引用,变相地获得Unsafe实例。需要说明的是,通过反射机制访问私有成员,需要取消Java语言访问检查。代码如下:
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class UnsafeSupport {
private static Unsafe unsafe;
static {
try{
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch(Exception e) {
System.out.println("访问失败");
}
}
public static Unsafe getInstance() {
return unsafe;
}
}
以上代码中,UnsafeSupport类封装了获取Unsafe实例的代码,通过公共静态方法getInstance返回结果,同样采用单一设计模式。在try代码块中,首先获取Unsafe类中theUnsafe成员域的Field对象;其次通过field.setAccessible(true)取消Java语言访问检查;最终,通过反射取得目标成员域的引用。
4 总结
Unsafe类封装了强大的功能,其中很多方法涉及到指针等底层操作,就像它的名称一样,会带来一定的不安全因素,这也是它不被公开的原因之一,在使用前要对代码做仔细的检查和测试。文中给出的方法需要取消Java语言访问检查,在不被允许的应用环境下会导致异常,使用前还需要进行全面的分析和评估。
参考文献:
[1]马朝晖,陈美红.Java语言导学.北京:机械工业出版社.2003,1.
[2]陈昊鹏.Java编程思想.北京:机械工业出版社.2007,6.
[3]陈昊鹏.JAVA核心技术卷II:高级特性.北京:机械工业出版社.2008,12.
[4]阎宏.Java与模式.北京:电子工业出版社.2002,10.
[5]耿祥义,张跃平.Java设计模式.北京:清华大学出版社.2009,5.
[6]O’Reilly Taiwan公司.JAVA技术手册.南京:东南大学出版社.2006,10.