面试总是问java基础_基础面试,为什么面试官总喜欢问String?

关于 java string,这是面试的基础,但是还有很多童鞋不能说清楚,所以本文将简单而又透彻的说明一下那个让你迷惑的 string

cb8b5220d51ca293273e1dc0858c7426.png

在 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 的引用指向堆中不同的对象,不同的对象肯定有不同的内存引用

举了两个例子,文字描述有点懵?我们来试图通过图形来理解上述两种情况:

646def2886f04ad1e315c1c71625454c.png

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

d2c9fe234af0629be4fca4df944e22c2.png

再次提醒一下,所有 new 的对象都会在 heap 中,这样以后你就好区分了

运行期字符串留驻

上面说的字符串留驻是在编译期,那么运行期可以吗?答案是肯定的,我们需要一个函数来帮忙

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")

如果方法区已存在"abcd", 那么只创建一个 new string 的对象

如果方法区没有"abcd", 那么要创建两个对象,一个在方法区,一个在堆中

所以,正常情况下我们没必要使用构造器创建对象,因为这很可能会产生一个额外的没用的对象,但是有例外哦,我们下面说

string s = "abcd";

s = s.concat("ef");

d3580fbc139183918dcf04fe99d6b721.png

当我们想在字符串 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,主要考察你的基本功

灵魂追问

string 和基本类型的包装类如 integer 和 long 都被 final 修饰,但为什么不建议作为 synchronized 同步块的参数适用呢?

基本类型自动装箱你知道发生了什么吗?和上一个问题有关系

提高效率工具

9a0046d49d6b74bec37a8e7dd7063b40.png

material theme ui

这是一款 idea 的主题插件,安装后,选择 material palenight 主题,同时作出如下设置

b6ff4732e98f82fdd6fa41936892c518.png

设置完后,你的 idea 就是下面这样,引起极度舒适

960d0d19e093be4a13c90733dd10586c.png

推荐阅读

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

前沿 java 技术干货分享

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

面试问题分析与解答

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

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

希望与广大网友互动??

点此进行留言吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值