引言:相信很多朋友初学Java的字符串中,对于String、StringBuffer还有StringBuilder的使用区分不清。在后续使用多线程以及程序的执行速度,对于使用三者的方法尤为重要,下面我将为大家介绍三者的使用细节、方法和区别
1.String是一个final类,代表不可变的字符序列
2.字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。
这里之所以说String是final类,我们需要深究String底层的源码
由上图我们可以看出在Java8后String的底层是通过char数组实现的,并且使用final修饰
相信很多小伙伴在学习Java的过程中,也会有人告诉这会是面试官问到的内容
String的不可变特性,由上面String的底层的源码可以看出,有final修饰。那么为什么是final呢?
关于==和equals,两者启着判断是否相等的作用,这里关于二者的细节不多赘述,之后我会发博客详细介绍,下面我们来介绍一下equals方法的使用。
我们先来看一个练习题
public class test01 {
public static void main(String[] args) {
String abc = new String("qby");
String abc1 = new String("qby");
System.out.println(abc == abc1); //false
System.out.println(abc.equals(abc1)); //true
}
}
public class test01 {
public static void main(String[] args) {
Cat a = new Cat("a");
Cat b = new Cat("b");
System.out.println(a.name == b.name); //false
System.out.println(a.name.equals(b.name)); //false
}
}
class Cat {
public String name; //实际开发中,我们设置private,这里设置public是因为方便调用
public Cat(String name) {
this.name = name;
}
}
通过上述代码,我们能发现什么呢?
大家都知道使用 == 我们new对象时,jvm会分配给我们一个内存地址,所以==判断的结果为false。
那么为什么equals方法判断一个为false,而另一个为true呢?
我们一起来看String中equals的底层源码
这里我把equals源码分为两部分
package StringMethod;
public class Method {
@SuppressWarnings({"all"})
public static void main(String[] args) {
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equals(str2)); //false
//1.忽略大小写判断内容是否相等
String username = "johN";
if ("john".equalsIgnoreCase(username)) {
System.out.println("success!");
} else {
System.out.println("Failure!");
} //success!
System.out.println("qwdqwd".length()); //6
//2.indexOf 获取字符在字符串对象中的第一次出现的索引,索引从0开始,找不到返回-1
String s1 = "wdqwd@dwa";
int index = s1.indexOf("@");
int index1 = s1.indexOf("w", 5); //从第五个位置开始查找
System.out.println(index); //5
System.out.println(index1); //7
//3.lastIndexOf 获取字符在字符串最后一次出现的索引,索引从0开始 找不到返回-1
index = s1.lastIndexOf("d");
System.out.println(index); //6
//4.截取指定范围字符串 左闭右开
String name = "hello,张三";
System.out.println(name.substring(6)); //张三
//5.从索引0开始截取到第5个
System.out.println(name.substring(0, 5)); //hello
//6.全部转为大小写
String a = "hello";
System.out.println(a.toUpperCase()); //HELLO
System.out.println(a.toLowerCase()); //hello
//7.拼接
String s2 = "123456";
s2 = s2.concat("dawd").concat("qwe");
System.out.println(s2); //123456dawdqwe
//8.替换 原来的 teststr没有影响 replaceAll replaceFirst
String teststr = "abcabcabcabcabc";
String teststr2 = teststr.replace("a", "k");
System.out.println(teststr); //abcabcabcabcabc
System.out.println(teststr2); //kbckbckbckbckbc
//9.split 分割
//String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
//String[] split = poem.split(",");
String poem = "E:\\aaa\\bbb";
String[] split = poem.split("\\\\");
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]); //E: aaa bbb
}
//10.toCharArry 转成字符数组
char[] chs = s2.toCharArray();
for (int i = 0; i < chs.length; i++) {
System.out.print(chs[i]); //123456dawdqwe
}
//11.compareTo 比较两个字符串大小 源码
//1. jac和jack时 返回 -1 长度-长度
//2. Hello 和 HEllo时 返回32 ASCLL表差值
String s4 = "jchn";
String s5 = "jack";
System.out.println(s4.compareTo(s5)); // 2
//12.判断字串是否存在
String s6 = "helloworld";
if (s6.contains("hello")) {
System.out.println("查询成功"); //查询成功
}
//13.判断开头结尾
String s7 = "##@@hello**";
System.out.println(s7.startsWith("##")); //true
System.out.println(s7.startsWith("@", 2));//索引第2个位置是否已@开头 //true
System.out.println(s7.endsWith("**")); //true
//14.判断是否为空 isEmpty
String s8 = "";
System.out.println(s8.isEmpty()); //true
//15.trim 去掉字符串左右两边空格 中间保留
String s9 = " hello world ";
System.out.println("[" + s9.trim() + "]"); //[hello world]
//16.去掉全部空格
System.out.println(s9.replaceAll(" ", "")); //helloworld
//17.format
int age = 18;
String MyName = "qby";
String info = MyName.format("我的名字是%s,年龄是%d", MyName,age);
System.out.println(info); //我的名字是qby,年龄是18
}
}
public class test01 {
public static void main(String[] args) {
String s1 = "abc";
s1 = "qby";
System.out.println(s1); //qby
}
}
我们都知道最后s1得出的答案是qby,但是重点是String底层怎么分配的,在String重新赋值时,不会覆盖之前的内容,而是重新指向常量池的另一个内容,在此过程中String创建了2个对象
public class test01 {
public static void main(String[] args) {
String a = "qby";
String b = new String("qby");
System.out.println(a == b); //false
System.out.println(a == b.intern()); //true
System.out.println(a.equals(b)); //true
}
}
public class test01 {
public static void main(String[] args) {
String a = "qby";
String b = "hello";
String c = b + a;
System.out.println(c); //helloqby
}
}
在此过程中,创建了三个对象,重点是其底层实现的原理,底层是StringBuilder ab = new StringBuilder(); ab.append(a); ab.append(b); ab在堆中,并且append是在原来字符串的基础上追加的。
如果是String c = “hello” + “qby”; 则是常量相加,是在池中;String c = a + b;是在堆中
package StringBufferMethod;
public class StringBuffer1 {
public static void main(String[] args) {
StringBuffer s = new StringBuffer("hello");
//增
s.append(",");
s.append("张三丰").append("100").append("true");
System.out.println(s); //hello,张三丰100true
//删
/*
删除 [)
*/
s.delete(9,12);
System.out.println(s); //hello,张三丰true
//修改
s.replace(9,13,"666");
System.out.println(s); //hello,张三丰666
//查
int index = s.indexOf("张三丰");
System.out.println(index); //6
//插入 原来索引为9的内容自动后移
s.insert(9,"赵敏");
System.out.println(s); //hello,张三丰赵敏666
//替换 replace 左开右闭
s.replace(11,14,"123");
System.out.println(s); //hello,张三丰赵敏123
//reverse
s.reverse();
System.out.println(s); //321敏赵丰三张,olleh
}
}
public class test01 {
public static void main(String[] args) {
String str = null;
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(str);
System.out.println(stringBuffer.length()); //4
}
}
直接在new StringBuilder() 中使用,报错空指针异常
public class test01 {
public static void main(String[] args) {
String str = null;
StringBuffer stringBuffer= new StringBuffer(str);
System.out.println(stringBuffer.length()); //java.lang.NullPointerException
}
}
StringBuilder的方法和StringBuffer一样,在这里就不再重复
名字 | 字符序列 | 效率 | 安全性 |
---|---|---|---|
String | 不可变 | 效率低 | 安全 |
StringBuffer | 可改变 | 效率高 | 安全 |
StringBuilder | 可改变 | 效率最高 | 不安全 |
选择String、StringBuffer、StringBuilder总结:
最后我也想告诉大家,以及告诫自己,双非并不是自己进不了大厂的借口,或许只是缺乏逼自己一把的勇气吧。少研究别人,多提升自己,不是成功来的慢,而是自己努力的不够狠,努力只能及格,拼命才能优秀。加油吧!