String对象用于保存字符串,也就是一组字符序列。
String字符串的字符使用Unicode的字符编码,一个字符无论是中文还是汉字都占两个字节。
String有许多重载的构造器,因此可以接收多种数据类型并创建对象,比如说下面的:
String我们大多不陌生,观察它的继承结构也可以发现它主要是实现了三个接口。
其中,实现Serializable接口表示串行化,可在网络传输,Compareable表示可以比较大小。
String是final类,因此不能被其他类继承。
String对象有两种创建方法。
//方式一
String s = "hx";
//方式二
String s2 = new String("hx");
方式二现在堆中创建空间,里面维护value空间,指向常量池的hx空间,如果常量池没有hx,创建一个,如果有,直接指向,总之最后指向的是堆中的对象。
下面是两种方式的内存分布图:
public class StringMethod02 {
public static void main(String[] args) {
String s1 = "heLLo java";
System.out.println(s1.toUpperCase());//全部转大写
System.out.println(s1.toLowerCase());//全部转小写
String s2 ="hx";
s2 = s2.concat("yyds").concat("jiayou");//拼接字符串
System.out.println(s2);
String s3 ="很喜欢喜欢喜欢";
s3 = s3.replace("喜欢","讨厌");
System.out.println(s3);//替换字符串
String poem ="锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
String[] poems = poem.split(",");
for(String s : poems) {
System.out.println(s);
}//将字符串分割并装入字符串数组
String address ="E:\\aaa\\bbb";
String[] addresss =address.split("\\\\");
for(String s:addresss) {
System.out.println(s);
}//分割标记是/的话要多加一个/
char[] ch =s1.toCharArray();
for(char c:ch) {
System.out.println(c);
}//将字符串放入字符数组
String s4 ="hello";
String s5 ="hel";
System.out.println(s4.compareTo(s5));//字符串比较
String name ="黄暄";
int age =19;
char gender = '男';
String school = "江苏师范大学";
String info = "我的名字是" + name +",年龄是" + age +
",性别是"+gender+",就读于"+ school;
System.out.println(info);
String formatStr ="我的名字是%s,年龄是%d,性别是%c," +
"就读于%s";
String s6 = String.format(formatStr,name,age,gender,school);
System.out.println(s6);//字符串拼接的两种方法
}
}
输出为
HELLO JAVA
hello java
hxyydsjiayou
很讨厌讨厌讨厌
锄禾日当午
汗滴禾下土
谁知盘中餐
粒粒皆辛苦
E:
aaa
bbb
h
e
L
L
oj
a
v
a
2
我的名字是黄暄,年龄是19,性别是男,就读于江苏师范大学
我的名字是黄暄,年龄是19,性别是男,就读于江苏师范大学
public class StringExercise01 {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
System.out.println(a.equals(b));
System.out.println(a==b);
}
}
public class StringExercise03 {
public static void main(String[] args) {
String a = "hx";
String b = new String("hx");
System.out.println(a.equals(b));
System.out.println(a==b);
System.out.println(a==b.intern());
System.out.println(b==b.intern());
}
}
第一个,两个String对象都是hx,所以equals输出true;
第二个,a指向的是常量池中的hx,b指向的是堆中的value,value指向常量池,所以输出false;
第三个,在做这题之前,我们应该先搞明白intern方法,我们来看定义
public class StringExercise04 {
public static void main(String[] args) {
String s1 = "hx";
String s2 = "java";
String s4 = "java";
String s3 = new String("java");
System.out.println(s2==s3);//F
System.out.println(s2==s4);//T
System.out.println(s2.equals(s3));//T
System.out.println(s1==s2);//F
}
还是之前讲的那一套,不多说。
public class StringExercise05 {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "hx";
Person p2 = new Person();
p2.name = "hx";
System.out.println(p1.name.equals(p2.name));//T
System.out.println(p1.name == p2.name);//T
System.out.println(p1.name == "hx");//T
String s1 = new String("hx");
String s2 = new String("hx");
System.out.println(s1 == s2);//F
}
}
public class StringExercise06 {
public static void main(String[] args) {
String s1 = "hello";
s1 = "haha";
//上面两行创建了两个对象
}
}
上面两句话的意思其实是在创建字符串s1的时候先看看字符串有没有hello,如果有,直接指向,如果没有,直接创建,再指向,然后再在常量池里面创建一个haha的常量,再让s1指向haha,所以根本上是创建了两个对象。
public class StringExercise07 {
public static void main(String[] args) {
String a = "hello" + "abc";//底层优化为helloabc
System.out.println(a);
//只创建了一个string对象
}
}
为什么这里只创建了一个对象,因为编译器很聪明,它在底层会自动把这两个字符串拼接在以前,也就是“helloabc”,所以只需要创建一个对象。
public class StringExercise008 {
public static void main(String[] args) {
String a = "hx";
String b ="yyds";
String c = a + b;
//创建了三个string对象
String d = "hxyyds";
System.out.println(c == d);
String e ="hx" +"yyds";
System.out.println(e == d);
}
}
输出
false
true
与上一题那种写法不一样,通过调试,我们可以知道,直接将字符串相加在底层的运行其实是,先创建一个StringBuilder的对象,然后执行它的append方法分别将hx与yyds拼接最后通过toString方法返回一个String对象给String c,所以c其实是指向堆的,然后堆中的value指向池中的hxyyds。
public class StringExercise09 {
public static void main(String[] args) {
String s1 ="hx";
String s2 = "java";
String s5 ="hxjava";
String s6 = (s1+s2).intern();
System.out.println(s5 == s6);//T
System.out.println(s5.equals(s6));//T
}
}
public class StringExercise10 {
String str = new String("hx");
final char[] ch = {'j','a','v','a'};
public void change(String str,char[] ch) {
str = "java";
ch[0] = 'h';
}
public static void main(String[] args) {
StringExercise10 ex = new StringExercise10();
ex.change(ex.str,ex.ch);
System.out.println(ex.str + "and");
System.out.println(ex.ch);
}
}
hxand
hava
与String不同的是,StringBuffer是可变的字符序列,也就是说,StringBuffer不用像String那样如果要修改一个字符串需要新建一个对象,而是可以直接修改自身的字符串,我们来看StringBuffer的结构。
我们可以看到,stringBuffer的直接父类是AbstractStringBuilder,同时由于StringBuffer实现了Serializable接口,所以StringBuffer也可以串行化,在父类中有属性char[] value,它是存放在堆中的,不是final,这也是我刚刚说StringBuffer是可变的字符序列的原因,StringBuffer还是一个final类,它不能被继承。
StringBuffer有多种多样的构造器,例如没有参数的,int参数的,String类型参数的,如果不传参数进去,那么StringBuffer默认创建一个容量为16的对象,不够的时候再自己增加,如果我们想要指定一个参数,那也可以,直接传一个int就可以指定大小啦,如果传的是String,那么就可以把这个String类型转为StringBuffer类型。当然,StringBuffer还有许多的方法,比如说下面的
常用方法如下
public class StringBufferMethod {
public static void main(String[] args) {
StringBuffer stringBuffer = new StringBuffer("hello");
stringBuffer.append(",world").append("!");
System.out.println(stringBuffer);//拼接
stringBuffer.delete(0,5);
System.out.println(stringBuffer);//删除0-5
stringBuffer.replace(0,2,"hxrrr");
System.out.println(stringBuffer);//将0-2替换为对应字符串
int index = stringBuffer.indexOf("o");
System.out.println(index);//找寻对应下标
stringBuffer.insert(5,"哈哈哈");
System.out.println(stringBuffer);//在对应下标插入
}
}
输出结果如下:
hello,world!
,world!
hxrrrorld!
5
hxrrr哈哈哈orld!
String与StringBuffer的相互转换各有两种方式,如下
public class StringAndStringBuffer {
public static void main(String[] args) {
//String->StringBuffer
String s = "hx";
//1.使用构造器
StringBuffer stringBuffer = new StringBuffer(s);
//2.使用append
StringBuffer stringBuffer1 = new StringBuffer();
StringBuffer stringBuffer2 = stringBuffer1.append(s);
//StringBuffer->String
//使用toString
StringBuffer stringBuffer2 = new StringBuffer("hx");
String s1 = stringBuffer2.toString();
//使用构造器
String s2 = new String(stringBuffer2);
}
}
总结下来就是要么构造器直接提供,要么使用方法。
public class StringBufferExercise01 {
public static void main(String[] args) {
String str = null;
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(str);
System.out.println(stringBuffer.length());
System.out.println(stringBuffer);
StringBuffer stringBuffer1 = new StringBuffer(str);
System.out.println(stringBuffer1);
}
}
有人会疑惑,为什么会输出null呢,而且长度是4,要想知道奥秘,我们需要看append的源码,我们首先找到append的String参数方法
我们发现它是调用父类的append方法,我们再看看它父类的append方法
通过父类的append方法,我们指定如果str为空,也就是我们现在这种情况,会返回appendNull方法返回的值,我们再看看appendNull方法返回的值。
哈哈,我们找到幕后真凶了,原来null是这里来的。
那空指针异常又是哪来的呢,我们可以看看StringBuffer的构造函数
我们发现,如果我们传进去的是空,那么就是导致null.length(),那必然会导致空指针异常的呀。
public class StringBufferExercise02 {
public static void main(String[] args) {
System.out.println("请输入金额");
Scanner scanner = new Scanner(System.in);
String s = scanner.next();
StringBuffer stringBuffer = new StringBuffer(s);
int index = stringBuffer.indexOf(".");
for(int i = index - 3; i > 0;i-=3)
{
stringBuffer.insert(i,",");
}
System.out.println(stringBuffer);
}
}
这个例子通过循环来实现将价格的整数位数三位三位的分隔开。
StringBuilder也是一个可变的字符序列,被设计用做StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用,如果可能,建议优先采用该类,因为再大多数实现中,它比StringBuffer要快。
StringBuffer在结构上与StringBuilder也相同。
StringBuilder也是final类,不能被继承。
StringBuffer的方法,没有做互斥处理,即没有synchroized关键字,因此在单线程的情况下使用
与StringBuffer方法类似,只是没有synchroized关键字,只能单线程,不过多赘述。
public class StringVSStringBufferVSStringBuilder {
public static void main(String[] args) {
long startTime = 0L;
long endTime = 0L;
StringBuilder stringBuilder = new StringBuilder("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
stringBuilder.append(i);
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的运行时间为"+(endTime-startTime));
StringBuffer stringBuffer = new StringBuffer("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
stringBuffer.append(i);
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的运行时间为"+(endTime-startTime));
String s = new String("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
s = s+i;
}
endTime = System.currentTimeMillis();
System.out.println("String的运行时间为"+(endTime-startTime));
}
}
我们可以看到,同样是拼接20000次,它们三者速度简直是天壤之别,前面说过,String每次都要新开一个对象,效率很慢,而StringBuider的效率是最快的。
因篇幅问题不能全部显示,请点此查看更多更全内容