<强>问题强>
(1)不安全是什么?
(2)不安全只有中科院的功能吗?
(3)不安全为什么是不安全的吗?
(4)怎么使用安全吗?
<强>简介强>
不安全为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。
但是,为了更好地了解java的生态体系,我们应该去学习它,去了解它,不求深入到底层的C/c++代码,但求能了解它的基本功能。
<强>获取不安全的实例强>
查看不安全的源码我们会发现它提供了一个getUnsafe()的静态方法。
<>之前 @CallerSensitive 公共静态安全getUnsafe () { 类var0=Reflection.getCallerClass (); 如果(! VM.isSystemDomainLoader (var0.getClassLoader ())) { 把新SecurityException(“不安全”); 其他}{ 返回theUnsafe; } }但是,如果直接调用这个方法会抛出一个SecurityException异常,这是因为不安全仅供java内部类使用,外部类不应该使用它。
那么,我们就没有方法了吗?
当然不是,我们有反射啊!查看源码,我们发现它有一个属性叫theUnsafe,我们直接通过反射拿到它即可。
<>之前 公开课UnsafeTest { 公共静态void main (String [] args)抛出NoSuchFieldException, IllegalAccessException { 场f=Unsafe.class.getDeclaredField (“theUnsafe”); f.setAccessible(真正的); 不安全不安全=(不安全)f.get(空); } }<强>使用安全实例化一个类强>
假如我们有一个简单的类如下:
<>之前 类用户{ int年龄; 公共用户(){ 这一点。年龄=10; } }如果我们通过构造方法实例化这个类,时代属性将会返回10。
<>之前 用户user1=新用户();//打印10 System.out.println (user1.age);如果我们调用不安全来实例化呢?
<>之前 用户user2=(用户)unsafe.allocateInstance (User.class);//打印0 System.out.println (user2.age);年龄将返回0,因为Unsafe.allocateInstance()只会给对象分配内存,并不会调用构造方法,所以这里只会返回int类型的默认值0。
<>强修改私有字段的值强>
使用不安全的putXXX()方法,我们可以修改任意私有字段的值。
<>之前 公开课UnsafeTest { 公共静态void main (String [] args)抛出NoSuchFieldException, IllegalAccessException InstantiationException { 场f=Unsafe.class.getDeclaredField (“theUnsafe”); f.setAccessible(真正的); 不安全不安全=(不安全)f.get(空); 用户用户=新用户(); 场年龄=user.getClass () .getDeclaredField(“年龄”); 不安全的。putInt(用户、unsafe.objectFieldOffset(年龄),20);//打印20 System.out.println (user.getAge ()); } } 类用户{ 私人int年龄; 公共用户(){ 这一点。年龄=10; } 公共int getAge () { 返回年龄; } }一旦我们通过反射调用得到字段的年龄,我们就可以使用安全将其值更改为任何其他int值。(当然,这里也可以通过反射直接修改)
<强>抛出检查异常强>
我们知道如果代码抛出了检查异常,要不就使用try…catch捕获它,要不就在方法签名上定义这个异常,但是,通过不安全我们可以抛出一个检查异常,同时却不用捕获或在方法签名上定义它。
<>之前//使用正常方式抛出IOException需要定义在方法签名上往外抛 公共静态孔隙readFile()抛出IOException { 把新IOException (); }//使用安全抛出异常不需要定义在方法签名上往外抛 公共静态孔隙readFileUnsafe () { 不安全的。throwException(新IOException ()); }<强>使用堆外内存强>
如果进程在运行过程中JVM上的内存不足了,会导致频繁的进行GC。理想情况下,我们可以考虑使用堆外内存,这是一块不受JVM管理的内存。
使用不安全的allocateMemory()我们可以直接在堆外分配内存,这可能非常有用,但我们要记住,这个内存不受JVM管理,因此我们要调用freeMemory()方法手动释放它。
假设我们要在堆外创建一个巨大的int数组,我们可以使用allocateMemory()方法来实现:
<>之前 类OffHeapArray {//一个int等于4个字节 私有静态最终int int=4; 私人长尺寸; 私人长地址; 私有静态不安全不安全; 静态{ 尝试{ 场f=Unsafe.class.getDeclaredField (“theUnsafe”); f.setAccessible(真正的); 不安全=(不安全)f.get(空); }捕捉(NoSuchFieldException e) { e.printStackTrace (); }捕捉(IllegalAccessException e) { e.printStackTrace (); } }//构造方法,分配内存 公共OffHeapArray(长尺寸){ 这一点。大?大小;//参数字节数 地址=不安全。allocateMemory(大小* INT); }//获取指定索引处的元素 公共int(我){ 返回不安全。getInt(地址+ i * INT); }//设置指定索引处的元素 公共空集(长我,int值){ 不安全的。putInt(地址+ i * INT值); }//元素个数 公共长尺寸(){ 返回大小; }//释放堆外内存 公共空间freeMemory () { unsafe.freeMemory(地址); } }