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虚拟机指令集 和 符号表 以及若 ...
随机推荐
- sshd服务
SSHD服务 介绍:SSH 协议:安全外壳协议.为 Secure Shell 的缩写.SSH 为建立在应用层和传输层基础上的安全协议. 作用 sshd服务使用SSH协议可以用来进行远程控制, 或在计算 ...
- python测试开发django-55.xadmin使用markdown文档编辑器(django-mdeditor)
前言 markdown是一个非常好的编辑器,用过的都说好,如果搭建一个博客平台的话,需要在后台做文章编辑,可以整合一个markdown的文本编辑器. github上关于django的markdown插 ...
- JS数字指定长度不足前补零的实现
问题描述: 要求输出的数字长度是固定的,如长度为2,数字为1,则输出01,即不够位数就在之前补足0. 解决方法: 方法1 function fn1(num, length) { ret ...
- [Web 前端] mobx教程(二)-mobx主要概念
cp from : https://blog.csdn.net/smk108/article/details/84960159 通过<Mobx教程(一)-Mobx简介>我们简单理解了Mob ...
- jquery append 和appendTo
原文: https://www.cnblogs.com/stitchgogo/p/5721551.html ---------------------------------------------- ...
- [CSS] Useful CSS tool for Web designer and developer
1. Color Picker (Chrome) You might know how to use color picker in Chrome, recently there is a featu ...
- servlet的xx方式传值中文乱码
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOEx ...
- Spring HttpInvoker 从实战到源码追溯
Spring HttpInvoker 作为 Spring 家族中老牌远程调用模型,深受开发者喜爱. 其主要目的是来执行基于 HTTP 的远程调用(轻松穿越防火墙),并使用标准的 JDK 序列化机制. ...
- maven在Idea建立工程,运行出现Server IPC version 9 cannot communicate with client version 4错误
问题的根源在于,工程当中maven dependencies里面的包,有个hadoop-core的包,版本太低,这样,程序里面所有引用到org.apache.hadoop的地方,都是低版本的,你用的是 ...
- 一个会学习(观察->活学->求变)的人,在任何领域都能变得强大无比
开始今天的话题之前,我说个小故事. 很早以前有一部美剧,叫<Hero>. 大概讲的是正反两派都是一群有超能力的人,彼此为了某个巨大的阴谋互相撕逼了十多集.虽然剧情很老套,但是让 ...