• Java基础:String不可变性和final修饰


    转载请注明出处: jiq•钦's technical Blog - 季义钦


    String的不可变性

    Java规定String是不可变的(immutable)。事实上这个不可变具备两层含义:


    1 内容不可变

    不论什么看起来改动它们的操作。实际上都是又一次new出一个对象。

    String s = new String("111");
    String newS = s;
    newS = "dsfsd";
    System.out.println(s);  //111

    假设不能做到内容不可变。在两个引用同一时候指向一个内存的时候,改动当中一个也会影响到另外一个。


    2 实现方式不可变

    使用String类型变量时,其不能表现出其他行为。这就是为什么String类须要用finalkeyword来进行修饰的原因,设想一下String不用final修饰。则其能够被继承并重写方法:

    /**
     * 模拟非final的String
     * @author jiyiqin
     *
     */
    class MyString{
        public char value[] = new char[10];
        MyStringtoLowerCase(MyString s)
        {
           System.out.println("将s转换为小写,但不改变原有s");
           char newValue[] = new char[s.value.length];
           for(int i = 0; i < s.value.length; i++)
               newValue[i]= LowerUtils.Lower(s.value[i]);
           MyStringnewString = newMyString();
           newString.value = newValue;
           return newString;
        }
    }
     
    /**
     * 继承MyString,将其toLowerCase重写
     * @author jiyiqin
     *
     */
    class OverrideString extends MyString{
        MyStringtoLowerCase(MyString s)
        {
           System.out.println("直接改动传递进来的s的内存。将s转换为小写");
           for(int i = 0; i < s.value.length; i++)
               s.value[i] = LowerUtils.Lower(s.value[i]);      
           return s;
        }
    }
     
    public classFinalStringTest { 
        /**
         * 測试函数,java设计String是不可变的
         * 所以必须将String修饰为final。否则其
         * 一旦被继承。相似以下这样的调用时。若传递
         * 进来的參数是其被继承的子类。调用的就是
         * 被重写的方法,这个重写的方法可能会破坏
         * String的不可变特性。
         * @param s
         * @return
         */
        static MyStringLowerCusString(MyString s)//多态
        {     
           return s.toLowerCase(s);
        }
       
        public static void main(String[] args) {
           OverrideStringss = newOverrideString();
           LowerCusString(ss);//传入重写后的字符串类    
           LowerUtils.testFinalClass();
        }
    }

    能够看到LowerCusString方法想要的是MyString对象,可是其子类能够传递进来使用,调用之后发现传递进来的对象s的内容被改动了,表现出和父类不同的行为!!

    同样地。应用程序能够编写新的String类,改动hasCode方法,让内容同样的String对象返回的hashCode不同,这样HashMap在使用String类型变量作为Key的时候,发现同样内容的Key居然哈希到不同的位置。

    除了实现方法不可改动这个原因。将String修饰为final另一个优点就是效率。

    String其他特性

    除了上面讲的不可变性。String还其具备以下特性:

    特性1:编译时和执行时差别

    u  编译时能确定的String对象都放入常量池

    u  执行时才干确定或者new出的String对象都放入堆


    特性2:hasCode唯一性

    两个内容同样的String对象hashCode返回值一定同样

    以下通过一个样例总结一下这两个特性:

    final String tmp = "ji"; //常量池、编译时确定
    String tmp2 = "ji"; //常量池、编译时确定  
    String s1 = "jiyiqin"; //常量池、编译时确定
          
    String s2 = "jiyiqin"; //常量池、编译时确定
    String s3 = "ji" + "yiqin"; //常量池、编译时确定
    String s4 = tmp + "yiqin";  //常量池、编译时确定(final一旦初始化就不可变)  
    String s5 = tmp2 + "yiqin";//堆、执行时确定
    String s6 = new String("jiyiqin");//堆、执行时确定
          
    System.out.println(s1.hashCode());
    System.out.println(s2.hashCode());
    System.out.println(s3.hashCode());
    System.out.println(s4.hashCode());
    System.out.println(s5.hashCode());
    System.out.println(s6.hashCode()); //所有同样
          
    System.out.println(s1 == s2); //true。指向常量池中同样内存
    System.out.println(s1 == s3); //true,指向常量池中同样内存
    System.out.println(s1 == s4); //true,指向常量池中同样内存
    System.out.println(s1 == s5); //false,一个指向堆一个指向常量池
    System.out.println(s1 == s6); //false,一个指向堆一个指向常量池
    System.out.println(s5 == s6); //false,指向堆中不同内存区域

    Double、Long、Integer

    对于String的不可变性(包含内容不可变和实现方式不可变),以及hashCode唯一性,Double、Long、Integer也是一样的。

    不同的是它们没有执行时和编译时的差别,都是在堆上分配内存。

  • 相关阅读:
    js 字符串转化成数字
    web项目中各种路径的获取
    个人作业——软件工程实践总结作业
    Beta 答辩总结
    Beta 冲刺 (7/7)
    Beta 冲刺 (6/7)
    Beta 冲刺 (5/7)
    Beta 冲刺 (4/7)
    Beta 冲刺 (3/7)
    软件产品案例分析(团队)
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/7220638.html
Copyright © 2020-2023  润新知