目录
3)StringBuilder具备一些String不具备的方法
注意:⭐String类的所有修改操作的内部都是创建一个新的字符串,字符串的不可变性要牢记!!!无法修改原字符串!!!
上一篇:面向对象编程之—抽象类和接口_林纾y的博客-CSDN博客
概念
三大特殊的类:String类,Object类,包装类(这三个都是经过特殊处理的类)
String类是final修饰的类,不能有子类,为了所有使用JDK的程序员,用的都是一个相同的String类。
1.创建字符串的三种方式
1)直接赋值[推荐]
String str="hello world";
所谓直接赋值,就是采用字面量来赋值。
字面量:直接写出来的值。如10--整型字面量--默认是int;11.2--浮点型字面量--默认是double;""--字符串字面量--默认是String类型。
2)通过new创建String对象
String str=new String("hello world");
3)通过char数组来创建字符串
char[] data={'h','e','l','l','o'};
String str=new String(data);
2.字符串的内存布局
字符串是一个引用数据类型,引用数据类型的默认值是null且在堆上存储
String str="world";
String str1=str;
str,str1叫做字符串类型的引用,在栈上保存 。“world”是字符串的字面量,String的对象,在堆上存储。
3.字符串相等时的比较
1)比较方法:equals
字符串相等时,使用equals方法比较,不能直接==比较,==比较的是地址,equals比较的是内容。
1)对于基本数据类型,==比较的就是两个变量的值,int a=10;a变量保存的值就是10这个值,int b=100;b保存的值就是100这个值,a==b;所以比较相等时当然比较的是值。
2)对于引用数据类型来说,引用变量保存的是堆内存的地址而不是值,不能使用"=="比较内容是否相等。
3)str1.compareTo(str2)?:这个主要是用来比大小的,返回值是一个整型。Java中,每个类都有equals方法,它是Object类自带方法,在java中,默认所有类都继承了Object类,所以每个类都有equals方法,它的返回值是个boolean值。
4)eg:
右边比较的是对象的地址,想看内容相等,需要equals方法。
左边true是因为字符串的常量池,稍后讨论
String str=new String("HELLO");
String str1=new String("HELLO");
System.out.println(str==str1);//false
System.out.println(str.equals(str1));//true
2)用户输入与字符串字面量比较
eg:假设现在str是由用户输入的,要判断str内容是否是“hello”有几种写法?【字符串与指定值比较时,对于写法的判断?】⭐
str.equals("hello");//1❌不推荐--空指针异常
"hello".equals(str);//2✔✔推荐
分析:equals方法是通过对象调用的,说明这是一个成员方法(必须有对象才能使用【所有成员方法,成员变量必须防范空指针异常】)
//1
//2:"hello"--字符串字面量--字符串的匿名对象,一定非空
3)字符串常量池
1)JVM会对字符串创建一个字符串的常量池:当使用直接赋值创建字符串时,字面量是第一次出现,JVM就会创建一个String对象并且扔入常量池中,当字面量第二次出现或者多次引用时,并不会创建新的对象,而是复用已有对象。
2)为什么JVM会维护一个常量池:高效
3)池:复用已有对象,保证效率。内存池,线程池,连接池等等,都是池的概念。
4)手工入池
String str1=new String("hello").intern();
String str="hello";
System.out.println(str==str1);//true
String str1=new String("hello").intern();//此时str一共创建了几个对象?:2
分析:程序从右向左执行,"hello"是字符串字面量,第一次出现,在堆上创建了"hello"的对象,这是第一次创建。有new就有新空间,此时在堆上创建了一个新的String对象,将值拷贝为hello。注意此时,创建的变量并未入池,只有直接赋值法创建变量才会入池,"hello"匿名对象在的空间成为垃圾空间。
4.小结
1.使用String创建对象就使用直接赋值法
2.比较内容是否相等调用equals方法
3.字符串的不可变性:字符串本身不可变,内容不可修改,变的只是引用的指向。若要达到修改目的,只能通过反射破坏封装。
5.理解字符串的不可变性⭐
内部存储数据的value数组对外不可见,外部无法直接使用,无法修改内容
问题:输出是world,已经变了,所以什么是不可变性??
分析: 此处只是str指向发生变化,指向了新的字符串对象"world",而对原字符串"hello"没有任何影响。
字符串本身不可变,变的只是引用的指向【数组abc本身内容变了】
a. 为何字符串对象内容无法修改?
String对象内容实际上存储在内部value[]中,value[]是一个private+final修饰的私有属性,private说明value[]出了String类不可见,无法识别【String类没有提供getter方法,外部无法使用value数组】,final保证了value指向的地址不变。
b.修改字符串
1)要想修改字符串变量内容,只能通过反射破坏封装
import java.lang.reflect.Field;
public class StringCode {
public static void main(String[] args) throws Exception {
String str="hello";
//通过反射获取到String类的value数组
Field field=String.class.getDeclaredField("value");
//value的private定义取消掉
field.setAccessible(true);
//获取到str对象内部的value数组
char[] value=(char[]) field.get(str);
value[0]='H';
System.out.println(str);
}
}
(课件上的substring是创建了一个新变量,并不是修改原来字符串对象肚子里的内容)
由于字符串的不可变,若需要修改大量字符串时,我们使用两个sb类替换:
StringBuffer:线程安全
StringBulider:线程不安全
这两个sb类和String类完全无关,是两种不同的类型,不能混为一谈
6.StringBuilder
1)String->StringBuilder
1)直接调用StringBuilder的append方法
2)构造方法传入String对象
2)StringBuilder—>String
调用StringBuilder得toString方法
eg:
public static void main(String[] args) {
String str="hello";
str+="world";
str+="!!!";
System.out.println(str);
}
问:234行代码产生了多少个对象?
分析:
“hello”一个,+=是str+world,先产生“world”对象,然后拼接“helloworld”-3个,继续+=,先“!!!”是一个对象-4个,再“helloworld!!!”-5个,所以理论上产生5个,但是JVM没那么笨,当发现有字符串拼接时,JVM会将字符串转为StringBuilder类【不推荐这么拼接,拼接就直接使用两个SB类】
正确写法:⭐⭐
// String->StringBuilder
// 通过构造方法将String类型转化为StringBuilder类型
StringBuilder sb=new StringBuilder("hello");
// 调用append方法将字符串变为StringBuilder
sb.append("world");//⭐StringBuilder通过append方法扩展拼接内容
sb.append("!!!");
// 此时StringBuilder内部储存的就是helloworld!!!
// 将StringBuilder还原为String:StringBuilder->String
String ret=sb.toString();
System.out.println(ret);
3)StringBuilder具备一些String不具备的方法
为何不具备:本质是StringBuilder内容可改,而String内容无法修改
1)reverse:反转(即字符串反转)
sb.reverse();
2)delete(int start,int end):删除指定范围内的字符串内容
sb.delete(5,10);//左闭右开区间
Java中牵扯到区间的取值一般来说都是左闭右开区间:[start,end)
3)insert(int start,插入的数据):插入字符串
7.String<->char[]的相互转换
1)String->char[]
String的toCharArray()方法
String str="hello";
//str->字符数组
char[] data=str.toCharArray();
System.out.println(Arrays.toString(data));
2)取得String某一个具体的字符:String->char
String的charAt()方法
//取出字符串中每个字符
for (int i = 0; i < str.length(); i++) {
char c=str.charAt(i);//取出字符串中某个字符
System.out.print(c+"、");
eg:判断用户输入的字符串是否由纯数字组成—逐个取出判断
public static boolean isNumStr(String str){
for (int i = 0; i < str.length(); i++) {
char c=str.charAt(i);
if(c<'0'||c>'9') {
return false;
}
}
return true;
}
3)char[]->String
public static void main(String[] args) {
char[] data={'h','e','l','l','o'};
String str=new String(data);
String str1=new String(data,0,3);//左闭右开
System.out.println(str);
System.out.println(str1);
}
8.String<->byte
1)String->byte[]【只能转化为byte[]数组,不能是单独byte】
String str="美女";
//未指定编码就是当前操作系统的默认编码
byte[] data=str.getBytes();
//指定UTF_8编码
byte[] data1=str.getBytes(StandardCharsets.US_ASCII);
System.out.println(Arrays.toString(data));
System.out.println(Arrays.toString(data1));
2)byte[]->String
什么时候用char什么时候用byte?
byte一般用在网络传输或者输入输出中(读写文件)使用。判断网速的默认单位就是byte;如果处理的是文本(或者说字符串的内容)用char。
9.其他常用操作
1)字符串比较
String str="hello";
String str1="HELLO";
System.out.println(str.equals(str1));
System.out.println(str.equalsIgnoreCase(str1));
System.out.println(str.compareTo(str1));
2)字符串查找
⭐⭐查找子串是否存在:contains()
System.out.println(str.contains("ell"));//true
System.out.println(str.contains("cdbo"));//false
3)字符串替换
注意:⭐String类的所有修改操作的内部都是创建一个新的字符串,字符串的不可变性要牢记!!!无法修改原字符串!!!
4)字符串拆分
注意:按照特殊字符拆分需要转义处理【eg拆字符串152.25.1,点操作符是属于java中特殊操作符(访问操作符),需要转义\\.】
5)字符串截取
String str="helloworld";
System.out.println(str.substring(5));//world
System.out.println(str.substring(5,9));//worl