深入解析常量池与装拆箱机制
2019独角兽企业重金招聘Python工程师标准>>>
常量池
常量 可分为 字面常量(也称为直接常量)和 符号常量。
字面常量: 是指在程序中无需预先定义就可使用的数字、字符、boolen值、字符串等。简单的说,就是确定值的本身。如 10,2L,2.3f,3.5,“hello”,'a',true、false、null 等等。
符号常量:是指在程序中用标识符预先定义的,其值在程序中不可改变的量。如 final int a = 5;
常量池: 常量池引入的 目的 是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。这是一种 享元模式 的实现。
java常量池
Java的常量池可以细分为以下三类:
- class文件常量池(又称静态常量池,编译阶段)
- 运行时常量池(又称动态常量池,运行阶段)
- 字符串常量池(全局的常量池)
class文件常量池
class文件常量池,也被称为 静态常量池 ,它是.class文件所包含的一项信息。用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。
字面量: 就是上面所说的字面常量。
符号引用: 是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用 一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。符号引用可以看作是一个虚拟地址,只有在JVM加载完类,确认了字面量的地址,才会将 符号引用 换成 直接引用。一般包括下面三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
运行时常量池
运行时常量池,又称为 动态常量池 ,是JVM在完成加载类之后将class文件中常量池载入到内存中,并保存在方法区中。也就是说,运行时常量池中的常量,基本来源于各个class文件中的常量池。 运行时常量池相对于CLass文件常量池的另外一个重要特征是具备 动态性 ,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。
jvm在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,也就是说,每个class对应运行时常量池中的一个独立空间,每个class文件存放的位置互不干扰。而在解析阶段,就会将符号引用替换成对应的直接引用。
字符串常量池(string pool也有叫做string literal pool)
字符串常量池存储的就是字符串的字面常量。详细一点,字符串常量池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。)。 在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。
运行时常量池 与 字符串常量池 的区别
- 字符串常量池是位于运行时常量池中的。
运行时常量池 与 字符串常量池 在HotSpot的JDK1.6以前,都是放在方法区的,JDK1.7就将字符串常量池移到了堆外内存中去。运行时常量池 为每一个Class文件的常量池提供一个运行时的内存空间;而字符串常量池则为所有Class文件的String类型的字面常量维护一个公共的常量池,也就是Class文件的常量池加载进运行时常量池后,其String字面常量的引用指向要与字符串常量池的维护的要一致。
自动装箱拆箱机制 与 缓存机制
装箱: 可以自动将基本类型直接转换成对应的包装类型。
拆箱: 自动将包装类型转换成对应的基本类型值;
public class Test {public static void main(String[] args) {//装箱Integer b = 5;//拆箱int c = b+5;}
}
编译以后
public class Test {public Test() {}public static void main(String[] args) {Integer b = Integer.valueOf(5);int c = b.intValue() + 5;}
}
所谓装箱拆箱并没有多厉害,还是要通过调用Integer.valueOf()(装箱) 和 Integer.initValue()(拆箱)来完成的。也就是说,自动装箱拆箱机制是一种语法简写,为了方便程序员,省去了手动装箱拆箱的麻烦,变成了自动装箱拆箱。
判别是装箱还是拆箱
Integer x = 1;//装箱
Integer y = 2;//装箱
Integer z = x+y;//先拆箱再装箱
Integer类型是引用类型,所以不能参与加法运算,必须拆箱成基本类型来求和,在装箱成Integer。
包装类的缓存机制
public class Test {public static void main(String[] args) {Integer a = 5;Integer b = 5;Integer c = 129;Integer d = 129;System.out.println("a==b "+ (a == b));System.out.println("c==d "+ (c == d));}
}
输出结果
a == b true
c == d false
咦,为什么是a和b所指向的是一个对象呢?难道JVM在类加载时也为包装类型维护了一个常量池?如果是这样,为什么变量c、d的地址不一样。事实上,JVM确实没有为包装类维护一个常量池。变量a、b、c、d是由装箱得到的,根据前面所说的,装箱其实是编译器自动添加了Integer.valueOf() 方法。秘密应该就在这个方法内,那么我们看一下Integer.valueOf()的源代码吧,如下:
public static Integer valueOf(int i) {assert IntegerCache.high >= 127;if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}
private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);}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() {}}
IntegerCache.cache是一个final的Integer数组,这个数组存储的Integer对象元素的值范围是[-128,127]。而且这个数组的初始化代码是包裹在static代码块中,也就是说IntegerCache.cache数组的初始化是在类加载时完成的。
再看回上面的例子,变量a和b的使用的基本类型值为5,没有超出[-128,127]的范围,所以就使用缓存数组中的元素,所以a、b的地址是一样的。而c、d使用的基本类型值为129,超出缓存范围,所以都是各自在堆上创建一个对,地址自然就不一样了。
包装类缓存总结与补充
- 包装类与String类很相似,都是非可变类,即一经创建后,便不可以修改。正因为这种特性,两者的对象实例在多线程下是安全的,不用担心异步修改的情况,这为他们实现共享提供了很好的保证,只需创建一个对象共享便可。
- 包装类的共享实现并不是由JVM来维护一个常量池,而是使用了缓存机制(数组),而且这个缓存是在类加载时完成初始化,并且不可再修改。
- 包装类的数组缓存范围是有限,只缓存基本类型值在一个字节范围内,也就是说 -128 ~ 127。(Character的范围是 0~127)
- 目前并不是所有包装类都提供缓存机制,只有Byte、Character、Short、Integer 4个包装类提供,Long、Float、Double 不提供。
转载于:https://my.oschina.net/jiansin/blog/1931172
深入解析常量池与装拆箱机制相关推荐
- java基础(八) 深入解析常量池与装拆箱机制
###引言 本文将介绍常量池 与 装箱拆箱机制,之所以将两者合在一起介绍,是因为网上不少文章在谈到常量池时,将包装类的缓存机制,java常量池,不加区别地混在一起讨论,更有甚者完全将这两者视为一个整体 ...
- java 自动装拆箱
title: "java 自动装拆箱" tags: Java 将基本数据类型封装成对象的过程叫做装箱(boxing),反之基本数据类型对应的包装类转换为基本数据类型的过程叫做拆箱( ...
- java支持的数据类型有哪些_Java支持的数据类型有哪些?什么时候自动装拆箱?...
java中的8种基本数据类型:boolean byte char short int float double long 自动拆装箱的问题引入: 由于在一开始学习java的时候,"万物皆对象 ...
- java unbox_java base-05-Box UnBox 自动装拆箱
java 语言的设计 java 为什么有基本类型 作为一门 OO 语言,java 为什么要保留基本类型呢. 个人的理解是,出于工程学上的考虑. 基本类型的内存占用,比对象要小得多. 基本对象 基本数据 ...
- 1.3.1 Object类 equals,hashMode,toString重写, 包装类的装拆箱, 数学处理类
&&&&&总结&&&&& 1.常用的包(熟悉) lang , util , io , net , sql ... 2.O ...
- java进阶--深入理解Java自动装箱拆箱机制(Autoboxing and unboxing)
1.自动装箱与拆箱的定义 装箱就是自动将基本数据类型转换为包装器类型:拆箱就是 自动将包装器类型转换为基本数据类型. Java中的数据类型分为两类:一类是基本数据类型,另一类是引用数据类型.如下图: ...
- 深入理解Java自动装箱拆箱机制(Autoboxing and unboxing)
1.自动装箱与拆箱的定义 装箱就是自动将基本数据类型转换为包装器类型(int-->Integer): 拆箱就是自动将包装器类型转换为基本数据类型(Integer-->int). Java中 ...
- JavaDay16-P104-P142—二分法查找—String类—常量池—包装—装箱、拆箱—NumberFormatException
1.二分法查找--建立在数组排序之上 案例程序: class BinarySearch{public static void binarySearch(int[] arr,int des) {Arra ...
- 类文件解析003-解析常量池
本文我们来介绍ClassFileParser 解析常量池的过程.解析常量池的过程是在ClassFileParser::parseClassFile 通过parse_constant_pool 来实现的 ...
最新文章
- Redis面试连环问:集群、复制以及与其他NOSQL数据库的区别?
- BZOJ1858 [Scoi2010]序列操作 线段树
- leetcode2 两数相加
- 启动rabbitmq,提示ERROR: node with name rabbit already running on localhost(亲测)
- Linux内存page,【原创】(十四)Linux内存管理之page fault处理
- 在C#里调用C++的dll时需要注意的一些问题转
- Android listview 中嵌套 listview
- 7500 cpuz跑分 i5_核心硬盘 i5 7500性能测试_DIY攒机酷品测试-中关村在线
- Windows版本nginx
- Ubuntu上可使用的15个桌面环境
- 如何才能不被社会淘汰?一个程序员的七年之痒
- delphi 2010之如何快速开发原生ActiveX控件
- EXCEL功能之Excel表格边框设置
- 物联网操作系统Zephyr(入门篇)之1.0 Zephyr简介
- 将VMware与SoftICE基于网络的远程调试功能相结合
- 摩斯密码基础知识介绍
- 乔治城大学计算机专业,美国乔治城大学计算机
- 看完这篇就够了,mac版本最新Camera Raw 15.1 新功能HDR详解
- astropy.io.fits 教程
- 粗糙集(Rough Sets)
热门文章
- S3C2440之MMU
- openwrt 设置samba服务器与pc共享文件
- JOSN的stringify()和parse()方法
- [转帖] 启动多个Tomcat 需要修改的端口
- python学习手记 pt1
- Netty工作笔记0052---Pipeline组件剖析
- Netty工作笔记0045---异步模型原理剖析
- Netty工作笔记0040---Netty入门--服务端1
- JAVA零碎要点008---tomcat启动的时候报错了严重: End event threw exception java.lang.reflect.InvocationTargetExcepti
- shell sed 替代1