常用类之String

我们使用Java语言写的第一个程序,就是向世界发出呐喊,在控制台输出了一个“Hello World!”。今天就来聊聊这个用""包裹的字符串。

1.String是什么?

String是Java中内置的一个类(java.lang.String),用“”包裹内容,表示字符串。

例:String name = "Tom";

name就是一个字符串对象,其值为“Tom”。

2.堆、栈、方法区

在这里要先简单的扩展一下JVM内存中的其中三个:堆、栈和方法区所存储的东西都是什么。

  • 栈:存放的是基本数据类型的值、引用数据类型的引用值。
  • 堆:引用数据类型的真实值。
  • 方法区:存放的是所有的calss、static变量。(方法区中包含的都是在程序中唯一的元素)

3.String的使用

首先我们需要知道的是,String对象的值(字符串)存储在方法区中的字符串常量池。因为在实际的开发中,可能会频繁的使用字符串,所以把它放在常量池中提高程序的执行效率。

我们通过查看String的源码可以发现:字符串的底层是通过private final byte[] value;来实现的,final修饰的变量是常量,不能二次赋值,所以,字符串是不可变的。

这是什么意思呢?我们举个例子,并且通过简单的图来理解一下:

  • 我们知道字符串是可以直接赋值的:
package com.dh.string;

public class StringDemo01 {

    public static void main(String[] args) {
        
        String s1 = "abc";
        //直接使用+进行字符串的拼接
        String s2 = s1 + "de";
    }
}

分析:执行第一句时,会将"abc"放在方法区的字符串常量中。然后执行第二句,字符串常量池中存在s1,即"abc"字符串,所以直接从字符串常量池中拿;因为字符串常量中不存在"de",所以需要在常量池中新开辟一个内存用于存放"de",但是没有引用指向它。此时执行字符串的拼接,但是因为字符串是不可变的,所以不能修改原来的"abc",而是需要在字符串常量中存放一个新的字符串"abcde",s2引用指向该字符串。

面试题:

package com.dh.string;

public class StringDemo2 {

    public static void main(String[] args) {

        String s1 = "abc";
        String s2 = "abc";

        //结果为true
        System.out.println(s1 == s2);
        
        /*
        分析:因为执行第一句代码时,常量池创建了一个字符串"abc",执行第2句代码时,因为存在了"abc",故不会新建,只是将s2的
	      引用指向s1,所以s1和s2的引用是相同的,==比较引用数据类型时比较的就是引用值,故结果为true。
        */
    }
}
  • 我们说String是个类,那么是类就会有构造方法,我们通过查看api文档就可以看到String类有哪些构造方法可以用来创建String对象

创建一个字符串对象,值为null:String s = new String();//s的值为""

创建一个字符串对象,值为hello:String s = new String("abc");

我们再来分析一下通过new创建的字符串对象又是如何存储的呢?

package com.dh.string;

public class StringDemo01 {

    public static void main(String[] args) {

        String s1 = "abc";
        //直接使用+进行字符串的拼接
        String s2 = s1 + "de";
        
        //通过new实例化字符串对象
        String s3 = new String("de");
    }
}

分析:new的话是一定会在堆中多一个对象的,即在栈中有一个引用,指向堆中的对象,由于字符串是放在常量池中的,所以堆中的对象存放的就是字符串常量池中字符串的引用。

面试题:

package com.dh.string;

public class StringDemo3 {

    public static void main(String[] args) {

        String s1 = new String("abc");
        String s2 = new String("abc");

        //结果为false
        System.out.println(s1 == s2);
    }
    
    /*
    分析:如下图
    */
}

面试题:此时共创建了几个对象?

注意:此时共创建了3个对象,堆中两个,常量池中一个。

扩展:垃圾回收器是不会释放常量的。


结论:通过上述例子,可以知道,使用==来比较字符串对象是不保险的,我们可以使用equals()方法来比较。(Object类中的equals()方法里还是使用 == ,所以String类重写了Object类的equals()。

package com.dh.string;

public class StringDemo3 {

    public static void main(String[] args) {

        String s1 = new String("abc");
        String s2 = new String("abc");

        //结果为false
        System.out.println(s1 == s2);

        //使用equals(),结果为true
        System.out.println(s1.equals(s2));
    }
}

可自行查看源码~


本人水平有限,若有错误和不足,欢迎指出纠正~

posted @ 2021-01-24 13:31  deng-hui  阅读(242)  评论(0编辑  收藏  举报