前言Java基本类型是Java世界中最常用的数据类型。除此之外,您一定会遇到一种数据类型,它在Java世界中被广泛使用。它是一个字符串!当你听到字符串时,你会想到字符的类型吗?但是在Java中,字符和字符串是两种不同的类型。您应该熟悉字符串的定义和形式。您可以使用关键字char来声明一个字符。该值只能是英文字符或中文字符或Unicode编码,用单引号括起来。如下://char被赋ascii字符charcharWithAscii='.';charcharWithZh='ox';chara='\u0041';//取值为Astring顾名思义就是由连接多个字符。字符串是用关键字String声明的。值可以是null或空字符串,也可以是单个或多个字符串,用双引号括起来。如下://空字符串,未指定地址StringnullnullStr=null;//空字符串,包含0个字符StringblankStr="";//包含一个字符StringoneCharStr="A";//包含多个字符,打印snail666AStringmultiCharStr="蜗牛666\u0041";你会发现字符串的值可以为null,因为字符串类型String是引用类型,值为null,表示该值不存在,即变量没有指向任何对象。这也是它和基本类型char的区别。作为引用类型,String可以通过new声明,例如:StringnewnewStr=newString("snail666");此外,你还会发现字符串存储的数据比字符更丰富。事实上,一个字符串可以存储0到任意数量的字符。只需将数据内容用双引号引起来。但是,如果您的数据内容本身带有双引号会怎样?没错,我连编译都不会!编译器会提示你字符串有非法行尾,因为编译器在判断的时候会把中间的引号当作字符串的结尾,导致以第三个引号开始的字符串出现语法错误。那么这个时候就用到了转义符。这种情况下,中间的引号可以用反斜杠\转义。Stringstr="Snail666\"";这样就不会报错了,我们知道字符串的存储方式,程序在运行的时候会对不同类型的变量数据进行运算,最后输出结果.实际上,运行过程中的变量数据是存放在栈中的,根据栈的特点,完成了程序的运行逻辑,对于Java这样的面向对象的编程语言来说,对象信息是不是存储在栈中,而是存储在堆中,栈只存储对象的引用地址。另外,有些数据要求是不可变的,Java会分配一个常量池。比如String的场景。Stringstr1="snail666";Stringstr2="snail666";StringnewnewStr1=newString("snail666");StringnewnewStr2=newString("蜗牛666");str1和str2存放在常量池中,常量中的数据只有一份池,所以str1和str2实际上指向同一个内存存储空间。newStr1和newStr2是通过new语法创建的对象,在创建的过程中,Java会先去常量池中查找是否已经有蜗牛666对象,如果没有,则在常量中创建一个蜗牛666对象pool,然后在堆中创建一个蜗牛666的拷贝对象。SonewString("xxx");这行代码会生成多少个对象?答案是一两个。如果常量池中没有xxx,就是两个。如果有,就是一个。字符串最大的特点就是不变性。前面说过,常量池中会有字符串的副本,常量信息表明该字符串是不可变的。让我们看一下这个例子,猜猜下面的程序会输出什么:StringstrChange="snail666";System.out.println(strChange);strChange="snail888";System.out.println(strChange);都是蜗牛666吗?因为字符串是不可变的,实际上不是:snail666snail888是不是snail666已经改成snail888了?其实不是,蜗牛666和蜗牛888都在,只是strChange的方向变了。当程序执行StringstrChange="snail666";时,JVM虚拟机首先在常量池中创建字符串snail666,然后将变量strChange指向它。那么在执行strChange="snail888";时,JVM虚拟机首先在常量池中创建字符串snail888,然后将变量strChange指向它。所以你会发现最初的字符串snail666还在,只是变量strChange不再指向它了。因此,字符串的不可变性意味着字符串的内容是不可变的。此外,字符串的不可变特性还带来了两个好处。一是String对象可以缓存哈希码。在String类的源码中,可以看到这样一个属性。/**Cachethehashcodeforthestring*/privateinthash;//Defaultto0hash的值是根据字符串的每个字符计算出来的。字符串的不可变性可以保证hash的唯一性,所以可以缓存起来,经常使用。这也是性能优化的一种手段。另一个是字符串的不可变特性确保了很多场景下的安全性。很多Java类库都会选择String作为参数,比如文件路径path。如果String会频繁更改,则存在各种安全隐患。字符串的常见场景比较与基本类型相比,字符串也具备比较的能力。例如下面的比较方法。StringequalChar="snail";StringeuqalCharCompare="snail";System.out.println(equalChar==euqalCharCompare);直接常量定义的方式,没问题,会输出true。但是如果用new来定义,就很容易出错。比如下面的代码,你知道输出的是什么吗?StringequalMethod=newString("snail");StringeuqalMethodCompare=newString("snail");System.out.println(equalMethod==euqalMethodCompare);将输出false,这不是我们所期望的。为什么会这样?因为==对于引用类型,比较的是引用的地址。上面两个字符串是new出来的新对象。参考地址自然不同。那么,如果您只想比较内容怎么办?可以使用JavaString自带的equals方法!System.out.println(equalMethod.equals(euqalMethodCompare));这时候可以正常输出true了。拼接最简单的拼接就是使用+连接器,比如下面的代码。/***字符串连接**@作者蜗牛*@来源:蜗牛互联网*/publicclassStringConnect{publicstaticvoidmain(String[]args){Stringname="snail";Stringage="18";Stringprofile=name+""+age;System.out.println(profile);}}运行代码会输出:snail18,我们可以反编译看看这段代码发生了什么。输入命令:javap-cStringConnect我们会看到如下输出:你会发现,加号其实是对Java编译器的一种优化。底层使用StringBuilder类,其append方法起到拼接的作用。如果拼接的字符串数量有限,比较固定,建议使用加号连接器,这样一行代码就搞定了!如果拼接的字符串有相关逻辑,比如循环拼接,字符串个数不固定,建议使用StringBuilder工具类。此外,StringBuilder不是线程安全的。如果在多线程开发环境下,为了保证程序不出错,可以使用它的兄弟类StringBuffer。该类方法与StringBuilder一致,只是增加了线程安全的能力。
