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
    }   
 




















