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个额,希望这个问题有读者多多留言,帮我解答一下这个问题)。

总结

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

(0)

相关推荐

  • Java 中运行字符串表达式的方法

    在日常的开发中,偶尔会遇到运行字符串表达式的情况,通常这样的需求会对需求进行进一步分析,然后进行进一步 "特殊化",最后直接写到硬代码中,这样做的话,就不太好扩展了:也有另外的处理方式是采用 Java 内置的 JavaScript 引擎等运行字符串表达式,但是内置引擎也有弊端,比如频繁运行片段式的字符串的效率非常低,并且与 Java 之间的数据交互比较麻烦,于是,便产生了写一个"字符串表达式计算引擎"的想法... 写的过程其实没想象中那么麻烦,最初版大概在今年 5

  • java转换字符串编码格式的方法

    java转换字符串编码格式 (解码错误,重新解码) 字符集概念:规定了某个文字对应的二进制数字存放方式(编码)和某串二进制数值代表了哪个文字(解码)的转换关系. 我们在计算机屏幕上看到的是实体化的文字,而在计算机存储介质中存放的实际是二进制的比特流. 乱码场景(纯属瞎掰): 1) 前台输入utf-8编码的一串汉字(string1). (页面编码为utf-8, 在内存中会将这串汉字以utf-8编码为对应的二进制流存储) 2) 这串汉字(string1)的二进制流在经过http协议传输到后台时,这段

  • Java实现Json字符串与Object对象相互转换的方式总结

    本文实例总结了Java实现Json字符串与Object对象相互转换的方式.分享给大家供大家参考,具体如下: Json-Lib.Org.Json.Jackson.Gson.FastJson五种方式转换json类型 只列举了最省事的方式.不涉及复制情况和速度. 测试用例,一个User类,属性name,age,location.重写toString(). public class User { private String name; private Integer age; private Stri

  • Java判断字符串是否含有乱码实例代码

    具体代码如下所示: /** * 判断字符是否是中文 * * @param c 字符 * @return 是否是中文 */ public static boolean isChinese(char c) { Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || ub == Character.UnicodeBlock.C

  • java字符串的重要使用方法以及实例

    1.返回String"长度"方法 你如何确定给定String的长度?java提供了一种称为"length()"的方法.将它用于您需要查找String的长度. public class Str_Sample { public static void main(String[] args){ //测试String长度的方法 String name="hello work"; //length方法返回的是整数 int num=name.length()

  • Java中字符数组和字符串与StringBuilder和字符串转换的讲解

    1.字符串->字符数组: String str = "abc": char[] a = str.toCharArray(); 记忆:字符串是个类,所以用内建函数 延伸: char b = str.charAt(1); str.length(); a.length; 2.字符数组->字符串: String str = String.valueOf(a): 记忆:类似强制类型转换格式,String(a) 延伸:字符转字符类 Character c = Character.val

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

    今天给大家带来的是在面试中经常被问到的一道题: 无论在Java还是Android中,String是一个很常见的类,但是大家真的很了解吗,我这里有几个题: 1. String str1 = "abc"; String str2 = new String("abc"); 这两种创建String对象的方法有什么不同? 2. String s = "a" + "b" + "c" + "d"; 这

  • Java中字符串的一些常见方法分享

    1.Java中字符串的一些常见方法 复制代码 代码如下: /** *  */package com.you.model; /** * @author Administrator * @date 2014-02-24 */public class Replace { /**  * @param args  */ public static void main(String[] args)  {  /**   * 原字符串   */  String str = "78454545855ksdjnf

  • Java中字符串String的+和+=及循环操作String原理详解

    String对象是不可变的:意思就是无论是对String的新增或修改,出现一个全新的String内容时,都意味着诞生了一个新的对象.但是如果内容不变的话,增加的只是对象的引用而已. 例如: String a = "ljh"; String b = "ljh"; String c = "ljh"; System.out.println(a==b); System.out.println(b==c); 结果都是true 但是这种不可变性会产生一些性能

  • 基于Java中字符串内存位置详解

    前言 之前写过一篇关于JVM内存区域划分的文章,但是昨天接到蚂蚁金服的面试,问到JVM相关的内容,解释一下JVM的内存区域划分,这部分答得还不错,但是后来又问了Java里面String存放的位置,之前只记得String是一个不变的量,应该是要存放在常量池里面的,但是后来问到new一个String出来应该是放到哪里的,这个应该是放到堆里面的,后来又问到String的引用是放在什么地方的,当时傻逼的说也是放在堆里面的,现在总结一下:基本类型的变量数据和对象的引用都是放在栈里面的,对象本身放在堆里面,

  • java中字符串转整数及MyAtoi方法的实现

    java中字符串转整数及MyAtoi方法的实现 该题虽然和我们正常使用的字符串转整数的API中函数不一致,但是通过增加了很多额外的边界或者异常处理,可以锻炼算法思维的敏锐性和处理边界异常等问题的能力. 思路:字符串题一般考查的都是边界条件.特殊情况的处理.所以遇到此题一定要问清楚各种条件下的输入输出应该是什么样的. 这里已知的特殊情况有: 能够排除首部的空格,从第一个非空字符开始计算 允许数字以正负号(+-)开头 遇到非法字符便停止转换,返回当前已经转换的值,如果开头就是非法字符则返回0 在转换

  • Java中两个大数之间的相关运算及BigInteger代码示例

    Java中两个大数之间的相关运算及BigInteger两段实例代码,具体如下. 大数相减 import java.util.Scanner; /* 进行大数相减,只能对两个正数进行相减 */ public class BigNumber { public static void main(String[] args) { Scanner scan=new Scanner(System.in); String a,b; while (scan.hasNext()) { BigNumber big=

  • Java中StringUtils工具类进行String为空的判断解析

    判断某字符串是否为空,为空的标准是str==null或str.length()==0 1.下面是StringUtils判断是否为空的示例: StringUtils.isEmpty(null) = true StringUtils.isEmpty("") = true StringUtils.isEmpty(" ") = false //注意在 StringUtils 中空格作非空处理 StringUtils.isEmpty(" ") = fals

  • 一文解开java中字符串编码的小秘密(干货)

    简介 在本文中你将了解到Unicode和UTF-8,UTF-16,UTF-32的关系,同时你还会了解变种UTF-8,并且探讨一下UTF-8和变种UTF-8在java中的应用. 一起来看看吧. Unicode的发展史 在很久很久以前,西方世界出现了一种叫做计算机的高科技产品. 初代计算机只能做些简单的算数运算,还要使用人工打孔的程序才能运行,不过随着时间的推移,计算机的体积越来越小,计算能力越来越强,打孔已经不存在了,变成了人工编写的计算机语言. 一切都在变化,唯有一件事情没有变化.这件事件就是计

  • 详细图解Java中字符串的初始化

    目录 前言 常量池 反编译代码验证字符串初始化操作 总结 前言 在深入学习字符串类之前,我们先搞懂JVM是怎样处理新生字符串的.当你知道字符串的初始化细节后,再去写String s = "hello"或String s = new String("hello")等代码时,就能做到心中有数. 首先得搞懂字符串常量池的概念,下面进入正文吧. 常量池 把经常用到的数据存放在某块内存中,避免频繁的数据创建与销毁,实现数据共享,提高系统性能. 八种基础数据类型除了float和

  • 一文详解Java中字符串的基本操作

    目录 一.遍历字符串案例 二.统计字符次数案例 三.字符串拼接案例 四.字符串反转案例 五.帮助文档查看String常用方法 一.遍历字符串案例 需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串 思路: 1.键盘录入一个字符串,用 Scanner 实现 2.遍历字符串,首先要能够获取到字符串中的每一个字符 public char charAt(int index):返回指定索引处的char值,字符串的索引也是从0开始的 3.遍历字符串,其次要能够获取到字符串的长度 public int

随机推荐