接上次博客:Java学习(11):Java实现图书馆书库管理系统_di-Dora的博客-CSDN博客
目录
String类
构造字符串的方法:
String 对象的比较
1、==比较是否引用了同一个对象。
2、比较字符串内容是否相同
3、"int compareTo(String s) "方法:
4、忽略大小写进行比较——compareToIgnoreCase ( ) 。
编辑
String的查找方法:
字符串转化
1、数值与字符的转换
2、大小写转换
编辑
3、数组和字符串之间的转换
4、格式化的转换
字符串的替换
1、String replace(CharSequence target, CharSequence replacement)方法
2、String replace(char oldChar, char newChar)方法
3、String replaceAll(String regex, String replacement)方法
4、String replaceFirst(String regex, String replacement)方法
字符串的拆分
split方法
编辑
字符串的截取
1、substring方法
2、splitAsStream方法
3、StringTokenizer类
其他方法
1、String的 trim( ) 方法
字符串的不可变性
StringBuilder和StringBuffer
StringBuilder和StringBuffer的一些相关的方法:
StringBuilder和StringBuffer的异同点:
StringBuilder和StringBuffer的优缺点论述:
StringBuilder、StringBuffer和String:
时间过得好快啊!转眼间初阶Java的学习就要结束了!
但是还是不能松懈。Keep looking ! Don't settle !
今天我们来学习一下String类。
通过跳转查看String的实现,我们可以了解到很多信息:
在Java中,字符串类型String是一种非常常用的数据类型。它是一种不可变的、序列化的字符集合,表示任意长度的Unicode字符序列。 区别于C语言,Java当中的字符串没有以 ' \ 0 ' 结尾这样的说法!
以下是Java字符串类型String的一些常见特点:
1、字符串是不可变的:一旦创建了字符串,它的值就不能被改变。如果需要修改字符串,必须创建一个新的字符串对象。
2、字符串是序列化的:Java字符串中的字符按顺序排列,并且每个字符都有一个唯一的索引位置。这使得可以通过索引位置访问字符串中的任何字符。
3、字符串是Unicode字符集合:Java字符串中的每个字符都是Unicode字符。这意味着可以在字符串中包含任何语言的字符,包括中文、日语、希腊语等。
4、字符串是对象:在Java中,字符串是一个引用对象,而不是基本数据类型。因此,可以对字符串应用Java中所有对象所支持的方法。
5、字符串的操作:Java字符串支持很多操作,例如拼接、比较、替换、截取等。其中,拼接是最常用的操作,可以使用"+"符号或concat()方法来实现。
6、字符串的创建:Java中可以使用双引号(" ")或者String类的构造函数来创建字符串。双引号创建的字符串是常量,而构造函数创建的字符串是可变的。
总而言之,让我们先来看看怎么构造一个String类吧。
在Java中,字符串类型String的基本的构造方法包括:
1、使用字符串常量构造String对象(其实是第二种的简化):
String str1 = "Hello World!";
2、直接new一个String对象:
String str2 = new String();
String str3 = new String("Hello World!");
它其实有很多个重载的构造方法:
打开帮助手册看看:
3、使用字符数组创建String对象:
char[] charArray = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
String str4 = new String(charArray);
4、使用字节数组创建String对象:
byte[] byteArray = {72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33};
String str5 = new String(byteArray);
5、使用StringBuilder或StringBuffer的toString方法创建String对象:
StringBuilder stringBuilder = new StringBuilder("Hello");
String str6 = stringBuilder.toString();
StringBuffer stringBuffer = new StringBuffer("World");
String str7 = stringBuffer.toString();
这里我们需要注意的是:
String是引用类型,内部并不存储字符串本身,我们刚刚有看到,在String类的实现源代码中,String类实例变量如下——它有两个成员变量:
可以调试了看看:
public class Test {
public static void main(String[] args) {
String str = "hello";
System.out.println(str);
//我们必然实现了自己的ToString方法
String str2 = new String("world");
System.out.println(str2);
char[] array = {'a','b','c'};//字符数组
String str3 = new String(array);
System.out.println(str3);
}
}
第一个: char value [ ] 是一个没有分配内存的数组(数组是一个引用类型);
第二个:哈希(比较复杂,以后再说)
那么,我们可以通过下面这个代码来看看数据具体是怎么存储的:
String s1 = new String("hello ");
String s2 = new String("world!");
String s3 = s1;
接下来是两个容易混淆的知识点:
1、怎么求字符串的长度——通过.length() :
String str = "hello";
System.out.println(str);
System.out.println(str.length());
System.out.println("hello".length());//"hello"还是一个字符串,可以运用字符串的所有方法
int[] arr={1,2,3,4,5,6};
System.out.println(arr.length);
如上代码可以看出,String的length是个方法,但是数组的length是个属性。
2、怎么判断字符串是不是为空?
String str1 = "";//一共空字符串
System.out.println(str1.isEmpty());//判断字符串是不是空的
String str2 = null;//str2这个引用 不指向任何的对象
//System.out.println(str2.isEmpty()); // 会报错——空指针异常
在Java中,字符串是一种特殊类型的对象。在比较字符串对象时,有多种方法可以使用,包括以下方法:
"=="运算符:这个运算符比较两个字符串对象的引用是否相同。如果两个字符串对象的引用指向相同的内存地址,那么这两个字符串对象是相等的。例如:
public static void main2(String[] args) {
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);//字符串常量池 有哈希表的基础
System.out.println("========");
String str3 = new String("Hello World!");
String str4 = new String("Hello World!");
System.out.println(str3 == str4);
}
为什么会出现这种情况?
很简单,str3 和 str4 代表两个不同的对象,它们存储的地址是不一样的。
但是,str1 和 str2 就涉及到了“字符串常量池”的概念了:
字符串常量池是Java中的一种特殊的存储区域,它用于保存字符串常量(也包括字面值常量)。
在Java中,字符串常量是一种不可变的对象,它们的值在创建之后不能被修改。由于字符串常量在应用程序中经常被使用,为了节省内存和提高性能,Java将这些常量保存在一个被称为字符串常量池的特殊存储区域中。
当程序需要使用一个字符串常量时,它首先会在常量池中查找是否已经存在相同值的字符串常量。如果存在,它将返回该常量的引用,否则将创建一个新的字符串常量,并将其添加到常量池中,然后返回其引用。
字符串常量池的使用可以提高程序的性能和效率,因为它可以避免创建多个相同内容的字符串对象,从而节省内存空间,并且能够提高字符串的比较效率。当使用双引号创建字符串常量时,Java会自动将其添加到常量池中,例如:
String str1 = "hello"; String str2 = "hello";
在这个例子中,str1和str2都是字符串常量,它们的值相同,因此它们实际上是同一个对象,Java会在常量池中只创建一个字符串常量"hello",并将str1和str2都指向该对象。这种使用字符串常量池的方式可以提高程序的性能,因为它避免了不必要的对象创建和内存分配。
需要注意的是,字符串常量池只存储字符串常量和字面值常量,而不存储使用new关键字创建的字符串对象。因此,如果使用new关键字创建一个字符串对象,它将被存储在堆中,而不是字符串常量池中。
总之,当“==”两边是引用类型是,一定要注意看此时真正比较的是什么。当使用双引号声明字符串时,Java会尝试在字符串池中查找相同内容的字符串,并返回相同的引用。因此,对于这种情况,使用"=="运算符比较字符串对象是可行的。但是,对于使用new关键字创建的字符串对象,"=="运算符比较的是它们的引用,而不是内容,因此通常应该使用接下来要介绍的equals()方法来比较它们的内容。
用equals()"方法:这个方法比较两个字符串对象的内容是否相等。如果两个字符串对象的内容相同,那么这两个字符串对象是相等的。例如:
String str1 = "hello";
String str2 = new String("hello");
System.out.println(str1.equals(str2)); // true
等等!equals不是Object的吗?为什么这里能用呢?
当然是因为这里String 重写了equals();如果不重写,就会默认调用Object的方法。
那么这里比较的还是地址,是不可以的。
所以自定义类型一定要重写hashCode方法和equals方法。
这里,str1和str2虽然是不同的对象,但它们的内容相同,因此它们相等。
还记得吗?String 实现了我们的Comparable接口,代表它重写了compareTo方法。
与equals方法不同的是,前者返回的是一个布尔值,而compareTo返回的是 int 。
这个方法比较两个字符串对象的字典顺序。
说的简单一点,就是:
例如:
String str1 = "abc";
String str2 = "def";
System.out.println(str1.compareTo(str2)); // -3
//这里,str1在字典顺序中排在str2之前,因此compareTo()方法返回了一个负数(-3)。
str1大于str2,返回正数;反之返回负数。如果相同,返回0。
从源代码中我们可以看出,它是先去比较两个字符串的长度,这就引出了一点:
相同长度的字符串我们知道怎么比较,不同长度呢?
比较逻辑:
public static void main3(String[] args) {
String str1 = "abcd";
String str2 = "abcdef";
//str1大于str2,返回正数; 如果不大于,返回负数; 如果相同 返回0
System.out.println(str1.compareTo(str2));
System.out.println(str2.compareTo(str1));
/**
* 比较逻辑:
* 1. 如果两个字符串的长度是一样的,那么第一个不一样的字符的大小就是整个字符串的大小
* 2. 如果两个字符串长度不一样,那么先比较两个长度的差值个数
* 3、在这个差值范围内,有不一样的字符就能比较出来大小
* 4、但是如果都是一样的,那么就看谁长,谁长谁就大 !
*/
}
public static void main(String[] args) {
String str1 = "Abcd";
String str2 = "abcd";
//忽略大小写进行 比较
System.out.println(str1.compareToIgnoreCase(str2));
}
Java中的String类提供了多种基本的查找方法,以下是其中几种常用的方法:
1、char charAt(int index)方法:返回指定索引处的字符。索引从0开始计数。如果索引超出字符串长度,则会抛出IndexOutOfBoundsException异常。
String str1 = "abcdef";
//获取指定位置的字符
char ch = str1.charAt(2);
System.out.println(ch);
char cr = str1.charAt(22);//下标越界!
System.out.println(ch);
2、int indexOf(int ch)方法:返回指定字符在字符串中第一次出现的索引。如果找不到指定字符,则返回-1。
String str = "Hello World";
int index = str.indexOf('o'); // index的值为 4
indexOf 有多个重载方法:
3、int indexOf(String str)方法:返回指定字符串在当前字符串中第一次出现的索引。如果找不到指定字符串,则返回-1。
String str = "Hello World";
int index = str.indexOf("World"); // index的值为 6 ,返回W的位置
4、int indexOf(int ch, int fromIndex)方法:返回指定字符在字符串中从指定位置开始第一次出现的索引。如果找不到指定字符,则返回-1。
String str = "Hello World";
int index = str.indexOf('o', 5); // index的值为 7
5、int indexOf(String str, int fromIndex)方法:返回指定字符串在当前字符串中从指定位置开始第一次出现的索引。如果找不到指定字符串,则返回-1。
String str = "Hello World";
int index = str.indexOf("World", 3); // index的值为 6
前面的indexOf默认是从头开始往后找,对应的,有一个lastIndexOf 是从后往前找。
6、int lastIndexOf(int ch)方法:返回指定字符在字符串中最后一次出现的索引。如果找不到指定字符,则返回-1。
String str = "Hello World";
int index = str.lastIndexOf('o'); // index的值为 7
7、int lastIndexOf(String str)方法:返回指定字符串在当前字符串中最后一次出现的索引。如果找不到指定字符串,则返回-1。
String str = "Hello World";
int index = str.lastIndexOf("or"); // index的值为 7
8、int lastIndexOf(int ch, int fromIndex)方法:返回指定字符在字符串中从指定位置开始向前搜索最后一次出现的索引。如果找不到指定字符,则返回-1。
String str = "Hello World";
int index = str.lastIndexOf('o', 6); // index的值为 4
9、int lastIndexOf(String str, int fromIndex)方法:返回指定字符串在当前字符串中从指定位置开始向前搜索最后一次出现的索引。如果找不到指定字符串,则返回-1。
String str = "Hello World";
int index = str.lastIndexOf("o", 6); // index的值为 4
下面是String类的几种基本转换方法的介绍和示例:
将数值类型转换为字符串类型,使用String类的静态方法 valueOf(),可以将int、long、float、double、boolean等基本类型转换为对应的字符串形式,甚至连对象都可以转换成对应的字符串。
例如:
class Student(){
//这里需要实现这个类自己的ToString方法
}
public static void main(String[] args) {
String s1 = String.valueOf(1234);
String s2 = String.valueOf(12.34);
String s3 = String.valueOf(true);
// String s4 = String.valueOf(new Student());
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
将字符串类型转换为数值类型,使用parseXXX()方法,可以将字符串类型的数值转换为对应的基本类型。其中使用到了基础数据类型的包装类型,例如:
String strNum = "456";
int num = Integer.parseInt(strNum);
System.out.println("转换后的整型数值为:" + num);
public static void main(String[] args) {
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.34");
System.out.println(data1);
System.out.println(data2);
}
总的输出一下:
public static void main(String[] args) {
String s1 = String.valueOf(1234);
String s2 = String.valueOf(12.34);
String s3 = String.valueOf(true);
//String s4 = String.valueOf(new Student());
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
String strNum = "456";
int num = Integer.parseInt(strNum);
System.out.println("转换后的整型数值为:" + num);
int data1 = Integer.parseInt("1234");
double data2 = Double.parseDouble("12.34");
System.out.println(data1);
System.out.println(data2);
}
将字符串转换为全大写,使用toUpperCase()方法。例如:
String str = "hello, world";
String upperStr = str.toUpperCase();
System.out.println("转换后的字符串为:" + upperStr);
将字符串转换为全小写,使用toLowerCase()方法。例如:
String str = "HELLO, WORLD";
String lowerStr = str.toLowerCase();
System.out.println("转换后的字符串为:" + lowerStr);
只进行部分转换也是可以的,有几个就转换几个,而且只转换字母。
String str3 = "HELLO, WorlD";
String lowerStr2 = str3.toLowerCase();
String upperStr2 = str3.toUpperCase();
System.out.println("转换后的字符串为:" + lowerStr2);
System.out.println("转换后的字符串为:" + upperStr2);
将字符数组转换为字符串,使用我们之前讲过的String类的构造函数或者valueOf()方法。例如:
char[] charArray = {'h', 'e', 'l', 'l', 'o'};
String str = new String(charArray);
System.out.println("转换后的字符串为:" + str);
String str2 = String.valueOf(charArray);
System.out.println("转换后的字符串为:" + str2);
将字符串转换为字符数组,使用toCharArray()方法。例如:
String str = "hello";
char[] charArray = str.toCharArray();
System.out.println("转换后的字符数组为:" + Arrays.toString(charArray));
/* for (int i = 0; i < chatArray.length; i++) {
System.out.print(chatArray[i]+" ");
}
System.out.println();
*/
格式化输出,使用String类的format()方法,可以将字符串按照指定的格式输出。例如:
String str = "world";
int num = 123;
String formattedStr = String.format("hello, %s, the number is %d", str, num);
System.out.println("格式化后的字符串为:" + formattedStr);
String s = String.format("%d-%d-%d", 1999, 9,14);
System.out.println(s);
将字符串按照指定的格式解析为日期,使用SimpleDateFormat类。例如:
String str = "2022-05-10 14:30:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(str);
System.out.println("解析后的日期为:" + date);
可是我们不是说:字符串是不可变的吗?
注意!!!
这里的转换不是在原来的字符串上转换,而是创建了一个新的字符串,对这个新的对象进行操作。
比如我们的toUpperCase ,它在最后返回时new了一个新的对象:
下面是String类的几种基本替换方法的介绍和示例:
记忆突然开始攻击我!
你还记得最开始的String实现的三个接口吗?
其中就有一个是CharSequence!
既然接口是可以发生向上转型的,那我们这里直接传一个字符串是不是可以呢?
当然:
该方法会将字符串中所有的指定子串 target 替换为新的子串 replacement,并返回一个新的字符串。注意,target 和 replacement 都是 CharSequence 类型,因此可以传入字符串或者字符序列等类型的参数。例如:
String str1 = "ababcabcdabcde";
//把所有的ab替换为 oooo 这个替换也是一样 不是在原来字符串的基础上进行替换 他是产生了
新的对象
String ret = str1.replace("ab","oooo");
System.out.println(ret);
String str = "hello world";
String newStr = str.replace("world", "Java");
System.out.println("替换后的字符串为:" + newStr);
该方法会将字符串中所有的指定字符 oldChar 替换为新字符 newChar,并返回一个新的字符串。例如:
String str = "hello world";
String newStr = str.replace('l', 'L');
System.out.println("替换后的字符串为:" + newStr);
输出结果为:heLLo worLd
该方法会将字符串中所有匹配正则表达式 regex 的子串替换为新的子串 replacement,并返回一个新的字符串。例如:
String str = "hello world";
String newStr = str.replaceAll("\\s", "-");
System.out.println("替换后的字符串为:" + newStr);
String str2 = "helloworldworld";
String newStr2 = str2.replace("world", "Java");
System.out.println("替换后的字符串为:" + newStr2);
输出结果为:hello-world helloJavaJava
该方法会将字符串中第一个匹配正则表达式 regex 的子串替换为新的子串 replacement,并返回一个新的字符串。例如:
String str = "asdkjfhabababllll";
String ret3 = str.replaceFirst("ab","kk");
System.out.println(ret3);
输出结果为:asdkjfhkkababllll
刚刚我们有提到一个概念——“正则表达式”,因为一会儿我们讲到拆分时也会提到它,所以可以先做一个简单了解:
Java中的正则表达式是一种用于处理字符串的模式匹配工具。
正则表达式由特殊字符和文本字符组成,可以用来匹配字符串中的特定模式。
在Java中,正则表达式通常使用java.util.regex包中的类来实现,主要包括以下几个类:
Pattern类:表示一个正则表达式模式,并且提供了一些方法来进行匹配操作。
Matcher类:用于匹配字符串,并且提供了一些方法来访问匹配结果。
PatternSyntaxException类:当正则表达式模式语法错误时抛出。
使用Java的正则表达式,我们可以进行以下操作:
- 检查字符串是否匹配某个正则表达式模式。
- 在字符串中查找符合正则表达式模式的子串。
- 替换字符串中符合正则表达式模式的子串。
- 分割字符串,获取符合正则表达式模式的子串数组。
同样的,替换字符串的操作也不是在原来的字符串的基础上进行的,而是产生了一个新的对象。
有时候我们需要对字符串进行一些拆分操作,可以使用以下几种基本的拆分方法:
split方法可以按照指定的分隔符将字符串拆分成一个字符串数组。例如:
public static void main(String[] args) {
String str = "name=zhagnsan&age=10";
//分割之后的结果 要存储到 数组当中
String[] strings = str.split("&");
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
//这里,我们也可以指定要分几组,如下就是按空格分,分两组
System.out.println("============");
String str2 = "hello world hello bit";
String[] strings2 = str2.split(" ",2);
for (int i = 0; i < strings2.length; i++) {
System.out.println(strings2[i]);
}
String str3 = "apple,banana,orange";
String[] strings3 = str3.split(",");
for (int i = 0; i < strings3.length; i++) {
System.out.println(strings3[i]);
}
}
还有,对于一些特殊的字符,我们必须对它们进行转义:
这里我们必须对' . '进行转义!用 ' \\' 类似的还有 | * +
如果是' \ ',为了不对数字转义,我们应该' \\ 234',所以最后应写为' \\\\ '。
这里的表达式其实就是我们刚刚说的正则表达式。
如果有多个分隔符,此时使用' | '当连接符,比如' &|% '就代表有两个分隔符“&”“%”。
好了,总的看一下:
public static void main(String[] args) {
String str = "192.168.0.0.1";
String[] strings= str.split("\\.");
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
String str2 = "192\\168\\0\\0\\1";
//System.out.println(str); //192\168\0\0\1
String[] strings2= str.split("\\\\");
for (int i = 0; i < strings2.length; i++) {
System.out.println(strings2[i]);
}
String str1 = "name=zhagnsan&age=10";
String[] strings1 = str.split("&|=");
for (int i = 0; i < strings1.length; i++) {
System.out.println(strings1[i]);
}
}
如果想要进行多次分割,
String str = "name=zhagnsan&age=10";
//分割之后的结果 要存储到 数组当中
String[] strings = str.split("&");
for (int i = 0; i < strings.length; i++) {
String[] stringsss=strings[i].split("=");
for(int j=0;j
substring方法可以截取字符串中的一段子串。
String str = "hello world";
String subStr = str.substring(6, 11);//[6,11)
以上代码将会把字符串 str 中的第7个字符(即字符"w")到第11个字符(即字符"d")截取出来,存放在字符串变量 subStr 中,最终输出的结果是 "world"。
String s = "abcdef";
//这里如果传入的是0下标 那么默认返回原来的对象 但是如果传入的是其他的对象,
//此时 返回新new的对象
String ret = s.substring(1);// bcdef
String ret2 = s.substring(1,4);//[1,4) //bcd
System.out.println(ret);
System.out.println(ret2);
splitAsStream方法和split方法类似,都可以按照指定的分隔符将字符串拆分成一个流。例如:
String str = "apple,banana,orange";
Stream stream = Pattern.compile(",").splitAsStream(str);
以上代码将会把字符串 str 以逗号为分隔符,拆分成一个流 stream,流中的元素分别为 "apple", "banana", "orange"。
StringTokenizer类也可以按照指定的分隔符将字符串拆分成一个字符串数组。例如:
String str = "apple,banana,orange";
StringTokenizer st = new StringTokenizer(str, ",");
while (st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
以上代码将会把字符串 str 以逗号为分隔符,拆分成一个字符串数组,并通过while循环逐个输出数组中的元素。
trim ( ) 是用来去除字符串开头和结尾的空格(包括制表符、换行符等空白字符)的方法。trim( ) 方法返回的是一个新的字符串对象,该对象是原始字符串去除空格后的结果。
下面是一个使用trim()方法的例子:
String str = " hello world ";
String trimmedStr = str.trim();
System.out.println(trimmedStr); // 输出:hello world
在上面的例子中,原始字符串 " hello world " 的开头和结尾都包含了空格字符,但是通过调用trim()方法,得到了一个去除了开头和结尾空格的新字符串对象 "hello world"。
需要注意的是,trim()方法只会去除开头和结尾的空格字符,并不会去除字符串中间的空格字符。
String s2 = " abc def ";
System.out.println(s2);
//只会去除左右两边的空格
String ret1 = s2.trim();
System.out.println(ret1);
如果需要去除字符串中间的空格字符,可以使用replaceAll()方法。例如:
String str = "hello world ";
String replacedStr = str.replaceAll(" ", ""); // 把空格替换成空字符串
System.out.println(replacedStr); // 输出:helloworld
在上面的例子中,使用replaceAll()方法把空格字符替换成空字符串,从而去除了字符串中间的空格字符。
在Java中,字符串(String)是不可变的,也就是说,一旦字符串被创建,它的值就不能被修改。这种特性被称为字符串的不可变性。
我们刚刚讲解的字符串的替换和截取或者是转换,都不是在原本的字符串上操作的,而是 new 了一个新的对象,对那个对象进行了相关操作。
字符串不可以被修改的原因:
这里其实就和我们以前讲 final 和 数组的时候提到过的知识类似,而这里 value [ ] 本来就是一共数组:
final int[] array = {1,2,3,4};
array[0] = 10; //---------------正确的,它的值是可以被修改的
array = new int[3]; //-----------错误的,它的实际指向是不可被修改的
字符串的不可变性有以下几个方面的好处:
方便实现字符串对象池。如果String可变,那么对象就需要考虑写入对象池时的拷贝问题了。
但是,字符串的不可变性也带来了一些不便之处。例如,如果需要对一个字符串进行大量的修改操作,每次都需要创建一个新的字符串对象,这样会造成不必要的内存浪费。
字符串修改?
String s = "hello";
s+=" world!";
System.out.println(s);
如上代码实际上也不可以代表字符串发生了修改!这个代码在不停地产生新的对象。
我们把它的反汇编代码简单翻译一下:
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(s);
stringBuilder.append("world");
s = stringBuilder.toString();
System.out.println(s);
再来一个更加直接的例子:
public static void main5(String[] args) {
long start = System.currentTimeMillis();//计算时间
String s = "";
for(int i = 0; i < 10000; ++i){
s += i;
}
long end = System.currentTimeMillis();//执行完再次计算时间
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuffer sbf = new StringBuffer("");
for(int i = 0; i < 10000; ++i){
sbf.append(i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
StringBuilder sbd = new StringBuilder();
for(int i = 0; i < 10000; ++i){
sbd.append(i);
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
可以看出来:关于字符串拼接,频繁使用"+"操作符会产生很多不必要的中间 String 对象,占用大量内存。因此,使用 StringBuilder 的 append 方法进行字符串拼接是更高效的做法。
综上,如果需要对字符串进行频繁的修改操作,可以使用可变字符串(StringBuilder或StringBuffer),它们是可变的字符串对象,支持在原字符串的基础上进行修改,避免了频繁创建新的字符串对象。但需要注意的是,可变字符串不具备字符串的不可变性带来的优点,需要在使用时考虑其安全性和线程安全性等问题。
正如刚刚所说的,由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的。
我们先来了解一下:
1、StringBuffer append(String str):
方法描述:在尾部追加字符串str,并返回修改后的StringBuffer对象。相当于String的+=,可以追加:boolean、char、char[ ]、 double、float、int、long、Object、String、StringBuff的变量。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello ");
sb.append("World");
System.out.println(sb.toString()); // 输出:Hello World
StringBuilder stringBuilder = new StringBuilder("abcd");
stringBuilder.append(1);
stringBuilder.append("po");
stringBuilder.append("abcd").append(12.5);
System.out.println(stringBuilder);//输出abcd1poabcd12.5
2、char charAt(int index):
方法描述:返回在指定索引位置index处的字符。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello");
char c = sb.charAt(1);
System.out.println(c); // 输出:e
3、int length():
方法描述:返回当前StringBuffer对象中的字符数。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello");
int len = sb.length();
System.out.println(len); // 输出:5
4、int capacity():
方法描述:返回当前StringBuffer对象中字符存储空间的总大小。下面是一个示例:
StringBuffer sb = new StringBuffer(10);
int cap = sb.capacity();
System.out.println(cap); // 输出:10
5、void ensureCapacity(int minimumCapacity):
方法描述:确保当前StringBuffer对象的存储空间不小于指定的最小容量。如果当前容量小于指定容量,则分配足够的空间来存储指定容量的字符。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello");
sb.ensureCapacity(20);
int cap = sb.capacity();
System.out.println(cap); // 输出:20
6、void setCharAt(int index, char ch):
方法描述: 设置在指定索引位置index处的字符为ch。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello");
sb.setCharAt(1, 'a');
System.out.println(sb.toString()); // 输出:Hallo
7、int indexOf(String str):
方法描述: 返回第一次出现字符串str的位置,如果没有找到则返回-1。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello");
int index = sb.indexOf("l");
System.out.println(index); // 输出:2
8、int indexOf(String str, int fromIndex):
方法描述: 从指定的索引位置fromIndex开始查找字符串str第一次出现的位置,如果没有找到则返回-1。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello");
int index = sb.indexOf("l", 3);
System.out.println(index); // 输出:3
9、int lastIndexOf(String str):
方法描述:返回最后一次出现字符串str的位置,如果没有找到则返回-1。下面是一个示例:
StringBuffer sb = new StringBuffer("Hello");
sb.append(" world!");
int index = sb.lastIndexOf("o");
System.out.println(index); // 输出:7
10、int lastIndexOf(String str, int fromIndex):
方法描述:从指定的索引位置fromIndex开始从后往前查找字符串str最后一次出现的位置,如果没有找到则返回-1。下面是一个示例:
StringBuffer sb3 = new StringBuffer("heerlerreo world");
int index3 = sb3.lastIndexOf("l", 5);
System.out.println(index3); // 输出4
在字符串"hello world"中,从下标5开始往前查找字符"l",找到最后一次出现的位置是下标4。
11、StringBuffer insert(int offset, String str)
方法描述:在offset位置插入:八种基类类型 & String类型 & Object类型数据。下面是一个示例:
StringBuffer sb = new StringBuffer("hello world");
sb.insert(5, "my ");
System.out.println(sb.toString()); // 输出"hello my world"
在字符串"hello world"的下标5位置插入字符串"my ",得到新的字符串"hello my world"。
12、StringBuffer deleteCharAt(int index)
方法描述:删除index位置字符。下面是一个示例:
StringBuffer sb = new StringBuffer("hello world");
sb.deleteCharAt(6);
System.out.println(sb.toString()); // 输出"hello orld"
在字符串"hello world"的下标6位置删除字符"w",得到新的字符串"hello orld"。
13、StringBuffer delete(int start, int end)
方法描述:删除[start, end)区间内的字符。下面是一个示例:
StringBuffer sb = new StringBuffer("hello world");
sb.delete(6, 11);//[6,11)
System.out.println(sb.toString()); // 输出"hello"
在字符串"hello world"的下标6到11的字符区间内删除字符"world",得到新的字符串"hello"。
14、StringBuffer replace(int start, int end, String str)
方法描述:将[start, end)位置的字符替换为str。下面是一个示例:
StringBuffer sb = new StringBuffer("hello world");
sb.replace(6, 11, "java");
System.out.println(sb.toString()); // 输出"hello java"
在字符串"hello world"的下标6到11的字符区间内替换为字符串"java",得到新的字符串"hello java"。
15、String substring(int start)
方法描述:从start开始一直到末尾的字符以String的方式返回。下面是一个示例:
示例代码:StringBuffer sb = new StringBuffer("hello world");
String str = sb.substring(6);
System.out.println(str); // 输出"world"
在字符串"hello world"中,从下标6开始一直到末尾的字符为"world",以String的方式返回。
16、String substring(int start,int end)
方法描述:将[start, end)范围内的字符以String的方式返回。下面是一个示例:
示例代码:StringBuffer sb = new StringBuffer("hello world");
String str = sb.substring(6, 11);
System.out.println(str); // 输出"world"
在字符串"hello world"中,从下标6到11的字符区间为"world",以String的方式返回。
17、StringBuffer reverse()
方法描述:反转字符串。下面是一个示例:
示例代码:StringBuffer sb = new StringBuffer("hello world");
sb.reverse();
System.out.println(sb.toString()); // 输出"dlrow olleh"
18、String toString()
方法描述:将所有字符按照String的方式返回。下面是一个示例:
StringBuilder sb1 = new StringBuilder("hello");
String str = sb1.toString(); // 将StringBuffer以String的方式返回
System.out.println(str);
对了,StringBuilder和StringBuffer不可以直接赋值,比如:
StringBuffer sb5 ="hello world";//绝对不可以
根据构造函数来创建:
StringBuilder stringBuilder = new StringBuilder();
StringBuffer stringbuffer = new StringBuffer();
相同点:
StringBuilder和StringBuffer的区别:
综上所述,当需要进行大量字符串操作或者在单线程环境下操作字符串时,应该优先选择StringBuilder;而在多线程环境下或者需要线程安全的情况下,应该使用StringBuffer。
优点:
缺点:
StringBuilder、StringBuffer和String都是Java中用于处理字符串的类,它们之间的区别主要体现在以下两个方面:
因此,在选择使用这些类时,需要根据具体的业务场景和需求进行权衡和选择。如果需要频繁对字符串进行修改,并且在多线程环境下安全,可以选择StringBuffer;如果不需要考虑多线程环境下的安全问题,而且需要频繁对字符串进行修改,可以选择StringBuilder;如果字符串不需要修改,或者只需要少量的修改,可以选择String。
还有一点要注意的:
StringBuilder和StringBuffer不可以直接转换,如果要转换:append和toString方法。
1、append方法:
当我们需要将一个String类型的字符串转换为StringBuilder或StringBuffer类型的字符串时,可以使用append方法将其添加到一个新建的StringBuilder或StringBuffer对象中。
以下是一个示例代码:
String str = "hello world"; // 定义一个String类型的字符串
StringBuilder sb = new StringBuilder(); // 新建一个StringBuilder对象
sb.append(str); // 使用append方法将str添加到sb中
在这个例子中,我们定义了一个String类型的字符串str,然后新建了一个StringBuilder对象sb。使用append方法将str添加到sb中,实现了从String类型到StringBuilder类型的转换。
同样,如果需要将一个StringBuffer类型的字符串转换为StringBuilder类型的字符串,也可以使用同样的方式:
StringBuffer sbf = new StringBuffer(); // 新建一个StringBuffer对象
sbf.append(str); // 使用append方法将str添加到sbf中
StringBuilder sb = new StringBuilder(sbf.toString()); // 新建一个StringBuilder对象,并将sbf转换为String类型,作为构造方法的参数传入
在这个例子中,我们新建了一个StringBuffer对象sbf,然后使用append方法将str添加到sbf中。接着,我们使用sbf的toString方法将其转换为String类型的字符串,并将其作为参数传入到新建的StringBuilder对象中,实现了从StringBuffer类型到StringBuilder类型的转换。
2、toString方法:
你应该可以分辨出来哪个是StringBuffer类的toString方法,哪个是StringBuilder类的toString方法吧?
事实上,你可能已经看出来了,StringBuilder和StringBuffer之间可以通过toString方法进行相互转换,而不需要使用append方法。
例如,假设有一个StringBuilder对象sb:
StringBuilder sb = new StringBuilder("Hello");
可以使用toString方法将其转换为一个String对象:
String str = sb.toString();
同样地,假设有一个StringBuffer对象sb:
StringBuffer sb = new StringBuffer("World");
同样可以使用toString方法将其转换为一个String对象:
String str = sb.toString();
易错题:
(2016年的网易笔试题):
如下代码输出什么?
public class Example{
String str = new String("good");
char[ ] ch = { 'a' , 'b' , 'c' };
public static void main(String args[]){
Example ex = new Example();
ex.change(ex.str,ex.ch);
System.out.print(ex.str + " and ");
System.out.print(ex.ch);
}
public void change(String str,char ch[ ]){
str = "test ok";
ch[0] = 'g';
}
}
答案:good and gbc
这是因为在 change 方法中,虽然 str 被赋值为 "test ok",但这实际上是创建了一个新的 String 对象,并将该对象的引用赋值给了 str,原来的 "good" 字符串对象仍然存在于内存中。因此,ex.str 持有原来的字符串引用,没有改变。
而 ch[0] 被赋值为 'g',这实际上是修改了 ch 数组中第一个元素的内容,因为 char[ ] 类型是一个数组类型,其在 change 方法中传递的是引用(你可以理解为指针),而不是该数组的副本,因此在方法中修改了数组中的元素,在 main 方法中也能够看到修改后的结果。
这个例子展示了基本类型和引用类型在 Java 中的不同行为,基本类型的值传递是传递值的副本,引用类型的值传递是传递引用的副本。在方法中修改引用类型对象的属性或者数组元素的值会影响到原始对象,但是重新赋值引用类型参数并不会改变原始对象。