首页 >> 大全

金九银十,收下这份 Java String 面试题

2024-01-08 大全 46 作者:考证青年

请点赞关注,你的支持对我意义重大。

前言

过去两年,我们在掘金平台上发布 专栏文章,小彭也受到了大家的意见和鼓励。最近,小彭会陆续搬运到公众号上。

在每种编程语言里,字符串都是一个躲不开的话题,也是面试常常出现的问题。在这篇文章里,我将总结Java 字符串中重要的知识点 & 面试题,如果能帮上忙,请务必点赞加关注,这真的对我非常重要。

学习路线图:

1. C 和 Java 中字符串和字符数组的对比 1.1 内存表示不同

java.lang.

public final class String {private final char value[];private int hash;...
}

1.2 char 类型的数据长度 语言类型存储空间(字节)最小值最大值

Java

char

65535

char(相当于 char)

-128

127

char

-128

127

char

255

2. 为什么 Java 9 内部将 char 数组改为 byte 数组?

Java 的内存表示本质上是基于UTF-16 BE编码的字符数组。UTF-16 是 2 个字节或 4 个字节的变长编码,这意味着即使是 字符集的拉丁字母,使用 ASCII 编码只需要一个字节,但是在 中需要两个字节的存储空间。

为了优化存储空间,从 Java 9 开始, 内部将 char 数组改为 byte 数组, 会判断字符串中是否只包含拉丁字母。如果是的话则采用单字节编码(Latin-1),否则使用 UTF-16 编码。

.java (since Java 9)

private final byte coder;
static final boolean COMPACT_STRINGS;
static {COMPACT_STRINGS = true;
}
byte coder() {return COMPACT_STRINGS ? coder : UTF16;
}
@Native static final byte LATIN1 = 0;
@Native static final byte UTF16  = 1;

不同编码实现的简单区别如下:

编码格式编码单元长度BOM字节序

UTF-8-无BOM

1 ~ 4 字节

大端序

UTF-8

1 ~ 4 字节

EF BB BF

大端序

UTF-16-无BOM

2 / 4 字节

大端序

UTF-16BE(默认)

2 / 4 字节

FE FF

大端序

UTF-16LE

2 / 4 字节

FF FE

小端序

UTF-32-无BOM

4 字节

大端序

UTF-32BE(默认)

4 字节

00 00 FE FF

大端序

UTF-32LE

4 字节

FF EE 00 00

小端序

关于字符编码的更多内容,见:计算机基础:今天一次把 和 UTF-8 说清楚

3. & & 的区别 3.1 效率

是不可变的,每次操作都会创建新的变量,而另外两个是可变的,不需要创建新的变量;另外, 的每个操作方法都使用 关键字保证线程安全,增加了更多加锁 & 释放锁的时间。因此,操作效率的简单排序为: > > 。

3.2线程安全

不可变,所以 和 都是线程安全的,而 是非线程安全的。

类型操作效率线程安全

安全(final)

安全()

非安全

4. 为什么 设计为不可变类? 4.1如何让 不可变?

《 Java》中可变性最小化原则,阐述了不可变类的规则:

以上规则 均满足。

4.2 为什么 要设计为不可变?

提示:反射可以破坏 的不可变性。

5. + 的实现原理

+操作符是编译器语法糖,编译后会被替换为#(...)语句,例如:

示例程序

// 源码:String string = null;
for (String str : strings) {string += str;
}
return string;// 编译产物:String string = null;
for(String str : strings) {StringBuilder builder = new StringBuilder();builder.append(string);builder.append(str);string = builder.toString();
}// 字节码:0 aconst_null1 astore_12 aload_03 astore_24 aload_25 arraylength6 istore_37 iconst_08 istore 4
10 iload 4
12 iload_3
13 if_icmpge 48 (+35)
16 aload_2
17 iload 4
19 aaload
20 astore 5
22 new #7 
25 dup
26 invokespecial #8 >
29 aload_1
30 invokevirtual #9 
33 aload 5
35 invokevirtual #9 
38 invokevirtual #10 
41 astore_1
42 iinc 4 by 1
45 goto 10 (-35)
48 aload_1
49 areturn

可以看到,如果在循环里直接使用字符串+,会生成非常多中间变量,性能非常差。应该在循环外新建一个,在循环内统一操作这个对象。

6. 对象的内存分配 6.1"abc" 与 new ("abc") 的区别 6.#() 的实现原理

如果字符串常量池中已经包含一个等于此 对象的字符串,则返回常量池中的这个字符串;否则,先将此 对象包含的字符串拷贝到常量池中,在常量池中的这个字符串。

从 JDK 1.7 开始,#()不再拷贝字符串到常量池中,而是在常量池中生成一个对原 对象的引用,并返回。

// 举例:
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);// 输出结果为:
JDK1.6以及以下:false false
JDK1.7以及以上:false true

7. 为什么 #() 要使用 31 作为因子?

public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h;
}

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了