字符串,是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:重要到别人只能当老二的字符串类的更多相关文章

  1. 从Java String实例来理解ANSI、Unicode、BMP、UTF等编码概念

    转(http://www.codeceo.com/article/java-string-ansi-unicode-bmp-utf.html#0-tsina-1-10971-397232819ff9a ...

  2. Java总结篇系列:Java String

    String作为Java中最常用的引用类型,相对来说基本上都比较熟悉,无论在平时的编码过程中还是在笔试面试中,String都很受到青睐,然而,在使用String过程中,又有较多需要注意的细节之处. 1 ...

  3. java String长度与varchar长度匹配理解(字符和字节长度理解)

    java String长度与varchar长度匹配理解(字符和字节长度理解) string中的length()长度,返回的是char的数量,每个char可以存储世界上任何类型的文字和字符,一个char ...

  4. Java String类相关知识梳理(含字符串常量池(String Pool)知识)

    目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...

  5. 深入浅出 java.String

    深入浅出 java.String Java 处理字符串常用的一些方法 Java定义一个字符串 直接定字符串 直接定义字符串表示直接使用""来表示字符串中的内容 String str ...

  6. Java String的相关性质分析

    引言 String可以说是在Java开发中必不可缺的一种类,String容易忽略的细节也很多,对String的了解程度也反映了一个Java程序员的基本功.下面就由一个面试题来引出对String的剖析. ...

  7. 面试之Java String 编码相关

    实话说,作为一个多年Java老年程序员,直到近来,在没有决心花时间搞清楚Java String的编码相关问题之前, 自己也都还是似懂非懂,一脸懵逼的.设想如果在面试中,有同学能够条理清晰的回答下面的问 ...

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

    请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · Android-NoteBook 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭 ...

  9. java String 详解

    1.java语言的字符串序列是通过字符串类实现的.java提供了3个字符串类:String类.StringBuilder类和StringBuffer类.String类是不变字符串,StringBuff ...

随机推荐

  1. 1、Linux文件结构介绍

    文件目录介绍 bin 可执行的命令 boot 启动相关的程序:boot→grub→grub.conf timeout修改启动时间 dev 设备.硬件相关信息 etc 程序.系统配置文件 home 用户 ...

  2. 建造者模式(Builder Pattern)

    建造者模式(Builder Pattern) 它可以将多个简单的对象一步一步构建成一个复杂的对象. 意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示. 主要解决:主要解决在软 ...

  3. RESTful-5开发API

    RESTful 是目前最流行的 API 设计规范,用于 Web 数据接口的设计. 它的大原则容易把握,但是细节不容易做对.本文总结 RESTful 的设计细节,介绍如何设计出易于理解和使用的 API. ...

  4. lombok @EqualsAndHashCode 注解讲解

    官方文档:@EqualsAndHashCode 原文中提到的大致有以下几点: 1. 此注解会生成equals(Object other) 和 hashCode()方法. 2. 它默认使用非静态,非瞬态 ...

  5. 浅谈构建前端自动化工作流程一 之 node

    一.Node环境 1.什么是Node? Node.js类似于jquery.js,不是js文件,也不是一个js框架,而是Server side JavaScript runTime,服务端的一个JS运行 ...

  6. C++何时需要NEW对象,new和定义对象的区别

    C++何时需要NEW对象 一.CString str; 不用new的内存区域是在栈,会自动分配空间,一般在局部变量时使用,函数结束后会自动释放空间, 定义对象就是申明对象(静态) str有系统创建并释 ...

  7. net view ERROR 6118

    如上图,使用net view命令出现错误6118(server2012),谷歌百度油管解决了. 尝试1: 把Computer Browser服务打开.https://blog.csdn.net/qq_ ...

  8. matplotlia应用

    一.简单使用 使用函数 plt.polt(x,y,label,color,width) 根据x,y 数组 绘制直,曲线 import numpy as np #引用numpy库,从新命名它为np(以后 ...

  9. sudo命令详解

    语法 sudo(选项)(参数) 选项 选项 说明 -b 在后台执行指令: -h 显示帮助: -H 将HOME环境变量设为新身份的HOME环境变量: -k 结束密码的有效期限,也就是下次再执行sudo时 ...

  10. pytroch nn.Module源码解析(1)

    今天在写一个分类网络时,要使用nn.Sequential中的一个模块,因为nn.Sequential中模块都没有名字,我一时竟无从下笔.于是决定写这篇博客梳理pytorch的nn.Module类,看完 ...