Java:String的“设计奥秘”


Java:String的“设计奥秘”
文章图片
Java:String的“设计奥秘”

花10分钟认真的看一篇文章或许有意想不到的收获

String类的内容非常多 , 一篇文章讲不完 , 当然如果一篇文章讲太多内容我相信你和我一样 , 就懒得看了 。 本文主要讲String类的“常量池” , 这是java在设计String类时一个很重要的设计 。
废话少说 , 先看定义!
01常量池的定义
String s1=\"test\";
String s2=\"test\";
String s3=new String(\"test\");
System.out.println(s1==s2);  //true
System.out.println(s1==s3);  //false
上面这个例子 , 很多小伙伴应该在很多的java面试题中见过 。
造成上面这个原因就是因为有“字符串常量池”的存在 。 下面就来看一下什么是“字符串常量池”
当我们通过 String s1=\"test\"这种方式去赋值给String对象的时候 , jvm会去先查看下在String类型的常量池中是否有这样的一个字符串 , 如果有则把引用返回 , 没有则新创建一个一样内容的字符串并将其引用返回 , 但是在String类型的常量池中同一时间只会存在一个内容一样的字符串 , 所以这也就保证了唯一性 。
看完上面这段话 , 小伙伴们应该就能明白为什么上面的s1=s2了 , 而s3这种方式其实在“字符串常量池”和“堆”上都创建了一个“test”但是s3的引用是指向“堆”的 , 而s1、s2的引用是指向“字符串常量池”中的“test”的 , 所以它们不想等 。
怕有些小伙伴不知道这里补充一句:Java中 , 只要使用new关键字来创建对象 , 则一定会堆区创建一个新的对象 , 堆中存放对象本身的数据 , 栈中存放对象的引用 , 例子中的s1、s2、s3都是引用都是存在栈中的 。
下面我们来看一下常量池在那
02常量池的位置介绍常量池的位置之前 , 需要简单的介绍一下 , “堆”、“栈”、“静态区”、“方法区”的概念 , 就简单了解一下 , 知道里面都放了些啥就行 , 不需要太深究  (ps:深究它干嘛 , 除非你想干程序员)
  • 堆:用于存放对象内容的内存区域
  • 方法区:也称非堆(Non-Heap) , 又是一个被线程共享的内存区域 。 其中主要存储加载的类字节码、class/method/field等元数据对象、static-final常量、static变量、jit编译器编译后的代码等数据
  • 栈:每个线程包含一个栈区 , 栈中只保存基础数据类型的对象和自定义对象的引用(不是对象)
常量池是在方法区中的


下面再用一张图描述一下 , 上面例子中s1、s2、s3之间的关系
那么为什么java要这么去设计一个常量池呢?弄清这个原因或许才真的对我们有帮助
03为什么这么做?我们都知道
  1. 在java中String是一个类 , 类的使用都是需要去new的 , 在前面我们也见过通过new去创建字符串的方式 。
  2. java中只要new了就一定会去产生一个新的对象 。
同时 , String在我们的编码中是大量存在的 , 使用的非常之多 。 如果每次使用都去new那岂不是消耗很大 。
这里java其实就做了特别处理 , 既然String类这么常用 , 那我就单独找一个空间来存放它 , 每次新建字符串的时候就到这个区域来看一下 , 如果有 , 就不新建了 。 这样一来就减少了内存开销 , 提高了性能 。


推荐阅读