字符串
字符串的重要性
字符串在我们的编程中是非常重要的一种数据类型。
1. 使用量最大;
2. 它几乎可以表达所有的我们常见的数据类型的值。或者换句话,对于程序外部的使用者
而言,所有的交互信息(文本)在本质都是字符串,他们是意识不到其他数据类型的存在
的。原因:数据类型是程序内部使用的,告知计算机空间划分,数据存储转换方式,对于外
部是没有意义的。
3. 字符串操作的场景非常多,所以也需要做一定层度的优化设计。
Java中的字符串类型
在Java中一共提供了三种类型可以表示字符串:String、StringBuilder、
StringBuffer。
要谈论为什么有三个字符串类型,我们要先聊聊String的一些特性。
String
1.String是唯一一个设计了字面常量的引用数据类型
```java
String str = "hello";
System.out.println("请输入:");
```
回顾:
常量指的是不能修改变化的数据量,与它对应的是变量。
常量分两种:
1、符号常量;2、字面常量
“符号常量”是给常量起一个名字(符号)
```java
final int STUDENT_NUM = 50;
final double PI = 3.14;
final double PRICE = 3.14;
```
“字面常量”其实更常用,但也更容易被忽略。
当我们在代码中出现:5L,-19,3.4,3.14f,'A',"你的年龄是:"。
所以,再次希望大家注意--字面常量的特点,特别是它的字面书写形式代表了数据类型。
我们可以很容易的发现几乎所有的字面常量都是基本数据类型,唯有String这个引用数据
类型拥有一个字面常量的设计!
2.String拥有一个“字符串常量池”的设计
由于String的使用量非常大,所以对于String的字面常量,Java在设计的时候专门提供的
了一个“常量池”来优化。
所谓池,就是预先在内存当中放置一系列的对象(字符串常量池就是放的字符串常量对
象)。当需要使用的时候,不用临时去创建,而是从池当中取一个来用就可以了。
JVM会在加载的时候,把加载到的类代码当中,所有书写的字符串常量对象,预存到一个专
门的内容空间---"字符串常量池"。然后开始执行指令语句,当需要用到这个字符串常量对
象的时候,就直接到常量池中去取。
*演示*
```java
String s0 = "hello";
String s1 = "hello";
```
此时,在加载期,就会在常量池当中产生一个String类型的对象,里面的值是hello。然后
运行起来以后,s0和s1都会被赋值为这个对象的引用。所以,用"=="比较的时候,我们能
得到true。
```java
String s3 = new String("hello");
```
由于使用了new的语法,那么会在内存的堆当中产生一个全新的String对象,里面的字符值
是hello。
3、String判断非空,应该判断两个条件。
其他的引用数据类型,只需要判断是否“==”null;但是String有一种特殊性,它可能不为
null,但是指向的String对象里面没有存放字符数据,是一个空串。
所以String判断非空要用两个条件
```java
if(str == null || str.equals("")){
}
if(str != null && !str.equals("")){
}
```
4、String对象的值一旦确定,不能改变。
String的这个特点其实是和String的源代码设计有关系。我们可以把String看成是一个封
装的char[]。
```java
public final class String{
private final char[] value;
}
```
而在这个设计当中,我们可以看到字符串中的数据值是被作为属性存在的,而且该属性是私
有的,所以外部不能直接操作,要利用String提供的方法来操作;同时该属性是final的,
所以它的值不能被修改。
到了新版本的JDK8之后,这个char[]被优化成了byte[]。因为不是所有的字符都需要2个
字节的空间,很多只需要1个字节空间就可以了。
这种内容不可更改的特性又会照成一个新的问题,当我们大量的在程序中做字符串拼接或
需要修改字符串内容的动作时,就会产生很多字符串对象。所以,Java又设计了新的字
符串类型专门解决这个问题。
StringBuilder和StringBuffer
StringBuilder 是在JDK5当中,提出来的一个辅助String的字符串类型。它最大的特点
是:内容可变。
注意:StringBuilder不是String类型,是一种新的类型,所以如下代码都是错的:
```java
StringBuilder sb = "hello";
System.out.println(sb + "world");
```
由于StringBuilder是为了弥补String在内容不可变上的缺点,所以它提供的方法几乎都
是对内容的修改方法。
1、append()方法
它的作用是在字符串的尾部添加内容。为了能够将多种数据类型都方便的添加到尾部,
提供了大量的重载方法。
2、delete()方法
它的作用是在字符串中删掉指定从开始下标到结束下标的内容。
3、insert()方法
它的作用是在字符串中指定位置插入新的内容。它的第一个参数就是插入位置的下标。
4、replace()方法******
它的作用是把字符串中指定位置的内容替换为新的内容。
StringBuffer也是一个可变的字符串序列,它和StringBuilder的构造、提供的行为完全
一样。唯一的区别就是:StringBuffer是线程安全的。
关于线程安全
1. 线程安全的,效率低;
2. 线程不安全的,效率高;
3. 不是多线程就一定要讲线程安全,多线程的不安全也是在特定条件下才会发生。
String与StringBuffer或StringBuilder的使用场景
虽然说StringBuffer和StringBuilder具有内容可变的特点,但是对于字符串操作来说它
们两个提供的方法远远不及String的丰富度。
所以这也就限制了他们两个的使用场景。对于我们来说大部分情况下,我们还是使用的
String。只有当我们需要“大量”的做字符串内容改变的动作,比如:大量的拼接,大量的
插入,大量的删除.....我们才会去考虑另外两个。
所谓“大量”,也不是程序员的感官感觉,而是要根据是否影响到了执行效率和内存空间,往
往是测试出来的。
*结论:* 对于StringBuffer和StringBuilder我们主要掌握内容改变的这些方法,以备
不时之需。更迫切的是掌握String当中的那些常用方法。
String当中的常用方法
与String当中的字符内容有关的方法
1. length() -- 获取字符串当中有多少个字符。
2. charAt() -- 根据下标,获取该位置的字符。下标从0开始,如果越界报
“StringIndexOutofBoundsException”。
3. indexOf() --- 根据内容,返回该内容在字符串当中出现的位置。内容可以
是:char、int、String
4. lastIndexOf() --- 根据内容,返回该内容在字符串当中最后一次出现的
位置。3和4如果找不到,均返回-1,不会报异常。
5. toCharArray() --- 将字符串的内容构造为一个char[],然后返回出来。
6. toUpperCase();toLowerCase() --- 把字符串的内容转换为全大写或全小
写。注意:由于String的内容不可变,所以其实是返回了一个新的字符串。
7. replace() --- 用新的字符或字符串,替换字符串中的老字符或子串。
8. replaceAll() --- 把字符串当中所有 *满足正则* 的内容都全部替换
9. subString() --- 获取字符串中的部分子串。两个参数代表截取的开始和结
束,但是前闭后开的。
与String内容比较有关的
1. equals()
2. equalsIgnoreCase() --- 忽略大小写比较相等
3. compareTo() --- 比较两个字符串的大小,返回的是它们的字典顺序。
4. compareToIgnoreCase() --- 比较两个字符串的大小,忽略大小写。
5. startWith() --- 判断字符串是否以某个子串开头(*支持正则*)
6. endWith() --- 判断字符串是否以某个子串结尾(*支持正则*)
7. contains() --- 判断字符串是否包含某个子串。
介绍3个特殊方法
1、trim() -- 去掉字符串的前后空格
当我们在界面上接收了用户的输入以后,很多公司都要求必须要先用这个方法把输入内
容的前后无效空格给去掉,然后才能使用。
2、split() -- 把字符串按分隔符进行拆分,返回一个字符串数组。其中分隔符*
支持正则*;
3、matches() -- 判断一个字符串是否满足正则表达式(regex) 。
正则表达式
正则表达式是一种判定字符串内容是否符合某种规范的表达式,它本身的形式就是一个字符串。
要学习正则表达式,我们首先就要学习正则当中的各种符号:
1、任意一个字符串如果不使用正则表达式的特殊符号,那么它也仍然是正则表达式;只不
过,这个字符串的所有内容没有变化度,全部被指定死了。
2、如果我们要匹配更多的灵活情况,那么我们必须要去学习正则表达式的特殊符号。
正则表达式在各种编程语言当中都已经实现了,且基本上都是遵循了统一的语法规则(全球
标准),只有部分语言自己多提了点特殊语法(用来号称自己更好用而已)。
虽然符号很多,但是抓住三个关键,就是三种括号:
[] --- 在正则表达式当中,一个[]代表一个字符,然后把该字符可以有哪些选择,写
在它内部。如果在[]内部出现^,那么表示除了这些符号,其他都可以。比如:[^0-9]
{} --- 在正则表达式当中,代表它前面的表达式能够出现的次数。
{m,n} --- 至少出现m次,最多出现n次
{m,} --- 至少出现m次,最多不限
{m} --- 只能出现m次
它也有简写:
?代表 0 - 1次
+代表 至少1次
*代表 任意次
() --- 代表可选,可选项之间用“|”号分隔。
正则表达式中正确性比简洁性要重要得多!
在实际开发中,经常使用到的正则表达式的地方很多,通常都是输入有效性的验证:
电话号码、E-mail、邮政编码、身份证......