1.String类(特殊的类)java.lang.String
表示字符串,本质是一个char[]数组

//public String(String original) 构造方法
public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    private final char value[];

从构造方法源码可以看出String类型的对象调用的是一个char[]数组,
由private final修饰,被final修饰就不能做出更改。这也是字符串不可改变的原因

2.实例化String对象 可以直接进行赋值操作
方式一:String s1=new String(“hello”);利用String类中的构造方法
方式二: String s2=“hello”;

 public static void main(String[] args) {
        String s1=new String("hello");
        String s2="hello";
         System.out.println("s1:"+s1);
        System.out.println("s2:"+s2);
        System.out.println(s1==s2);//false内容相同但地址值不同
        //运行结果
        s1:hello
        s2:hello
        false

废话不多说,直接上图。
字符串内容相同而地址值不同,创建字符串内存地址的引用-小白菜博客
3.字符串是常量,会保存在常量池。
注意:必须使用双引号包裹的字符串才是常量,会保存到常量池。
s1和s2的地址值()是不相同的,因为存储机制是不一样的
但我们直接使用双引号(" ")写出一个字符串时,会先在常量池查找这个字符串
如果没有相同的则会在常量池开辟空间并创建字符串,如果有则会直接使用常量池中的字符串
4.内存存储的分配:
s1创建的对象为字符串,所以会先在元空间(Metaspace)中的常量池保存字符“hello”,而因为new 需要创建对象,所以会在堆内存中开辟空间(堆内存开辟的空间会在堆中有一个新的内存地址)并保存元空间中的“hello”内存地址值(引用)堆内存中的地址会传给栈中main()里的String s1,而s2会在常量池创建,
因为“hello”在元空间存在,所以s2仅仅保存的是元空间中常量池的hello内存引用,不会指向堆内存。
所以:
s1的内存地址引用 :
创建时:常量池 ->堆->s1(栈)
调用时:s1(栈)->堆->常量池
s2的内存指向为:
创建时:常量池->s2(栈)
调用时:s2(栈)->常量池
5.需要注意的是不同的jdk版本常量池的位置也会不同,本文以JDK8为例。
在JDK6以及之前,常量池在方法区
在JDK7的时候,常量池在堆内存
在JDK8的时候,常量池在元空间(方法区)

如有错误,请指出,我会及时更正。
三人行必有我师焉,谢谢。