String类
1551字约5分钟
2025-09-23
String类
类 | 实现接口 |
---|---|
String | Serializable ,Comparable ,CharSequence |
实现 Serializable
接口就表示该类可以串行化(也就是可以在网络上传输)
实现 Comparable
接口就表示该类可以进行比较
CharSequence
接口就是字符序列
String
中常用的构造器有 String()
,String(String)
,String(char[])
;网络编程中常用的构造器String(byte[],int,int)
,String(byte[])
字符串的字符使用的是
Unicode
字符编码,一个字符不管是字母还是汉字都占用两个字节String
是final
类,不能被其它类继承,也不能被修改所以修改
String
的值并不像普通类那样直接修改原本的值,当我们修改值的时候,首先查看常量池中有没有该常量,如果有,那么堆中的value
对象则指向该常量(内存地址会变化);如果常量池中没有该常量,则在常量池中创建一个新常量,然后堆中的value
对象则指向该常量(内存地址会变化)String
是用字符数组private final char value[];
来存放字符串内容的;字符串被分为多个字符,然后在被存放到该数组中注意
String
的value
常量是final
类型intern
方法是用来返回常量池中的地址的String name = new String("zhang"); // name在堆中创建了一个value对象 // 该对象会在常量池中创建一个 zhang 的数据空间,并且指向该空间 // name.intern()的作用就是返回该常量空间 // 而name指向的则是堆中的对象,没有直接指向常量池中的数据空间 System.out.println(name.intern()); // 输出 zhang // 为什么直接输出name也能正常输出内容呢?为什么没有输出对象的哈希值呢? // 是因为String类重写了toString方法 // 所以当我们直接调用对象的时候就直接返回我们传进去的值
// name 是变量名
// "张三"是字符串常量,也是字符序列
String name = "张三";
两种创建String对象的区别
方式一:
String name = "Hello";
(直接指向常量池)先从常量池中查看是否有
Hello
的数据空间,如果有,直接指向该空间;如果没有则重新创建,然后在指向该空间
name
最终指向的是常量池中的空间地址方式二:
String name = new String("World");
(先创建对象)先在堆中创建空间,该空间里面维护了
value
属性,然后该属性在指向常量池中的World
的数据空间如果常量池中没有
World
数据空间,则重新创建如果有则直接通过
value
指向该数据空间name
最终指向的是堆中的空间地址,然后在通过堆中的空间地址指向常量池中的空间地址
细节
细节1
String a = "abc";
String b = "abc";
System.out.println(a.equals(b)); // true
System.out.println(a == b); // true
/*
* 直接赋值给String,没有重新new一个String对象
* 这样String是不会在栈中创建对象然后在指向常量池
* 所以首先a会先查看常量池中是否有abc的数据空间
* 如果有则指向该空间,如果没有则自己重新创建一个空间,并指向该空间
* b也是同理,因为a已经创建abc的数据空间了,所以b这里就直接指向a所创建的数据空间
* 所以a和b是同一个对象
*
* 如果是如下代码
* String a = new String("abc");
* String b = new String("abc");
* 因为a和b都创建了一个对象,所以a和b不是同一个对象
* */
细节2
String a = "abc";
String b = new String("abc");
System.out.println(a.equals(b)); // true
System.out.println(a == b); // false
/*
* intern方法是用来返回常量池中的地址的
* 变量b的值是abc,变量a的值也是abc
* 所以变量a和b在常量池中的地址是一样的
* 所以a == b.intern()为true
* 因为b指向的是堆,而b.intern()则表示常量池中的常量地址
* 所以b == b.intern()为false
* */
System.out.println(a == b.intern()); // true
System.out.println(b == b.intern()); // false
细节3
Person p1 = new Person();
p1.name = "abc";
Person p2 = new Person();
p2.name = "abc";
System.out.println(p1.name.equals(p2.name)); // true
/*
* 虽然p1和p2是两个不同的对象
* 但是这里比较的是p1和p2中的name,也就是两个属性相互比较
* p1和p2的name的值都是abc
* 所以p1和p2的name都指向的是同一个常量地址
* 所以p1.name == p2.name 为 true
* */
System.out.println(p1.name == p2.name); // true
System.out.println(p1.name == "abc"); //true
细节4
两个常量相加,则看常量池;如果是两个变量相加,则看堆
// 两个常量相加,jvm会将其优化成ab,不会调用对象
// 所以两个字符串常量相加是不会创建对象的
String a1 = "a" + "b";
/*
* 先创建StringBuilder sb = new StringBuilder();
* 调用sb.append("hello");
* 调用sb.append("abcd");
* append会相互叠加
* 有几个变量相加就调用几个sb.append(String str);
* 然后调用重写好的sb.toString()返回new String(value, 0, count);其中value的值就是相加好的字符串
* 最后变量c指向该value,因为value的值是相加好的字符串,而字符串又在常量池中
* 所以value指向常量池中的"helloabcd"
* */
String a = "hello";
String b = "abcd";
// 两个变量是在堆中相加的
// 所以两个String变量相加会在堆中创建一个新对象
String c = a + b;
细节5
public class Homework05 {
String str = new String("hello");
final char[] arr = {'j', 'a', 'v', 'a'};
public void index(String str, char[] arr) {
// str 原本指向的是 new String("hello");
// 现在则指向常量池中的 "java"
str = "java";
arr[0] = 'h';
}
public static void main(String[] args) {
// 创建本类对象,从而方便调用本类对象
Homework05 h5 = new Homework05();
// 传入str对象和arr数组
h5.index(h5.str, h5.arr);
// 输出 h5 中 的 str
System.out.println(h5.str); // hello
// 因为上面只是修改了数组元素
// 并没有像str一样指向别的地方
// 所以输出的值是修改过后的值
System.out.println(h5.arr); // hava
}
}