这是蜗牛互联网的第 113 期原创。

作者 | 红色蜗牛

起源 | 蜗牛互联网(ID: woniu_internet)

转载请分割受权(微信ID: 919201148)

本文纲要:

前言

在上一篇文章 Java 根本类型的各种运算,你真的理解了么? 里,咱们学习了 Java 的根本类型和他们之间的运算。Java 根本类型堪称是 Java 世界里应用最频繁的数据类型了。除此之外,有种数据类型你也肯定会遇到,它在 Java 世界里应用也相当频繁。它就是字符串!

听到字符串,你是不是想起了字符这种类型。不过在 Java 里,字符和字符串是两种不同的类型。

字符串的定义与模式

字符类型你应该比拟相熟,通过关键词 char 来申明一个字符。

值只能是一个英文字符或者一个中文字符或者是 Unicode 编码,用单引号包住。如下:

`// char 用 ascii 字符赋值``char charWithAscii = '.';``char charWithZh = '牛';``char a = '\u0041';// 值为 A`

而字符串,顾名思义,就是多个字符连贯而成的串。通过关键词 String  来申明一个字符串。

值能够是 null 或者空字符串或者单字符串或者多字符串,用双引号包住。如下:

`// null 字符串,未指定地址``String nullStr = null;``// 空字符串,蕴含 0 个字符``String  blankStr = "";``// 蕴含一个字符``String oneCharStr = "A";``// 蕴含多个字符,打印 蜗牛666 A``String multiCharStr = "蜗牛666 \u0041";`

你会发现字符串的值能够是 null,因为字符串类型 String 是援用类型,值为 null,就阐明值不存在,也就是这个变量不指向任何对象。这也是它和根本类型 char 的区别所在。

那作为援用类型,String 就能够通过 new 的形式申明,比方:

`String newStr = new String("蜗牛666");`

另外,你也会发现字符串比字符存储了更丰盛的数据,实际上,一个字符串能够存储 0 到任意多个字符。只有把数据内容用双引号包起来就好。

不过,如果你的数据内容自身就有双引号,会产生什么呢?

没错,连编译都过不去!编译器会提醒你字符串非法行尾,因为编译器判断的时候,会把两头引号当成字符串结尾,导致第三个引号开始的字符串语法出错。

那此时就要用到转义字符了,这个 case 里能够通过反斜杠 \ 本义两头的引号。

`String str = "蜗牛666\"";`

这样就不会报错了。

字符串的存储形式

咱们晓得,程序在运行时,会针对不同类型的变量数据做运算,最终输入后果。那其实运算过程中的变量数据都是存在栈里边,依据栈先进后出的特点,实现程序的运行逻辑。而对 Java 这种面向对象编程的语言,对象的信息就没放栈里边,而存到了堆里边,栈只存对象的援用地址。

另外,有些数据要求是不可变的,Java 会调配一块常量池进去。

比方 String 的场景。

`String str1 = "蜗牛666";``String str2 = "蜗牛666";``String newStr1 = new String("蜗牛666");``String newStr2 = new String("蜗牛666");`

str1 和 str2 就存储在常量池中。而常量池中的数据只有一份,因而 str1 和 str2 其实是指向同一块内存空间。

newStr1 和 newStr2 是通过 new 语法创立的对象,在创立的过程中,Java 会先去常量池中查找是否已有 蜗牛666 对象,如果没有则在常量池中创立一个 蜗牛666 对象,而后在堆中创立一个 蜗牛666 的拷贝对象。

所以 new String("xxx"); 这行代码会产生几个对象?

答案是一个或两个。如果常量池中原来没有 xxx,就是两个。如果有就是一个。

字符串的特点

字符串最大的特点就是不可变性。前边也有提过,字符串在常量池会有一份,常量这个信息就阐明字符串具备不可变性了。

咱们看下实例,你猜下以下程序会输入什么:

`String strChange = "蜗牛666";``System.out.println(strChange);``strChange = "蜗牛888";``System.out.println(strChange);`

都是 蜗牛666?因为字符串不可变嘛

事实上不是:

`蜗牛666``蜗牛888`

难道 蜗牛666 被改成 蜗牛888 了?

实际上不是,蜗牛666蜗牛888 都在,只是 strChange 的指向变了。

程序在执行 String strChange = "蜗牛666"; 时,JVM 虚拟机先在常量池创立字符串 蜗牛666 ,而后把变量 strChange 指向它。

而后在执行 strChange = "蜗牛888"; 时,JVM 虚拟机先在常量池创立字符串 蜗牛888 ,而后把变量 strChange指向它。

所以你会发现,刚开始的字符串 蜗牛666 还在,只是变量 strChange 不再指向它了。

因而,字符串的不可变个性,是指字符串内容不可变

另外,字符串的不可变个性,也带来了两个益处。

一个是 String 对象能够缓存哈希码。在 String 类的源码中,你能够看到这么一个属性。

`/** Cache the hash code for the string */``private int hash; // Default to 0`

hash 的值是基于字符串的每个字符计算得出。那字符串的不可变个性,就能保障 hash 的唯一性,因而能够缓存起来,被频繁应用。这也是性能优化的一种伎俩。

另外一个就是字符串的不可变个性保障了很多场景下的平安。

很多 Java 类库都会抉择 String 作为参数,像文件门路 path 这些。如果 String 会常常扭转,那就有各种安全隐患。

字符串的罕用场景

比拟

和根本类型相比,字符串也有比拟的能力。比方上面的比拟形式。

`String equalChar = "蜗牛";``String euqalCharCompare = "蜗牛";``System.out.println(equalChar == euqalCharCompare);`

间接常量定义的形式,没有问题,会输入 true 。但如果通过 new 的形式定义那就容易出错了,比方以下的代码,你晓得输入什么么?

`String equalMethod = new String("蜗牛");``String euqalMethodCompare = new String("蜗牛");``System.out.println(equalMethod == euqalMethodCompare);`

会输入 false ,而这是不合乎咱们预期的。为什么会这样呢?

因为 == 对于援用类型而言,比拟的是援用的地址。而上边两个字符串都是 new 进去的新对象。援用地址天然不同。

那如果想只比拟内容怎么做呢?能够应用 Java String 自带的 equals 办法!

`System.out.println(equalMethod.equals(euqalMethodCompare));`

此时就能失常输入 true 了。

拼接

最简略的拼接就是用 + 连接符,比方以下代码。

`/**` `* 字符串连贯` `*` `* @author 蜗牛` `* @from 公众号:蜗牛互联网` `*/``public class StringConnect {` `public static void main(String[] args) {` `String name = "蜗牛";` `String age = "18";` `String profile = name + " " + age;` `System.out.println(profile);` `}``}`

运行代码会输入:

`蜗牛 18`

咱们能够通过反编译看下,这段代码产生了什么,输出命令:

`javap -c StringConnect`

咱们看到有如下输入:

你会发现,加号连接符实际上是 Java 编译器的优化,底层是用了 StringBuilder 这个类,它的 append 办法就起了拼接的成果。

如果拼接的字符串数量无限,绝对固定,倡议用加号连接符,这样一行代码搞定!

如果拼接的字符串有相干的逻辑,比方循环拼接,字符串数量不太固定,那倡议用 StringBuilder 这个工具类。

另外, StringBuilder 是线程不平安的,如果你是多线程开发环境,为了保障程序不出错,能够用它的兄弟类 StringBuffer ,这个类办法和 StringBuilder 统一,只是减少了线程平安的能力。

总结

本文围绕着字符串介绍了它的定义和模式,另外它的存储形式如果理解不清晰的话,在一些逻辑判断上容易出错。字符串的不可变个性,既晋升了它的拜访性能,又保障了安全性。字符串最罕用的场景,就是比拟和拼接,当然还有很多其余应用场景,咱们后边能够再细讲。


我是蜗牛,大厂程序员,专一技术原创和个人成长,正在互联网上摸爬滚打。欢送关注我,和蜗牛一起成长,咱们一起牛~下期见!