今天给大家带来的是在面试中经常被问到的一道题:
无论在Java还是Android中,String是一个很常见的类,但是大家真的很了解吗,我这里有几个题:
1.
String str1 = "abc"; String str2 = new String("abc");
这两种创建String对象的方法有什么不同?
2.
String s = "a" + "b" + "c" + "d";
这里面一共创建了多少对象?
这两道题昨天给笔者搞得是一脸懵逼,后来一听这是一道很经典的面试题,就赶快查阅各种资料,现在已经解决了。
在解决这两个题之前,我们先来明确几个知识点,相信把这几个知识点弄完之后再回头看这两个题,就很简单了:
我们首先来开第一个:
引用在栈内存中存储,对象在堆内存中存储。
这个是我粗略画的一张图,这张图可能不是很准确,但是我只想表达一个意思,我们的栈内存,存放的是我们对象的引用和我们基本数据类型的值。而堆内存中存放的是我们的对象。就是这么简单。
为什么说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这个引用值放到常量池中。
在这里我们要明确几个点了:
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个额,希望这个问题有读者多多留言,帮我解答一下这个问题)。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对小牛知识库的支持。如果你想了解更多相关内容请查看下面相关链接
4.1 Trim Trim函数删除给定输入字符串的前导空格和尾随空格。 语法:Trim(String) 4.2 Instr 和 InStrRev InStr函数返回一个字符串第一次出现在一个字符串,从左到右搜索。返回搜索到的字符索引位置。 InStrRev函数与InStr功能相同,从右到左搜索。返回搜索到的字符索引位置。 语法:InStr([start, ]string1, string2[, c
目标是在两个给定的Strings和中找到共同的字母。 常见符号的字符串必须至少有两个字母长。 步骤: 我是一个初学者,在这个案子上挣扎了4个小时。有人能帮我吗?很高兴能收到java的解决方案。 提前谢谢。
问题内容: 我正在尝试 使用循环从两个不同的用户输入中打印常用字母。(我需要使用for循环来完成它。)我遇到了两个问题:1.我的语句“ If char not in output …”没有提取唯一值。2.输出为我提供了单个字母列表,而不是单个字符串。我尝试分割输出,但是分割遇到类型错误。 问题答案: 您正在尝试执行“设置相交”。Python有 相同的方法。您可以将其用于您的用例,例如: 将返回字符
本文向大家介绍十个最常见的Java字符串问题(翻译),包括了十个最常见的Java字符串问题(翻译)的使用技巧和注意事项,需要的朋友参考一下 翻译自:Top 10 questions of Java Strings 1.怎样比较字符串?用”==”还是用equals()? 简单地说,”==”测试两个字符串的引用是否相同,equals()测试两个字符串的值是否相同。除非你希望检查两个字符串是否是同一个对
配置了策略,一直没有报警,如何排查? 排查sender、alarm、judge、hbs、agent、transfer的log 浏览器访问alarm的http页面,看是否有未恢复的告警,如果有就是生成报警了,后面没发出去,很可能是邮件、短信发送接口出问题了,检查sender中配置的api 打开agent的debug,看是否在正常push数据 看agent配置,是否正确配置了heartbeat(hbs
问题内容: 我是学习Java的C ++人。我在读《有效的Java》,使我有些困惑。它说永远不要写这样的代码: 因为它创建了不必要的String对象。但是应该这样写: 到目前为止还可以…但是,考虑到此类: 为什么第一个陈述可以?不是吗 我如何使行为像这样,使上面的语句可以正常运行(带有和不带有)?字符串到底有什么用,它能够像这样传递文字就可以了吗?据我了解,Java中没有“复制构造函数”的概念吗?
问题内容: 上面的代码片段中的代码产生以下输出。 a =’Hello’和b =’Hello’的长度分别为6和6,equals()为false 虽然两者的价值,并在控制台上显示的是,回报。怎么样? 问题答案: 和是 不是 可打印字符。它们都是控制字符,它们决定了文本应如何呈现-从左到右或从右到左。 您不会在终端中看到它们,并且它们不应该是等效的字符串。
问题内容: 这与Java字符串常量池有关。在我的一个程序中,我正在解密数据库的密码并将其存储在字符串中。我听说Java字符串将存储在Constant池中,并且不会被VM重新启动或加载了String Quits的ClassLoader破坏。 如果是这种情况,我的密码将存储在字符串池中。我非常关心这个问题。还有其他方法可以销毁这些文字或我可以做的其他任何事情。 请对此提出建议, 问候,阳光明媚。 问题