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

 int a=10;
float b=1.234f;
String c="abc";
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的这些类的对象。

 public class Test{

 public static void main(String[] args){

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

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

    Integer i1=127;

    Integer i2=127;

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

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

    Integer i3=128;

    Integer i4=128;

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

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

    Boolean bool1=true;

    Boolean bool2=true;

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

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

    Double d1=1.0;

    Double d2=1.0;

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

 }

 }

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<String> list = new ArrayList<String>();
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中的字符串常量池详细介绍

    引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...

  2. Java中的字符串常量池,栈和堆的概念

    问题:String str = new String(“abc”),“abc”在内存中是怎么分配的?    答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...

  3. Java中的字符串常量池

    ava中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new ...

  4. Java中String字符串常量池总结

    最近到广州某建站互联网公司面试,当时面试官问假设有两个字符串String a="abc",String b = "abc";问输出a==b是true还是fals ...

  5. Java中String字符串常量池

    首先看一个例子,通过这个例子更能快速理解String常量池 public static void main(String[] args) { String a = "ab"; St ...

  6. Java中的字符串常量池和JVM运行时数据区的相关概念

    什么是字符串常量池 JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池 工作原理 当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量 ...

  7. Java进阶——Java中的字符串常量池

    转载. https://blog.csdn.net/qq_30379689/article/details/80518283 字符串常量池 JVM为了减少字符串对象的重复创建,其内部维护了一个特殊的内 ...

  8. 关于java中接口定义常量和类定义常量的区别

    /** * * @author YZJ * @Description java中定义常量的最佳方法 */ public final class Contants{ /** * @Description ...

  9. 008、Java中变量与常量的区别

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  10. Java中线程和线程池

    Java中开启多线程的三种方式 1.通过继承Thread实现 public class ThreadDemo extends Thread{ public void run(){ System.out ...

随机推荐

  1. GAME-BASED LEARNING

    https://collegestar.org/modules/game-based-learning Introduction   Appalachian State University Game ...

  2. 学习:STL_deque容器

    deque容器: 功能:双端数组,可以对头端进行插入删除操作 deque与vector区别: 1.vector对于头部的插入删除效率低,数据量越大,效率越低 2.deque相对而言,对头部的插入删除速 ...

  3. 常见WinDbg问题及解决方案

    当你调试一个程序时,你最不想处理的是调试器不能正常工作.当你试图集中精力跟踪一个bug时,总是会因为次要的问题而被忽略,尤其是当调试器的问题导致你失去一个重新编程或者浪费了大量的时间等待调试器完成它, ...

  4. luoguP4721 【模板】分治 FFT

    P4721 [模板]分治 FFT 链接 luogu 题目描述 给定长度为 \(n-1\) 的数组 \(g[1],g[2],..,g[n-1]\),求 \(f[0],f[1],..,f[n-1]\),其 ...

  5. 洛谷p2672推销员题解

    日常扯废话: 话说题解里的思路都写得真的是很奈斯啊 但是 代码看不懂确实让人头疼(可能是我太弱了) 就像题解里的第一篇题解代码简洁但是属实看不明白 趁着学姐刚给我讲了知识还热乎赶紧给泥萌说说哈 正文: ...

  6. nginx 配置虚拟主机( 基于域名 )

    一.创建网站目录及文件: [root@localhost data]# tree /data /data └── wwwroot ├── www..com │   └── index.html └── ...

  7. 如何实现大麦场在线选座 svg js

    本实例来源于此网站,内有Demo,可查看 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...

  8. JVM和ClassLoader

    JVM和ClassLoader 2019-11-08 目录 1 JVM架构整体架构 1.1 类加载器子系统 1.1.1 加载 1.1.2 链接 1.1.3 初始化 1.2 运行时数据区(Runtime ...

  9. 【BigData】Java基础_冒泡排序

    1.实现需求 根据已经存在的数组,使用冒泡排序将数组中的元素排序后输出. 2.代码 package cn.test.logan.day02; /** * 冒泡排序在数组上的实现 * @author Q ...

  10. mongo helper

    import datetime import pymongo import click # 数据库基本信息 db_configs = { 'type': 'mongo', 'host': '127.0 ...