String类
- 一,常用方法
- 二,StringBuilder,StringBuffer
一,常用方法
1.1,字符串的构造方法
在Java里面,我们知道String是一个类,叫做字符串类,那么作为一个类,自然有它自己的构造方法,常用的有下面三种(其余的可以用到的时候去查看API文档):
public class TestDemo220528 {
public static void main(String[] args) {
// 1,使用常量串进行构造,其实和数组的静态赋值一样,这里也是省略的写法
String str1 = "hello";
System.out.println(str1);
// 2,使用关键字new一个对象
String str2 = new String("hahaha");
System.out.println(str2);
// 3,利用字符数组进行构造
char[] arr = {'a','b','c','d'};
String str3 = new String(arr);
System.out.println(str3);
}
}
//输出结果:
//hello
//hahaha
//abcd
注意:
首先,我们来看看String类的定义的小部分源码:
内 存 图 : \color{orange}{内存图:} 内存图:
1,str1于str2:
2,str3:
因为str3在进行构造的时候,我们传入的是一个字符串数组,源码中定义的方法如下:
也即是说我们传入的字符数组会先被拷贝一份之后让value去引用这个数组。
1.2,String对象的比较
1,== 比较是否引用同一个对象
public class TestDemo220528 {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println(a == b);
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);//str1,str2就是两个对象,地址的值肯定不相同
}
}
2,equals方法
public class TestDemo220528 {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println(a == b);
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);//str1,str2就是两个对象,地址的值肯定不相同
System.out.println(str1.equals(str2));//true,字符串的内容是一样的
}
}
3,compareTo()方法
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("hello");
String str2 = new String("hello");
String str3 = new String("abcd");
String str4 = new String("abcdef");
System.out.println(str1.compareTo(str2));//输出0
System.out.println(str1.compareTo(str3));//输出7
System.out.println(str3.compareTo(str4));//输出-2
}
}
4,compareToIgnoreCase()方法
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("HELLO");
String str2 = new String("hello");
System.out.println(str1.equalsIgnoreCase(str2));//忽略大小写进行比较 true
System.out.println(str1.compareToIgnoreCase(str2));//忽略大小写进行比较 false
}
}
1.3,字符串查找
String类提供的常用查找方法如下:
1,char charAt(int index)
作用:返回字符串index下标处对应的字符,如果index为负数或者越界都会抛出异。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("hello");
for (int i = 0; i < str1.length(); i++) {
System.out.println(str1.charAt(i));//将字符串的字符一个个输出
}
}
}
2,int indexOf(int ch)
作用:返回字符ch第一次出现的位置,没有就返回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("hello");
int ret = str1.indexOf('l');
System.out.println(ret);//输出2
}
}
3,int indexOf(int ch, int fromIndex)
作用:从fromIndex位置开始找ch第一次出现的位置,没有返回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("hello");
int ret = str1.indexOf('l',3);
System.out.println(ret);//输出3,注意,是从fromindex开始找,但是你的下标的参照还是从起始位置开始的哦
}
}
4,int indexOf(String str)
作用:返回str第一次出现的位置,没有返回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("hello");
int ret = str1.indexOf("ll");
System.out.println(ret);//输出2
}
}
5,int indexOf(String str, int fromIndex)
作用:从fromIndex位置开始找字符串str第一次出现的位置,没有返回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("helloll");
int ret = str1.indexOf("ll",4);
System.out.println(ret);//输出5
}
6,int lastIndexOf(int ch)
作用:从后往前开始找字符ch,如果没有则返回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("hello");
int ret1 = str1.lastIndexOf('e');
System.out.println(ret1);//输出1
}
}
7,int lastIndexOf(int ch, int fromIndex)
作用:从fromIndex位置开始找,从后往前找ch第一次出现的位置,没有返 回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("abcdcef");
int ret = str1.lastIndexOf('c',3);
System.out.println(ret);//输出2
}
}
8,int lastIndexOf(String str)
作用:从后往前找,返回str第一次出现的位置,没有返回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("abcdcef");
int ret = str1.lastIndexOf("dc");
System.out.println(ret);//输出3
}
}
9,int lastIndexOf(String str, int fromIndex)
作用:从fromIndex位置开始找,从后往前找str第一次出现的位置,没有返 回-1。 |
public class TestDemo220528 {
public static void main(String[] args) {
String str1 = new String("abcdcedcf");
int ret = str1.lastIndexOf("dc",5);
System.out.println(ret);//输出3
}
}
1.4,字符串转化
1,数值转字符串
class Student{
public int age;
public Student(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
'}';
}
}
public class TestDemo220529 {
public static void main(String[] args) {
// 将其他数据类型转换成字符串
String str1 = String.valueOf(123);
String str2 = String.valueOf(11.2);
String str3 = String.valueOf(new Student(18));//也可以将一个对象转换成字符串
System.out.println(str1);//输出123
System.out.println(str2);//输出11.2
System.out.println(str3);//输出 Student{age=18}
}
}
2,字符串转数值
public class TestDemo220529 {
public static void main(String[] args) {
int a = Integer.valueOf("100");
int b = Integer.valueOf("100",8);//八进制转化
System.out.println(a);//输出100
System.out.println(b);//输出64
int c = Integer.parseInt("123");
System.out.println(c);//输出123
}
}
在大多数的情况下,我们用parseInt()之类的方法比较多。
3,大小写转化
public class TestDemo220529 {
public static void main(String[] args) {
String s1 = new String("HELLO");
System.out.println(s1.toLowerCase());//输出hello
System.out.println(s1);//输出HELLO
String s2 = new String("hello");
System.out.println(s2.toUpperCase());//输出HELLO
System.out.println(s2);//输出hello
}
}
4,字符串转数组
public class TestDemo220529 {
public static void main(String[] args) {
// 字符串转数组
String s1 = new String("hello");
char[] ch = s1.toCharArray();//字符串转数组的方法
for (char x:ch) {
System.out.println(x);
}
// 数组转字符串
char[] ch1 = {'h','e','l','l','o'};
String s2 = new String(ch1);
System.out.println(s2);
}
}
5,格式化输出为字符串
public class TestDemo220529 {
public static void main(String[] args) {
String s1 = String.format("%d - %d - %d",2022,5,29);
System.out.println(s1);//输出字符串 “2022 - 5 - 29”
}
}
1.5,字符串替换
public class TestDemo220529 {
public static void main(String[] args) {
String s1 = new String("abcdefabdddd");
//替换单个字符
String ret = s1.replace('a','t');//替换生成一个新的对象
System.out.println(ret);//tbcdeftbdddd
//替换字符串
String ret1 = s1.replace("ab","qq");
System.out.println(ret1);//qqcdefqqdddd
//替换字符串中的某一个全部的内容
String ret2 = s1.replaceAll("ab","ss");
System.out.println(ret2);//sscdefssdddd
//替换第一次出现的内容
String ret3 = s1.replaceFirst("ab","hh");
System.out.println(ret3);//hhcdefabdddd
}
}
1.6,字符串拆分
1,将字符串全部拆分
//注意split的参数是字符串不是字符
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = new String("I am a student");
String[] ret = s1.split(" ");
for (String s:ret) {
System.out.println(s);
}
}
}
字符串拆分的方法split的返回值是字符串数组,按照传入的字符串,把原字符串进行拆分。比如上面的空格,虽然是一个字符,但是你得当作一个字符串传入,也就是 “ ”。
2,将字符串进行部分的拆分
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = new String("I am a student");
String[] ret = s1.split(" ",2);
for (String s:ret) {
System.out.println(s);
}
}
}
//输出 I
// am a student
注意:
1,字符"|","*","+"都得加上转义字符,前面加上 "\\" 。 |
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = new String("192.168.11.11");
String[] ret = s1.split(".");
for (String s:ret) {
System.out.println(s);
}
}
}
//按照常理而言,好像并没有任何的使用错误,但是实质就是没有任何的输出,调试后也会发现ret数组里面根本什么都没有,也即是没有拆分
//正确做法:
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = new String("192.168.11.11");
String[] ret = s1.split("\\.");
for (String s:ret) {
System.out.println(s);
}
}
}
2,如果存在多个分割符,可以用|来作为连字符。也就是表示|左右两边都是分隔符。 |
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = new String("name==zhangsan&&age==20");
String[] ret = s1.split("==|&&");
for (String s:ret) {
System.out.println(s);
}
}
}
//输出
//name
//zhangsan
//age
//20
当然,这是选择用|,其实也可以嵌套使用split达到相同的效果
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = new String("name==zhangsan&&age==20");
String[] ret = s1.split("&&");//首先按照&&拆分一次
for (String s:ret) {
String[] ret1 = s.split("==");//把按照&&拆分的每一部分再按照==拆分一次就好
for (String ss:ret1) {
System.out.println(ss);
}
}
}
}
1.7,字符串截取
public class TestDemo220530 {
public static void main(String[] args) {
String s = new String("hello world");
//截取beginIndex到字符串的末尾
String ret1 = s.substring(3);
//截取beginIndex到endIndex,但是注意这个范围是一个左闭右开的!!!!!
String ret2 = s.substring(3,7);
System.out.println(ret1);//输出 lo world
System.out.println(ret2);//输出 lo w
}
}
1.8,其他操作方法
String trim()//去掉字符串左右两边的空格
public class TestDemo220530 {
public static void main(String[] args) {
String s = new String(" ab ababab abab ");
System.out.println(s);
System.out.println(s.trim());
}
}
程序运行截图:
1.9,字符串常量池
1,为什么引入常量池
2,字符串常量池的介绍
字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(哈希表)。
3,再谈字符串对象的创建
3.1,直接使用字符串常量进行赋值
当然,所有的字符串常量需要创建的,在字节码文件加载的时候就已经创建好了。
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);
}
}
对于上面的这段代码,可能主观上会觉得s1与s2是两个对象,所以输出结果为false,但是实际是true,但是为什么就是true呢?从内存图上看的会清楚,如下:
3.2,通过new创建String对象
public class TestDemo220530 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = new String("hello");
String s3 = new String("world");
System.out.println(s1 == s2);//输出 false
}
}
内
存
图
:
\color{orange}{内存图:}
内存图:
所以,总的来说,直接使用常量串进行赋值会比new字符串对象在空间的节省以及速度上都会好很多。
3.3,intern方法
public class TestDemo220530 {
public static void main(String[] args) {
char[] ch = {'a','b','c'};
String s1 = new String(ch);
String s2 = "abc";
System.out.println(s1 == s2);//输出false
}
}
上面这段代码毫无疑问的会输出false,因为s1,s2是两个不同的对象的引用,地址肯定是不同的。
public class TestDemo220530 {
public static void main(String[] args) {
char[] ch = {'a','b','c'};
String s1 = new String(ch);
s1.intern();
String s2 = "abc";
System.out.println(s1 == s2);//输出true
}
}
内存图:
通过intern()方法将s1添加进常量池后,因为s1与s2的字符串的内容是一样的,所以在常量池里面就只会有一个相同的字符串对象,那么自然s1 == s2就是true了。
注意:
public class TestDemo220530 {
public static void main(String[] args) {
char[] ch = {'a','b','c'};
String s1 = new String(ch);
String s2 = "abc";
s1.intern();
System.out.println(s1 == s2);
}
}
如同现在这段代码就会直接输出false,因为常量池中已经存在有相同内容的字符串对象s2,所以s1.intern()不会把s1添加进常量池,所以s1 == s2是不成立的。
总结,既然几种情况下的String对象的创建都已经介绍清楚了,那下面来看看面试题,进行下相关的总结:
【面试题:请解释String类中几种对象实例化的区别】
1.10,字符串的不可变性
String是一种不可变对象. 字符串中的内容是不可改变。字符串不可被修改
1.11,字符串的修改
//以拼接字符串为例
public class TestDemo220603 {
public static void main(String[] args) {
String s = "hello";
s += "world";
System.out.println(s);//输出helloworld
}
}
//模拟实现下上面的编译过程
public class TestDemo220603 {
public static void main(String[] args) {
String s = "hello";
// 想要实现在后面追加一个world
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("hello");
stringBuilder.append("world");
s = stringBuilder.toString();
System.out.println(s);
}
}
那正如上面代码所展示的,我们在对字符串进行修改的时候底层上利用的是StringBuilder,那为什么StringBuilder就可以呢?
二,StringBuilder,StringBuffer
上面既然用到了StringBuilder与StringBuffer,那么下面就给大家详细介绍下这两个类。
public class TestDemo220603 {
public static void main(String[] args) {
StringBuilder stringBuilder1 = new StringBuilder("hello");//"hello"还是在常量池
stringBuilder1.append("world");
System.out.println(stringBuilder1);//追加 相当于String类的 +=
System.out.println(stringBuilder1.length());//求字符串的长度
stringBuilder1.reverse();//将字符串进行逆置
String s1 = stringBuilder1.toString();//用String类型的变量接收
System.out.println(s1);
}
}
//输出结果:
//helloworld
//10
//dlrowolleh
【String类与StringBuilder类的转化问题:】
1,String转化为StringBuilder:利用StringBuilder的构造方法或者append方法
StringBuilder stringBuilder1 = new StringBuilder("hello");
StringBuilder stringBuilder2 = new StringBuilder();
stringBuilder.append("hello");
2,StringBuilder转化为String:利用StringBuilder的toString方法
StringBuilder stringBuilder1 = new StringBuilder("hello");
String s1 = stringBuilder1.toString();
【常见面试题:】
1,String,StringBuilder,StringBuffer的区别
2,以下总共创建了多少个String对象【前提不考虑常量池之前是否存在】
String str = new String("ab"); // 会创建多少个对象
会创建两个对象 new String()一个,然后常量池"ab"中一个
String str = new String("a") + new String("b"); // 会创建多少个对象
会创建六个对象 new String()一个,常量池"a"一个,new String()一个,常量池"b",前后拼接会创建一个StringBuilder对象
,赋值给str默认调用toString()方法的时候会new一个String对象