Java中字符串常见题之String相关讲解

 更新时间:2019年01月14日 17:03:36   作者:Zy_JiBai  
今天小编就为大家分享一篇关于Java中字符串常见题之String相关讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

今天给大家带来的是在面试中经常被问到的一道题:

无论在Java还是Android中,String是一个很常见的类,但是大家真的很了解吗,我这里有几个题:

1.

String str1 = "abc";
String str2 = new String("abc");

这两种创建String对象的方法有什么不同?

2.

String s = "a" + "b" + "c" + "d";

这里面一共创建了多少对象?

这两道题昨天给笔者搞得是一脸懵逼,后来一听这是一道很经典的面试题,就赶快查阅各种资料,现在已经解决了。

在解决这两个题之前,我们先来明确几个知识点,相信把这几个知识点弄完之后再回头看这两个题,就很简单了:

  • 1.引用在栈内存中存储,对象在堆内存中存储。
  • 2.String对象不可变。
  • 3.String创建对象的形式:
  • 4.字符串常量池的意义:
  • 5.intern()方法使用。

我们首先来开第一个:

引用在栈内存中存储,对象在堆内存中存储。

这个是我粗略画的一张图,这张图可能不是很准确,但是我只想表达一个意思,我们的栈内存,存放的是我们对象的引用和我们基本数据类型的值。而堆内存中存放的是我们的对象。就是这么简单。

为什么说String对象不可变

这里我们用一下大佬的图:

如果用代码表示上面的图,那就是:

String s = "abcd";
s = s + "el";

如果没有上面这张图,大家可能会觉得我们只是给s对象后面加了一个el,这不还是原来的s吗?不,我们说的String对象不可变就是这个意思,当我们给s再加上el时候,新的字符串abcdel被存放到了一个新的内存中。已经不是原来的内存了。所以说String对象不可变,如果变了,那就已经变成了一个新的对象。

那说到这里大家可能会返回去看我写的第二个问题:按你的说法这不就7个对象吗?abcd各占一个,每次+到一起都要重新生成对象,一共生成了7个对象。话是没错,但是我想说这道题说多解,等我们讲完了下面这两个知识点,这道题就完全解开了。

String创建对象的形式:

正如我们上面看到的,String有两种创建对象的形式:

1.字面量形式:

String str = "asd";

2.标准的new形式:

String str = new String("asd");

字符串常量池的意义:

字符串常量池,又称为字符串在字面量池。大家不要他想象的多么高深额,其实说白了他就是一块内存,它里面存放的是我们的字符串的引用。

大家可能疑问这个东西有什么意义:假如我们要创建一个字符串,"a" + "b" + "c" + ....+ 我们+了一万次,那么按照上面的说法,我们是不是为了创建一个很长的字符串,创建了很多的对象。这样不但有的字符串被重复的创建了,而且占用了很多不必要的内存,代价有点大。

我们的JVM为了减少字符串的重复创建,维护了一个特殊的内存:就是这个字符串常量池。

在这个字符串常量池中,存放着我们字符串对象的引用。下面我们根据字符串创建对象的两种形式来说明一下常量池是如何存放String对象引用的:

1.通过字面量创建String对象:

我们举个例子,String str = "abc";我们通过字面量形式创建一个值为"abc"的对象,首先我们判断常量池中是否存在一个引用,这个引用的值也是"abc",如果有这个值,那么我们就从常量池中拿到这个引用,返回给str。

如果没有,那么我们就创建这个对象,然后把str这个引用值放到常量池中。

在这里我们要明确几个点了:

  • 1首先常量池存放的不是字符串对象,而是字符串对象的引用。
  • 2.无论常量池中是否存在这个对象的引用,最后结果都会存放有这个引用。
  • 3.该方法可能不会创建新的对象。

2.通过new创建String对象。

这里其实只有一句话,无论常量池中是否存在相同值的引用,至少创建一个对象。因为不管别的,new这个语法就一定会创建一个新的对象,然后我们要看构造方法中的字符串,如果常量池中存在与该字符串相同值的引用,那么就不会创建新的对象,反之会创建对象之后,在常量池中存放这个引用。

这样看起来我们的常量池的意义就很明确了,减少重复创建String对象,从而减少不必要的内存消耗。

如果要说他的弊端的话,应该就是牺牲了CPU的计算时间来换取空间吧,因为查找是否有相同内容的引用是CPU耗时计算,但是与前者占用内存相比,这个还是算不了什么的吧(笔者自己观点,没有官方支持哈哈)。

intern()方法使用

最后我们来看一下这个intern()方法,这个方法其实理解为查看常量池中是否存在对应内容的引用。如果有他会返回常量池中的引用,如果没有则会将当前String的引用放入常量池。

我们举几个例子对比下就好:

public static void main(String[] args) { 
 String str1 = "gfzy";
 String str2 = str1.intern();
 System.out.println(str1 == str2);
}

他的结果是true。很简单,我们首先通过字面量形式创建了一个“gfzy”对象,此时常量池中没有相同内容引用,所以常量池存放str1的引用。

然后str2为str1的.intern()方法,现在常量池中存在“gfzy”这个内容的引用,所以我们返回这个引用,也就是str1,所以str1和str2指向同一个引用。

public static void main(String[] args) { 
 String str1 = new String("gfzy");
 String str2 = "gfzy";
 System.out.println(str1 == str2);
}

这个结果为false,首先我们看到str1是new了一个对象,所以他肯定是在堆内存中一块新的内存,而构造方法中的“gfzy”,首先在常量池中会拿到他的引用,然后返回这个引用。

str2直接拿到了常量池中的引用,所以一个是堆内存新创建的,一个是原来常量池中的引用,两者指向不是一个内存,所以是false。

而如果是这样呢:

public static void main(String[] args) { 
 String str1 = new String("gfzy").intern();
 String str2 = "gfzy";
 System.out.println(str1 == str2);
}

这个结果为true。在原来的基础上只是添加了一个inter方法,在new String("gfzy");时候,现在常量池中已经有了这个引用。而现在intern,我们会拿到常量池中的引用,所以str1为常量池中的引用,str2和上面一样,所以返回true。

现在这几个问题都解决完了,我们再返回头看之前的两个问题,第一个问题就很简单了,而我们刚才也说过了。

第二个是看常量池中是否已经存在了对应的引用,如果没有,那么是"a","b","ab","c","abc","d","abcd"七个对象,如果常量池中存在引用,那么只创建了一个“abcd”(这个是编译器优化后的结果,其实笔者这里也是有点懵逼,个人感觉应该是3个额,希望这个问题有读者多多留言,帮我解答一下这个问题)。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。如果你想了解更多相关内容请查看下面相关链接

相关文章

  • SpringCloud Open feign 使用okhttp 优化详解

    SpringCloud Open feign 使用okhttp 优化详解

    这篇文章主要介绍了SpringCloud Open feign 使用okhttp 优化详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02
  • springboot无法从静态上下文中引用非静态变量的解决方法

    springboot无法从静态上下文中引用非静态变量的解决方法

    这篇文章主要介绍了springboot无法从静态上下文中引用非静态变量的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-06-06
  • mybatis拦截器无法注入spring bean的问题解决

    mybatis拦截器无法注入spring bean的问题解决

    本文主要介绍了mybatis拦截器无法注入spring bean的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-02-02
  • JavaCV与FFmpeg音视频流处理技巧总结大全

    JavaCV与FFmpeg音视频流处理技巧总结大全

    JavaCV是一个开源的Java接口,它为几个著名的计算机视觉库(如OpenCV、FFmpeg)提供了Java封装,这篇文章主要给大家介绍了关于JavaCV与FFmpeg音视频流处理技巧总结的相关资料,需要的朋友可以参考下
    2024-05-05
  • Java Date(日期)对象进行格式化的思路详解

    Java Date(日期)对象进行格式化的思路详解

    Date类是经常会使用到的一个用来处理日期、时间的一个类。Date类是在java.util包下的Date类,这篇文章主要介绍了Java Date(日期)对象如何进行格式化呢,需要的朋友可以参考下
    2022-09-09
  • Spring MVC整合 freemarker及使用方法

    Spring MVC整合 freemarker及使用方法

    Spring MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,这篇文章主要介绍了Spring MVC整合 freemarker及使用方法,需要的朋友可以参考下
    2019-07-07
  • 在Java中判断两个Long对象是否相等

    在Java中判断两个Long对象是否相等

    这篇文章主要介绍了在Java中判断两个Long对象是否相等的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java实现简易版图形界面计算器

    java实现简易版图形界面计算器

    这篇文章主要为大家详细介绍了java实现简易版图形界面计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-05-05
  • Swing常用组件之单选按钮和复选框

    Swing常用组件之单选按钮和复选框

    Swing是一个用于开发Java应用程序用户界面的开发工具包,这篇文章主要介绍了Swing常用组件之单选按钮和复选框,感兴趣的朋友可以参考一下
    2016-05-05
  • spring cloud学习教程之config修改配置详解

    spring cloud学习教程之config修改配置详解

    这篇文章主要给大家介绍了关于spring cloud学习教程之config修改配置的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-09-09

最新评论