字符串的重要性
字符串在编程中是非常重要的一种数据类型。
使用量最大;
它几乎可以表达所有的我们常见的数据类型的值。或者换句话,对于程序外部的使用者而言,所有的交互信息(文本)在本质都是字符串,他们是意识不到其他数据类型的存在的。原因:数据类型是程序内部使用的,告知计算机空间划分,数据存储转换方式,对于外部是没有意义的。
字符串操作的场景非常多,所以也需要做一定层度的优化设计。
Java中的字符串类型
在Java中一共提供了三种类型可以表示字符串:String、StringBuilder、StringBuffer。
要谈论为什么有三个字符串类型,我们要先聊聊String的一些特性。
String
1.String是唯一一个设计了字面常量的引用数据类型
String str = "hello";
System.out.println("请输入:");
回顾: 常量指的是不能修改变化的数据量,与它对应的是变量。 常量分两种:
1、符号常量;
2、字面常量 “符号常量”是给常量起一个名字(符号)
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会在加载的时候,把加载到的类代码当中,所有书写的字符串常量对象,预存到一个专门的内容空间---"字符串常量池"。然后开始执行指令语句,当需要用到这个字符串常量对象的时候,就直接到常量池中去取。 演示
String s0 = "hello";
String s1 = "hello";
此时,在加载期,就会在常量池当中产生一个String类型的对象,里面的值是hello。然后运行起来以后,s0和s1都会被赋值为这个对象的引用。所以,用"=="比较的时候,我们能得到true。
String s3 = new String("hello");
由于使用了new的语法,那么会在内存的堆当中产生一个全新的String对象,里面的字符值是hello。
String判断非空,应该判断两个条件。
其他的引用数据类型,只需要判断是否“==”null;但是String有一种特殊性,它可能不为null,但是指向的String对象里面没有存放字符数据,是一个空串。 所以String判断非空要用两个条件
//如果输入为空
if(str == null || str.equals("")){
}
//如果输入不为空
if(str != null && !str.equals("")){
}
String对象的值一旦确定,不能改变。
String的这个特点其实是和String的源代码设计有关系。我们可以把String看成是一个封装的char[]。
public final class String{
private final char[] value;
/*
还有其他属性和一大堆的方法。
*/
}
在这个设计当中,我们可以看到字符串中的数据值是被作为属性存在的,而且该属性是私有的,所以外部不能直接操作,要利用String提供的方法来操作;同时该属性是final的,所以它的值不能被修改。
到了JDK8之后,这个char[]被优化成了byte[]。因为不是所有的字符都需要2个字节的空间,很多只需要1个字节空间就可以了。
这种内容不可更改的特性又会照成一个新的问题,当我们大量的在程序中做字符串拼接或需要修改字符串内容的动作时,就会产生很多字符串对象。 所以,Java又设计了新的字符串类型专门解决这个问题。
StringBuilder和StringBuffer
StringBuilder 是在JDK5当中,提出来的一个辅助String的字符串类型。它最大的特点是:内容可变。 注意:StringBuilder不是String类型,是一种新的类型,所以如下代码都是错的:
StringBuilder sb = "hello";
System.out.println(sb + "world");
由于StringBuilder是为了弥补String在内容不可变上的缺点,所以它提供的方法几乎都是对内容的修改方法。
1、append()方法 它的作用是在字符串的尾部添加内容。为了能够将多种数据类型都方便的添加到尾部,提供了大量的重载方法。
2、delete()方法 它的作用是在字符串中删掉指定从开始下标到结束下标的内容。
3、insert()方法 它的作用是在字符串中指定位置插入新的内容。它的第一个参数就是插入位置的下标。
4、replace()方法 它的作用是把字符串中指定位置的内容替换为新的内容。
StringBuffer也是一个可变的字符串序列,它和StringBuilder的构造、提供的行为完全一样。
唯一的区别就是:StringBuffer是线程安全的。
String与StringBuffer或StringBuilder的使用场景
虽然说StringBuffer和StringBuilder具有内容可变的特点,但是对于字符串操作来说它们两个提供的方法远远不及String的丰富度。
所以这也就限制了他们两个的使用场景。对于我们来说大部分情况下,我们还是使用的String。只有当我们需要“大量”的做字符串内容改变的动作,比如:大量的拼接,大量的插入,大量的删除.....我们才会去考虑另外两个。
所谓“大量”,也不是程序员的感官感觉,而是要根据是否影响到了执行效率和内存空间,往往是测试出来的。
String当中的常用方法
与String当中的字符内容有关的方法
length() -- 获取字符串当中有多少个字符。--- 平时使用以及面试常见。
charAt() -- 根据下标,获取该位置的字符。下标从0开始,如果越界报“StringIndexOutofBoundsException”。
indexOf() --- 根据内容,返回该内容在字符串当中出现的位置。内容可以是:char、int、String
lastIndexOf() --- 根据内容,返回该内容在字符串当中最后一次出现的位置。3和4如果找不到,均返回-1,不会报异常。
toCharArray() --- 将字符串的内容构造为一个char[],然后返回出来。
toUpperCase();toLowerCase() --- 把字符串的内容转换为全大写或全小写。注意:由于String的内容不可变,所以其实是返回了一个新的字符串。
replace() --- 用新的字符或字符串,替换字符串中的老字符或子串。
replaceAll() --- 把字符串当中所有满足正则的内容都全部替换
subString() --- 获取字符串中的部分子串。两个参数代表截取的开始和结束,但是前闭后开的。
与String内容比较有关的
equals()
equalsIgnoreCase() --- 忽略大小写比较相等
compareTo() --- 比较两个字符串的大小,返回的是它们的字典顺序。
compareToIgnoreCase() --- 比较两个字符串的大小,忽略大小写。
startWith() --- 判断字符串是否以某个子串开头(支持正则)
endWith() --- 判断字符串是否以某个子串结尾(支持正则)
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、邮政编码、身份证......