Java 9中如何对IntegerCache进行修改详解

 更新时间:2017年12月03日 10:28:35   作者:Peter Verhas  
这篇文章主要给大家介绍了关于Java 9中如何对IntegerCache进行修改的相关资料,文中通过示例代码介绍的非常详细,对大家学习或使用java9具有一定的参考学习价值,需要的朋友们下面随着小编来一起看看吧。

在开始本文的正文之前,我们下面来看看下面这段代码:

Java中Integer类的IntegerCache的作用

包名:java.lang

文件名:Integer.java

方法名:IntegerCache

方法的代码如下:

private static class IntegerCache { 
static final int high; 
static final Integer cache[]; 

static { 
final int low = -128; 

// high value may be configured by property 
int h = 127; 
if (integerCacheHighPropValue != null) { 
// Use Long.decode here to avoid invoking methods that 
// require Integer's autoboxing cache to be initialized 
int i = Long.decode(integerCacheHighPropValue).intValue(); 
i = Math.max(i, 127); 
// Maximum array size is Integer.MAX_VALUE 
h = Math.min(i, Integer.MAX_VALUE - -low); 
} 
high = h; 

cache = new Integer[(high - low) + 1]; 
int j = low; 
for(int k = 0; k < cache.length; k++) 
cache[k] = new Integer(j++); 
} 

private IntegerCache() {} 
} 

我们在代码中看到,low为-128,high为127,这样的话,在Java编程中,如果要使用-128——127这个区间的对象的话,是直接使用这个Cache中的对象的。

上面是段简单的介绍,帮助大家理解下IntegerCache,下面开始本文的正文:

引言

5 年前,我在 Hungarian 上发表了一篇关于 JDK 中如何改变 IntegerCache 的文章。这种做法其实是深入进 Java 运行时,在实际并没有使用的场景。当你开发这些研究代码时,你才能更好的理解反射是如何工作的,以及 Integer 类是如何实现的。

Integer 类有一个私有的嵌套内,名为 IntegerCache ,包含了值从 -127 到 128 的 Integer 对象。

当代码需要从 int 类型封箱成 Integer 对象,而且值在这个范围内时,那么 Java 运行时会使用这个缓存,而不是创建一个新的 Integer 对象。这主要是处于性能优化的考虑,我们必须牢记在心的是很多 int 值在程序中很多时候都处于这个范围内(例如数组的下标索引)。

这样做的副作用是,很多时候,使用等号操作符来比较两个 Integer 对象时,只要值在范围内都是有效的。这在单元测试中很典型。而在运行模式下,当数值大于 128 时,代码执行会失败。

使用反射来访问 IntegerCache 类时会导致一些奇怪的副作用,注意这会影响到整个的 JVM。如果一个 Servlet 重新定义了小的 Integer 缓存值,那么所有运行在同一个 Tomcat 下的其他 Servlet 也遭遇同样问题。

Lukas EderSitepoint 上面还有其他一些文章描述此问题。

现在我已经开始在玩弄 Java 9 的早期发布版本,在我脑海里我一直要做的就是对新的 Java 版本进行各种实验。在开始之前,让我们先看看在 Java 8 中的做法。

在 Lukas 的文章中,我将他的示例代码贴在此处:

import java.lang.reflect.Field;
import java.util.Random;

public class Entropy {
 public static void main(String[] args)
 throws Exception {

 // Extract the IntegerCache through reflection
 Class << ? > clazz = Class.forName(
  "java.lang.Integer$IntegerCache");
 Field field = clazz.getDeclaredField("cache");
 field.setAccessible(true);
 Integer[] cache = (Integer[]) field.get(clazz);

 // Rewrite the Integer cache
 for (int i = 0; i < cache.length; i++) {
  cache[i] = new Integer(
  new Random().nextInt(cache.length));
 }

 // Prove randomness
 for (int i = 0; i < 10; i++) {
  System.out.println((Integer) i);
 }
 }
}

此代码通过反射方式访问 IntegerCache,然后使用随机值对缓存进行填充(淘气!)。

我们尝试在 Java 9 中执行相同的代码,别指望有什么乐趣。当有人尝试违反它时会发现 Java 9 的限制更加严格。

Exception in thread "main" java.lang.reflect.InaccessibleObjectException:
 Unable to make field static final java.lang.Integer[]
 java.lang.Integer$IntegerCache.cache
 accessible: module java.base does not "opens java.lang" to unnamed module @1bc6a36e

程序抛出了异常,这个异常在 Java 8 中是不会发生的。相当于说对象是不可范文的,因为 java.base 模块的原因,这是 JDK 的组成部分,在每个 Java 程序启动时被自动的导入,不允许打开未命名的模块。这个异常是在当我们尝试设置字段可访问属性时抛出的。

我们在 Java 8 可轻松访问的对象,现在在 Java 9 中不能访问了,因为新的模块系统对此进行了保护。代码只能访问字段、方法和其他用反射能访问的信息,只有当类在相同的模块中,或者模块打开了包用于反射方式访问。这个可以通过  module-info.java 模块定义文件来实现:

module myModule {
 exports com.javax0.module.demo;
 opens com.javax0.module.demo;
}

这个模块 java.base 不用不用自行打开用于反射访问,特别是未命名模块更不需要。如果我们创建了一个模块并进行命名,那么错误信息将包含模块的名称。

我们能否在程序里打开模块呢? java.lang.reflect.Module 模块有一个 addOpens 的方法可以做到。

可行吗?

对开发者来说坏消息是:不可行。它只能在另外一个模块中打开一个模块中的包,并且包已经在该模块中通过调用这个方法打开过。这种方法只能让模块传递给另外的模块权利,前提是另外的模块已经以某种方式打开过相同的包,而不能打开没有打开过的包(译者注:很难理解,不是吗?)。

但与此同时好消息是:Java 9 不像 Java 8 那么容易被破解。最少这个漏洞被关闭了。看起来 Java 开始往专业级发展,而不仅仅是个玩具(译者注:谁说 Java 是个玩具了?)。不久的将来你可以非常严肃的将 RPG 和 COBOL 语言的项目迁移到 Java 上了。(很抱歉,我开玩笑的)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

本文翻译自:https://dzone.com/articles/hacking-the-integercache-in-java-9

相关文章

  • SpringBoot使用AOP实现防重复提交功能

    SpringBoot使用AOP实现防重复提交功能

    这篇文章主要为大家详细介绍了SpringBoot如何使用AOP实现防重复提交功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-03-03
  • java模拟客户端向服务器上传文件

    java模拟客户端向服务器上传文件

    这篇文章主要为大家详细介绍了java模拟客户端向服务器上传文件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • Java ArrayList如何实现生成不重复随机数

    Java ArrayList如何实现生成不重复随机数

    这篇文章主要介绍了Java ArrayList如何实现生成不重复随机数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • IDEA Eval Reset 使用方法汇总

    IDEA Eval Reset 使用方法汇总

    本文给大家介绍了IDEA Eval Reset 使用方法,安装插件包括离线安装方式和在线安装方式,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2023-10-10
  • SocketIo+SpringMvc实现文件的上传下载功能

    SocketIo+SpringMvc实现文件的上传下载功能

    这篇文章主要介绍了SocketIo+SpringMvc实现文件的上传下载功能,socketIo不仅可以用来做聊天工具,也可以实现局域网。文中给出了实现代码,需要的朋友可以参考下
    2018-08-08
  • 解决Error:(5,55)java:程序包org.springframework.cloud.netflix.eureka.server不存在问题

    解决Error:(5,55)java:程序包org.springframework.cloud.netflix.eure

    这篇文章主要介绍了解决Error:(5,55)java:程序包org.springframework.cloud.netflix.eureka.server不存在问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 详解高性能缓存Caffeine原理及实战

    详解高性能缓存Caffeine原理及实战

    Caffeine是基于Java 8开发的,提供了近乎最佳命中率的高性能本地缓存组件,Spring5开始不再支持Guava Cache,改为使用Caffeine。Caffeine提供的内存缓存使用参考Google guava的API
    2021-06-06
  • 简单谈谈Java中String类型的参数传递问题

    简单谈谈Java中String类型的参数传递问题

    这篇文章主要介绍了简单谈谈Java中String类型的参数传递问题的相关资料,需要的朋友可以参考下
    2015-12-12
  • Springmvc异常处理器及拦截器实现代码

    Springmvc异常处理器及拦截器实现代码

    这篇文章主要介绍了Springmvc异常处理器及拦截器实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • spring @Lazy延迟注入的逻辑实现

    spring @Lazy延迟注入的逻辑实现

    有时候我们会在属性注入的时候添加@Lazy注解实现延迟注入,今天咱们通过阅读源码来分析下原因,感兴趣的可以了解一下
    2021-08-08

最新评论