Java 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类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
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 || str.length == 0 |
isBlank(String str) |
str == null || str.length == 0 || str.trim().length == 0 |
join(Object[] arrey) |
把数组中的元素连接成一个字符串返回 |
推荐阅读:
上一篇:Java内部类
下一篇:Java 数组,看这篇就够了
Java String:重要到别人只能当老二的字符串类的更多相关文章
- 从Java String实例来理解ANSI、Unicode、BMP、UTF等编码概念
转(http://www.codeceo.com/article/java-string-ansi-unicode-bmp-utf.html#0-tsina-1-10971-397232819ff9a ...
- Java总结篇系列:Java String
String作为Java中最常用的引用类型,相对来说基本上都比较熟悉,无论在平时的编码过程中还是在笔试面试中,String都很受到青睐,然而,在使用String过程中,又有较多需要注意的细节之处. 1 ...
- java String长度与varchar长度匹配理解(字符和字节长度理解)
java String长度与varchar长度匹配理解(字符和字节长度理解) string中的length()长度,返回的是char的数量,每个char可以存储世界上任何类型的文字和字符,一个char ...
- Java String类相关知识梳理(含字符串常量池(String Pool)知识)
目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...
- 深入浅出 java.String
深入浅出 java.String Java 处理字符串常用的一些方法 Java定义一个字符串 直接定字符串 直接定义字符串表示直接使用""来表示字符串中的内容 String str ...
- Java String的相关性质分析
引言 String可以说是在Java开发中必不可缺的一种类,String容易忽略的细节也很多,对String的了解程度也反映了一个Java程序员的基本功.下面就由一个面试题来引出对String的剖析. ...
- 面试之Java String 编码相关
实话说,作为一个多年Java老年程序员,直到近来,在没有决心花时间搞清楚Java String的编码相关问题之前, 自己也都还是似懂非懂,一脸懵逼的.设想如果在面试中,有同学能够条理清晰的回答下面的问 ...
- 金九银十,收下这份 Java String 面试题
请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · Android-NoteBook 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭 ...
- java String 详解
1.java语言的字符串序列是通过字符串类实现的.java提供了3个字符串类:String类.StringBuilder类和StringBuffer类.String类是不变字符串,StringBuff ...
随机推荐
- GLSL ES 3.0 和 2.0 的区别
GLSL ES 3.0 和 2.0 的区别 语法区别 attribute和varying. 取而代之的是 in和out 头文件多了个#version 300 es 纹理 texture2D 和 tex ...
- springMVC 多文件上传前后台demo
只是个demo,需要数据校验,流程是通的 配置上传文件的解析器 前端代码; <%@ page language="java" contentType="text/h ...
- UOJ#41. 【清华集训2014】矩阵变换 构造
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ41.html 题解 首先写个乱搞: 一开始每一行都选择第一个非0元素,然后,我们对这个方案不断做更新,直到 ...
- C语言 第三次作业--函数
1.本章学习总结 1.1 思维导图 1.2本章学习体会及代码量学习体会 1.2.1学习体会 学习C语言也半个学期了,前天也才刚刚进行了半期考试,emmm,成绩很差,可以反应出来我这半学期学习的效果并不 ...
- IntelliJ IDE 基础经验备案
1.配置本地的JAVA环境 2.配置本地安装的Maven环境 详情 1.配置本地的JAVA环境 准备: 本地已经安装java环境,目录:C:\Program Files\Java\jdk1.8.0_1 ...
- Centos7中kubernetes-1.11.2基于配置亲和与反亲和
1.题目 通过命令行,创建两个个deployment. – 需要集群中有2个节点 – 第1个deployment名称为<hwcka-002-app1>,使用nginx镜像,用有2个pod, ...
- BFS —— 信息学一本通(1451:棋盘游戏)
题目描述 在一个4*4的棋盘上有8个黑棋和8个白棋,当且仅当两个格子有公共边,这两个格子上的棋是相邻的.移动棋子的规则是交换相邻两个棋子.现在给出一个初始棋盘和一个最终棋盘,要求你找出一个最短的移动序 ...
- 查看mac系统版本
打开终端, 输入命令 uname -a 回车 x86_64 表示系统为64位 i686 表示系统32位的
- iphone 屏蔽系统自动更新,消除设置上的小红点
苹果ios系统的更新频率大家应该都知道,一般来说1个月就会来次更新.这一点让很多人讨厌.主要原因还是iPhone会自动下载更新包,然后一直不停地提示你是否安装更新,问题是我们还找不到关闭提醒和关闭自动 ...
- 快排实现仿order by多字段排序
class OrderBy(object): def __init__(self, sequence, *condition, **extra_condition): ""&quo ...