目录
2.2.2boolean equals(Object anObject) 方法
2.2.3int compareTo(String s) 方法
2.2.4int compareToIgnoreCase(String str) 方法
2.3.3 int indexOf(int ch, int fromIndex)
2.3.5 int indexOf(String str, int fromIndex)
2.3.7 int lastIndexOf(int ch, int fromIndex)
2.3.8 int lastIndexOf(String str)
2.3.9 int lastIndexOf(String str, int fromIndex)
2.5.1String peplace(char oldChar,char newChar)
2.5.2 public String replaceAll(String regex, String replacement)
2.5.3 public String replaceFirst(String regex, String replacement)
2.6.1 String[] split(String regex)
2.7.1 String substring(int beginIndex)
2.7.2 String substring(int beginIndex, int endIndex)
3.3 StringBuilder和StringBuffer区别
3.4模拟实现Arrays.ToString(String/StringBuilder)
一、String类的重要性
1.1重要性介绍
在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面向对象的思想,而字符串应用又非常广泛,因此Java语言专门提供了String类。在开发和校招笔试中,字符串也是常客。
面试中也频繁被问到,比如:String、StringBuff和StringBulider之间的区别等。
1.2String类
1.查看内部源码:
编辑
2.String类组成
编辑
编辑
注意:在Java中不存在字符串以‘\0’结尾的说法
二、常用方法
2.1字符串构造
2.1.1常量串构造
public static void main(String[] args) { String str="Hello World!"; System.out.println(str); //Hello World! }
2.1.2直接new String对象
public static void main(String[] args) { String str=new String("Hello World!"); System.out.println(str); //Hello World! }
2.1.3使用字符数组构造
public static void main(String[] args) { char[] array={'H','e','l','l','o'}; String str=new String(array); System.out.println(str); //Hello }
2.1.4注意事项
1.String是引用类型,内部并不存储字符串本身,在String类的实现源码中,String类实例变量如下:
编辑
2.字符串的两个简单方法
- 变量名.length:获取到字符串长度
- 变量名.isEmpty:检查字符串长度是否为0
注意:字符串new是在堆区开辟空间
public static void main(String[] args) { // s1和s2引用的是不同对象 s1和s3引用的是同一对象 String s1 = new String("hello"); String s2 = new String("world"); String s3 = s1; System.out.println(s1.length()); // 获取字符串长度---输出5 System.out.println(s1.isEmpty()); // 如果字符串长度为0,返回true,否则返回false }
编辑
3.在Java中“”引起来的也是String类型对象
// 打印"hello"字符串(String对象)的长度 System.out.println("hello".length());
不需要通过将"xx"的值赋值给变量也可完成方法的调用
2.2String对象的比较
2.2.1==比较是否引用同一个对象
注意:对于内置类型,==比较的是变量中的值;对于引用类型==比较的是引用中的地址
public static void main(String[] args) { int a = 10; int b = 20; int c = 10; // 对于基本类型变量,==比较两个变量中存储的值是否相同 System.out.println(a == b); // false System.out.println(a == c); // true // 对于引用类型变量,==比较两个引用变量引用的是否为同一个对象 String s1 = new String("hello"); String s2 = new String("hello"); String s3 = new String("world"); //s4的引用指向s1引用所指向的对象 String s4 = s1; System.out.println(s1 == s2); // false System.out.println(s2 == s3); // false System.out.println(s1 == s4); // true }
2.2.2boolean equals(Object anObject) 方法
:按照字典序比较(字典序:字符大小的顺序)
String类重写了父类Object中equals方法,Object中equals默认按照==比较,String重写equals方法后,按照如下规则进行比较,比如: s1.equals(s2)
//源码实现 public boolean equals(Object anObject) { // 1. 先检测this 和 anObject 是否为同一个对象比较,如果是返回true if (this == anObject) { return true; } // 2. 检测anObject是否为String类型的对象,如果是继续比较,否则返回false if (anObject instanceof String) { // 将anObject向下转型为String类型对象 String anotherString = (String)anObject; int n = value.length; // 3. this和anObject两个字符串的长度是否相同,是继续比较,否则返回false if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; // 4. 按照字典序,从前往后逐个字符进行比较 while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
public static void main(String[] args) { String s1 = new String("hello"); String s2 = new String("hello"); String s3 = new String("Hello"); // s1、s2、s3引用的是三个不同对象,因此==比较结果全部为false System.out.println(s1 == s2); // false System.out.println(s1 == s3); // false // equals比较:String对象中的逐个字符 // 虽然s1与s2引用的不是同一个对象,但是两个对象中放置的内容相同,因此输出true // s1与s3引用的不是同一个对象,而且两个对象中内容也不同,因此输出false System.out.println(s1.equals(s2)); // true System.out.println(s1.equals(s3)); // false }
2.2.3int compareTo(String s) 方法
功能:: 按照字典序进行比较
与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。具体比较式:
- 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值
- 如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值
public static void main(String[] args) { String s1 = new String("abc"); String s2 = new String("ac"); String s3 = new String("abc"); String s4 = new String("abcdef"); System.out.println(s1.compareTo(s2)); // 不同输出字符差值-1 System.out.println(s1.compareTo(s3)); // 相同输出 0 System.out.println(s1.compareTo(s4)); // 前k个字符完全相同,输出长度差值 -3 }
2.2.4int compareToIgnoreCase(String str) 方法
:与compareTo方式相同,但是忽略大小写比较
public static void main(String[] args) { String s1 = new String("abc"); String s2 = new String("ac"); String s3 = new String("ABc"); String s4 = new String("abcdef"); System.out.println(s1.compareToIgnoreCase(s2)); // 不同输出字符差值-1 System.out.println(s1.compareToIgnoreCase(s3)); // 相同输出 0 System.out.println(s1.compareToIgnoreCase(s4)); // 前k个字符完全相同,输出长度差值 -3 }
2.3字符串查找
字符串查找也是字符串中非常常见的操作,String类提供的常用查找的方法:
2.3.1 char charAt(int index)
功能:返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常
public static void main(String[] args) { //可以用来遍历数组/取出字符串某个下标的字符 String str="Hello World!"; for (int i = 0; i < str.length(); i++) { char ch=str.charAt(i); System.out.print(ch); } } //输出结果:Hello World!
2.3.2 int indexOf(int ch)
功能:返回ch第一次出现的位置,没有返回-1
public static void main(String[] args) { String str="Hello World!"; //返回字符'o'第一次出现的位置的对应索引 int ret=str.indexOf('o'); System.out.println(ret); }
2.3.3 int indexOf(int ch, int fromIndex)
功能:从fromIndex位置开始找ch第一次出现的位置,没有返回-1
public static void main(String[] args) { String str="Hello World!"; //返回字符'o'第一次出现的位置的对应索引 int ret=str.indexOf('o',5); System.out.println(ret); }
2.3.4 int indexOf(String str)
功能:返回str第一次出现的位置,没有返回-1
public static void main(String[] args) { String str="Hello World!"; //返回字符'o'第一次出现的位置的对应索引 int ret=str.indexOf("World!"); System.out.println(ret); }
2.3.5 int indexOf(String str, int fromIndex)
功能:从fromIndex位置开始找str第一次出现的位置,没有返回-1
2.3.6 int lastIndexOf(int ch)
功能:从后往前找,返回ch第一次出现的位置,没有返回-1
public static void main(String[] args) { String str="Hello World!"; //返回字符'o'第一次出现的位置的对应索引 int ret=str.lastIndexOf('l'); System.out.println(ret); }
2.3.7 int lastIndexOf(int ch, int fromIndex)
功能:从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返回-1
2.3.8 int lastIndexOf(String str)
功能:从后往前找,返回str第一次出现的位置,没有返回-1
public static void main(String[] args) { String str="Hello World!"; //返回字符'o'第一次出现的位置的对应索引 int ret=str.lastIndexOf("Hello"); System.out.println(ret); }
2.3.9 int lastIndexOf(String str, int fromIndex)
功能:从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返回-1
public static void main(String[] args) { String s = "aaabbbcccaaabbbccc"; System.out.println(s.charAt(3)); // 'b' System.out.println(s.indexOf('c')); // 6 System.out.println(s.indexOf('c', 10)); // 15 System.out.println(s.indexOf("bbb")); // 3 System.out.println(s.indexOf("bbb", 10)); // 12 System.out.println(s.lastIndexOf('c')); // 17 System.out.println(s.lastIndexOf('c', 10)); // 8 System.out.println(s.lastIndexOf("bbb")); // 12 System.out.println(s.lastIndexOf("bbb", 10)); // 3 }
2.4转化
2.4.1数值和字符串转化
1.数字转字符串
public static void main(String[] args) { int num=100; String str=String.valueOf(num); System.out.println(str); //100 }
2.字符串转数字
public static void main(String[] args) { String str1="1234"; String str2="3.14"; int num1=Integer.parseInt(str1);//显式拆箱 double num2=Double.parseDouble(str2);//显式拆箱 System.out.println(num1); System.out.println(num2); //1234 //3.14 }
3.大小写转换
public static void main(String[] args) { String str1="Hello World!"; String str2=str1.toUpperCase(); str1=str2.toLowerCase(); System.out.println(str1); System.out.println(str2); //hello world! //HELLO WORLD! }
4.字符串转数组
public static void main(String[] args) { String str1="Hello World!"; //字符串转换为数组 char[] array=str1.toCharArray(); System.out.println(Arrays.toString(array)); //[H, e, l, l, o, , W, o, r, l, d, !] //字符数组转为字符串 char[] ch={'h','e','l','l','o'}; String str2=new String(ch); System.out.println(str2); //hello }
5.格式化输出
public static void main(String[] args) { String str=String.format("%d->%d->%d",2023,3,29); System.out.println(str); //2023->3->29 }
6.Java的可变参数
public class Main1 { public static int add(int a,int b) { return a+b; } public static int add(int a,int b,int c) { return a+b+c; } public static int add(int... args) { //形参为数组/多个参数 int ret = 0; for (int i = 0; i < args.length; i++) { ret += args[i]; } return ret; } public static void main(String[] args) { int[] array2 = {1,2,3,4,5}; System.out.println(add(array2)); System.out.println(add(1,2,3,4,5,6,7,8,9,0,1111,1,2,13,13,13,14)); } }
2.5 字符串替换
编辑
2.5.1String peplace(char oldChar,char newChar)
编辑
public static void main(String[] args) { String str1="Hello TMD World!"; String str2=str1.replace("TMD","***"); System.out.println(str2); //Hello *** World! }
2.5.2 public StringreplaceAll(String regex, String replacement)
功能:替换所有的指定内容
编辑
public static void main(String[] args) { String str = "helloworld" ; System.out.println(str.replaceAll("l", "_")); //he__owor_d }
2.5.3 public StringreplaceFirst(String regex, String replacement)
功能:替换首个内容
编辑
public static void main(String[] args) { String str = "helloworld" ; System.out.println(str.replaceFirst("l", "_")); //he_loworld }
2.5.4String.valueOf(参数)
功能:返回某参数类型的字符串表示形式
String ret=String.valueOf(10); System.out.println(ret);
【注意事项-->所有的字符串的操作:由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串】
2.6字符串拆分
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串
2.6.1 String[] split(String regex)
功能:将字符串全部拆分
public static void main(String[] args) { String str = "hello world hello bit" ; String[] result = str.split(" ") ; // 按照空格拆分 for(String s: result) { System.out.println(s); } //hello //world //hello //bit }
2.6.2 String[] split(String regex, int limit)
功能:将字符串以指定的格式,拆分为limit组
public static void main(String[] args) { String str = "hello world hello bit" ; String[] result = str.split(" ",2) ; for(String s: result) { System.out.println(s); } //hello //world hello bit } }
2.6.3拆分注意事项
拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义。
代码示例: 拆分IP地址
public static void main(String[] args) { String str = "192.168.1.1" ; String[] result = str.split("\\.") ; for(String s: result) { System.out.println(s); } //192 //168 //1 //1 }
注意事项:
1. (通配符)字符"|","*","+"都得加上转义字符,前面加上 "\\" 进行转义
public static void main(String[] args) { String str = "192.168.1.1" ; String[] array=str.split("."); System.out.println(Arrays.deepToString(array)); //输出结果:[] //因为编译器不认识.,所以分割失败。此类通配符需要配合"\\"才能被编译器识别 }
public static void main(String[] args) { String str = "192.168.1.1" ; String[] array=str.split("\\."); System.out.println(Arrays.deepToString(array)); //输出结果:[192, 168, 1, 1] }
2. 而如果是 "\\" ,那么就得写成 "\\\\" 。一个'\'需要用两个"\"表示。
编辑
输出一个'\'程序会报错,修改如下:
public static void main(String[] args) { System.out.println("\\"); //输出结果:\ }
3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符
public static void main(String[] args) { String str="Hello&World.2023"; String[] array=str.split("&|\\."); System.out.println(Arrays.deepToString(array)); //输出结果:[Hello, World, 2023] }
2.6.4多次拆分
public static void main(String[] args) { String str = "name=zhangsan&age=18" ; String[] result = str.split("&") ; //拆分后在进行拆分 for (int i = 0; i < result.length; i++) { String[] temp = result[i].split("=") ; System.out.println(temp[0]+" = "+temp[1]); } //name = zhangsan //age = 18 }
2.7字符串截取
从一个完整的字符串之中截取出部分内容
2.7.1 String substring(int beginIndex)
功能:从指定索引截取到结尾
public static void main(String[] args) { String str = "helloworld" ; System.out.println(str.substring(5)); //world }
2.7.2 String substring(int beginIndex, int endIndex)
功能:截取部分内容
public static void main(String[] args) { String str = "helloworld" ; System.out.println(str.substring(0, 5)); //hello }
注意事项:
1. 索引从0开始
2. 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标
2.8其他操作方法
2.8.1String trim()
功能:去掉字符串中的左右空格,保留中间空格
代码示例: 观察trim()方法的使用
public static void main(String[] args) { String str = " hello world " ; System.out.println("["+str+"]"); System.out.println("["+str.trim()+"]"); //[ hello world ] //[hello world] }
trim 会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)
2.9字符串的不可变
String是一种不可变对象,字符串中的内容是不可改变。字符串不可被修改,是因为:
1.String类在设计时就是不可改变的,String类实现描述中已经说明了:
编辑
编辑 String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:
- String类被final修饰,表明该类不能被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。比如:
public static void main(String[] args) { final int array[] = {1,2,3,4,5}; array[0] = 100; System.out.println(Arrays.toString(array)); // array = new int[]{4,5,6}; // 编译报错:Error:(19, 9) java: 无法为最终变量array分配值 }
- value被修饰被final修饰,表明value自身的值不能改变,即不能引用其它字符数组(不可指向其他对象),但是其引用空间中的内容可以修改。
- 不可改的原因不是final,而是因为被private(只能在当前类访问)修饰后,String类中没有提供set和get方法来访问value数组。如果强制修改,需要用反射实现
2.所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
通过replace源码查看:
编辑
为什么 String 要设计成不可变的?(不可变对象的好处是什么?) (选学)
1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了.
2. 不可变对象是线程安全的.
3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中.
2.10字符串修改
2.10.1字符串拼接效率
注意理解:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。
public static void main(String[] args) { String s = "hello"; s += " world"; System.out.println(s); // 输出:hello world }
但是这种方式不推荐使用,因为其效率非常低,中间创建了好多临时对象:
编辑
2.10.2引入StringBuilder类
查看通过+拼接字符串和通过StringBuilder/StringBuffer创建字符串对象进行拼接字符串的效率对比:
public static void main(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类进行修改时,效率是非常慢的,因此:尽量避免对String的直接需要,如果要修改建议尽量使用StringBuffer或者StringBuilder。
三、StringBuilder和StringBuffer
3.1StringBuilder的介绍
3.1.1StringBuilder介绍
由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的,这里介绍 StringBuilder常用的一些方法。
编辑
编辑
StringBuilder的引入---提高字符串的操作效率 :
- 一个可变的字符序列
- StringBuilder是字符串缓冲区,将其理解为一个容器,这个容器可以存储任意数据类型。但只要进入这个容器,就都被拼接为一个字符串
3.1.2构造方法:
StringBuilder():初始容量为16个字符,超过容量自动扩容
StringBuilder(int capacity):指定容量
StringBuilder(String str):创建一个字符串缓冲区,容器创建后自带参数的内容
注意:String和StringBuilder之间不能直接进行转化,可通过如下方法:
编辑
3.1.3字符串对象创建进行拼接
//存在于堆区的常量池 String str1="abc"; //存在于堆区--->new开辟新的内存 String str2=new String("abc"); if(str1==str2){ System.out.println("相同!"); } //在堆内存创建一个StringBuilder类的对象-->拼接字符串 //但是str3无法记录拼接后的abc,需要toString到String的类中,然后str3记录这个地址 String str3=str2+"c";//变量和字符串常量拼接原理如上 //当变量拼接变量的原理如上不同,他的原理是直接存在堆区常量池 String str4="a"+"b"+"c";
3.2方法介绍
3.2.1reverse()
判断是否为回文字符串:
思路:先通过STringBuilder对字符串进行反转,然后用String方法的equals进行比较
注意:String转换为StringBuilder:通过StringBuilder的构造方法实现
public static void main(String[] args) { StringBuilder str=new StringBuilder(); str.append("123321"); StringBuilder str2=str.reverse(); if(str.toString().equals(str2.toString())){ System.out.println("结果相同!"); } str.append("132"); str2=str.reverse(); if(str.toString().equals(str2.toString())){ System.out.println("不同"); } }
3.2.2apend(参数)方法
功能:向缓冲区添加数据,返回对象自己,指向同一块内存空间
链式编程:继续调用方法
public static void main(String[] args) { StringBuilder str=new StringBuilder(); str.append(100); str.append(200); System.out.println(str); //100200 }
3.2.3length()方法
功能:返回长度
3.2.4toString()方法
功能:将缓冲区的内容,以String字符串类型返回---->用于调用String的方法
StringBuilder str=new StringBuilder(); str.append("你好,").append("世界!"); String str2=str.toString(); String[] array=str2.split(","); for (int i = 0; i < array.length; i++) { System.out.println(array[i]); }
3.3 StringBuilder和StringBuffer区别
StringBuilder 和 StringBuffer的使用效果相同,区别如下:
1.StringBuilder用于多线程是不安全的,但是效率高
2.StringBuffer用于多线程是安全的,但是效率
3.String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
4.StringBuffer与StringBuilder大部分功能是相似的查看StringBuilder和StringBuffer的append源码对比:
编辑
3.4模拟实现Arrays.ToString(String/StringBuilder)
//通过String实现 public static void main(String[] args) { int[] arr={1,2,3,4,5,6}; String str=myToString(arr); System.out.println(str); } public static String myToString(int[] arr){ String str1="["; String str4="]"; for (int i = 0; i < arr.length-1; i++) { str1+=arr[i]+","; } str1+=arr[arr.length-1]; str1+=str4; return str1; } //通过StringBuilder实现 public static void main(String[] args) { int[] arr={1,2,3}; String str=myToString(arr); System.out.println(str); } public static String myToString(int[] arr){ StringBuilder str=new StringBuilder(); str.append("["); for (int i = 0; i < arr.length-1; i++) { str.append(arr[i]+","); } str.append(arr[arr.length-1]).append("]"); return str.toString(); }