Java String,看这篇就够了

蚀纹容器
• 阅读 1112

String,是Java中最重要的类。这句肯定的推断不是Java之父詹姆斯·高斯林说的,而是沉默王二说的,因此你不必怀疑它的准确性。

关于字符串,有很多的面试题,但我总觉得理论知识绕来绕去没多大意思。你比如说:String cmower = new String("沉默王二");定义了几个对象?

我总觉得问我这样的问题,就好像是在拷问我:“既然你家买了冰箱,你难道不应该知道冰箱制冷的原理?”

再说,为什么要用String cmower = new String("沉默王二");而不是String cmower = "沉默王二";

我劝各位面试官不要再缠住这样的问题不放了,切记“学以致用”。理论知识如果一直是在绕弯弯,那真的毫无价值。如果要我来做面试官,我想要问的问题是:“你平常是怎么判断两个字符串相等的?是用equals()还是==?”

前言就说这么多。接下来,我们来探讨几个实用的知识点。

01、 字符串是不可变的

我们来看一下String类的定义:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
}

可以发现,String类是final类型的,因此不能被继承。

如果类可以被继承,那么就会破坏类的不可变性机制。因为子类可以覆盖父类的方法,并且可以改变父类的成员变量值,一旦子类以父类的形式出现时,就不能保证类是不可变的。

String类的不可变性有什么好处呢?

1)作为HashMap的键。

因为字符串是不可变的,因此它在创建的时候哈希码(hash code)就计算好了。这也就意味着每次在使用一个字符串的哈希码的时候不用重新计算一次,这样更加高效,很适合作为HashMap中的键。

2)线程安全。

同一个字符串对象可以被多个线程共享,如果访问频繁的话,可以省略同步和锁等待的时间,从而提升性能。

3)字符串常量池的需要。

特别要注意的是,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象

推荐阅读:为什么 Java 字符串是不可变的?

02、 字符串常量池

在Java中,常用的创建字符串的方式有两种:

String cmower = "沉默王二";

String cmowsan = new String("沉默王三");

cmower使用双引号,cmowsan使用new关键字,它们有什么区别呢?

答案如下:

String cmower = "沉默王二";
String cmower1 = "沉默王二";
System.out.println(cmower == cmower1); // 输出true

String cmowsan = new String("沉默王三");
String cmowsan1 = new String("沉默王三");
System.out.println(cmowsan == cmowsan1); // 输出false

双引号创建的相同字符串使用==判断时结果为true,而new关键字创建的相同字符串使用==判断时结果为false。

这是为什么呢?

String在Java中使用过于频繁,为了避免在系统中产生大量的String对象,Java的设计者引入了“字符串常量池”的概念

当使用双引号创建一个字符串时,首先会检查字符串常量池中是否有相同的字符串对象,如果有,则直接从常量池中取出对象引用;如果没有,则新建字符串对象,并将其放入字符串常量池中,并返回对象引用。

这也就是说,"沉默王二"是放在字符串常量池中的,cmower和cmower1两个字符串对象引用是相同的。

而new关键字创建的字符串对象是不涉及字符串常量池的,直接放在堆中,也就是说,虽然cmowsan和cmowsan1都叫沉默王三,但不一个人。

强烈建议:不要使用new关键字的形式创建字符串对象。

03、 +号和StringBuilder

由于字符串是不可变的,因此字符串在进行拼接的时候会创建新的字符串对象。大家都知道,内存是一定的,因此对象创建多了就会影响系统性能。

StringBuilder正是为了解决字符串拼接产生太多中间对象的问题而提供的一个类,可以通过append()方法把字符串添加到已有序列的末尾,非常高效。

那么有人在进行字符串拼接的时候,就会产生疑惑:“我到底是用+号还是StringBuilder?”

我们先来看这样一段代码:

String chenmo = "沉默";
String wanger = "王二";
System.out.println(chenmo + wanger);

这段代码是怎么编译的呢?可以使用JAD(Java反编译工具)来看一看。

String s = "\u5A0C\u5910\u7CAF";
String s1 = "\u941C\u5B29\u7C29";
System.out.println((new StringBuilder()).append(s).append(s1).toString());

你是不是看到了StringBuilder的影子?

没错,使用+号进行字符串拼接的时候,Java编译器实际是通过StringBuilder类来完成的。

难道可以使用+号来随意拼接字符串?反正Java编译器已经自动地为我们优化了。

但事实并非如此,来看这样一段代码:

String cmowers = "";
for (int i = 0; i < 9; i++) {
    cmowers += "沉默王二";
}
System.out.println(cmowers);

闭上眼睛先想一想,Java编译器会怎么做?我们期望的结果是在循环外部就创建StringBuilder,Java编译器能如我们所愿吗?

JAD反编译后的结果如下:

String s = "";
for(int i = 0; i < 10; i++)
    s = (new StringBuilder()).append(s).append("\u5A0C\u5910\u7CAF\u941C\u5B29\u7C29").toString();

System.out.println(s);

这么看来,StringBuilder是在for循环内部创建的,也就是说会创建10次。天呐,这可不是我们期望的结果!我们只希望StringBuilder创建一次。

没办法,Java编译器是做不到的,只能靠我们自己:

StringBuilder cmowers = new StringBuilder();
for (int i = 0; i < 9; i++) {
    cmowers.append("沉默王二");
}
System.out.println(cmowers);

强烈建议:如果只是三四个字符串的拼接,尽管使用+号操作符,别想什么性能优化(举个例子,你离目的地只有100米,你是打算打个出租车,还是自己步行走过去?);如果遇到多于四个字符串的拼接,或者需要用到循环来拼接,那就选择StringBuilder。

在我年轻的时候,我还会犯这样一个错误:

StringBuilder cmowers = new StringBuilder();
for (int i = 0; i < 9; i++) {
    cmowers.append("沉默王二" + "和他的读者朋友们");
}
System.out.println(cmowers);

我去,竟然在append()方法的内部使用+号!因为这个错误,我差点没被领导打死。你可要小心点。

04、 关于concat()

除了使用+号和StringBuilder对字符串进行拼接,还可以使用String类的concat()方法。

concat()方法只不过是String类的一个方法而已,为什么我要单独拎出来说呢?

因为之前我要在JSP页面的EL表达式中拼接字符串,刚开始想到的是用+号操作符,但EL表达式不是Java,+号操作符是不能拼接字符串的。我当时竟然没想起来用concat()

重新铭记一下:

${item.username.concat('-').concat(item.realname)}

05、 关于intern()

关于字符串的性能问题,我常在一些技术文章中看到这样的建议:“如果一个字符串使用的频率非常高,建议使用String.intern()将其缓存。”

但我并不建议你这么做,因为这个方法要显式的调用,这样很麻烦;况且,在代码编写阶段,怎么可能知道哪个字符串使用频率很高呢?

06、 关于StringUtils

据我的编程经验来看,字符串的操作往往需要用到一个工具类,那就是org.apache.commons.lang3.StringUtils(null安全的,也就是说,StringUtils类的方法可以接受为null的字符串,但不会抛出NullPointerException)。

不过,我最常用的方法就那么几个:

方法 等价
IsEmpty(String str) str == null or str.length == 0
isBlank(String str) str == null or str.length == 0 or str.trim().length == 0
join(Object[] arrey) 把数组中的元素连接成一个字符串返回

上一篇:Java内部类

下一篇:Java 数组,看这篇就够了

微信搜索「沉默王二」公众号,关注后回复「免费视频」获取 500G Java 高质量教学视频(已分门别类)。

点赞
收藏
评论区
推荐文章
秋刀鱼 秋刀鱼
3年前
Java字符串:内存分析/方法梳理
近期在学习Java字符串,梳理总结一下,更多是关于内存角度去分析,希望对你有帮助。一、Java字符串的特性1.java.lang.String使用的final修饰,不能被修饰2.字符串底层封装了字符数组以及针对字符串数组的操作算法3.Java使用的是Unicode编码方式,任何一个字符对应两个字节的编码4.字符串一旦创建,对象内容不能改变,
推荐学java 推荐学java
3年前
一文回顾 Java 入门知识(上)
Java前世今生Java最早是由SUN公司(已被Oracle收购)的詹姆斯·高斯林(高司令,人称Java之父)在上个世纪90年代初开发的一种编程语言,最初被命名为Oak,目标是针对小型家电设备的嵌入式应用,结果市场没啥反响。谁料到互联网的崛起,让Oak重新焕发了生机,于是SUN公司改造了Oak,在1995年以Java的名称正式发布,原因是Oak已经被人注册了
Wesley13 Wesley13
3年前
java基础语法详细介绍
一.概述1.java语言概述1.是SUN(StanfordUniversityNetwork,斯坦福大学网络公司)1995年推出的一门高级编程语言;java之父JamesGosling(詹姆斯.高斯林).2.应用领域:JavaSE(JavaSt
东方客主 东方客主
4年前
一文弄懂Java中String的所有小秘密
简介String是java中非常常用的一个对象类型。可以说java中使用最多的就是String了。那么String到底有哪些秘密呢?接下来本文将会一一讲解。String是不可变的String是不可变的,官方的说法叫做immutable或者constant。String的底层其实是一个Char的数组。priv
Wesley13 Wesley13
3年前
@程序员,你该如何选择技术方向?
👆 “沉默王二”,你值得星标的公众号!(https://oscimg.oschina.net/oscnet/faa367fb9d15b4b3d7704b78ffedbed1584.jpg)01、困惑的提问昨天,读者小何在微信上问了我这样一个问题:二哥,我刚参加工作一年,感觉什么也不精通。大学学的是Java,但这一
Wesley13 Wesley13
3年前
Java泛型的重要目的:别让猫别站在狗队里
《Java编程思想》第四版足足用了75页来讲泛型——厚厚的一沓内容,很容易让人头大——但其实根本不用这么多,只需要一句话:我是一个泛型队列,狗可以站进来,猫也可以站进来,但最好不要既站猫,又站狗!01、泛型是什么泛型,有人拆解这个词为“参数化类型”。这种拆解其实也不好理解,还是按照沉默王二的意思来理解一下吧。现在有一只玻璃杯,你可
Wesley13 Wesley13
3年前
Java学习:字符串概述与特点
字符串概述与特点java.lang.String类代表字符串API当中说:Java程序中的所有字符串字面值(如“abc“)都作为此类的实例实现。其实就是说:程序当中所用的双引号字符串,都是String类的对象。(就算没有new,也照样是。)字符串的特点1.字符串的内容永远不可改变。【重点】
Wesley13 Wesley13
3年前
2020 年最牛逼的 10 门编程语言
先看再点赞,给自己一点思考的时间,微信搜索【沉默王二】关注这个有颜值却假装靠才华苟且的程序员。本文GitHubgithub.com/itwanger(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fitwanger%2Fitwa
Wesley13 Wesley13
3年前
Java中的字符串的最大长度
Java中的字符串的最大长度看String的源码可以看出来,String实际存储数据的是charvalue\\,数组的长度是int类型,整数在java中是有限制的,我们通过源码来看看int类型对应的包装类Integer可以看到,其长度最大限制为2^311,那么说明了数组的长度是0~2^311,那么计算一下就是(2^31121474
Wesley13 Wesley13
3年前
Java语言的发展史
  Java的发明人詹姆斯·高斯林  Java的名字来自于一种咖啡的品种名称,所以Java语言的Logo是一杯热气腾腾的咖啡。  Java是一种广泛使用的计算机编程语言,拥有跨平台、面向对象、泛型编程的特点,广泛应用于企业级Web应用开发和移动应用开发。  任职于太阳微系统的詹姆斯·高斯林等人于1990年代初开发Java语
待兔 待兔
12个月前
Java 之父退休,一个时代的谢幕!
helloworld社区最新消息,两个月前刚过完自己69岁生日,编程语言Java之父JamesGosling(詹姆斯・高斯林)在领英上官宣了自己退休的消息。《Java之父宣布退休》两个月前刚过完69岁生日的编程语言Java之父JamesGosling,近日