Java String:字符串常量池(转)
作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么?
字符串常量池的设计思想是什么?
字符串常量池在哪里?
如何操作字符串常量池?
字符串常量池的设计思想
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化
为字符串开辟一个字符串常量池,类似于缓存区
创建字符串常量时,首先坚持字符串常量池是否存在该字符串,存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中
实现的基础
实现该优化的基础是因为字符串是不可变的,可以不用担心数据冲突进行共享
运行时实例创建的全局字符串常量池中有一个表,总是为池中每个唯一的字符串对象维护一个引用,这就意味着它们一直引用着字符串常量池中的对象,所以,在常量池中的这些字符串不会被垃圾收集器回收
代码:从字符串常量池中获取相应的字符串
String str1 = “hello”;
String str2 = “hello”; System.out.printl("str1 == str2" : str1 == str2 ) //true
字符串常量池在哪里
在分析字符串常量池的位置时,首先了解一下堆、栈、方法区:
堆
存储的是对象,每个对象都包含一个与之对应的class
JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定
栈
每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象)
每个栈中的数据(原始类型和对象引用)都是私有的
栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)
数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会自动消失
方法区
静态区,跟堆一样,被所有的线程共享
方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量
字符串常量池则存在于方法区
代码:堆栈方法区存储字符串
String str1 = “abc”;
String str2 = “abc”;
String str3 = “abc”;
String str4 = new String(“abc”);
String str5 = new String(“abc”);
字符串对象的创建
面试题:String str4 = new String(“abc”) 创建多少个对象?
在常量池中查找是否有“abc”对象
有则返回对应的引用实例
没有则创建对应的实例对象
在堆中 new 一个 String("abc") 对象
将对象地址赋值给str4,创建一个引用
所以,常量池中没有“abc”字面量则创建两个对象,否则创建一个对象,以及创建一个引用
根据字面量,往往会提出这样的变式题:
String str1 = new String("A"+"B") ; 会创建多少个对象?
String str2 = new String("ABC") + "ABC" ; 会创建多少个对象?
str1:
字符串常量池:"A","B","AB" : 3个
堆:new String("AB") :1个
引用:str1 :1个
总共 :5个
str2 :
字符串常量池:"ABC" : 1个
堆:new String("ABC") :1个
引用:str2 :1个
总共 :3个
代码:基础类型的变量和常量,变量和引用存储在栈中,常量存储在常量池中
int a1 = 1;
int a2 = 1;
int a3 = 1;
public static int INT1 =1 ;
public static int INT2 =1 ;
public static int INT3 =1 ;
操作字符串常量池的方式
JVM实例化字符串常量池时
String str1 = “hello”;
String str2 = “hello”; System.out.printl("str1 == str2" : str1 == str2 ) //true
String.intern()
- 当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。
// Create three strings in three different ways.
String s1 = "Hello";
String s2 = new StringBuffer("He").append("llo").toString();
String s3 = s2.intern(); // Determine which strings are equivalent using the ==
// operator
System.out.println("s1 == s2? " + (s1 == s2)); // false
System.out.println("s1 == s3? " + (s1 == s3)); // true
字面量可以理解为实际值,int a = 8中的8和String a = "hello"中的hello都是字面量
符号引用就是一个字符串,只要我们在代码中引用了一个非字面量的东西,不管它是变量还是常量,它都只是由一个字符串定义的符号,这个字符串存在常量池里,类加载的时候第一次加载到这个符号时,就会将这个符号引用(字符串)解析成直接引用(指针)
补充:字面量和常量池初探
字符串对象内部是用字符数组存储的,那么看下面的例子:
String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");
会分配一个11长度的char数组,并在常量池分配一个由这个char数组组成的字符串,然后由m去引用这个字符串
用n去引用常量池里边的字符串,所以和m引用的是同一个对象
生成一个新的字符串,但内部的字符数组引用着m内部的字符数组
同样会生成一个新的字符串,但内部的字符数组引用常量池里边的字符串内部的字符数组,意思是和u是同样的字符数组
使用图来表示的话,情况就大概是这样的(使用虚线只是表示两者其实没什么特别的关系):
测试demo:
String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world"); System.out.println(m == n); //true
System.out.println(m == u); //false
System.out.println(m == v); //false
System.out.println(u == v); //false
结论:
m和n是同一个对象
m,u,v都是不同的对象
m,n,u,v但都使用了同样的字符数组,并且用equal判断的话也会返回true
Java String:字符串常量池(转)的更多相关文章
- 讲一讲Java的字符串常量池,看完你的思路就清晰了
前言 很多朋友Java的字符串常量池的概念困扰了很长一段时间,最近研究了一下jvm指令码,终于对它有了大概的了解. 在展示案例前,我们需要先搞清楚一个概念,众所周知,jvm的内存模型由程序计数器.虚拟 ...
- Java中String字符串常量池总结
最近到广州某建站互联网公司面试,当时面试官问假设有两个字符串String a="abc",String b = "abc";问输出a==b是true还是fals ...
- Java中String字符串常量池
首先看一个例子,通过这个例子更能快速理解String常量池 public static void main(String[] args) { String a = "ab"; St ...
- string字符串常量池在内存中的位置
这里仅仅是举个简单的样例说明字符串常量池在内存中的位置. 闲言少叙,直接上代码. <span style="font-size: large;">import java ...
- String字符串常量池简介
直接贴代码---> public class Test { public static void main(String[] args) { /** * 为了提升字符串的访问效率,在程序中使用了 ...
- Java String类相关知识梳理(含字符串常量池(String Pool)知识)
目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...
- [JAVA]字符串常量池String pool
字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定.不仅如此,还可以使用 String 的 intern() 方法在运行过程中将 ...
- Java中,那些关于String和字符串常量池你不得不知道的东西
老套的笔试题 在一些老套的笔试题中,会要你判断s1==s2为false还是true,s1.equals(s2)为false还是true. String s1 = new String("xy ...
- 结合字符串常量池/String.intern()/String Table来谈一下你对java中String的理解
1.字符串常量池 每创建一个字符串常量,JVM会首先检查字符串常量池,如果字符串已经在常量池中存在,那么就返回常量池中的实例引用.如果字符串不在池中,就会实例化一个字符串放到字符串池中.常量池提高了J ...
随机推荐
- HashMap的方法及功能、StringBuffer的方法
Hashmap的存值:(map.put(key,value)) 1 public static void main(String[] args) { 2 ///Integer/map.put(&quo ...
- Zookeeper——Watcher原理详解
文章目录 引言 正文 一.如何注册监听 二.如何触发监听事件 三.事件类型有哪些 四.Watcher可以被无限次触发么?为什么要这么设计? 五.Watcher实现原理 1. 客服端发送请求 a. 初始 ...
- selenium(9)- Xpath的详细使用
什么是Xpath 官方:XPath 是一门在 XML 文档中查找信息的语言.XPath 用于在 XML 文档中通过元素和属性进行导航 [XPath 使用路径表达式来选取 XML 文档中的节点或者节点集 ...
- 在Docker中运行PostgreSQL + pgAdmin 4
拉取postgresql镜像:docker pull postgres 运行postgresql:docker run -d -p 5432:5432 --name postgresql -v pgd ...
- weui上传多图片,前端压缩,base64编码
记录一下在做一个报修功能的心路历程,需求功能很简单,一个表单提交,表单包含简单的文字字段以及图片 因为使用的是weui框架,前面的话去找weui的表单和图片上传组件,说实话,weui的组件写的还不错, ...
- DataOutputStream是用来处理什么类型数据的
FileOutputStream f=new FileOutputStream(new File("sa.txt")); DataOutputStream do=new DataO ...
- 【JAVA8新的时间与日期 API】- 传统时间格式化的线程安全问题
Java8之前的日期和时间API,存在一些问题,最重要的就是线程安全的问题.这些问题都在Java8中的日期和时间API中得到了解决,而且Java8中的日期和时间API更加强大. 传统时间格式化的线程安 ...
- Spring IoC bean 的加载
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
- MongoDB快速入门教程 (4.1)
4.Mongoose使用 4.1.nodejs操作mongodb 文档地址:http://mongodb.github.io/node-mongodb-native/3.2/tutorials/cru ...
- max depth exceeded when dereferencing c0-param0问题的解决
在做项目的时候,用到了dwr,有一次居然报错,错误是 max depth exceeded when dereferencing c0-param0 上网查了一下,我居然传参数的时候传的是object ...