对java开发相关知识感兴趣的小伙伴们,还记得Unsafe魔法类吗?以大家对Unsafe的了解,认为Unsafe魔法类应该有什么作用呢?它应该如何去使用呢?有兴趣的朋友可以跟小编一起来看看吧。
首先Unsafe类为我们提供了访问底层的机制,这种机制呢,是仅仅提供了java核心类库的使用,而不应该去被普通用户使用的。
不过呢,为了更好地去了解java的生态体系,我们也应该去学习它了解它,不说要深入到底层的C/C++代码,但需要能了解它的基本功能。
那么获取Unsafe的实例如下:
查看Unsafe的源码我们会发现它提供了一个叫getUnsafe()的静态方法。
@CallerSensitivepublicstaticUnsafegetUnsafe() { Class var0 = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(var0.getClassLoader())) { thrownewSecurityException("Unsafe"); } else { returntheUnsafe; } }
但是呢,如果直接调用这个方法就会抛出一个SecurityException的异常,这是因为Unsafe仅仅供java内部类使用,外部类都不应该使用它。
那我们就没有方法了吗?其实当然不是啦,我们有反射啊,查看源码,我们会发现它有一个属性叫theUnsafe,所以我们直接通过反射拿到它就好了。
publicclassUnsafeTest { publicstaticvoidmain(String[] args) throws NoSuchFieldException, IllegalAccessException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafeunsafe = (Unsafe) f.get(null); } }
我们使用Unsafe实例化一个类。假如说我们有一个简单的类如下:
classUser { intage; publicUser() { this.age = 10; } }
要是我们通过构造方法实例化这个类,age属性就会返回10。
User user1 = newUser(); // 打印10 System.out.println(user1.age);
要是我们调用Unsafe来实例化呢?
User user2 = (User)unsafe.allocateInstance(User.class); // 打印0 System.out.println(user2.age);
age将返回0,因为Unsafe.allocateInstance()方法只会给对象分配内存,并且不会调用构造方法,所以这里只会返回int类型的默认值0。
可以修改私有字段的值。使用Unsafe的putXXX()方法,我们其实可以修改任意私有字段的值。
publicclassUnsafeTest { publicstaticvoidmain(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafeunsafe = (Unsafe) f.get(null); User user = newUser(); Field age = user.getClass() .getDeclaredField("age"); unsafe.putInt(user, unsafe.objectFieldOffset(age), 20); // 打印20System.out.println(user.getAge()); } } classUser { privateintage; publicUser() { this.age = 10; } publicintgetAge() { returnage; } }
一旦呢我们通过了反射调用得到字段age,我们就可以使用Unsafe将其值更改为任何其他int值。
抛出了checked异常。我们知道如果代码抛出了checked异常,要不就使用try...catch去捕获它了,要不就在方法签名上定义这个异常,但是,通过Unsafe其实可以抛出一个checked异常,同时却不用捕获或在方法签名上去定义它。
// 使用正常方式抛出IOException需要定义在方法签名上往外抛 publicstaticvoidreadFile() throwsIOException { thrownewIOException(); } // 使用Unsafe抛出异常不需要定义在方法签名上往外抛 publicstaticvoidreadFileUnsafe() { unsafe.throwException(newIOException()); }
使用堆外内存。如果说进程在运行过程中JVM上的内存不足了,这就会导致频繁的进行GC。那么理想的情况下,我们可以考虑使用堆外内存,这是一块不受JVM管理的内存。
在使用Unsafe的allocateMemory()方法时,我们可以直接在堆外分配内存,这可能非常的有用,但是我们得要记住,这个内存是不受JVM管理的,所以我们要调用freeMemory()方法手动去释放它。
假如说我们要在堆外创建一个较大的int数组,我们可以使用allocateMemory()方法去实现:
classOffHeapArray { // 一个int等于4个字节 private static final int INT = 4; privatelongsize; privatelongaddress; privatestaticUnsafeunsafe; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } // 构造方法,分配内存 publicOffHeapArray(longsize) { this.size = size; // 参数字节数address =unsafe.allocateMemory(size * INT); } // 获取指定索引处的元素publicintget(longi){returnunsafe.getInt(address + i * INT); } // 设置指定索引处的元素publicvoidset(longi,intvalue){unsafe.putInt(address + i * INT,value); } // 元素个数publiclongsize(){ returnsize; } // 释放堆外内存publicvoidfreeMemory(){unsafe.freeMemory(address); } } //在构造方法中调用allocateMemory() 分配内存, 在使用完成后调用freeMemory() 释放内存。
使用方式如下所示:
OffHeapArray offHeapArray = newOffHeapArray(4); offHeapArray.set(0, 1); offHeapArray.set(1, 2); offHeapArray.set(2, 3); offHeapArray.set(3, 4); offHeapArray.set(2, 5); // 在索引2的位置重复放入元素 int sum = 0; for (inti = 0; i < offHeapArray.size(); i++) { sum += offHeapArray.get(i); } // 打印12 System.out.println(sum); offHeapArray.freeMemory();
最后,一定要记得调用freeMemory()将内存释放回操作系统。
好了,以上就是有关Unsafe魔法类的所有内容了,还想了解更多java架构师的相关信息吗?如果答案是肯定的,那么就赶快来关注本站消息吧。