zoukankan      html  css  js  c++  java
  • JavaSE 第二次学习随笔(String的坑 + ==)

    String 类是一个final类, 其内部是使用的 private final char value[]; 来存储内容, 其既可以当作一个基本类型来使用也可以当作一个类来使用;
    final 类(String): 被final修饰的类将不能被继承
    final char value[]: 不能修改String类型的对象的内容, 但是我可以修改他的引用指向啊~

    "==" 作用 : 判断引用是否指向堆内存的同一块地址

    equals()的作用: 比较是否值相等(先比较两个String的地址然后用instanceof比较类型, 然后遍历两个String的value[] 挨个值比较)

    hashcode()的作用: 根据String中的value[]计算出的散列值, 和它的地址一丁点毛关系都没有-_-|

    equals();

    /**
         * Compares this string to the specified object.  The result is {@code
         * true} if and only if the argument is not {@code null} and is a {@code
         * String} object that represents the same sequence of characters as this
         * object.
         *
         * @param  anObject
         *         The object to compare this {@code String} against
         *
         * @return  {@code true} if the given object represents a {@code String}
         *          equivalent to this string, {@code false} otherwise
         *
         * @see  #compareTo(String)
         * @see  #equalsIgnoreCase(String)
         */
        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;
        }
    

     

    hashCode();

    /**
         * Returns a hash code for this string. The hash code for a
         * {@code String} object is computed as
         * <blockquote><pre>
         * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
         * </pre></blockquote>
         * using {@code int} arithmetic, where {@code s[i]} is the
         * <i>i</i>th character of the string, {@code n} is the length of
         * the string, and {@code ^} indicates exponentiation.
         * (The hash value of the empty string is zero.)
         *
         * @return  a hash code value for this object.
         */
        public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                char val[] = value;
    
                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }
    

      

    针对String作为一个基本类型来使用:
     
    1。如果String作为一个基本类型来使用,那么我们视此String对象是String缓冲池所拥有的。
    2。如果String作为一个基本类型来使用,并且此时String缓冲池内不存在与其指定值相同的String对象,那么此时虚拟机将为此创建新的String对象,并存放在String缓冲池内。
    3。如果String作为一个基本类型来使用,并且此时String缓冲池内存在与其指定值相同的String对象,那么此时虚拟机将不为此创建新的String对象,而直接返回已存在的String对象的引用。
     
    针对String作为一个对象来使用:
     
    1。如果String作为一个对象来使用,那么虚拟机将为此创建一个新的String对象,即为此对象分配一块新的内存堆,并且它并不是String缓冲池所拥有的,即它是独立的。

    关于String的各种==判定相等问题:

    new 的对象一定是两块内存() 

    final String s1 = "123";这时 s1 已经可以当作一个常量(等同于 ["123"] ) 使用jvm对String的常量优化对它起作用(后边有例子)

    * 对于Integer来说, 数字范围在在一个字节范围内  
    * 后边再定义它,直接使用前边的        (Integer i = new Integer(100); Integer j = new Integer(100); i==j;-->>true )
    * 超过一个字节其会新建对象          (Integer i = new Integer(129); Integer j = new Integer(129); i==j;-->>true )

    Character ch1 = '1';			
    Character ch2 = '1';
    System.out.println("'1' == '1' " + (ch1 == ch2) );				//true
    ch1 = new Character('1');
    System.out.println( "new Character('1') == '1' " + (ch1 == ch2));		//false
    System.out.println("new Character('1').equals('1') " + ch1.equals(ch2));     //true
    		
    Character ch3 = '我';
    Character ch4 = '我';
    System.out.println("'我' == '我' " + (ch3 == ch4));				//true
    ch3 = new Character('我');
    System.out.println("new Character('我') == '我' " + (ch3 == ch4));		//true
    System.out.println("new Character('我').equals('我') "+ch3.equals(ch4));	     //true


    //这里由于Character是char的包装类, char是整数类型表示的所以在128前的 '1' 在常量池里 ch1 ch2 指向一个(常量池里的)对象, 而 '我' 则不在常量池里, ch3 ch4分别指向不同的对象
    //关于常量池, 下面有详细的解释

    String s1 = “abc”; 
    final String s2 = “a”; 
    final String s3 = “bc”; 
    String s4 = s2 + s3; 
    System.out.println(s1 == s4); 
    输出true,因为final变量在编译后会直接替换成对应的值,所以实际上等于s4=”a”+”bc”,而这种情况下,编译器会直接合并为s4=”abc”,所以最终s1==s4。

    (摘自 https://blog.csdn.net/soonfly/article/details/70147205 后面多点理解String.intern()是摘抄于此)

    String s1 = new String("1234");
    String s2 = new String("1234");
    System.out.println(s1 == s2);  //false
    String s3 = "1234";
    String s4 = "1234";
    System.out.println(s3 == s4); //true
    
    String s1 = new String("1234");
    String s2 = new String("12345");
    s1+="5";
    System.out.println(s1 == s2);//false
    
    String s1 = "12345";
    String s2 = "1234" + "5";
    String s3 = "1234";
    s3+=5;
    System.out.println(s1 == s2);//true
    System.out.println(s1 == s3);//false
    
    String s1 = new String("12345");
    String s2 = new String("1234") + "5";
    System.out.println(s1 == s2);//false
    
    String s2 = "1234" ;
    String s3 = s2 + "5";
    System.out.println(s1 == s3);//false
    

    String.intern();作用: (jdk1.7后)

      一、new String都是在上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用

      二、通过字面量赋值创建字符串(如:String str=”twm”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串;若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串

      三、常量字符串的“+”操作,编译阶段直接会合成为一个字符串。如string str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用

      四、对于final字段,编译期直接进行了常量替换(而对于非final字段则是在运行期进行赋值处理的)

      五、常量字符串和变量拼接时(如:String str3=baseStr + “01”;)会调用stringBuilder.append()在堆上创建新的对象

      六、JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池.  ( https://blog.csdn.net/soonfly/article/details/70147205 )

      为了加深我自己的印象(万一大佬找上门来, 怂的不行ing), 大佬的图我就不复制了 下边是我自己画哒, 画图真累...

    举例说明(不错这是大佬写的, 人家写的非常好我就复制过来了还是上边那个地址)

    String str2 = new String("str")+new String("01");
    str2.intern();
    String str1 = "str01";
    System.out.println(str2==str1);
    

    在JDK 1.7下,当执行str2.intern();时,因为常量池中没有“str01”这个字符串,所以会在常量池中生成一个对堆中的“str01”的引用(注意这里是引用 ,就是这个区别于JDK 1.6的地方。在JDK1.6下是生成原字符串的拷贝),而在进行String str1 = “str01”;字面量赋值的时候,常量池中已经存在一个引用,所以直接返回了该引用,因此str1和str2都指向堆中的同一个字符串,返回true。

    String str2 = new String("str")+new String("01");
    String str1 = "str01";
    str2.intern();
    System.out.println(str2==str1);
    

      将中间两行调换位置以后,因为在进行字面量赋值(String str1 = “str01″)的时候,常量池中不存在,所以str1指向的常量池中的位置,而str2指向的是堆中的对象,再进行intern方法时,对str1和str2已经没有影响了,所以返回false。

    有了对以上的知识的了解,我们现在再来看常见的面试或笔试题就很简单了: 
    Q:下列程序的输出结果: 
    String s1 = “abc”; 
    String s2 = “abc”; 
    System.out.println(s1 == s2); 
    A:true,均指向常量池中对象。
    
    Q:下列程序的输出结果: 
    String s1 = new String(“abc”); 
    String s2 = new String(“abc”); 
    System.out.println(s1 == s2); 
    A:false,两个引用指向堆中的不同对象。
    
    Q:下列程序的输出结果: 
    String s1 = “abc”; 
    String s2 = “a”; 
    String s3 = “bc”; 
    String s4 = s2 + s3; 
    System.out.println(s1 == s4); 
    A:false,因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。
    
    Q:下列程序的输出结果: 
    String s1 = “abc”; 
    final String s2 = “a”; 
    final String s3 = “bc”; 
    String s4 = s2 + s3; 
    System.out.println(s1 == s4); 
    A:true,因为final变量在编译后会直接替换成对应的值,所以实际上等于s4=”a”+”bc”,而这种情况下,编译器会直接合并为s4=”abc”,所以最终s1==s4。
    
    Q:下列程序的输出结果: 
    String s = new String(“abc”); 
    String s1 = “abc”; 
    String s2 = new String(“abc”); 
    System.out.println(s == s1.intern()); 
    System.out.println(s == s2.intern()); 
    System.out.println(s1 == s2.intern()); 
    A:false,false,true。
    

      

     

     

    由于它其实是由char[] 实现的所以我们来看看它的构造方法以便了解它的char[] 到底指向了哪里

    这是其中的三个构造方法,

    1. 我们可以看到 当输入的是一个String类型的时候, 他的value和hashcode 是直接赋值过来的 所以毫无疑问这时 两个new String("123") 的char[]是一个对象,
    2. 我们还看到单纯的new String()就是个"",
    3. 当new String(char[]); 时,String类会new 一个char[] 然后把你的char[] 复制过来(Arrays.copyOf(value, value.length)会返回一个new char[]),所以这里应该是两个char[] 对象 那么两个String对象肯定不同了.
    4.String 类对于它内部的char[]的一个原则就是坚决不让你得到它的引用, 任何你能接触到的关于它的char方法返回的要么是一个copy的char[] 要么就是一个char;这么做主要是为了保证String 的 final性 以保证它在多线程情况下程序的稳定(我猜的, 嘿嘿).

    /**
         * Initializes a newly created {@code String} object so that it represents
         * an empty character sequence.  Note that use of this constructor is
         * unnecessary since Strings are immutable.
         */
        public String() {
            this.value = "".value;
        }
    
        /**
         * Initializes a newly created {@code String} object so that it represents
         * the same sequence of characters as the argument; in other words, the
         * newly created string is a copy of the argument string. Unless an
         * explicit copy of {@code original} is needed, use of this constructor is
         * unnecessary since Strings are immutable.
         *
         * @param  original
         *         A {@code String}
         */
        public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
        }
    
        /**
         * Allocates a new {@code String} so that it represents the sequence of
         * characters currently contained in the character array argument. The
         * contents of the character array are copied; subsequent modification of
         * the character array does not affect the newly created string.
         *
         * @param  value
         *         The initial value of the string
         */
        public String(char value[]) {
            this.value = Arrays.copyOf(value, value.length);
        }
    
  • 相关阅读:
    JUnit4_2
    软件工程
    Java核心API需要掌握的程度
    VS2008工具箱不显示的解决方法
    SQLServer 2005中的Row_Number()分页
    关于UITableView的一点儿新认识
    二叉树的建立和遍历
    In App Purchases(IAP 应用程序內购买): 完全攻略 (转)
    [转]内存分配函数 _alloc/malloc/calloc
    ObjectiveC学习笔记
  • 原文地址:https://www.cnblogs.com/chinashenkai/p/9417480.html
Copyright © 2011-2022 走看看