博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
String s=new String(“abc“)创建了几个对象?
阅读量:1824 次
发布时间:2019-04-25

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

图片

 

1、引言

本文将围绕字符串常量池与字符串展开介绍。

字符串常量池和运行时常量池是两个不同的概念。运行时常量池存储的是类的字面量,是每个类独有的,而字符串常量池存储的是字符串字面量,是所有类共享的。

JDK1.7字符串常量池在方法区,JDK1.7之后字符串常量池就转移到了堆区。

 

2、"abc"与newString("abc")的执行原理

2.1、JVM如何执行String s="abc"

String s="abc"会先从字符串常量池(下文简称常量池)中查找,如果常量池中已经存在"abc",而"abc"必定指向堆区的某个String对象,那么直接将s指向这个String对象即可;如果常量池中不存在"abc",则在堆区new一个String对象,然后将"abc"放到常量池中,并将"abc"指向刚new好的String对象。

2.2、JVM如何执行new String("abc")

new String("abc")相当于new String(String s1="abc"),即先要执行String s1="abc"(2.1已经讲过了),然后再在堆区new一个String对象。

因此,现在可以解答本文的标题了,String s=new String("abc")创建了1或2个对象,String s="abc"创建了0或1个对象。

 

2.3、String类的intern方法

下面介绍一个方法,String类的intern方法,现有如下代码。

String s=new String("abc");s.intern();

首先会检查常量池中是否存在"abc",如果存在,则s.intern()会返回"abc"指向堆区的对象,如果不存在,则将"abc"放入常量池,并将"abc"指向s指向的对象。

 

2.4、new String("abc")会将"abc"放到常量池里吗?

答案是会,如何验证呢?

String s1 = new String("abc");s1.intern();String s = "abc";System.out.println(s==s1);// false

 

下面用反证法证明。

如果不会,那么是.intern()执行之后,常量池中"abc"指向的就是s1指向的对象,s也是指向常量池中"abc"指向的对象,那么s和s1指向的是同一对象,结果就是true,但结果是false,说明会放到常量池。

 

3、字符串拼接+操作符原理

首先来看String s="hello"+"world"这句代码。

通过javap -c class文件名称可以查看字节码,如下图所示。

图片

可以看到String s="hello"+"world"与String s="helloworld"是一样的。

 

再看下面一段代码

String s1="hello";String s2="world";String s3=s1+s2;

 

通过javap命令查看字节码,如下图所示。

图片

  • 第一步:new一个StringBuilder;

  • 第二步:append s1;

  • 第三步:append s2;

  • 第四步:调用StringBuilder的toString方法;

这下我们知道了,+操作符的原理是StringBuilder的append方法。

 

下面再看一道经典面试题,如下代码所示。

String s1 = "a";String s2 = "b";String s3 = s1+s2;String s4 = "ab";System.out.println(s3==s4);

 

这道题的疑惑点在于:s1+s2拼接之后的"ab"会不会放进常量池里,如果会,那么s3==s4返回true,如果不会,则返回false。

我们已经知道s1+s2会通过StringBuilder的append方法进行拼接,然后通过StringBuilder的toString方法返回"ab",那我们看一下toString方法的源码,如下图所示。

图片

这下我们知道了,这是new了一个String对象,并且new好的String对象不会放进常量池里,因此s3==s4返回false。

 

4、为什么String是不可变的?

我们知道,用+操作符拼接字符串,会产生中间对象,如果是线程安全的环境,我们会用StringBuffer拼接字符串,线程不安全的环境则使用StringBuilder。

产生中间对象的原因是String是不可变的,我们看看String类的源码,如下图所示。

图片

String类用声明为final的char数组来存储字符串,在拼接的时候由于char数组不可变,因此会产生中间对象;

那到底产生的是哪些中间对象呢?其实上面已经讲到了,+操作符拼接字符串时,每次会new一个StringBuilder对象,然后将+操作符连接的两个字符串append上,最后toString,而toString又会new一个String对象,因此每使用一次+操作符都会产生2个中间对象。

String类的replace方法只是替换原来的值,因此不会产生中间对象;

那StringBuilder和StringBuffer在拼接字符串时为什么效率高呢?StringBuilder和StringBuffer都继承自AbstractStringBuilder,AbstractStringBuilder的源码如下图所示。

图片

可以看到AbstractStringBuilder中的char数组没有声明为final,因此是可变的。

 

 

 

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

你可能感兴趣的文章
基于 Hystrix 高并发服务限流第 5 篇 —— Hystrix 监控
查看>>
Eureka 如何快速的、优雅的停止某个微服务
查看>>
Eureka 实现安全认证
查看>>
基于 Hystrix 高并发服务限流第 6 篇 —— 服务限流,基于 RateLimiter 实现
查看>>
Nginx 反向代理、负载均衡配置、Location正则表达式
查看>>
SpringBoot + WebSocket 实现前后端的收发消息
查看>>
SpringBoot 整合 JWT 实现统一认证
查看>>
SpringBoot 使用 CompletableFuture 实现非阻塞异步编程
查看>>
即刻就业:本科毕业如何快速高薪就业?
查看>>
即刻就业:java的应用程序有哪些,java程序有哪些编码规范,开发java应用程序有哪些步骤
查看>>
JAVA中的浮点数与二进制
查看>>
JAVA笔记(二)--Java初始
查看>>
JAVA笔记(三)--变量及运算符
查看>>
JAVA笔记(四)--三大结构语句
查看>>
JAVA语言基础(五)--数组
查看>>
JAVA项目案例详解带代码
查看>>
JAVA九种排序算法详解
查看>>
JAVA笔记(六)面向对象--类和对象
查看>>
JAVA笔记(十一)面向对象--多态
查看>>
webpack打包错误:Invalid configuration object. Webpack has been initialised using a configuration object
查看>>