java String类型的理解

String并不是基础数据类型之一,而是一个封装的字符串类型(java.lang.String),这个类应该是我们使用频次最高的类了,那么这个类我们如何去理解使用和原理呢?

1、内存模型(重点讲述常量池);

2、String使用的基本场景及底层原理;

3、intern方法的理解;

一、内存模型

简单描述下JVM的内存模型包含:程序计数器、本地方法栈、方法区、虚拟机栈、堆;其中重点要描述的是常量池,常量池jdk7以前(jdk6及以前)存放在方法区中,jdk1.7及之后存放在堆中,如下图:

 为啥要介绍常量池呢,因为字符串常量是存放在常量池中,一个特殊的区域。了解这个点之后,才能讲述后面的底层原理。

二、String使用的基本场景及底层原理

2.1 String的比较equals 与 ==

equals从Object下继承的效果与==是一样的,但是String重写了equals方法,进行了字符串值的比较。如下是:String.equals方法

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

 如下例: 

public static void compare(){
        String s1 = "abc";
        String s2 = new String("abc");
        String s3= "abc";
        System.out.println(s1 == s2); // false
        System.out.println(s1 == s3); // true
        System.out.println(s1.equals(s2)); // true
    }

上述的例子原理如下:

1、s1 == s2 为false:s1是直接赋值了abc常量,该常量存储在常量池中;s2指向的是堆里面的new String对象,然后new String对象指向了abc,那么s1和s2指向都不是同一个东西,自然不相等;

2、s1 == s3 为true,是因为s1和s3都指向了abc常量池的位置;

3、s1.equals(s2)为true,是因为String重写了equals方法,比较的只是字符串的值,并不是引用地址,因此是true都是abc。

内存结构如下(JDK7):(s1==s3, s2 != s1 or s3)

 

2.2 String的“+”

String与String之间,String对象与常量字符串、常量字符串与常量字符串之间是如何完成字符串拼接的。

public static void append() {
        String s1 = "ab";
        String s2 = "abc";
        String s3 = s1 + "c";
        System.out.println(s3 == s2); // false

        final String s4 = "ab";
        final String s5 = "c";
        String s6 = s4 + s5;
        System.out.println(s6 == s2); // true
    }

(1) “+” 编译器会做特殊处理,如果是s1 + "c",实际上上会新建一个StringBuilder对象,然后append("c")字符,然后toString返回一个新的字符串(new StringBuidler("ab").append("c").toString()的效果,因此s3指向的实际上是一个堆中的StringBuilder对象;那么s2 != s3;(下图摘自字节指令)

 64: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      67: ldc           #12                 // String c
      69: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      72: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

(2)那为啥s6 == s2,因为final关键字标识了s4和s5,他们在编译期就写入到了常量池,然后s4+s5就会在编译期就直接拼接好,写入到常量池,最后就是一个常量池,等价于s6=“abc”;

(3)内存模式:

三、intern方法的理解

介绍String的时候,就不得不介绍intern方法了,如下图:String.intern是一个native方法。

有啥用呢???----- Jdk1.7以前之后又区别,例如:

public static void internTest(){
        String s1 = new StringBuilder().append("String").append("Test").toString();
        System.out.println(s1.intern() == s1);

        String s2 = "hello";
        String s3 = new StringBuilder().append("hel").append("lo").toString();
        System.out.println(s3.intern() == s3);
    }

3.1 jdk 1.7之前版本(例如:JDK1.6)

调用String.intern方法,首先常量池在方法区。

如果常量池内没有字符串,那么会将字符串复制到常量池,并返回指向当前字符串常量的引用;如果常量池中已存在,那么返回常量池该字符串的引用。

如果在jdk1.6中执行,结果会是:false,false,这是为啥呢?

(1)StringTest在常量池中不存在,因此会复制StringTest字符串在常量池中,这个时候返回的是这个字符串的引用,那就是在堆中的StringBuilder;s1.intern != s1

(2)java在常量池早已存在,这个时候返回的s3.intern()指向常量池中的hello,s3 != s3.intern()

3.2 jdk 1.7及以后版本

调用String.intern方法,常量池在堆中。(true、false)

如果常量池没有字符串,那么会将字符串的引用复制到常量池中,并返回指向当前字符串常量的引用;如果常量池中已存在,那么返回常量池该字符串的引用;

发现了一个区别没有,如果没有某个字符串,是在常量池中写入了:字符串的引用,而不是字符串,因为都在堆区,目的是节省空间,就是因为这个变动,导致了版本的差异。

(1)StringTest常量池不存在,那么常量池中会存放一个StringTest字符串引用,指向StringTest字符串,而不是StringTest字符串,也就是:s1.intern() = s1,指向同一个对象;

(2)这个时候s3.intern()调用的时候常量池中存在hello字符串,那么返回常量池中的java字符串引用,即:s3.intern() --> hello,那么s3 --> StringBuilder,那么s3 != s3.intern()

  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值