博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
你真的了解String吗?(修正版)
阅读量:6241 次
发布时间:2019-06-22

本文共 2884 字,大约阅读时间需要 9 分钟。

修正前:new出来的对象,会在堆中存放真正的值; 大错特错!!!!

修正后:new出来的对象,堆存放的并不是真正的值,而是常量池中字符串常量的地址。

一、抛砖引玉

​ 不知道大家在做面试题时是否会遇到关于String的题,记得校招时,楼主经常遇到String的题,有时候会很懵逼。先来看一个例子:

public class StringTest {
    public static void main(String[] args) {
        String str1 = "hello";         String str2 = "hello";         String str3 = "he" + "ll" + "o";         System.out.println(str1 == str2);         System.out.println(str1 == str3);         String str = "o";         String str4 = "hell" + str;         System.out.println(str1 == str4);         String str5 = new String("hello");         System.out.println(str1 == str5);         System.out.println(str4 == str5);         System.out.println(str1.equals(str5));         //intern()函数作用:直接获取常量池中字符串常量的地址并返回。         System.out.println(str1==str5.intern());         String str6 = new String("beyond");     } } //输出的结果 //true //true //false //false //false //true //true 复制代码

你答对了吗?

二、细说原理

1:简单说明==与equals()区别:

  • ==:比较的是内存地址是否相同,即引用是否指向了内存中的同一个对象。指向同一个内存地址则为true;否则,为false。
  • equals():比较的是两个引用指向的值是否相同。如果指向的值是相同的,则返回true,否则,返回false。

2:从内存角度详谈String

首先我们要知道,String是Java的一个类,有两种创建对象的方式【不考虑反射、clone、反序列化】。

  • 方式一:String str1 = "hello"; 将对象放到了常量池中
  • 方式二:String str2 = new String("hello"); 在堆中新建了一个对象
1554981844054

​ (我这个图画得太丑了。。。。。。[摊手])

​ 通过方式一创建String对象,会先查看常量池中是否有这个字符串常量,如果存在,则直接将引用指向这个常量(即例子中:str1==str2为true)。比较疑惑的会是str3,str4。因为str3由字符串常量“he”、"ll"、“o”组成,编译器发现这三个常量组成的字符串常量已经存在了常量池中,即编译期间就已经确定了最终值,则也会将引用指向“hello”字符串常量。对于str4,因为编译器不能在编译时就确定字符串最终的值,所以会将字符串常量“hell”存放在常量池,再在堆中生成最终的对象【引申一下:通过“+”连接字符串,底层是通过new StringBuilder()的append()方法进行拼接,所以应避免在循环中使用“+”来拼接字符串,以免创建大量垃圾对象】。

​ 通过方式二而创建String对象,首先会先查看常量池中是否存在这个字符串常量,如果存在,则直接在对内存中new出新对象;否则,会先在常量池生成这个字符串常量,再在堆中生成新对象(如图中str6)。特别注意:(new出对象的值为常量池中这个字符串常量的地址,也就是堆中存放的都是字符串常量中的地址)

3:使用javap命令从编译结果验证

​ javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

javap的用法格式:

javap <options> <classes>
其中classes就是你要反编译的class文件。
在命令行中直接输入javap或javap -help可以看到javap的options有如下选项:

-help  --help  -?        输出此用法消息  -version                 版本信息,其实是当前javap所在jdk的版本信息,不是class在哪个jdk下生成的。  -v  -verbose             输出附加信息(包括行号、本地变量表,反汇编等详细信息)  -l                         输出行号和本地变量表  -public                    仅显示公共类和成员  -protected               显示受保护的/公共类和成员  -package                 显示程序包/受保护的/公共类 和成员 (默认)  -p  -private             显示所有类和成员  -c                       对代码进行反汇编  -s                       输出内部类型签名  -sysinfo                 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)  -constants               显示静态最终常量  -classpath 
        指定查找用户类文件的位置  -bootclasspath 
    覆盖引导类文件的位置 复制代码

比如本例子:javap -verbose StringTest

1554891308064

三、总结

​ String在内存的存在形式有两种,如果通过直接赋值的形式(方式一)会将对象直接放到常量池中【如果由几个字符串常量拼接而成,并且在编译时就确定了最终的结果(即常量池中存在最终拼接而成的字符串),则直接将引用指向这个字符串常量;如果在编译时不能确定最终的结果,则会将最终结果在堆中生成,并其中的字符串常量会在常量池中存在】;如果通过new的方式(方式二),先在常量池生成字符串常量,再在堆中生成字符串对象,对象的值为这个字符串常量在常量池中的地址。

小弟才疏学浅,如有错误,敬请指正,十分感谢。

转载地址:http://dqcia.baihongyu.com/

你可能感兴趣的文章
Shell命令-文件及内容处理之sort、uniq
查看>>
Android 之文件夹排序
查看>>
Java Assert 用法简介
查看>>
关于redo size(一)
查看>>
We Know What @You #Tag: Does the Dual Role Affect Hashtag Adoption-20160520
查看>>
(转)Eclipse新增安卓虚拟机
查看>>
SpringMvc访问Controller去掉do
查看>>
PHPnow升级PHP 5.4与Mysql 5.5
查看>>
正则表达式验证邮箱格式
查看>>
如何围绕企业战略,建设BI驾驶舱?
查看>>
java多线程stop,suspend使用代码实际例子
查看>>
中小型研发团队架构实践三:微服务架构(MSA)
查看>>
Windows动态库学习心得
查看>>
在VMware虚拟机上安装Ubuntu 10.04
查看>>
LDA主题模型简介
查看>>
可拖动的DIV续
查看>>
关于“类型初始值设定项引发异常”
查看>>
MySql 小表驱动大表
查看>>
Redis 数据结构的底层实现 (一) RealObject,embstr,sds,ziplist,quicklist
查看>>
SQL语句注入的问题
查看>>