1、基本介绍:
·String s5=new String(byte[] b);
(5)String实现了Serializable,说明String可以串行化,即可以网络传输
String实现了Comparable,说明String对象可以比较
(6)String和所有包装类都是final,不可被继承
String是一个final类,代表不可变的字符序列,字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的
(7)String有属性private final char value[]; 用于存放字符串内容(注意value是final类型,指向的地址不可修改(里面单个字符内容可以修改),因为基本类型的final是值不可修改,但char是引用类型,所以是地址不可修改)
package String_;
public class String01 {
public static void main(String[] args) {
String name="jack";
name="tom";
final char[] value={'a','b','c'};
char[] v2={'t','o','m'};
value[0]='H';//修改value里的内容
//value=v2;//报错,因为value不能指向新的地址v2
System.out.println(value);//输出:Hbc
}
}
2、创建String对象的两种方式:
方式一:直接赋值String s="hspedu";
先从常量池查看是否有"hspedu"数据空间,如果有,直接指向;如果没有则重新创建,然后指向,s最终指向的是常量池的空间地址
方式二:调用构造器String s=new String("hspedu");
先在堆中创建空间,里面维护了value属性,指向常量池的"hspedu"空间,如果常量池没有"hspedu",重新创建,如果有,直接通过value指向,最终指向的是堆中的空间地址
String a="abc";
String b="abc";
System.out.println(a.equals(b));//T,比较内容
//源码:public boolean equals(Object anObject) {
// if (this == anObject) {
// return true;
// }
// return (anObject instanceof String aString)
// && (!COMPACT_STRINGS || this.coder == aString.coder)
// && StringLatin1.equals(value, aString.value);
// }
System.out.println(a==b);
//T,比较地址,a在常量池没有找到"abc",所以创了一个"abc",
// b在常量池里找到了a的"abc",所以指向了a的"abc",不用自己创了,
// 所以它们指向的是同一个地址
String a="abc";
String b=new String("abc");
System.out.println(a==b);//F,a指向的是常量池里的对象,
// b指向的是堆中的对象(该对象又指向常量池里a的"abc")
System.out.println(a==b.intern());//T
System.out.println(b==b.intern());//T
//b.intern()指向的是常量池里a的对象,
补:b.intern()方法最终返回的是常量池的地址(对象)
当调用intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串,否则将此String对象添加到池中,并返回此String对象的引用
Person p1=new Person();
p1.name="hsp";
Person p2=new Person();
p2.name="hsp";
System.out.println(p1.name==p2.name);
//T,因为比较的是p.name的地址不是p的地址,
// 而p1.name和p2.name都指向常量池里的同一个地址
String s="abc";
s="hsp";
//创建了两个对象
String a="hello"+"abc";
//创建了一个对象,
//因为编译器会优化,判断创建的常量池对象,是否有引用指向,
//即等价于String a="helloabc"
(大家做题做着做疯魔了,像赌徒一样,好搞笑啊!)
String a="hello";
String b="abc";
String c=a+b;
//底层分析:
//1、先创建一个StringBuilder sb=StringBuilder();
//2、执行sb.append("hello");
//3、执行sb.append("abc");
//4、String c=sb.toString;
//sb是在堆中,并且append是在原来字符串的基础上追加的
//最后其实是c指向堆中的对象(String) value[]——》池中"helloabc"
String d="hello"+"abc";//d="helloabc"
System.out.println(c==d);//false
String e="helloabc"
System.out.println(d==e);//true
//常量相加,看的是池
//变量相加,看的是堆
提示:尽量看源码学习
3、String类的常见方法:
String类是保存字符串常量的,每次更新都需要开辟空间,效率较低,因此java设计者还提供了StringBuilder和StringBu ffer来增强String的功能,并提高效率
package String_;
public class String01 {
public static void main(String[] args) {
String str1="hello";
String str2="Hello";
System.out.println(str1.equals(str2));//f, equals方法比较内容是否相同,区分大小写
System.out.println(str1.equalsIgnoreCase(str2));//t, equalsIgnoreCase方法比较内容是否相同,不区分大小写
System.out.println(str1.indexOf('e'));//输出:1,该字符第一次出现的地址
System.out.println(str1.lastIndexOf('e'));//输出:1,该字符最后一次出现的地址
System.out.println(str1.indexOf("el"));//输出:1,也可以是查找字符串第一个字母的地址
System.out.println(str1.lastIndexOf("el"));//输出:1,查找字符串第一个字母最后一次出现的地址
System.out.println(str1.substring(2));//输出:llo,截取索引2后面所有的字符
System.out.println(str1.substring(0,2));//输出:he,截取索引为0到索引为2的所有字符
}
}
package String_;
public class String01 {
public static void main(String[] args) {
String s="hello";
System.out.println(s.toUpperCase());//HELLO
System.out.println(s.toLowerCase());//hello
s=s.concat(" cheer ").concat(" up ").concat("TV");
System.out.println(s);//hello cheer up TV
System.out.println(s.replace("cheer","CHEER"));//hello CHEER up TV
//注意:s.replace()方法执行后对原来的s没有任何影响
String poem="锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
String[] split=poem.split(",");//以逗号为标准对poem进行分割,返回一个数组
for(int i=0;i<split.length;i++){
System.out.println(split[i]);
}
//输出:
//锄禾日当午
//汗滴禾下土
//谁知盘中餐
//粒粒皆辛苦
//注意:在对字符串进行分割时,如果有特殊字符,需要加入转义字符\
//如要以"\\"为标准分割,那应该是String[] split=poem.split("\\\\");
//因为一个\对应一个\,两个\\加起来就一共是\\\\个了
char[] ch=s.toCharArray();//把字符串转换成字符数组
for(int i=0;i<ch.length;i++){
System.out.println(ch[i]);
}
String s1="hello";
String s2="hi";
System.out.println(s1.compareTo(s2));//输出:-4 //因为e-i=-4
//前>后,返回正数;后>前,返回负数,前=后,返回0
String s3="hello";
String s4="h";
System.out.println(s3.compareTo(s4));//输出:4 //因为s3.length-s4.length=4
//如果前面部分都相同,如hello和he比较,则是则返回的是字符串长度的差
}
}
package String_;
public class String01 {
public static void main(String[] args) {
String name="tom";
int age=10;
double score=98.8;
char gender='男';
String s="姓名:%s,年龄:%d,成绩:%.2f,性别:%c";//占位符,由后面的变量来替换
System.out.println(String.format(s,name,age,score,gender));
//输出:姓名:tom,年龄:10,成绩:98.80,性别:男
}
}
(1)StringBuffer类
1)java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删
2)很多方法与String相同,但StringBuffer是可变长度的
3)StringBuffer是一个容器
4)StringBuffer的直接父类是AbstractStringBuilder
5)StringBuffer实现了Serializable, 说明StringBuffer可以串行化,即可以网络传输
6)父类AbstractStringBuilder有属性char[] value,不是final,该value数组存放字符串内容,引出的是存放在堆中的(不再是常量池)
7)StringBuffer是一个final类,不能被继承
StringBuffer stringBuffer=new StringBuffer();
StringBuffer stringBuffer1=new StringBuffer(100);
StringBuffer stringBuffer2=new StringBuffer("hello");
package String_;
public class String01 {
public static void main(String[] args) {
StringBuffer s=new StringBuffer("hello");
s.append(" h");
s.append("i").append(" 100").append(true).append(10.5);//追加
System.out.println(s);//hello hi 100true10.5
s.delete(2,3);//删除
System.out.println(s);//helo hi 100true10.5
s.replace(0,5,"cheer");//替换
System.out.println(s);//cheerhi 100true10.5
s.indexOf("c");//查找
System.out.println(s);//输出:0
s.insert(5," up! ");//增加
System.out.println(s);//输出:cheer up! hi 100true10.5
}
}
8)String 和StringBuffer的区别
4、练习题
//我的代码
package String_;
import java.util.Scanner;
public class Homework01 {
public static void main(String[] args) {
System.out.println("请输入商品名称:");
Scanner scanner1=new Scanner(System.in);
System.out.println("请输入商品价格:");
Scanner scanner=new Scanner(System.in);
StringBuffer stringBuffer=new StringBuffer(String.valueOf(scanner));
int j=stringBuffer.indexOf(".");
for(int i=j;i>0;){
stringBuffer.insert(i,",");
i=i-3;
}
System.out.println("商品名称\t"+"商品价格");
System.out.println(scanner1+"\t"+stringBuffer);
}
}
//老师的代码
package String_;
import java.util.Scanner;
public class Homework01 {
public static void main(String[] args) {
String price="1234567.59";
StringBuffer sb=new StringBuffer(price);
for(int i=sb.lastIndexOf(".")-3;i>0;i-=3){
sb=sb.insert(i,",");
}
System.out.println(sb);//1,234,567.59
}
}
(2)StringBuilder类
1)是一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全),该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用时,如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快
2)在StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据
3)StringBuilder和StringBuffer均代表可变的字符序列,方法是一样的,所以使用和StringBuffer一样
4)StringBuilder是final
5)继承了AbstractStringBuilder,属性char[] value,内容存到value,因此字符序列是在堆中
6)实现了Serializable接口,StringBuilder对象可以串行化,即可以网络传输,可以保存到文件。序列化(所谓序列化即可以保存类型和数据本身)
7)StringBuilder的方法,没有做互斥处理,即没有synchronized关键字,因此在单线程的情况下使用