Integer 的 valueOf 方法 与 常量池(对 String Pool 的部分理解)
举例:
public class Test {
@org.junit.Test
public void intTest() {
Integer t1 = 128;
Integer t2 = 127;
}
}
使用 javap -c 查看字节码
public void intTest();
Code:
0: sipush 128
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_1
7: bipush 127
9: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
12: astore_2
13: return
说明:
造成两种区别对待的等价方式,在于 valueOf 方法的实现:(low 与 high 分别是 -128 与 127),底层原理:IntegerCache 本质是编译期常量 static final Integer cache[], 一个 Integer 数组。
public static Integer valueOf ( int i){
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
测试:
Integer t1 = new Integer(128); Integer t2 = 128; Integer t3 = 128; int t4 = 128; Integer t5 = 127; Integer t6 = 127; Integer t7 = new Integer(127);
结果:
System.out.println(t1 == t2); // false 因为 t2/t3 等价于 new Integer(128),所以 t1、t2、t3 互不相等。 System.out.println(t1 == t4); // true 因为与 int 运算时,会进行自动拆箱。所以 t1、t2、t3 与 t4 相等。 System.out.println(t5 == t6); // true 因为 t5/t6 其值默认指向常量池中的 127 常量。 System.out.println(t5 == t7); //false 因为 t5 是常量,而 t7 是Object实例。
拓展:
与 Integer 类似,String 有两种创建方式。对于使用字面量赋值方式。JVM为了节省空间,会首先查找JVM中是否有对应的字符串常量。如果已经存在,则直接返回该常量或字符串实例对象的地址引用,而无需重新创建对象。对象new创建方式,JVM将添加字面量常量和创建字符串实例并返回实例引用。
注意:
1>String 与 Integer 包装类都是不可变的:
private final int value; // Integer.java private final char value[]; // String.java
2>常量池的实现有多种,String 与 Integer 的实现方式有区别。 Integer 的常量池直接是 Integer 数组,而 String 在 Java7后移动到堆空间(共享),底层是由 C++ 中的StringTable(类似固定容量的 HashMap)实现,每个 bucket 存储 相同 hash 对应的字符串 列表,效率更高(参考)。
测试1
String a = "we";
String a2 = new String("we");
String b = "we";
String c = "we";
System.out.println(a ==a2.intern());
结果
true
测试2
String a = "1";
String b = "1";
int aHashCode = System.identityHashCode(a);
int bHashCode = System.identityHashCode(b);
System.out.println("\na:" + a + "\nb:" + b);
System.out.print("\naHashCode:" + aHashCode + "\nbHashCode:" + bHashCode); char[] valueChar = new char[0];
try {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
valueChar = (char[]) value.get(b);
} catch (Exception e) {
e.printStackTrace();
}
valueChar[0] = '2';
String c = "1";
String d = "2";
int cHashCode = System.identityHashCode(c);
int dHashCode = System.identityHashCode(d);
System.out.print("\na:" + a + "\nb:" + b + "\nc:" + c + "\nd:" + d);
System.out.print("\naHashCode:" + aHashCode + "\nbHashCode:" + bHashCode + "\ncHashCode:" + cHashCode + "\ndHashCode:" + dHashCode);
结果
a:1
b:1 aHashCode:476800120
bHashCode:476800120
a:2
b:2
c:2
d:2
aHashCode:476800120
bHashCode:476800120
cHashCode:476800120
dHashCode:1254526270
注意:
Object对象 的 hashCode() 方法与 System.identityHashCode(o) 方法返回结果是一致的。返回相应的 hash码。但 String 类重写了 hashCode() 方法,它是根据以下公式计算:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
导致的 String 对象的 hashCode 与 System.identityHashCode 返回的原值不相等。
为什么要重写 hashCode 方法呢?
1.String Pool 常量池的实现,底层类似 HashMap,一个 hash 对应多个相同字符串。
2.作为HashSet、HashMap等容器的 key 被使用。这些容器依赖于 hashCode 方法的实现(先使用 hashcode 比较,再使用 equals 比较)。
3.根据规范,如果根据 equals(Object) 方法,两个对象是相等(不是相同)的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。所以,根据字符串的 equals 方法的实现(根据字符串值判断相等),hashCode 也需要根据字符串值来返回哈希,以确保对于两个相等的值返回相同的 hashCode。此外,也说明 hashCode 不能是随机的数字,一定要按照相应的实现确立。
不可变的引用类型
在不使用常量池的情况下,new Integer() 返回的是一个引用类型,但是对这个引用实例的修改却并没有改变原来的引用绑定的值,原因是每次修改其实是在重新创建对象并重新绑定。这就是 primitive 类型的实现方式之一。
Integer 的 valueOf 方法 与 常量池(对 String Pool 的部分理解)的更多相关文章
- Java String类相关知识梳理(含字符串常量池(String Pool)知识)
目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...
- 基本数据类型的常量池与String类型常量池解析
抛出样例: Integer a1 = new Integer(123); Integer a2 = new Integer(123); System.out.print ...
- 资深架构师教你String 常量池、 String.itern()
什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实例变量和局部变量,分别表示三种类型的常量. Class文件中的常量池 在Class文件结 ...
- Java字符串池(String Pool)深度解析
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 在工作中,String类是我们使用频率非常高的一种对象类型.JVM为了提升性能和减少内存开销,避免字符串的重复创建,其维护了一块特殊的内存 ...
- Java字符串池(String Pool)深度解析(转)
出自 http://www.cnblogs.com/fangfuhai/p/5500065.html 在工作中,String类是我们使用频率非常高的一种对象类型.JVM为了提升性能和减少内存开销,避 ...
- Java的Integer常量池和String常量池
1.Integer的常量池 看下面一段代码: package cn.qlq.test; public class ArrayTest { public static void main(String[ ...
- JVM体系结构之七:持久代、元空间(Metaspace) 常量池==了解String类的intern()方法、常量池介绍、常量池从Perm-->Heap
一.intern()定义及使用 相信绝大多数的人不会去用String类的intern方法,打开String类的源码发现这是一个本地方法,定义如下: public native String inter ...
- 常量池之String.intern()方法
JDK7将String常量池从Perm区移动到了Java Heap区.在JDK1.6中,intern方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中的实例.但是在JDK1.7以后,Str ...
- [一]class 文件浅析 .class文件格式详解 字段方法属性常量池字段 class文件属性表 数据类型 数据结构
前言概述 本文旨在讲解class文件的整体结构信息,阅读本文后应该可以完整的了解class文件的格式以及各个部分的逻辑组成含义 class文件包含了java虚拟机指令集 和 符号表 以及若 ...
随机推荐
- [Web 前端] mobx教程(二)-mobx主要概念
cp from : https://blog.csdn.net/smk108/article/details/84960159 通过<Mobx教程(一)-Mobx简介>我们简单理解了Mob ...
- [Python设计模式] 第25章 联合国维护世界和平——中介者模式
github地址:https://github.com/cheesezh/python_design_patterns 题目背景 联合国在世界上就是中介者的角色,各国之间的关系复杂,类似不同的对象和对 ...
- ionic生成全尺寸icon和splash
http://www.jianshu.com/p/eda363eb28d3 重新添加platform --no-resources可以禁止重新生成icon和splash ionic cordova p ...
- cmd命令行的ping用法
1.打开cmd 2.ping 域名 (如:ping baidu.com) 3.输出结果 C:\WINDOWS\system32>ping baidu.com 正在 Ping baidu.c ...
- 读取PBOC电子现金指令流
该指令流仅适用于T=0协议卡片. 终端对IC卡的响应: 60 须要额外的工作等待时间,说明IC卡端数据还未处理好. 61 发送GET RESPONSE命令取应答数据 6C 加上取字节数,命令重发 ...
- mysql多实例配置下,用脚本启动mysql时,出现Please read "Security" section of the manual to find out how to run mysqld as root!
[root@localhost 3308]# mysqld stop170414 0:35:28 [Note] --secure-file-priv is set to NULL. Operation ...
- WINDOWS API ——GETFILETIME——获取文件时间
GetSystemTime(LPSYSTEMTIME lpSystemTime)得到系统时间,这个时间是标准的UTC时间,也就是没有包含任何时区的时间的GetLocalTime(LPSYSTEMTIM ...
- TensorFlow+Keras 02 深度学习的原理
1 神经传递的原理 人类的神经元传递及其作用: 这里有几个关键概念: 树突 - 接受信息 轴突 - 输出信息 突触 - 传递信息 将其延伸到神经元中,示意图如下: 将上图整理成数学公式,则有 y = ...
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- 游戏开发中IIS常见支持MIME类型文件解析
游戏开发中IIS常见支持MIME类型文件解析 .apkapplication/vnd.android .ipaapplication/vnd.iphone .csbapplication/octet- ...