1、举例说明 变量 常量 字面量

1 int a=10;2 float b=1.234f;3 String c="abc";4 final long d=10L;

a,b,c为变量,d为常量 两者都是左值;10,1.234f,"abc",10L都是字面量;

2、常量池:

常量池专门用来用来存放常量的内存区域,常量池分为:静态常量池和运行时常量池;

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

运行时常量池:是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。

备注:java虚拟机内存分为虚拟机栈、虚拟机堆、本地方法栈、程序计数器、方法区(jdk8中,移除了方法区,转而用Metaspace区域替代)

2.1 字符串常量池

1 String s1 = "Hello";

2 String s2 = "Hello";

3 String s3 = "Hel" + "lo";

4 String s4 = "Hel" + new String("lo");

5 String s5 = new String("Hello");

6 String s6 = s5.intern();

7 String s7 = "H";

8 String s8 = "ello";

9 String s9 = s7 + s8;

10

11 System.out.println(s1 == s2); // true

12 System.out.println(s1 == s3); // true

13 System.out.println(s1 == s4); // false

14 System.out.println(s1 == s9); // false

15 System.out.println(s4 == s5); // false

16 System.out.println(s1 == s6); // true

java程序经过编译和运行两步:

s1 == s2,编译时,将字面量"Hello"直接放入class文件的常量池中,从而实现复用,载入运行时常量池后,s1、s2指向的是同一个内存地址,所以相等。

s1 == s3,编译时,这种拼接会被优化,编译器直接拼好,在class文件中被优化成String s3 = "Hello";,所以s1 == s3成立。

s1 == s4,编译时,new String("lo") 如何生成 在哪生成还不确定,是一个不可预料的部分,编译器不会优化,必须等到运行时才可以确定结果,所生成后的引用在堆中,而不是方法区,所以地址肯定不同。

s1 == s9 编译时,s7、s8在赋值的时候使用的字符串字面量,但是拼接成s9的时候,s7、s8作为两个变量,都是不可预料的,编译器毕竟是编译器,不可能当解释器用,所以不做优化,等到运行时,s7、s8拼接成的新字符串,在堆中地址不确定,不可能与方法区常量池中的s1地址相同。

s4 == s5已经不用解释了,绝对不相等,二者都在堆中,但地址不同。

s1 == s6这两个相等完全归功于intern方法(手工在常量池添加常量),s5在堆中,内容为Hello ,intern方法会尝试将Hello字符串添加到常量池中,并返回其在常量池中的地址,因为常量池中已经有了Hello字符串,所以intern方法直接返回地址;而s1在编译期就已经指向常量池了,因此s1和s6指向同一地址,相等。

2.2 8种基本类型的包装类和对象池

java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。

1 public classTest{2

3 public static voidmain(String[] args){4

5 //5种整形的包装类Byte,Short,Integer,Long,Character的对象,6

7 //在值小于127时可以使用常量池

8

9 Integer i1=127;10

11 Integer i2=127;12

13 System.out.println(i1==i2)//输出true14

15 //值大于127时,不会从常量池中取对象

16

17 Integer i3=128;18

19 Integer i4=128;20

21 System.out.println(i3==i4)//输出false22

23 //Boolean类也实现了常量池技术

24

25 Boolean bool1=true;26

27 Boolean bool2=true;28

29 System.out.println(bool1==bool2);//输出true30

31 //浮点类型的包装类没有实现常量池技术

32

33 Double d1=1.0;34

35 Double d2=1.0;36

37 System.out.println(d1==d2)//输出false

38

39

40

41 }42

43 }

2.3 查看常量池

1 String s = "hi";

将代码编译成class文件后,用winhex打开二进制格式的class文件。如图:

class文件的结构:

1.开头的4个字节是class文件魔数,用来标识这是一个class文件,说白话点就是文件头,既:CA FE BA BE。

2.紧接着4个字节是java的版本号,这里的版本号是34,是用jdk8编译的。

3.接下来就是常量池入口,入口处用2个字节标识常量池常量数量,本例中数值为00 1A,十进制是26,也就是有25个常量,其中第0个常量是特殊值,所以只有25个常量。

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

接下来再说说运行时常量池,由于运行时常量池在方法区中,我们可以通过jvm参数:-XX:PermSize、-XX:MaxPermSize来设置方法区大小,从而间接限制常量池大小。

假设jvm启动参数为:-XX:PermSize=2M -XX:MaxPermSize=2M,然后运行如下代码:

1 //保持引用,防止自动垃圾回收

2 List list = new ArrayList();

3

4 int i = 0;

5

6 while(true){

7 //通过intern方法向常量池中手动添加常量

8 list.add(String.valueOf(i++).intern());

9 }

程序立刻会抛出:Exception in thread "main" java.lang.outOfMemoryError: PermGen space异常。PermGen space正是方法区,足以说明常量池在方法区中。

在jdk8中,移除了方法区,转而用Metaspace区域替代,所以我们需要使用新的jvm参数:-XX:MaxMetaspaceSize=2M,依然运行如上代码,抛出:java.lang.OutOfMemoryError: Metaspace异常。同理说明运行时常量池是划分在Metaspace区域中。

参考:

java 查看类常量池_Java中常量以及常量池相关推荐

  1. java最终类最终方法_Java中,什么是最终类与最终方法?它们的作用是什么?

    final class A {},这样定义的类A就是最终类,最终类不可被继承,也就是说,不能写这样的代码,class B extends A {}了. final void B() {},这样定义的方 ...

  2. java实体类怎么写_Java中(entity)实体类的书写规范

    在日常的Java项目开发中,entity(实体类)是必不可少的,它们一般都有很多的属性,并有相应的setter和getter方法.entity(实体类)的作用一般是和数据表做映射.所以快速写出规范的e ...

  3. java修饰类的关键字_JAVA中的修饰关键字

    今天我们来说说JAVA中的修饰关键字. 常用的有: 1.final 关键字 修饰成员变量:a. 必须初始化值. b. 被fianl修饰的成员变量赋值,有两种方式:1.直接赋值 2.全部在构造方法中赋初 ...

  4. java引用类的关键字_java中定义类通常使用的关键字有哪些

    下面四种 1) private 类中限定为private的成员,只能被这个类本身访问. 如果一个类的构造方法声明为private,则其它类不能生成该类的一个实例. 2) default 类中不加任何访 ...

  5. java常用类有哪些_java中的常用类有哪些

    String类 构造方法: StringBuffer类 带缓冲区的字符串.Buffer,缓冲区(缓存),只要看到Buffer字样,就是"高效"的代名词. String和String ...

  6. java 矩形类的作用_java中关于矩形类

    创建矩形类Rectangle存储两个顶点坐标(m_x1,m_y1,m_x2,m_y2),构造方法接受2个顶点坐标的值,并调用设置方法setCoordinates将得到的坐标写入对应的属性中,设置方法s ...

  7. java 所有类的超类_Java中所有类的超类Object

    Object的地位 在Java中所有的类都继承于Object类,但不用在声明一个类时显示的extends Object Object中几个重要的方法: 1.equals方法: 用于检测一个对象是否等于 ...

  8. java反射类带构造函数_java中反射类的加载和构造函数

    我们先创建了一个Person类 package reflect; public class Person { public String name = "zhangsan"; pu ...

  9. java 更改 常量池_JVM中三个常量池(两种常量池)的解析及其随jdk版本的变化

    目录 常量池 静态常量池 运行时常量池 字符串常量池 三个常量池的关系 其随jdk版本的变化 常量池 请注意常量池是线程共享数据区,常量池的内容: 常量池的好处: 常量池是为了避免频繁的创建和销毁对象 ...

  10. java long常量池_Java提高篇之常量池

    一.相关概念 1. 什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实例变量和局部变量,分别表示三种类型的常量. 2. Class文件中的 ...

最新文章

  1. python中plot柱状图-Matplotlib中柱状图bar使用
  2. seo建设者_前5名最佳免费和付费网站建设者
  3. java获取jsp_JSP、JAVA获取各种路径总结
  4. jclasslib查看字节码
  5. Module build failed: ReferenceError: Unknown plugin module-resolver specified
  6. 遇到一个在脚本中读取文件内容占用文件句柄的小问题
  7. ssh-copy-id命令的介绍,使免密更方便
  8. 64. 整合异步脚本
  9. 解决微信小程序要求TLS版本不低于1.2问题
  10. 关于redis HSCAN count参数不生效的问题
  11. Intelligent Reflecting Surface Assisted Secrecy Communication via Joint Beamforming and Jamming
  12. android studio try again,完美解决Android Studio在gradle上的各种问题
  13. 程序员加油站,不是人人都懂的学习要点
  14. C#文件操作 追加、拷贝、删除、移动文件、创建目录 修改文件名、文件夹名
  15. 详解从零搭建企业级 vue3 + vite2+ ts4 框架全过程
  16. java切割音频文件
  17. ZQOJ 1123: 最佳校友
  18. JVM SandBox简要介绍
  19. Flink1.11的CDC-Connectors操作记录
  20. 电子身份证在微信哪里?微信电子身份证的查看方式有2种,仅需3步

热门文章

  1. PC端和移动端的区别你知道吗?
  2. nmap 扫描服务器开放了哪些端口
  3. 3dmax Maxscript 回调脚本异常
  4. LDA模型困惑度计算出现的问题
  5. java中的BigDecimal类型
  6. 百度、火星(高德)、84坐标系相互转换
  7. HashMap hash冲突解决方法
  8. Unity2D入门(一):编辑素材、绘制地图
  9. 嵌入式软件开发之程序架构(一)
  10. 【python】将多个txt文件合并为一个txt文件