点进来你就是我的人了
博主主页:戳一戳,欢迎大佬指点!
目录
前言
1.了解String类
2.String类常用方法
2.1字符串构造
2.2字符串比较(★★★★★)
(1)equals(比较字符串是否相等,返回值是boolean类型)
(2)compareTo(比较字符串大小,返回值是int类型)
(3)compareToIgnoreCase与equalsToIgnoreCase
2.3字符串查找
2.4字符串截取(注意下标从0开始)
2.5字符串替换
2.6字符串拆分(spllit)
2.7字符串相互转化
(1)数字转字符串,用String.valueOf(),各种类型的数字都可以传
(2)用String.valueOf()将一个对象转为字符串
(3)数字字符串转数字,用Integer.valueOf()
(4)字母大小写转化toUpperCase()和toLowercase()
(5)字符串和数组转化toCharArray()和new String()
(6)格式化输出用String.format()
2.8去除字符串左右空格
2.9字符串拼接
3. StringBuilder和StringBuffer的用法
3.1 StringBuilder用法
3.2 StringBuilder底层分析
3.3 StringBuffer用法
3.3 面试题StringBuffer和StringBuilder和String的区别
String类在java.lang包中,java使用String类创建一个字符串变量,字符串变量属于对象。java中String类被final修饰,不能有继承。String类对象创建后不能修改,由0或多个字符组成,包含在一对双引号之间。
【1】String str = “abc”;
"abc"就是String类下的一个具体的对象
【2】字符串是不可变的
【3】这个String类不可以被继承,不能有子类:被final修饰
验证:
构造方法:就是给对象底层的value数组进行赋值操作。
public static void main(String[] args) {
//1.直接定义一个字符串
String str = "hello";
System.out.println(str);
//2.通过new String对象
String str2 = new String("小盈盈");
System.out.println(str2);
//3.通过字符数组进行构造
char[] chars = {'a','b','c'};
String str3 = new String(chars);
System.out.println(str3);
}
引入:
String s6 = new String("abc");
String s7 = new String("abc");
String s8 = new String("hello");
int a =6;
int b =6;
System.out.println(s6 == s7); // false
System.out.println(s7 == s8); // false
System.out.println(a == b); // true
通过上述代码:我们发现只要new对象就会在堆上创建不同的空间,通过s6,s7,s8,分别去指向他们 ,因此我们可以得到以下结论
对于引用类型,==比较的是地址;
对于基本类型变量,==比较的是存储的值 ;
那么对于引用类型我们该如何比较呢?
从JDK帮助文档中我们可以发现String类重写了父类Object中equals方法,因为Object中equals默认按照==比较,而String重写equals方法后,就会发生动态绑定!
让我们来看一下来源码中equals具体重写的细节吧
下面使用重写后的equals方法进行比较
String s6 = new String("abc");
String s7 = new String("abc");
String s8 = new String("hello");
System.out.println(s6.equals(s7));//true
System.out.println(s7.equals(s8));//false
String类实现了Comparable,里面有一个抽象方法叫compareTo,所以String中一定要对这个方法进行重写!
让我们来看一下来源码中compareTo具体重写的细节吧
下面使用重写后的compareTo方法进行比较
String s6 = new String("abc");
String s7 = new String("abc");
System.out.println(s6.compareTo(s7));
输出结果:0
使用compareToIgnoreCase进行比较,可以忽略字母大小写,来进行比较大小关系
使用equalsToIgnoreCase进行比较,可以忽略字母大小写,来进行比较是否相等
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("HELLO");
//直接比较大小关系
System.out.println(s1.compareTo(s2));//32
//忽略大小写进行比较
System.out.println(s1.compareToIgnoreCase(s2));//0
//直击比较是否相等
System.out.println(s1.equals(s2));//false
//忽略大小写比较是否相等
System.out.println(s1.equalsIgnoreCase(s2));//true
}
(1)char charAt(int index),输入位置index,找单个字符
(2)int indexOf(int ch) ,返回ch字符第一次出现的位置下标,没有就返回-1
(3) int indexOf(int ch, int fromIndex),从fromIndex位置开始找ch字符第一次出现的位置,没有就返回-1
(4)int indexOf(String str),找Str字符串第一次出现的位置,返回其字符串首字母下标,没有返回-1
(5)int indexOf(String str,int formIndex),从formIndex开始,找Str字符串第一次出现的位置,返回其字符串首字母下标,没有返回-1
(6)int lastIndexOf(int ch),从后往前找字符ch,返回从后往前第一次出现ch字符的下标,没有找到返回-1
(7)int lastIndexOf(int ch,int fromIndex),从fromIndex开始,从后往前找字符ch,返回从后往前第一次出现ch字符的下标,没有找到返回-1
(8)int laseIndexOf(String str),从后往前找,返回字符串str第一次出现位置的首字母下标,没有找到返回-1
(9)int laseIndexOf(String str,int formIndex),从fromIndex开始,从后往前找,返回字符串str第一次出现位置的首字母下标,没有找到返回-1
public static void main(String[] args) {
String s1 = "hello";
char ch = s1.charAt(1); //找下标1位置的字符
System.out.println(ch);//e
int index1 = s1.indexOf('l'); //返回l字符第一次出现的位置下标,没有就返回-1
System.out.println(index1);//2
int index2 = s1.indexOf('l',4); //从下标4位置开始找ch字符第一次出现的位置,没有就返回-1
System.out.println(index2);//-1
String s2 = "helloxawllxh";
int index4 = s2.indexOf("xawl"); //找xawl第一次出现的位置,返回其字符串首字母下标,没有返回-1
System.out.println(index4);//5
String s3 = "helloxawllxhxawllxh";
int index5 = s3.indexOf("xawl",6); //从下标6开始,找xawl出现的位置,返回其字符串首字母下标,没有返回-1
System.out.println(index5);//12
int index6 = s3.lastIndexOf('a'); //从后往前找字符ch,返回从后往前第一次出现a字符的下标,没有找到返回-1
System.out.println(index6);//13
int index7 = s3.lastIndexOf('a',7); //从下标7开始,从后往前找字符a,返回从后往前第一次出现a字符的下标
System.out.println(index7);//6
int index8 = s3.lastIndexOf("xawl"); //从后往前找,返回xawl第一次出现位置的首字母下标,没有找到返回-1
System.out.println(index8);//12
int index9 = s3.lastIndexOf("xawl",9); //从下标9开始,从后往前找,返回xawl第一次出现位置的首字母下标
System.out.println(index9);//5
}
输出结果:
e
2
-1
5
12
13
6
12
5
进程已结束,退出代码0
总结:
输入下标,找单个字符,用charAt方法;
找字符或字符串,需要返回下标
如果是从前往后找,用indexOf方法;
如果是从后往前找,用lastIndexOf方法;
(1)从字符串中指定位置截取后面字符串的内容,通过substring(指定位置)
public static void main(String[] args) {
String str = "abcdefg";
String ret = str.substring(2); //从第二个位置开始截取
System.out.println(ret);//cdefg
}
输出结果:
cdefg
(2)截取指定范围的字符串,通过substring(起始位置,结束位置),注意左闭右开(不包括结束位置)
public static void main(String[] args) {
String str = "abcdefg";
String ret = str.substring(2,5);//截取[2,5)里面的字符
System.out.println(ret);//cde
}
输出结果:
cde
总结一下,
截取字符串,可以使用subString
原理:原来的字符串不变,创建一个新的字符串,替换新字符串中某个字符或字符串
(1)替换字符串中的字符 ,用replace
(2)替换字符串中的字符串 ,用replace或replaceAll
(3)替换字符串中的首个字符串,用replaceFirst
代码演示如下:
public static void main(String[] args) {
String str1 = "xawlxawlxawlxawl";
System.out.println(str1);//xawlxawlxawlxawl
String ret = str1.replace('a','B'); //替换字符串中的字符
System.out.println(ret);//xBwlxBwlxBwlxBwl
String ret2 = str1.replace("xa","B"); //替换字符串中的字符串
String ret3 = str1.replaceAll("xa","B"); //替换字符串中的字符串
System.out.println(ret2);//BwlBwlBwlBwl
System.out.println(ret3);//BwlBwlBwlBwl
String ret4 = str1.replaceFirst("xa","B"); //替换字符串中的首个字符串
System.out.println(ret4);//Bwlxawlxawlxawl
}
输出结果:
xawlxawlxawlxawl
xBwlxBwlxBwlxBwl
BwlBwlBwlBwl
BwlBwlBwlBwl
Bwlxawlxawlxawl
(1)可以将一个完整的字符串按照指定的分隔符,分隔为若干个字符串,用spllit(指定分隔符)
(2)将字符串以指定的格式,拆分为limit组,用spllit(指定分隔符,指定几组)
代码演示如下:
public static void main(String[] args) {
String str1 = "Hello this is xawl rjgc professional";
String[] ret = str1.split(" "); //按空格分隔
for (String s : ret) {
System.out.println(s);
}
System.out.println("=========分隔符==========");
String str2 = "Hello this is xawl rjgc professional";
String[] ret2 = str2.split(" ",3); //按空格分成3组
for (String s : ret2) {
System.out.println(s);
}
}
输出结果:
Hello
this
is
xawl
rjgc
professional
=========分隔符==========
Hello
this
is xawl rjgc professional
注意,有些特殊字符(| + * . ,)作为分割符可能无法正确切分, 需要加上转义'\'(一个'\'只能转义一个字符).
比如," . "点号,如果要识别IP地址中的点号,直接通过split进行拆分是识别不出点号作为分割符的,需要加上转义字符,也就是 \\.
代码演示如下:
public class Test2 {
public static void main(String[] args) {
String str2 = "192.188.12.1";
String[] ret1 = str2.split("\\.");
for (String s1: ret1) {
System.out.println(s1);
}
}
}
输出结果:
192
188
12
1
代码演示如下:
public static void main(String[] args) {
String str = String.valueOf(1234);
String str1 = String.valueOf(12.34);
System.out.println(str);
System.out.println(str1);
}
我们可以通过下面调试发现确实转换成字符串了!
public class Test01 {
public static void main(String[] args) {
String str2 = String.valueOf(new Student(20));
System.out.println(str2);
}
}
输出结果:
Student@74a14482
通过调试我们发现底层调用的是Object的toString方法.
public static void main(String[] args) {
int a = Integer.valueOf("1234");
System.out.println(a);//1234
}
输出结果:
1234
我们可以通过下面调试发现确实转换成数字了!
代码演示如下:
public static void main(String[] args) {
String str1 = "abcdef阿凡达";
String ret = str1.toUpperCase(); //小写转大写
System.out.println(ret);//ABCDEF阿凡达
String str2 = "HIJK光头强";
String ret2 = str2.toLowerCase(); //大写转小写
System.out.println(ret2);//hijk光头强
}
输出结果:
ABCDEF阿凡达
hijk光头强
这里要注意,大小写转化,不是在原来的字符串中修改,
而是产生一个新的字符串对象进行修改 ,并且汉字不参与转化
字符串和数组转化toCharArray():
public static void main(String[] args) {
String str5 = "hello";
char[] chars = str5.toCharArray();
for (char x : chars) {
System.out.println(x);
}
}
运行结果:
h
e
l
l
o
通过调试我们发现确实转换成数组了
数组转字符串直接new String
public static void main(String[] args) {
char[] chars1 = {'a','b','c'};
String str6 = new String(chars1);
System.out.println(str6);//abc
}
输出结果:
abc
public static void main(String[] args) {
String str7 = String.format("%d-%d-%d",2022,5,28);
System.out.println(str7);//2022-5-28
}
输出结果:
2022-5-28
总结:
(1)数字和字符串转化用 String.valueof和Integer.valueof
(2)大小写转化用toUpperCase和toLowerCase
(3)字符串和数组转化用toCharArray和new String
(4)格式化输出用format
去掉字符串中的左右空格,保留中间空格,用trim
public static void main(String[] args) {
String str = " abcdefg ";
System.out.println(str.trim());//abcdefg
}
输出结果:
abcdefg
1.常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量
2.只要其中有一个结果是变量,结果就在堆中
3.如果拼接的结果调用intern()方法,返回值就在常量池中
代码演示如下:
public static void main(String[] args) {
String sb = "JavaAndroid";
String s1 = "Java";
String s2 = "Android";
String s3 = "Java" + "Android";
String s4 = s1 + "Android";
String s5 = "Java" + s2;
String s6 = s1 + s2;
System.out.println(sb == s3);//true
System.out.println(sb == s4); //false
System.out.println(sb == s5);//false
System.out.println(sb == s6);//false
System.out.println(s4 == s5);//false
String s7 = s4.intern();
System.out.println(sb == s7);//true
}
注意:有变量参与的字符串拼接在底层都会实例化StringBuilder的对象
例如看下面这段代码:
public static void main(String[] args) {
String a = "abc";
String b = a + "def";
System.out.println(b);
}
a变量在编译的时候不知道a是字符串“abc”,所以不会进行编译期优化,不会直接合并为“abcdef”
经过反编译一下:
通过反编译我们可以看到它在底层先是创建了一个StringBuilder对象,然后通过对象调用append方法添加字符串,打印的时候调用toString方法再转化为字符串输出在控制台.
StringBuffer与StringBuilder都继承自AbstractStringBuilder类,AbstractStringBuilder中也是使用字符数组保存字符串,是可变类。
并且都提供了一系列插入、追加、改变字符串里的字符序列的方法,它们的用法基本相同
StringBuilder的创建:使用构造方法
- StringBuilder() 构造一个不带任何字符的字符串生成器,其初始容量为 16 个字符。
- StringBuilder(CharSequence seq) 构造一个字符串生成器,它包含与指定的 CharSequence 相同的字符。
- StringBuilder(int capacity) 构造一个不带任何字符的字符串生成器,其初始容量由 capacity 参数指定。
- StringBuilder(String str) 构造一个字符串生成器,并初始化为指定的字符串内容。
容量与长度
容量=缓冲区大小+实例存放字符的长度
长度=容器中实际存放的字符串的字符长度
public class Test03 {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
StringBuilder sb=new StringBuilder("nihaojavawodeshijie");
//增
sb.append("光头强");
System.out.println(sb);//nihaojavawodeshijie光头强
//删
sb.delete(3, 6);//删除位置在[3,6)上的字符
System.out.println(sb);//nihavawodeshijie光头强
sb.deleteCharAt(16);//删除位置在16上的字符
System.out.println(sb);//nihavawodeshijie头强
//改-->插入
StringBuilder sb1=new StringBuilder("你好呀灰太狼");
sb1.insert(3, ",");//在下标为3的位置上插入 ,
System.out.println(sb1);
StringBuilder sb2=new StringBuilder("你好呀喜羊羊灰太狼");
//改-->替换
sb2.replace(3, 5, "懒羊羊");//在下标[3,5)位置上插入字符串
System.out.println(sb2);
sb2.setCharAt(3, '!');
System.out.println(sb2);
//查
StringBuilder sb3=new StringBuilder("asdfa");
for (int i = 0; i < sb3.length(); i++) {
System.out.print(sb3.charAt(i)+"\t");
}
System.out.println();
//截取
String str=sb3.substring(2,4);//截取[2,4)返回的是一个新的String,对StringBuilder没有影响
System.out.println(str);
System.out.println(sb3);
}
}
输出结果:
nihaojavawodeshijie光头强
nihavawodeshijie光头强
nihavawodeshijie头强
你好呀,灰太狼
你好呀懒羊羊羊灰太狼
你好呀!羊羊羊灰太狼
a s d f a
df
asdfa
(1)StringBuilder底层:有非常重要的两个属性:
(2)对应内存分析:
public class Test01 {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建StringBuilder的对象:
StringBuilder sb3 = new StringBuilder();
//表面上调用StringBuilder的空构造器,实际底层是对value数组进行初始化,长度为16
StringBuilder sb2 = new StringBuilder(3);
//表面上调用StringBuilder的有参构造器,传入一个int类型的数,实际底层就是对value数组进行初始化,长度为你传入的数字
StringBuilder sb = new StringBuilder("abc");
System.out.println(sb.append("def").append("aaaaaaaa").append("bbb").append("ooooooo").toString());;//链式调用方式:return this
}
}
StringBuilder sb3 = new StringBuilder();
//表面上调用StringBuilder的空构造器,实际底层是对value数组进行初始化,长度为16
StringBuilder sb2 = new StringBuilder(3);
//表面上调用StringBuilder的有参构造器,传入一个int类型的数,实际底层就是对value数组进行初始化,长度为你传入的数字
StringBuilder sb = new StringBuilder("abc");
//底层源代码分析如下
再配上下面简单内存图加深一下理解吧
方法 | 用途于解释 |
---|---|
StringBuffer append(boolean b) | 将boolean类型的b转换为字符串以后追加到当前StringBuffer对象所表示的字符序列的末尾。(追加: 内部的末尾) |
StringBuffer append(char c) | 将一个字符追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer appendCodePoint(int codePoint) | 将一个字符追加到当前StringBuffer对象所表示的字符序列的末尾。(CodePoint: Ascii ) |
StringBuffer append(char[] str) | 将一组字符追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(char[] str, int offset, int len) | 将一组字符(从数组的offset开始,取出length个长度)追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(CharSequence s) | 将一个字符序列(字符串,StringBuffer,StringBuilder)追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(CharSequence s, int start, int end) | 将一个字符序列(字符串,StringBuffer,StringBuilder)的一部分追加到当前StringBuffer对象所表示的字符序列的末尾。(区间为[start,end)) |
StringBuffer append(double d) | 将一个double追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(float f) | 将一个float追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(int i) | 将一个int追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(long l) | 将一个long追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(Object obj) | 将任意一个对象转换成字符串(toString())后追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(String str) | 将一个字符串追加到当前StringBuffer对象所表示的字符序列的末尾。 |
StringBuffer append(StringBuffer sb) | 将一个StringBuffer追加到当前StringBuffer对象所表示的字符序列的末尾。 |
char charAt(int index) | 在当前StringBuffer中取出位于index位置的元素(返回的是字符) |
int codePointAt(int index) | 在当前StringBuffer中取出位于index位置的元素 ,返回的是ascii码 |
StringBuffer delete(int start, int end) | 在当前StringBuffer对象中,删除[start,end)区间内的字符 |
StringBuffer deleteCharAt(int index) | 在当前StringBuffer对象中,删除位于index位置的的字符 |
int indexOf(String str) | 在当前StringBuffer对象中,查找指定的str(字符串)第一次出现的位置,并返回它所处的位置,找不到时返回-1 |
int indexOf(String str, int fromIndex) | 在当前StringBuffer对象中,查找指定的str(字符串)第一次出现的位置,并返回它所处的位置,找不到时返回-1( fromIndex:从哪一个位置开始找) |
int lastIndexOf(String str) | 在当前StringBuffer对象中,查找指定的str(字符串)最后一次出现的位置,并返回它所处的位置,找不到时返回-1 |
int lastIndexOf(String str, int fromIndex) | 在当前StringBuffer对象中,查找指定的str(字符串)最后一次出现的位置,并返回它所处的位置,找不到时返回-1 ( fromIndex: 截止到哪个位置为止) |
int length() | 返回当前StringBuffer所有字符的个数(StringBuffer的长度) |
StringBuffer insert(int offset, boolean b) | 将一个 boolean类型的值转成字符串后,插入到当前当前StringBuffer对象中,从offset位置插值 |
StringBuffer insert(int offset, char c) | 将一个 char类型的值转成字符串后,插入到当前当前StringBuffer对象中,从offset位置插值 |
StringBuffer insert(int offset, char[] chars) | 将char数组当中的所有元素插入到当前当前StringBuffer对象中,从offset位置插值 |
StringBuffer insert(int index, char[] str, int offset, int len) | 将char数组当中的一部元素插入到当前当前StringBuffer对象中,从index位置插值, index: 插入到哪个位置,offset: 从char[]的哪个位置开始取,len:从char[]当中取几个元素 |
StringBuffer insert(int dstOffset, CharSequence s) | 将一个字符序列插入到当前StringBuffer的offset位置 |
StringBuffer insert(int dstOffset, CharSequence s, int start, int end) | 将一个字符序列的一部分([start,end))插入到当前StringBuffer的offset位置 |
StringBuffer insert(int offset, double d) | 将一个double类型的值转成字符串后,插入到当前当前StringBuffer对象中,从offset位置插值将一个 boolean类型的值转成字符串后,插入到当前当前StringBuffer对象中,从offset位置插值, … float,int,long,Object |
StringBuffer replace(int start, int end, String str) | 替换当前StringBuffer的start到end置的字符串为str |
StringBuffer reverse() | 将当前StringBuffer反转 abc -> cba |
void setCharAt(int index, char ch) | 修改当前StringBuffer的index的值为ch |
String substring(int start) | 从当前StringBuffer中截取一段,区间:[start,length) |
String substring(int start, int end) | 从当前StringBuffer中截取一段,区间:[start,end) |
public class Test03 {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
StringBuffer sb=new StringBuffer("nihaojavawodeshijie");
//增
sb.append("光头强");
System.out.println(sb);//nihaojavawodeshijie光头强
//删
sb.delete(3, 6);//删除位置在[3,6)上的字符
System.out.println(sb);//nihavawodeshijie光头强
sb.deleteCharAt(16);//删除位置在16上的字符
System.out.println(sb);//nihavawodeshijie头强
//改-->插入
StringBuffer sb1=new StringBuffer("你好呀灰太狼");
sb1.insert(3, ",");//在下标为3的位置上插入 ,
System.out.println(sb1);
StringBuffer sb2=new StringBuffer("你好呀喜羊羊灰太狼");
//改-->替换
sb2.replace(3, 5, "懒羊羊");//在下标[3,5)位置上插入字符串
System.out.println(sb2);
sb2.setCharAt(3, '!');
System.out.println(sb2);
//查
StringBuffer sb3=new StringBuffer("asdfa");
for (int i = 0; i < sb3.length(); i++) {
System.out.print(sb3.charAt(i)+"\t");
}
System.out.println();
//截取
String str=sb3.substring(2,4);//截取[2,4)返回的是一个新的String,对StringBuffer没有影响
System.out.println(str);
System.out.println(sb3);
}
}
nihaojavawodeshijie光头强
nihavawodeshijie光头强
nihavawodeshijie头强
你好呀,灰太狼
你好呀懒羊羊羊灰太狼
你好呀!羊羊羊灰太狼
a s d f a
df
asdfa
总结:
二者相比较,StringBuffer更安全,但并不是说StringBuffer比StringBuilder要更好,
因为StringBuffer的安全是建立在相对来说降低效率和耗费资源的基础之上的。
二者就方法来说,基本都是一样的。
(1)String内容不可以修改,而StringBuffer与StringBuilder,提供了一系列插入追加、改变字符串里的字符序列的方法,并且修改不产生新的对象,而是在原对象的基础上修改
(2)就三者效率进行比较
StringBuilder > StringBuffer > String
(3)从安全性和操作数据量来比较
如果要操作的数量比较小,应优先使用String类;
如果是在单线程下操作大量数据,应优先使用StringBuilder类;
如果是在多线程下操作大量数据,应优先使用StringBuffer类。
(4)StringBuffer使用了缓存区,StringBuilder没有使用缓存区,所以没有修改数据的情 况下,多次调用StringBuffer的toString方法获取的字符串是共享底层的字符数组的。
而StringBuilder不是共享底层数组的,每次都生成了新的字符数组