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

请点赞关注,你的支持对我意义重大 。
前言
过去两年 , 我们在掘金平台上发布专栏文章 , 小彭也受到了大家的意见和鼓励 。最近,小彭会陆续搬运到公众号上 。
在每种编程语言里,字符串都是一个躲不开的话题,也是面试常常出现的问题 。在这篇文章里,我将总结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 410 iload 412 iload_313 if_icmpge 48 (+35)16 aload_217 iload 419 aaload20 astore 522 new #7 25 dup26 invokespecial #8 >29 aload_130 invokevirtual #9 33 aload 535 invokevirtual #9 38 invokevirtual #10 41 astore_142 iinc 4 by 145 goto 10 (-35)48 aload_149 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 falseJDK1.7以及以上:false true
7. 为什么 #() 要使用 31 作为因子?
【金九银十,收下这份 Java String 面试题】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;}