目录:
一、String字符串的储存原理一、String字符串的储存原理
二、String类的构造方法
三、String类当中常用方法
四、[重点] StringBuffer
五、StringBuffer和StringBuilder的区别
关于Java JDK 中内置的一个类: java.lang.String
1、String表示字符串类型,属于引用数据类型,不属于基本数据类型。
2、在java 中随便使用双引号括起来的都是String对象。例如: "abc" , "def","hello world!" ,这是3个String对象
3、java中规定,双引号括起来的字符串,是不可变的,也就是说"abc"自出生到最终死亡,不可变,不能变成"abcd","abcde".....
4、在JDK当中双引号括起来的字符串,例如: "abc" "def" 都是直接存储在“方法区”的“字符串常量池”当中的。
为什么SUN公司把字符串存储在一个“字符串常量池”当中呢。因为字符串在实际的开发中使用太频繁。为了执行效率,
所以把字符串放到了方法区的字符串常量池当中。
1、分析以下代码:
public class StringTest01 {
public static void main(String[] args) {
// 这两行代码表示底层创建了3个字符串对象,都在字符串常量池当中
String s1 ="abcdef";
String s2 ="abcdef" +"xy";
// 分析:这个使用new的方式创建的字符串对象,这个代码中的”xy“是从哪里来的?
// 凡是双引号括起来的都在字符串常量池中有一份
// new对象的时候一定在堆内存当中开辟空间
String s3 =new String("xy");
}
}
以上代码的内存图如下所示:
2、分析以下代码:
User类
package com.bjpowernode.javase.string;
public class User {
private int id;
private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
代码测试:
package com.bjpowernode.javase.string;
public class UserTest {
public static void main(String[] args) {
User user =new User(109,"junker");
}
}
代码内存图演示如下:
3、分析以下代码:
package com.bjpowernode.javase.string;
public class StringTest02 {
public static void main(String[] args) {
String s1 ="hello";
String s2 ="hello";
// 分析结果是true还是false
// == 双等号比较的是不是变量中保存的内存地址? 是的
// hello存放在方法区的字符串常量池当中,s1、s2引用同指向hello 所以内存地址相同
System.out.println(s1==s2); // true
String x =new String("xyz");
String y =new String("xyz");
// 分析结果是true还是false
System.out.println(x==y); // false
// 通过这个案例的学习,我们知道了,字符串对象之间的比较不能用”==“
// ”==“不保险,应该调用String类的equals方法 // 判断对象内容是否相同
System.out.println(x.equals(y)); // true
String k =new String("testString");
// 为什么"testString"这个字符串可以后面加”.“呢
// 因为"testString"是一个String字符串对象,只要是对象都能调用方法
System.out.println("testString".equals(k)); // true
}
}
代码内存图演示如下:
注意:(int 和 String的区别)// i变量中保存的是100这个值,
int i =100;
// s变量中保存的是字符串对象的内存地址
// s引用中保存的不是"a",是0x11111
// 而0x11111是"a"字符串对象在字符串常量池当中的内存地址
String s ="a";
分析:String面试题创建了几个对象
代码演示如下:
package com.bjpowernode.javase.string;
public class StringTest03 {
public static void main(String[] args) {
String s1 =new String("hello");
String s2 =new String("hello");
}
}
二、String类的构造方法
package com.bjpowernode.javase.string;
public class StringTest04 {
public static void main(String[] args) {
// 这里只掌握常用的构造方法
// 1、构造方法:public String(byte[] bytes)
// 能往String类当中传数组
byte[] bytes ={97,98,99}; // 97是a,98是b,99是c
String s1=new String(bytes); // 因为String类当中有能传数组的构造方法,所以创建String对象把数组bytes传进去
// 前面说过:输出一个引用的时候,(String类默认继承object类),默认调用父类的toString方法,会自动输出对象的内存地址
// 通过输出结果我们得出一个结论:String类已经重写了toString方法
System.out.println(s1); // abc
// 2、构造方法:public String(byte[] bytes,
// int offset,
// int length)
String s2 =new String(bytes,1,2);
System.out.println(s2); // bc
// 结果说明:String类当中的构造方法 public String(字节数组,数组元素下标起始位置,长度)
// 将byte数组当中的一部分转换成字符串
// 3、构造方法: public String(char[] value)
// 将char数组全部转换成字符串
char[] chars ={'我','是','中','国','人'};
String s3 =new String(chars);
System.out.println(s3); // 我是中国人
// 4、构造方法: public String(char[] value,
// int offset,
// int count)
// 将char数组部分转换成字符串
String s4 =new String(chars,1,4);
System.out.println(s4); // 是中国人
}
}
三、String类当中常用方法
package com.bjpowernode.javase.string;
public class StringTest05 {
public static void main(String[] args) {
// String类当中常用方法
// 第一种:charAt(int index)
char c ="中国人".charAt(1); //"中国人"是一个字符串String对象 相当于String c =new String("中国人"); 然后调用String类当中的charAt方法
System.out.println(c); // 国
// 由此可以得出:int index 是一个索引下标
// 第二种:public boolean contains(CharSequence s)
// 用法: 判断前面的字符串中是否包含后面的子字符串
System.out.println("hello world".contains("hello ")); //true
// 第三种:public boolean endsWith(String suffix)
// 判断当前字符串是否以某个子字符串结尾
System.out.println("test.txt".endsWith(".txt")); // true
System.out.println("adfghj".endsWith("ghjk")); // false
// 第四种: public boolean equalsIgnoreCase(String anotherString)
// 判断两个字符串是否相等,并且同时忽略大小写
System.out.println("aBc".equalsIgnoreCase("abc")); // true
// 第五种: public byte[] getBytes()
// 将字符串转换成字节数组
byte[] bytes="abcde".getBytes();
for (int i=0;i
四、[重点] StringBuffer
为什么使用StringBuffer?
StringBuffer:默认初始化容量为16个char[]数组 进行存放拼接字符串
因为java中的字符串是不可变的,每一次拼接都会产生新的字符串
这样会占用大量的方法区内存(字符串常量池),造成内存空间的浪费 垃圾回收机制不会回收释放内存
String s ="abc";
s+="hello";
就以上两行代码,就导致在方法区字符串常量池当中创建了3个对象;
"abc"
"hello"
"abchello"
如何优化StringBuffer的性能? 扩容后垃圾回收机制能够把第一个数组的内存释放
在创建StringBuffer的时候尽可能的给定一个初始化容量
最好减少底层数组的扩容次数
关键点:给一个合适的初始化容量,可以提高程序的执行效率
代码演示:
public class StringBufferTest01 {
public static void main(String[] args) {
// 创建一个初始化容量为16个char[] 数组 (字符串缓存区对象) StringBuffer类当中构造方法默认super(16) 也就是默认初始化容量为16个char[]数组
StringBuffer stringBuffer =new StringBuffer(); // 初始化16个数组满后会自动扩容数组
// 往数组里面追加字符串
// StringBuffer类当中自带的 append()方法 [追加]
// 拼接字符串,以后拼接字符串统一调用append()方法
stringBuffer.append("hello");
stringBuffer.append("a");
stringBuffer.append("b");
stringBuffer.append("c");
stringBuffer.append(3.14); // 查看底层代码 可以什么类型都能传进去
System.out.println(stringBuffer); // helloabc3.14 // 自动调用toString方法 【将stringBuffer转成字符串】
// 手动给定一个初始化容量 StringBuffer类当中有这样一个构造方法: public StringBuffer(int capacity)
// 我们可以手动进行初始化赋值
StringBuffer stringBuffer1 =new StringBuffer(50); // 当容量满后会自动进行数组扩容
stringBuffer1.append("dafagagaewgwa");
stringBuffer1.append("fafwarfwa");
stringBuffer1.append("fawafwataw");
stringBuffer1.append("wgtatrawtwat");
System.out.println(stringBuffer1);
}
输出结果:
思考:String为什么拼接字符串就浪费内存 而StringBuffer就可以省内存呢?(看底层代码)
内存图解释String和StringBuffer对拼接字符串的内存优化特点:
五、StringBuffer和StringBuilder的区别
StringBuffer中的方法有:synchronized关键字修饰,表示StringBuffer在多线程环境下运行是安全的
StringBuilder中的方法没有:synchronized关键字修饰,表示StringBuffer在多线程环境下运行是不安全的



