java面试必备String详解
引言
众所周知在java里面除了8种基本数据类型的话,还有一种特殊的类型String,这个类型是我们每天搬砖都基本上要使用它。
String 类型可能是 Java 中应用最频繁的引用类型,但它的性能问题却常常被忽略。高效的使用字符串,可以提升系统的整体性能。当然,要做到高效使用字符串,需要深入了解其特性。
String类
我们可以看下String类的源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
从源码上我们是不是可以发现String类是被final关键字所修饰的,String类的数据是通过char[] 数组来存储的。数组也是被final修饰的所以String 对象是不可被更改的。接下来我们再看看String的一些方法:像concat、replace、substring等都是返回了一个新的new String感兴趣的可以去看看String的一些常见方法。当我们执行这些方法之后最原始的字符串是没有改变的,都是返回新的字符串。
public static void main(String[] args) {
String str = new String("java金融");
String str1 = str.substring(0, 4);
String str2 = str.concat("公众号");
String str3 = str.replace("java金融", "关注:【java金融】");
// 还有其他的方法
System.out.println(str1);
System.out.println(str2);
System.out.println(str3);
System.out.println(str);
}
输出结果
java
java金融公众号
关注:【java金融】
java金融
所以我们只要记住一点:“String对象一旦被创建就是固定不变的了,
对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
字符串常量池
在JVM中,为了减少字符串对象的重复创建,维护了一块特殊的内存空间,这块内存就被称为全局字符串常量池(string pool也有叫做string literal pool)。
字符串常量池的位置
字符串常量池所在的位置也是跟不同的jdk版本有关系的。
- 在
JDK6及之前字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代。 - 在
JDK7字符串常量池被从方法区拿到了堆中, 这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代。 - 在
JDK8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆里只不过把方法区的实现从永久代变成了元空间(Metaspace) 。
String# intern
String::intern()是一个本地方法,它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象的引用;
否则,会将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
上述定义出自《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》我们知道了这个 String::intern()这个方法的作用下面来看几道并没有什么用的题目看看你是否都能够回答对?
String str2 = new String("java") + new String("金融"); // 1
str2.intern(); // 2
String str1 = "java金融"; // 3
System.out.println(str2 == str1);
这个代码在JDK6中输出结果是false,在jdk7输出是true。
为何会因为不同的jdk版本输出结果不一样,因为不同版本字符串常量池的位置发生了变化。
下面来分析下为何会产生这种差异。
字符串虽然不属于基本数据类型但是它也可以想基本类型一样,直接通过字面量来赋值,同时也是可以通过new 来生成字符串对象。通过字面量赋值的方式和new 的方式 生成字符串还是有区别的。
- 字面量赋值:通过字面量赋值(使用双引号声明出来的
String)会先去常量池中查找是否已经有相同的字符串,如果已经存在栈中的引用直接指向该字符串,如果不存在就在常量中生成一个字符串再将栈中的引用指向该字符串。 new的方式创建:而通过new的方式创建字符串时,就直接在堆中生成一个字符串的对象栈中的引用指向该对象。对于堆中的字符串对象,可以通过intern() 方法来将字符串添加的常量池中,并返回指向该常量的引用。
jdk6结果是false,是因为常量池是在永久代的Perm区和java堆是两个区域。所以两个区域的对象地址比较是不同的。
JDK7结果是true, 这个原因主要是从JDK 7及以后,HotSpot将常量池从永久代移到了堆,正因为如此,JDK7及以后的intern方法在实现上发生了比较大的改变,JDK7及以后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于如果在常量池找不到对应的字符串则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。所以为什么返回true 是因为执行完标号为1的时候常量池中没有"java金融"对象的,接下来标号为2的时候 会在常量池生成一个“java金融”的对象会直接存一个对堆中“java金融”的引用,标号为3:进行字面量赋值的时候常量池已经存在了所以直接返回该引用。所以都是指向堆中的字符串返回true。
如果把3行代码放到第一行上面结果又不一样了,感兴趣的可以动手试一试并且分析下原因哦。
string 常见性能优化
使用+号拼接字符串
字符串拼接是我们平时在代码中使用最频繁的了。
- +号拼接静态字符串
String str = "关注"+"公众号:"+"java金融";
我们可以通过反编译查看下上述代码:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=1
0: ldc #2 // String 关注公众号:java金融
2: astore_1
3: return
LineNumberTable:
line 11: 0
line 12: 3
}
我们可以发现编译器直接帮我们优化了,直接生成了一个字符串“关注公众号:java金融” 并没有生成中间变量的String实例。如果我们上述代码稍微变化下
public static void main(String[] args) {
String str ="关注";
String str1 = str + "公众号:java金融";
}
stack=2, locals=3, args_size=1
0: ldc #2 // String 关注
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String 公众号:java金融
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: return
LineNumberTable:
line 11: 0
line 12: 3
line 13: 23
从反编译代码中我们会发现生成了StringBuilder对象来进行追加。
- 所以
String+拼接变量的时候底层是通过StringBuilder来实现的,我们循环操作拼接字符串的时候也应当使用StringBuilder替代+,否则的话每一次循环都会创建
一个StringBuilder对象。 - 对于静态字符串的拼接操作,Java在编译时会进行彻底的优化,会把多个拼接字符串在编译时合成一个单独的长字符串。
常见字符串经典面试题
关于字符串最常见的面试题,面试宝典常见的题目。
String s = new String("xyz") 创建了多少个实例?
一般的回答都会是2个,(一个是“xyz”,一个是指向“xyz”的引用对象s)
答案并没有那么简单哦,可以看看大佬的回答还是非常精彩的。
连接地址https://www.iteye.com/blog/rednaxelafx-774673(文末第一个参考地址)
结束
- 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
- 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
- 感谢您的阅读,十分欢迎并感谢您的关注。
https://www.iteye.com/blog/rednaxelafx-774673
https://www.zhihu.com/question/36908414/answer/69724311
https://www.cnblogs.com/paddix/p/5326863.html
https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
https://www.jianshu.com/p/6bee67a7f6ce
https://juejin.im/post/6844903741032759310
java面试必备String详解的更多相关文章
- 面试必备:详解Java I/O流,掌握这些就可以说精通了?
@TOC Java IO概述 IO就是输入/输出.Java IO类库基于抽象基础类InputStream和OutputStream构建了一套I/O体系,主要解决从数据源读入数据和将数据写入到目的地问题 ...
- Java面试宝典答案详解与感悟(第一天)
一.Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法,虚拟机方面的语法. 1.一个".java&quo ...
- Java面试宝典答案详解与感悟(第二天)
19.构造器 Constructor 是否可被 override? 答案:构造器Constructor不能被继承,因此不能重写Override,但是可以被重载Overload. 解析:构造器:在面向对 ...
- Java的String详解
Java的String详解 博客分类: Java javaStringString详解常用方法 Java的String类在开发时经常都会被使用到,由此可见String的重要性.经过这次认真仔细的学习 ...
- java反射机制深入详解
java反射机制深入详解 转自:http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html 一.概念 反射就是把Java的各种成分映射成 ...
- Java 8 Stream API详解--转
原文地址:http://blog.csdn.net/chszs/article/details/47038607 Java 8 Stream API详解 一.Stream API介绍 Java8引入了 ...
- 国际化,java.util.ResourceBundle使用详解
java.util.ResourceBundle使用详解 一.认识国际化资源文件 这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以: 轻松地本地化或翻译成不同的 ...
- java之StringBuffer类详解
StringBuffer 线程安全的可变字符序列. StringBuffer源码分析(JDK1.6): public final class StringBuffer extends Abstract ...
- java.util.ResourceBundle使用详解
java.util.ResourceBundle使用详解 一.认识国际化资源文件 这个类提供软件国际化的捷径.通过此类,可以使您所编写的程序可以: 轻松地本地化或翻译成不同的 ...
随机推荐
- pthread 读写锁
pthread 读写锁 (Read Write Lock, rwlock) 把对共享资源的访问者分为读者和写者,读者仅仅对共享资源进行读访问,写者仅仅对共享资源进行写操作. 如果使用互斥量 mutex ...
- (十九)hashlib模块
hashlib模块用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法 注意:md5和sha25 ...
- (十三)利用BASE_DIR来import模板
实际工程的组织架构一般是这样的: bin包下的bin.py是实际的执行文件,my_mould包下的是业务逻辑的实现模板 bin.py需要import my_mould下的py文件,而bin和my_mo ...
- 【Linux】CentOS4 系统最后的网络yum源
------------------------------------------------------------------------------------------------- | ...
- 【TNS】listener.ora模板;tnsnames.ora模板
好多人使用监听的时候误操作,将监听弄的不好使了,这次这个模板,不光是针对大家出现的这种问题,也是给我自己留一个记录,方便他人,方便自己. listener.ora模板样例 -------------- ...
- XSS类型,防御及常见payload构造总结
什么是XSS? XSS全称是Cross Site Scripting即跨站脚本,当目标网站目标用户浏览器渲染HTML文档的过程中,出现了不被预期的脚本指令并执行时,XSS就发生了. 最直接的例子:&l ...
- oracle绑定变量测试及性能对比
1.创建测试数据 2.查看cursor_sharing的值 SQL> show parameter cursor_sharing; NAME TYPE VALUE --------------- ...
- 带你走进memcache,老牌内存缓存技术
一.核心优化概述 什么是优化:以更小的资源支持更大负载网站的运行,以小博大. 思路:尽量减少用户等待时间,节省系统资源开销,节省带宽使用. 优化什么地方?有三方面:Memcache内存缓存技术.静态化 ...
- 2021年【线上】第一性原理vasp技术实战培训班
材料模拟分子动力学课程 3月19号--22号 远程在线课 lammps分子动力学课程 3月12号--15号 远程在线课 第一性原理VASP实战课 3月25号-28号 远程在线课 量子化学Gaussia ...
- 零基础怎么学Python编程,新手常犯哪些错误?
Python是人工智能时代最佳的编程语言,入门简单.功能强大,深获初学者的喜爱. 很多零基础学习Python开发的人都会忽视一些小细节,进而导致整个程序出现错误.下面就给大家介绍一下Python开发者 ...