基础面试,为何面试官总喜欢问String?

关于 Java String,这是面试的基础,可是还有不少童鞋不能说清楚,因此本文将简单而又透彻的说明一下那个让你迷惑的 Stringjava

在 Java 中,咱们有两种方式建立一个字符串面试

String x = "abc";
String y = new String("abc");复制代码

你常见也常写第一种,不多见第二种,但面试还总问这类问题,双引号和构造器两种形式建立字符串到底有什么差异呢?缓存

先来看例子安全

例子 1

String a = "abcd";
String b = "abcd";
System.out.println(a == b);  // True
System.out.println(a.equals(b)); // True复制代码

a == b 结果为 true,是由于 a 和 b 都指向 方法区(method area) 同一个字符串文字,内存引用是同一个网络

当屡次建立相同的字符串文字时,只存储每一个不一样字符串值的一个副本。这个叫作字符串留驻/留用,Java 中全部编译期字符串常量都会被自动留驻函数

例子 2

String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d);  // False
System.out.println(c.equals(d)); // True复制代码

c==d 结果为 false,由于 c 和 d 的引用指向中不一样的对象,不一样的对象确定有不一样的内存引用工具

举了两个例子,文字描述有点懵?咱们来试图经过图形来理解上述两种状况:学习

也许你已经看看出来了,一个是在方法区,一个是在中,在 JVM 模型中这是两个不一样的区域,也许你面试时也常常被问到吧,来看下图:ui

再次提醒一下,全部 new 的对象都会在 Heap 中,这样之后你就好区分了spa

运行期字符串留驻

上面说的字符串留驻是在编译期,那么运行期能够吗?答案是确定的,咱们须要一个函数来帮忙

String c = new String("abcd").intern();
String d = new String("abcd").intern();
System.out.println(c == d);  // Now true
System.out.println(c.equals(d)); // True复制代码

看到 c == d 结果为 true,你应该理解 intern (英文有拘留,软禁的意思)的做用了,经过调用 intern()方法,就比如把建立的字符串拘留在方法区同样了

在面试时甚至还会问你下面代码建立了几个对象:

String d = new String("abcd")复制代码
  1. 若是方法区已存在"abcd", 那么只建立一个 new String 的对象
  2. 若是方法区没有"abcd", 那么要建立两个对象,一个在方法区,一个在堆中

因此,正常状况下咱们不必使用构造器建立对象,由于这极可能会产生一个额外的没用的对象,可是有例外哦,咱们下面说

String s = "abcd";
s = s.concat("ef");复制代码

当咱们想在字符串 s 后面拼接字符"ef"时,会在堆中建立一个新的对象,并将 s 的引用指向新建立的对象,因为 String 建立的是不可变对象,因此 String 类中的全部方法都不会改变它自身,而是返回一个新的字符串(快打开你的 IDE,看看是否每一个操做String 的方法最后都是返回有 return new String 字样),到这里你也应该理解了一个道理:

若是咱们须要一个字符串被修改,咱们最好使用 StringBuffer 或者 StringBuilder,不然,因为每次操做字符串都会建立一个新的对象,而旧的对象不会有引用指向它,这样咱们会浪费不少垃圾回收的时间

到这里还没完,你有没有想过为何 String 会被设置/制形成 final?

为何 String 类被 final 修饰

谈及这个问题咱们须要一些倒推的或者相互约束思惟来思考

字符串池的需求

字符串池(String intern pool)是方法区域中的一个特殊存储区域。当建立一个字符串时,若是该字符串已经存在于池中,那么返回现有字符串的引用,而不是建立一个新对象。因此说,若是一个字符串是可变的,那么改变一个引用的值,将致使本来指向该值的引用获取到错误的值

缓存 hashcode

字符串的hashcode在Java中常用。例如,在HashMap或HashSet中。不可变保证hashcode始终是相同的,这样就能够在不担忧更改的状况下兑现它。这意味着,不须要每次使用hashcode时都计算它。这样更有效率。因此你会在 String 类中看到下面的成员变量的定义:

/** Cache the hash code for the string */
private int hash; // Default to 0复制代码

安全性

String被普遍用做许多java类的参数,例如网络链接、打开文件等。若是字符串不是不可变的,链接或文件将被更改,这可能致使严重的安全威胁。该方法认为它链接到一台机器上,但实际上并无。可变字符串也可能致使反射中的安全问题,由于参数是字符串

不可变对象天生是线程安全的

因为不可变对象不能被更改,因此它们能够在多个线程之间自由共享。这消除了同步的需求。

总之,出于效率和安全性的考虑,String 被设计为不可变的。这也是为何在通常状况下,不可变类是首选的缘由。

附加说明

关于不可变对象和不可变引用老是有同窗搞不清楚

final User user = new User();复制代码

上面的代码指的是 user 引用不能被更改指向内存的其余地址,可是因为 User 是可变对象,咱们能够调用 user 的 setter 方法修改其属性

在String类中包含不少学问,包括你对JVM模型的理解,这也就是为何面试官为何喜欢问String,主要考察你的基本功

灵魂追问

  1. String 和基本类型的包装类如 Integer 和 Long 都被 final 修饰,但为何不建议做为 synchronized 同步块的参数适用呢?
  2. 基本类型自动装箱你知道发生了什么吗?和上一个问题有关系

提升效率工具

Material Theme UI

这是一款 IDEA 的主题插件,安装后,选择 Material Palenight 主题,同时做出以下设置

设置完后,你的 IDEA 就是下面这样,引发极度温馨

推荐阅读

--------

欢迎持续关注公众号:「日拱一兵」

- 前沿 Java 技术干货分享

- 高效工具汇总 | 回复「工具」

- 面试问题分析与解答

- 技术资料领取 | 回复「资料」

以读侦探小说思惟轻松趣味学习 Java 技术栈相关知识,本着将复杂问题简单化,抽象问题具体化和图形化原则逐步分解技术问题,技术持续更新,请持续关注......

相关文章
相关标签/搜索