String类的概述
String类:代表字符串。
Java 程序中的所有字符串字面值(如 “abc” )都作 为此类的实例实现。
String是JDK中内置的一个类:java.lang.string 。
String表示字符串类型
,属于引用数据类型
,不属于基本数据类型 。
String 类, 创建出来的 对象, 也就是 String对象 。
String是一个final类,代表不可变的字符序列
。
字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
String对象的字符内容是存储在一个字符数组value[]中的。
创建String
日常创建String对象,使用的就是 2种方式 :
🧨字面量方式:
什么是字符串字面量?一个字符串字面量就是两个双引号之间的字符序列,形如“张三”、“zs”。
String str1="字面量";
✨new 的方式:
String str2 = new String("new");
因为String 是引用数据类型 , 所以str1,str2 都是 String 引用数据类型的变量
。这两个String类型的变量存在栈里面
栈 (stack)
存储变量str1,str2
栈 (stack) :
- 是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量.
- 在java中,所有基本类型和引用类型都在栈中存储.
- 栈中数据的生存空间一般在当前scopes内(就是由{…}括起来的区域).
- 函数中定义的基本类型变量,对象的引用变量都在函数的栈内存中分配。
- 栈内存特点,数数据一执行完毕,变量会立即释放,节约内存空间。
- 栈内存中的数据,没有默认初始化值,需要手动设置。
然后String 类 创建的对象,也就是 new String(“new”), 也是需要有东西存。这些东西存在堆里面
堆(heap)
堆内存用来存放new创建的对象和数组。
堆内存中所有的实体都有内存地址值。
🎨创建String对象的2种方式存储情况:
new String(“ACV”) 存在堆
里面。
String str1=“JCccc”; 也是在堆里面,但是是在堆里面的字符串常量池
里面。
(现在的java版本是 jdk 8,不考虑字符串常量池的一个位置演变过程了,明确知道一点,jdk7之后,字符串常量池在堆里面 )
字符串常量池(jdk7之后)
为什么明明说好了 堆是用来存对象的,为什么又搞个字符串常量池 ? 也是存对象?
是的,JAVA 设计者确实是这样设计的。
🎈为什么有字符串常量池?
因为我们会发现 String这个引用类型,用起来基本跟其他八个基本类型 (byte、char、short、int、long、float、double、boolean)没有什么区别,甚至String还用的更多。
而且String对象,对象的使用,分配要付出时间和空间上的开销。
既然用的这么勤,我们如果搞一个缓存池给这个String, 分配过的不再分配不再开销,
是不是就能提升性能,更加合理?
所以字符串常量池出来了。
什么东西能存到字符串常量池?
String 对象 通过字面量赋值
的,能进去 ,例如 String str=“张三”
再回顾一下 :
什么是字符串字面量?一个字符串字面量就是两个双引号之间的字符序列,形如“张三”、“abc”。
还有就是 通过 String提供的方法 intern() ,可以将字符串丢进去常量池里面。
调用intern方法时,如果池已包含 等于此 string 对象的字符串(通过由使用equals方法),则返回池中的字符串的对象引用。
否则,此Stringobject将添加到池中, 并返回对该String对象的引用。
理解String的不可变性
import org.junit.Test;
//String的使用
public class StringTest {
/*
String:字符串,使用一对""引起来表示。
1.String声明为final的,不可被继承
2.String实现了Serializable接口:表示字符串是支持序列化的。
实现了Comparable接口:表示String可以比较大小
3.String内部定义了final char[] value用于存储字符串数据
4.String:代表不可变的字符序列。简称:不可变性。
体现:1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
2. 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
6.字符串常量池中是不会存储相同内容的字符串的。
*/
@Test
public void test1(){
String s1 = "abc";//字面量的定义方式
String s2 = "abc";
s1 = "hello";
System.out.println(s1 == s2);//比较s1和s2的地址值,false
System.out.println(s1);//hello
System.out.println(s2);//abc
System.out.println("*****************");
String s3 = "abc";
s3 += "def";
System.out.println(s3);//abcdef
System.out.println(s2);
System.out.println("*****************");
String s4 = "abc";
String s5 = s4.replace('a', 'm');//将a替换成m
System.out.println(s4);//abc
System.out.println(s5);//mbc
}
}
String s1 = "abc";
//首次声明,在方法区的字符串常量池中新造abc
//栈中S1指向字符串常量池中的abc
String s2 = "abc";
//声明S2时,会先在常量池中寻找是否有abc,有的话将地址赋给S2
s1 = "hello";
//再次为S1赋值,新造hello,将S1的指针指向hello
String不同实例化方式的对比
String对象的创建
String str = "hello";
//本质上this.value = new char[0];
String s1 = new String();
//this.value = original.value;
String s2 = new String(String original);
//this.value = Arrays.copyOf(value, value.length);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count);
/*
String的实例化方式:
方式一:通过字面量定义的方式
方式二:通过new + 构造器的方式
面试题:String s = new String("abc");方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:"abc"
*/
@Test
public void test2(){
//通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。
String s1 = "javaEE";
String s2 = "javaEE";
//通过new + 构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
System.out.println("***********************");
Person p1 = new Person("Tom",12);
Person p2 = new Person("Tom",12);
System.out.println(p1.name.equals(p2.name));//true
System.out.println(p1.name == p2.name);//true
//name的地址指向方法区
p1.name = "Jerry";
System.out.println(p2.name);//Tom
}