• 概念
  • 常量池
    • 常量池的好处
  • Class类文件中的常量池
    • 常量池

      • 运行时常量池
  • 包装类常量池对象池
  • Java中装箱和拆箱
    • 赋值时
    • 方法调用时
    • 方法运算时
  • 参考

概念

什么是常量?
对于这个问题,可能很多人都可以脱口而出 : 用final修饰的变量是常量 ,或者是在编译时期定义好的字符串。(字符串常量)

但是这种说法是不严谨的,因为准确来说 : 常量是用final修饰的成员变量!常量在类编译时期载入类的常量池中。

即final修饰的成员变量(实例变量)和静态变量(静态变量也只能是用static修饰的成员变量),那么用final修饰的局部变量(方法内)我们也可以称之为不可变变量。(存储在栈中)

常量池

Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。

  • 静态常量池 : *.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。(编译时期)

  • 运行时常量池 : jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。(运行时期)
    补充 : 运行时常量池中的常量,基本来源于各个class文件中的常量池。(即每个class文件都有对应的常量池)

常量池的好处

常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。

(1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。

(2)节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等

双等号==的含义
基本数据类型之间应用双等号,比较的是他们的数值。
复合数据类型(类)之间应用双等号,比较的是他们在内存中的存放地址。(引用地址)

        String hello="helloMoto";   String hello2="helloMoto";  

例如我们定义hello和hello2,并且字符串常量池中没有存在”helloMoto”这个字符串常量。
那么首先会在字符串常量池中创建”helloMoto”字符串对象,hello指向字符串常量池中”helloMoto”字符串对象。
第一行代码,hello2首先会去常量池中寻找是否有”helloMoto”,发现已经存在,就直接指向该字符串常量池中”helloMoto”字符串对象。(String对象探索)

Class类文件中的常量池

  • 魔数 : 每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。很多文件存储标准中都使用魔数来表示身份识别。使用魔数而不是扩展名来进行识别主要是基于安全方面的考虑,因为文件扩展名可以随意的改动。Class文件的魔数有很浪漫的气息,值为0x CAFEBABE这也是java是咖啡图标和商标名的原因之一。

  • 版本号 : 紧接着4个魔数字节后面存储的是Class文件的版本号:第5和6个字节是次版本号,第7和第8个字节是主版本号。

  • 常量池 : 接着主次版本号之后的是常量池入口,常量池可以理解为Class文件之中的资源仓库,它是Class文件结构中其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一。(Class类文件中的常量池在类未加载到内存中可以称为静态常量池) 。入口处用2个字节标识常量池常量数量。

我们使用十六进制编辑器WinHex打开Class文件

public class Test2 {public static void main(String[] args) {String hello="helloMoto";   }}

常量池中存放了各种类型的常量,他们都有自己的类型,并且都有自己的存储规范,本文只关注字符串常量,字符串常量以01开头(1个字节),接着用2个字节记录字符串长度,然后就是字符串实际内容。

常量池

常量池主要用于存放两大类常量: 字面量和符号引用量

字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值(成员变量)等。

符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:

  • 类和接口的全限定名

  • 字段名称和描述符

  • 方法名称和描述符

运行时常量池

在Class类文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用如不过不经过运行期转换的话无法得到真正的内存入口地址,也就无法直接被虚拟机使用。 这部分内容将在类加载后进入方法区的运行时常量池中存放。

运行时常量池相对于CLass文件常量池(静态常量池)的另外一个重要特征是 具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。

包装类常量池(对象池)

java中基本类型的包装类的大部分都实现了常量池技术,
Byte,Short,Integer,Long,Character,Boolean;Float,Double

          Integer i1 = 127;Integer i2 = 127;System.out.println(i1==i2);//trueInteger i3 = 128;Integer i4 = 128;System.out.println(i3==i4);//false

对于上面2段代码不同结果我们可以追溯Integer源码

//Integerpublic static Integer valueOf(int i) {if (i >= -128 && i <= 127)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}

可以看到如果值位于[-128,127]区域中,会使用IntegerCache类缓存数据,类似于字符串常量池。
所以如果赋的值超出这个区域, 便会创建一个新的Integer对象。(好处是平时如果频繁的使用Integer,并且数值在[-128,127]中,便不会重复创建新的Integer对象)

但是DoubleFloat这两个基本数据类型的包装类就没有对应常量池(对象池)的实现。

//Doublepublic static Double valueOf(double d) {return new Double(d);}

Java中装箱和拆箱

基本数据类型 包装类
int(4字节) Integer
byte(1字节) Byte
short(2字节) Short
long(8字节)) Long
float(4字节) Float
double(8字节) Double
char(2字节) Character
boolean(1字节) Boolean

赋值时

  • 装箱
    如果要生成一个数值为10的Integer对象,只需要这样:
Integer i = 10;

这个过程中会自动根据数值创建对应的 Integer对象,这就是装箱。

  • 拆箱
Integer i = 10;  //装箱
int n = i;   //拆箱

简单一点说,装箱就是 自动将基本数据类型转换为包装器类型;拆箱就是 自动将包装器类型转换为基本数据类型。

方法调用时

public class Test2 {public static void main(String[] args) {int result = print(5);//int值 5 转换成对应的Integer对象(装箱)}private static int print(Integer a) {//接收Integer对象作为参数System.out.println("a==" + a);return a;//返回int 类型,Integer自动拆箱转为int类型。}
}//a==5

方法运算时

public class Test2 {public static void main(String[] args) {Integer sum = 0;for (int i = 1000; i < 5000; i++) {//自动拆箱为int类型才能运算//运算结果再自动装箱为Integer类型sum += i;}}}

上面的代码sum+=i可以看成sum = sum + i,但是+这个操作符不适用于Integer对象,首先sum进行自动拆箱操作,进行数值相加操作,最后发生自动装箱操作转换成Integer对象。其内部变化如下

int result = sum.intValue() + i;
Integer sum = new Integer(result);

由于我们这里声明的sum为Integer类型,在上面的循环中会创建将近4000个无用的Integer对象,在这样庞大的循环中,会降低程序的性能并且加重了垃圾回收的工作量。因此在我们编程时,需要注意到这一点,正确地声明变量类型,避免因为自动装箱引起的性能问题。

参考

梦工厂的简书
深入剖析Java中的装箱和拆箱
技术小黑屋

final修饰的变量就是常量?final修饰局部变量在栈还是堆还是常量池中?相关推荐

  1. Java中栈、堆和常量池

    2019独角兽企业重金招聘Python工程师标准>>> Java内存分配主要包括以下几个区域: 寄存器 最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.. 栈 存放基 ...

  2. Java中栈,堆,常量池的简单理解

    常量池存储的是常量,即不能改变的量(final,非new出来的String) 栈存储的是简单数据类型 堆存储的是对象 转载于:https://www.cnblogs.com/ztt0918/p/807 ...

  3. Java中final和static修饰的变量是在什么时候赋值的?

    开始 一位朋友在群里问了这样一个问题: 本着乐于助人的想法,我当时给出的回答: 后来我总觉得哪里不对劲. 于是我仔细翻阅了<Java虚拟机规范>和<深入理解Java虚拟机>关于 ...

  4. 细说final的的四种用法-----修饰类,修饰方法,常量,修饰参数 及内部类与final

    final关键字用于修饰类 public final class 类名称 { - } 一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写,java中典型的final修饰的类 Strin ...

  5. java 中final修饰的变量_java中final修饰符的使用方法

    本文为大家分享了java中final修饰符的使用,供大家参考,具体内容如下 1.final修饰符的用法: final可以修饰变量,被final修饰的变量被赋初始值之后,不能对它重新赋值. final可 ...

  6. java 常量 内存分配_Java内存分配之堆、栈和常量池

    寄存器:最快的存储区,位于不同于其他存储区的地方--处理器内部.寄存器的数量极其有限,所以寄存器由编译器根据需求 进行分配.你不能直接控制,也不能在程序中感觉到寄存器存在的任何迹象. 栈:存放基本类型 ...

  7. final修饰的变量就是常量?

    概念 什么是常量? 对于这个问题,可能很多人都可以脱口而出 : 用final修饰的变量是常量 ,或者是在编译时期定义好的字符串.(字符串常量) 但是这种说法是不严谨的,因为准确来说 : 常量是用fin ...

  8. final修饰的变量是引用不能改变还是引用的对象不能改变

    我们都知道final修饰变量时 会变为常量,但是使 用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变? 下面让我们来看这段代码: [java] view plain copy /* ...

  9. 被final修饰的变量在哪存储_final,static,this,super 关键字总结,一点课堂(多岸学院)...

    final 关键字 final关键字主要用在三个地方:变量.方法.类. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改:如果是引用类型的变量,则在对其初始化之后便 ...

最新文章

  1. python中文件路径如何读取提示nosuch file_Python 解决相对路径问题:No such file or directory...
  2. C++调用Matlab 注意事项
  3. mysql8出现1045报错+常用的加密plugin汇总
  4. 计算机2018创业方向,推荐2018年创业的方向
  5. Cookie / Session 的机制与安全
  6. buidulbs android.jar,在将AS项目迁移到IDEA时,无法将com.android.bui...
  7. Vuejs模拟Ajax请求接口(天气预报API)跨域问题 - 案例篇
  8. react全家桶实现招聘app-项目准备(一)
  9. 美团买菜:采购量提升至平时3-4倍 提供无接触配送服务
  10. 基于JavaSwing+MySql的学生信息管理系统
  11. com词根词缀_用词根词缀背英语单词靠谱吗丨效果方法
  12. opengl 雷霆战机 2d
  13. 大数据24小时:腾讯云在美新增两个数据中心,依图科技与华为合作发布人工智能一体机
  14. Unity3D_Could not produce Class With ID xxx
  15. 中国剩余定理(也叫孙子定理)
  16. 如何解决GitHub仓库README文件插入图片无法显示的问题
  17. 攻略-联想 Yoga 900 安装 Arch Linux 系统
  18. php控件不显示,响应式织梦后台模板辅助插件不显示插件模块不显示解决办法
  19. intra_op_parallelism_threads参数设置
  20. java给字符串数组追加字符串_java往字符串数组追加新数据

热门文章

  1. idea中创建maven依赖下的web工程(一)----用户登录界面
  2. 模型的“参数”与“超参数”
  3. 【论文阅读|深读】DRNE:Deep Recursive Network Embedding with Regular Equivalence
  4. 判断是否为回文字符串(Java)
  5. 你的工作单休,但是工资双休
  6. 西南交大计算机绘图a答案,西南交大 计算机绘图A客观题答案(1-3次作业).doc
  7. Sigmoid 与 Softmax 的区别
  8. 重磅!在 Mac 上接收 iphone 手机短信!
  9. C++编程故障处理——error: assignment of read-only data-member ‘std::pair
  10. 【机器学习】贝叶斯学习