一文教你Java字符串处理(String,StringBuffer,StringBuild)
前言
本文篇幅较长,但都是满满的干货,请大家耐心观看,相信会有不小的收获。本人在总结的过程中也收获了很多的知识,也希望大家可以一起借鉴学习下,希望大家最后都能有所收获!
再言
字符串的分类在java.lang包中,定义了两个大类来表示字符串: String和 String Buffer(StringBuild)类,它封装了字符串的数据结构,并定义了许多方法来处理字符串。Java将字符串分为两类的目的是为了提高系统对字符串的处理效率:
1.在程序运行中值不会改变的字符串,用 String类来存储和维护;
如果能够确信程序中使用的字符串的值在运行过程中不会改变,应尽量使用 String类对象;
2.在程序运行中值会改变的字符串,也称为带字符缓冲区的字符串,用 StringBuffer(StringBuild)类来存储和维护。
如果使用的字符串值在程序运行过程中会改变,就要使用 StringBuffer(StringBuild)类个对象,这样可以提高程序的运行性能。
【注意】Java提供的 String和 StringBuffer(StringBuild)类都定义为 final,意思是不能用它们派生出子类,这样做的目的是为了对字符串的操作进行优化。
String
String类用于存储和维护值不变的字符串对象,String类的定义原型如下:
//String定义原型public final class java.lang.String extends java.lang.Object { }
我们可以清楚的看出来,String该类的父类是一个Object类,由final进行修饰,表明不能产生子类(如导言所说),并且是一种工具类(由public修饰)。
了解了String原型,还可以看一下String源码:
//String部分源码public final class String implements java.io.Serializable, Comparable<String>, CharSequence{ /** The value is used for character storage. */ private final char value[]; /** The offset is the first index of the storage that is used. */ private final int offset; /** The count is the number of characters in the String. */ private final int count; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; ........}
从上面的源码中可以看出来几点:
1)String类是final类并且它的成员方法也都默认为final方法,意味着String类和其中的方法都不可以被继承。
注:在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。
2)上面列举出了String类中的部分成员属性,从上面可以看出String类其实是通过char数组来保存字符串的。也可以说char是String的根本属性。
创建String字符串
创建String字符串可以分为两种情况:
(1)使用 "" 引号创建字符串;
(2)使用new关键字创建字符串。
方法一
这个方法中使用""创建字符串比较的简单:String 变量名="你自己需要定义的值";
方法二
在这个方法中的创建方式就会有很多种——String和其他类一样,可以用new操作符,并且调用该类的构造函数创建这种类型的对象。String类的构造函数具有以下11种格式:
public String ()public String(String value)public String (char value[])public String(char value[], int offset, int count)public String(byte ascii[], int hibyte, int offset, int count)public String(byte ascii[], int hibyte)public string(byte bytes[], int offset, int length, String enc)public string(byte bytes[],String enc)public String(byte bytes [], int offset, int length)public String(byte bytes[])public String (StringBuffer buffer)
构造函数各个参数的含义可以自己参阅有关的手册,这里就对几个经常使用的构造函数进行解析:
public String ()
不带参数的构造函数,采用该构造函数创建一个不含字符的空对象。eg:String str=new String();
public String(String value)
采用参数value对象的值构造一起新的String对象。eg:
String str1="YYH";
String str2=new String("YYH");
System.out.println(str2); //显然str2对象的内容是“YYH”
System.out.println(str1==str2); //显然是false,这是因为str1和str2不是同一个对象,尽管他们的内容相同。
public String (char value[])
采用一个字符数组作为参数,把这个字符数组的内容转换为字符串,并赋予新建的对象。eg:
char a[]={'Y','Y','H'}; //字符数组
String str=new String(a); //字符串str的内容是“YYH”
public String(char value[], int offset, int count)
从对字符数组value指定的起始字符下标offset开始,将字符个数为count的字符字串作为参数,创建一个新的String对象。eg:
char a[]={'Y','Y','H'}; //字符数组
String str=new String (a,1,2); //字符串str的内容是“YH”
注:若所取的字符超出字符数组的范围,将产生StringIndexOutOfBoundsException字符串下标越界的异常。
public String(byte ascii[], int hibyte)
以 hibyte 的值作为高8位,ascii[] 的内容作为低8位,构成一个16位字符的ASCII码,将这些字符组成新建字符串对象的内容,但 hibyte的值常常取0。
由于Java的char类型变量占16位,可表示 Unicode字符集,但在 Internet 上最常用的的还是8位表示的ASCII字符集。由于这个原因, String类提供了该构造函数,可以根据字节数组将每个字符的高位字节指定成特定值来构造字符串。对普通的ASCII文本来说,每个字符的高位字节取0,而对其他字符集则应设置为非0。eg:
byte ascii[]={65,66,67,68};
String str=new String(ascii, 0);
则str串的内容是"ABCD",因为,字符'A'和'B'的 ASCII A码分别是65和66。
public String(byte ascii[], int hibyte, int offset, int count)
这种格式是前面两种格式的结合。eg:
byte ascii[]={65,66,67,68};
String str=new String(ascii, 0,1,2);
则str串的内容是“BC”。
public String (StringBuffer buffer)
以一个动态可改变大小的 StringBuffer 对象为参数,创建一个新的String对象,两个对象的内容相同。
【注意】除了可以通过new操作符和构造函数创建 String对象以外,还可以采用字符串常量初始化一个 String类引用,相当于采用new为这个引用创建对象,并且其内客也是字精串常量。eg:
String str= "abc"; //就是方法一的创建方法
等价于下列程序段:
char data[]={'a','b','c'};
String str=new String(data);
并且str的字符串内容是"abc"。
字符串常量池
我们知道字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串我们使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串。
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
来看下面的程序:
String a = "YYH";String b = "YYH";
a、b和字面上的chenssy都是指向JVM字符串常量池中的"chenssy"对象,他们指向同一个对象
String c = new String("YYH");
new关键字一定会产生一个对象YYH(注意这个YYH和上面的YYH不同),同时这个对象是存储在堆中。所以上面应该产生了两个对象:保存在栈中的c和保存堆中YYH。但是在Java中根本就不存在两个完全一模一样的字符串对象。故堆中的YYH应该是引用字符串常量池中YYH。所以c、YYH、池YYH的关系应该是:c--->YYH--->池YYH。整个关系如下:
通过上面的图我们可以非常清晰的认识他们之间的关系。所以我们修改内存中的值,他变化的是所有。
总结:虽然a、b、c、YYH是不同的对象,但是从String的内部结构我们是可以理解上面的。String c = new String("YYH");虽然c的内容是创建在堆中,但是他的内部value还是指向JVM常量池的YYH的value,它构造chenssy时所用的参数依然是YYH字符串常量。
String类常用的方法
(1)charAt(int index) ** 返回指定索引处的 char 值 ,index的范围在0到字符串数组长度减一。
(2)compareTo(String anotherString)** 按字典顺序比较两个字符串,返回负值,0和正值,分别代表小于,等于和大于(3)compareToIgnoreCase(String str) ** 按字典顺序比较两个字符串,不考虑大小写
(4)concat(String str)** 将指定字符串连接到此字符串的结尾(5)contains(CharSequence s) 当且仅当此字符串包含指定的 char 值序列时,返回 true(6)endsWith(String suffix) 测试此字符串是否以指定的后缀结束(7)equals(String anString) 将此字符串与指定的字符串比较(8)format(String format, Object args) 使用指定的格式字符串和参数返回一个格式化字符串(9)indexOf(int ch) 与 indexOf(int ch, int fromIndex) 返回在此字符串中第一次出现指定字符处的索引,没有则返回-1,后者要从指定的索引(fromIndex)开始搜索(10)indexOf(String str) 与 indexOf(String str, int fromIndex) 返回指定子字符串在此字符串中第一次出现处的索引,后者要从指定的索引开始搜索(11)isEmpty() ** 当且仅当 length() 为 0 时返回 true
(12)lastIndexOf(int ch) 与 lastIndexOf(int ch, int fromIndex)**返回指定字符在此字符串中最后一次出现处的索引,后者要从指定的索引处开始进行反向搜索(13)lastIndexOf(String str) 与 lastIndexOf(String str, int fromIndex) **
返回指定子字符串在此字符串中最后一次出现处的索引,后者要从指定的索引开始反向搜索
(14)length()** 返回此字符串的长度(15)matches(String regex) 字符串是否匹配给定的正则表达式(16)replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的(17)replaceAll(String regex, String replacement) 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串(18)replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串(19)split(String regex) 根据给定正则表达式的匹配拆分此字符串(20)startsWith(String prefix)
startsWith(String prefix, int toffset) **
测试此字符串是否以指定的前缀开始,后者要从指定的索引开始搜索
(21)char[] ** toCharArray() ** 将此字符串转换为一个新的字符数组
(22)toLowerCase() 与toLowerCase(Locale locale) **将此 String 中的所有字符都转换为小写,前者是使用默认环境,后者是使用指定Locale的规则(23)toString() 返回此对象本身(24)toUpperCase() 与 toUpperCase(Locale locale) **
将此 String 中的所有字符都转换为大写,前者是使用默认环境,后者是使用指定Locale的规则
(25)trim()** 返回一个新的字符串,忽略前导空白和尾部空白(26)valueOf(Object) 返回参数的字符串表示,其中的参数可以为各种类型 (27)substring (int beginIndex,int endIndex) ** 提取string对象中从beginIndex开始到endIndex-1**结束的字串,并返回提取的字串
下面对几个比较常用方法进行简单的解析:
public int length()
求数组的长度。eg:
String str="YYH";
System.out.println(str.length()); //输出 的是3
【注意】数组长度的实例变量length和这个字符串对象求解长度不可以混淆,一个是实例变量length,还有一个是方法length(),获得数组长度的方法为:实例变量.length。
public boolean equals(String anString)
比较两个字符串对象的内容是否相等,相等的话返回true,不等返回false。
public boolean equalsIgnoreCase(String antherString)
忽略字母大小写方式,比较两个字符串变量的内容是否相等
【注意】关于equals和==
(1)对于==,如果作用于基本数据类型的变量(byte,short,char,int,long,float,double,boolean ),则直接比较其存储的"值"是否相等;如果作用于引用类型的变量(String),则比较的是所指向的对象的地址(即是否指向同一个对象)。
(2)equals方法是基类Object中的方法,因此对于所有的继承于Object的类都会有该方法。在Object类中,equals方法是用来比较两个对象的引用是否相等,即是否指向同一个对象。
(3)对于equals方法,注意:equals方法不能作用于基本数据类型的变量。如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;而String类对equals方法进行了重写,用来比较指向的字符串对象所存储的字符串是否相等。其他的一些类诸如Double,Date,Integer等,都对equals方法进行了重写用来比较指向的对象所存储的内容是否相等。
/** * 关于equals和== */public void test12(){ String s1="hello"; String s2="hello"; String s3=new String("hello"); System.out.println("===========test12============"); System.out.println( s1 == s2); //true,表示s1和s2指向同一对象,它们都指向常量池中的"hello"对象 //flase,表示s1和s3的地址不同,即它们分别指向的是不同的对象,s1指向常量池中的地址,s3指向堆中的地址 System.out.println( s1 == s3); System.out.println( s1.equals(s3)); //true,表示s1和s3所指向对象的内容相同}
public String conc****at(String str)
将参数对象连接到调用对象的后面,返回连接后的新串。eg:
String s1="hello",s2="YYH",s3;
s3=s1.concat(s2);
System.out.println(s3); //输出 的是"helloYYH",但是s1和s2没有发生变化
【注意】如果参数str为空(null),则concat()方法不会创建新串,而仅仅返回当前串。
字符串还可以使用+号连接:eg:
public class Test<c> { public static void main(String[] args) { String a = "--"; String b = "--"; String c = "Y" + "Y " + a + "H" + "H" + b; System.out.println(c); } }
OUTPUT:YY --HH--
【注意】****String c = "Y" + "Y " + a + "H" + "H" + b; 实质上的实现过程是: String c = new StringBuilder("YY").append(a).append("H").append("H").append(b).toString();
由此得出结论:当使用+进行多个字符串连接时,实际上是产生了一个StringBuilder对象和一个String对象。
public String replace(char oldChar ,char newChar)
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的 。eg:
String path="D:/YYHjava/documents";
System.out.println(path.replace('/','\\')); //输出D:\YYHjava\documents
【注意】'\'代表'',他是一个转义字符
public String toString()
该方法返回当前字符串对象本身。由于 String类是 Object类的子类,在 Object类中,定义了 tostring())方法,所以这里的 tostring()实际上是对 Object类中 tostring()方法的重载。事实上,很多 Object类的子类都重载了该方法。 Object类的子类的 tostring()方法都用于将该类对象转换为相应的一个不变字符串。
public static String valueOf(各种类型 f)
这个方法的用途比较广泛,它可以将各种数据类型转换成一个相应的字符串表示,由于它是一个 static()方法,所以不需要用 String类对象调用,而是以类名调用。在 String类中,提供了该方法的多个重载版本:
public static String valueof(boolean)
public static String valueof(char)
public static String valueof(char [])
public static String valueof(char[], int, int)
public static String valueof(double)
public static String valueof(float)
public static String valueof(int)
public static String valueof(long)
public static String valueof(Object)
valueOf()方法的应用:
public class ValueOf { public static void main (String arg[]){ char a[]={'A','B','C','D','E','F'}; int i =123456; float f=3.1415926f; boolean b=false; Object o=null; System.out.println(String.valueOf(a)); //字符数组 System.out.println(String.valueOf(a,2,3)); System.out.println(String.valueOf(i)); //int类型 System.out.println(String.valueOf(f)); //float类型 System.out.println(String.valueOf(b)); //boolean类型 System.out.println(String.valueOf(o)); //object类型 }}
OUTPUT:ABCDEFCDE1234563.1415925falsenull
StringBuffer
前面讨论的 String类适用于程序中不改变字符的情况,若对字符串的每次修改都创个新的 String对象,显然不利于提高程序的效率。Java为了解决这个问题提供了与则String类同级别的StringBuffer类。 StringBuffer类对象是一个内容可以改变的字符串,修改后仍然存放在原来的对象中。这样做是为了减少由于少量字符的插入而引起的空间分配问题。许多程序员只使用 String类,而让Java在幕后通过“+”运算符重载来调用Stringbuffer类。 StringBuffer类的原型如下:
//StringBuffer原型:public final class java.lang.StringBuffer extends java.lang.Object{ ..............}
我们可以清楚的看出来,StringBuffer该类的父类是一个Object类,由final进行修饰,表明不能产生子类(如导言所说),并且是一种工具类(由public修饰)。
【注意】String Buffer类对象有一块内存缓冲区,字符串被存放在缓冲区中,缓冲区的大小可以随程序的需要进行调整。
缓冲区的大小称为对象的容量。
当修改对象内容时,只要对象包含的字符个数没有超出容量,就不会分配新的空间,而直接在原空间内进行修改。
若字符的个数超出了容量, StringBuffer对象会自动调整容量,从而话应对象的存储要求。
创建StringBuffer类对象
创建StringBuffer类对象是通过调用该类的构造函数实现的,StringBuffer类的构造函数有三种格式:
1)public StringBuffer()
该方法创建的对象为一个空对象,容量最大为16个字符。eg:
StringBuffer s1=new StringBuffer();
2)public StringBuffer(int length)
创建的对象为空,容量的长度为参数length确定。
StringBuffer s2=new StringBuffer(3); //容量为3
【注意】length应大于或等于零,不可以为负数,否则会报错
3)public StringBuffer(String str)
创建对象的内容和str一样,容量为参数str的长度加上16。eg:
String s3="YYH";
StringBuffer s4=new StringBuffer(s3); //s4的容量为19,内容是“YYH”
StringBuffer类常用的方法
常用方法见下表:
<caption style="box-sizing: border-box; outline: 0px; margin: 0px; padding: 0px; font-weight: normal; word-wrap: break-word;">StringBuffer常用方法</caption>
** 方法名称**** 用途****append(各种类型) **将其他类型的数据转换为一个字符串,并追加到目前缓冲区中,如果有必要,缓冲区会变大。注意:该方法有多个重载版本capacity()返回目前分配的空间大小,即对象的缓冲区容量ensureCapacity(int)要求缓冲区容量不少于参数指定值
Insert(int,多种类型)
| 将指定类型的数据转换为一个字符串,插人到int参数指定的位置 || **length() ** | 返回字符的个数 || **reverse() ** | 将缓冲区中的字符逆序存放 || setCharAt(int, char) | 将指定位置上的字符修改为另一个指定的字符 || setLength(int) | 截断或扩展原来的字符串,如果是扩展,新增加的空间全部填'\0' || **toString() ** | 根据此对象生成一个新的 String对象 || getChars(int ,int ,char[] ,int) | 将对象的一段字符复制到指定的字符数组中 |
进行简单的解析:
下面对几个比较常用方法进行简单的解析:
1)public int length()
返回字符串的长度。
2)public int capacity()
返回缓冲区的大小。
3)public void setLength(int newLength)
指定对象的长度,将对象的内容进行裁减。如果参数 newlength小于对象的长度,则将对象截断;如果 newlength大于或等于对象的长度,则对新增加的部分填充空字符。
4)public void ensureCapacity(int NewCapacity)
设定对象的缓冲区的大小,若 New Capacity小于对象的容量,则新的设置将不起作用,也就是说容量只能扩大而不能缩小。
【注意】StringBuffer类对象的长度可以扩大或者缩小,但该类对象的容量只能扩大而不能缩小。
5)public void setCharAt(int index, char ch)
将参数index位置上的值转化为ch指定的字符。eg:
StringBuffer str=new StringBuffer("Hello,java");
str.setCharAt(3,‘L’); //str的内容变为“HelLo,java”。
6)public StringBuffer append(多种数据类型)
这是一个用于追加字符串的方法,它将其他类型的数据(如int、char类型等)添加到String Buffer对象的尾部,返回修改后的 String Buffer对象。考虑到数据类型的多样性,Java提供了 append()方法的多个重载版本:
public StringBuffer append(boolean)
public StringBuffer append(char)
public StringBuffer append(char[])
public StringBuffer append(char[], int, int)
public StringBuffer append(double)
public StringBuffer append(float)
public StringBuffer append(int)
public StringBuffer append(long)
public StringBuffer append(Object)
public StringBuffer append(String)
上述这些方法的返回值均是更新后的 Stringbuffer对象,而不是新建的 Stringbuffer的对象。eg:
StringBuffer s1,s2=new StringBuffer();
s1=s2; //s1和s2代表一个对象
s2.append(3.14).append(' ').append("java");
System.out.println(s1==s2); //输出true
System.out.println(s1); //输出3.14 java
【注意】****append()方法之所以能够将其他的数据添加到StringBuffer对象的尾部,是因为他可以****自动的调用String类中的一个静态的方法ValueOf(),从而将其他类型的数据暂时转换为一个String的临时对象****,然后将这个对象添加到StringBuffer尾部。这就是append()方法的实现技术内幕。
“+”运算符可以连接两个String类对象,但是不能连接连个StringBuffer对象。
7)public String toString()
该方法完成了由StringBuffer对象到String对象的转换。因为String对象具有一定的安全性(不可修改,一旦修改就返回一个新对象)。
【注意】不可以对StringBuffer对象直接赋值,例如,str=“Good”是错误的,如果要赋值必须用构造函数实现。
8)public StringBuffer insert(int offest,多种类型 b)
该方法是将一个 String对象、 Object对象、字符数组和基本类型的变量(int、 float、long、char、 boolean、 double)b插入到 offset指定的位置。
StringBuild
StringBuilder也是一个可变的字符串对象,他与StringBuffer不同之处就在于它是线程不安全的,基于这点,它的速度一般都比StringBuffer快。与StringBuffer一样,StringBuider的主要操作也是append与insert方法。这两个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符添加或插入到字符串生成器中。
String,StringBuffer,StringBuild的区别
执行速度:在这方面的快慢为 StringBuilder > StringBuffer >String
在线程安全上:StringBuilder是线程不安全的,而StringBuffer是线程安全的
总结
字符串的知识很重要需要详细的学习一下,我认为字符串的处理的知识学会我上面所有的内容就差不多够用了。所有要说的都在上面了,哈哈哈!!!
后言
这次的文章总结的内容有点多,因为我觉得字符串的操作很重要,我们需要点耐心仔细的看完,细细评味当中的各个知识点(看完必定会有很多的收获)整理了好久才整理好 ,很不容易!!!
有喜欢学习Java的小伙伴可以关注我一下,本人也是学习Java路上的小白,但是会自己会经常总结出一些基础并且很实用的知识点拿出来分享给大家,希望在学习的道路上可以对你们有所帮助,我们可以一起学习一起进步。还有也欢迎大佬们和我讨论关于Java的一些知识,相互学习!!!互相进步!!!