java 查看类常量池_Java中常量以及常量池
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中常量以及常量池相关推荐
- java最终类最终方法_Java中,什么是最终类与最终方法?它们的作用是什么?
final class A {},这样定义的类A就是最终类,最终类不可被继承,也就是说,不能写这样的代码,class B extends A {}了. final void B() {},这样定义的方 ...
- java实体类怎么写_Java中(entity)实体类的书写规范
在日常的Java项目开发中,entity(实体类)是必不可少的,它们一般都有很多的属性,并有相应的setter和getter方法.entity(实体类)的作用一般是和数据表做映射.所以快速写出规范的e ...
- java修饰类的关键字_JAVA中的修饰关键字
今天我们来说说JAVA中的修饰关键字. 常用的有: 1.final 关键字 修饰成员变量:a. 必须初始化值. b. 被fianl修饰的成员变量赋值,有两种方式:1.直接赋值 2.全部在构造方法中赋初 ...
- java引用类的关键字_java中定义类通常使用的关键字有哪些
下面四种 1) private 类中限定为private的成员,只能被这个类本身访问. 如果一个类的构造方法声明为private,则其它类不能生成该类的一个实例. 2) default 类中不加任何访 ...
- java常用类有哪些_java中的常用类有哪些
String类 构造方法: StringBuffer类 带缓冲区的字符串.Buffer,缓冲区(缓存),只要看到Buffer字样,就是"高效"的代名词. String和String ...
- java 矩形类的作用_java中关于矩形类
创建矩形类Rectangle存储两个顶点坐标(m_x1,m_y1,m_x2,m_y2),构造方法接受2个顶点坐标的值,并调用设置方法setCoordinates将得到的坐标写入对应的属性中,设置方法s ...
- java 所有类的超类_Java中所有类的超类Object
Object的地位 在Java中所有的类都继承于Object类,但不用在声明一个类时显示的extends Object Object中几个重要的方法: 1.equals方法: 用于检测一个对象是否等于 ...
- java反射类带构造函数_java中反射类的加载和构造函数
我们先创建了一个Person类 package reflect; public class Person { public String name = "zhangsan"; pu ...
- java 更改 常量池_JVM中三个常量池(两种常量池)的解析及其随jdk版本的变化
目录 常量池 静态常量池 运行时常量池 字符串常量池 三个常量池的关系 其随jdk版本的变化 常量池 请注意常量池是线程共享数据区,常量池的内容: 常量池的好处: 常量池是为了避免频繁的创建和销毁对象 ...
- java long常量池_Java提高篇之常量池
一.相关概念 1. 什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实例变量和局部变量,分别表示三种类型的常量. 2. Class文件中的 ...
最新文章
- python中plot柱状图-Matplotlib中柱状图bar使用
- seo建设者_前5名最佳免费和付费网站建设者
- java获取jsp_JSP、JAVA获取各种路径总结
- jclasslib查看字节码
- Module build failed: ReferenceError: Unknown plugin module-resolver specified
- 遇到一个在脚本中读取文件内容占用文件句柄的小问题
- ssh-copy-id命令的介绍,使免密更方便
- 64. 整合异步脚本
- 解决微信小程序要求TLS版本不低于1.2问题
- 关于redis HSCAN count参数不生效的问题
- Intelligent Reflecting Surface Assisted Secrecy Communication via Joint Beamforming and Jamming
- android studio try again,完美解决Android Studio在gradle上的各种问题
- 程序员加油站,不是人人都懂的学习要点
- C#文件操作 追加、拷贝、删除、移动文件、创建目录 修改文件名、文件夹名
- 详解从零搭建企业级 vue3 + vite2+ ts4 框架全过程
- java切割音频文件
- ZQOJ 1123: 最佳校友
- JVM SandBox简要介绍
- Flink1.11的CDC-Connectors操作记录
- 电子身份证在微信哪里?微信电子身份证的查看方式有2种,仅需3步