• Java中的String介绍


    一、概述

      String是代表字符串的类,本身是一个最终类,使用final修饰,不能被继承。

    二、String字符串的特征

           1. 字符串在内存中是以字符数组的形式来存储的。

             示例如下,可以从String的底层源码中看到。

        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final char value[];
    
        /** Cache the hash code for the string */
        private int hash; // Default to 0
    
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;
    
        /**
         * Class String is special cased within the Serialization Stream Protocol.
         *
         * A String instance is written into an ObjectOutputStream according to
         * <a href="{@docRoot}/../platform/serialization/spec/output.html">
         * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
         */
        private static final ObjectStreamField[] serialPersistentFields =
            new ObjectStreamField[0];
    
        /**
         * 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;
        }
    ...
    }

           2.因为字符串是常量,所以本身是存储在方法区的常量池中。只要字符串的实际值一样,那么用的就是同一个字符串-->字符串是一个常量,字符串是被共享的。直接使用字符串赋值时,在常量池中创建一个字符串对象,然后将栈中的引用指向常量池中的对象。   

      例如:

            String str = "abc";
            //重新创建一个地址,使str指向该地址,栈内存直接指向方法区
            str = "def";
            //在方法区中查找,如果存在,再次指向原地址
            str = "abc";
            //在方法区中查找,如果存在,新对象也指向原地址
            String str2 = "abc";
            //栈内存指向堆内存,堆内存指向方法区
            String str3 = new String("abc");
            System.out.println(str == str2); //true  
            System.out.println(str == str3);
            

           其中,str和str2的地址就是相同的。

         当使用new关键字创建String对象时,先在常量池中创建一个字符串常量对象,然后再在堆中new一个字符串对象,将该对象的地址指向常量区;然后在栈中创建一个引用,指向堆中的对象。

    String str3 = new String("abc");

       相当于在内存中创建了两个对象。其内存结构图如下所示

         

          3. 如果需要拼接多个字符串,建议使用StringBuilder。因为使用StringBuilder拼接一次只产生一个新的对象,而使用+要产生3个对象。 具体示例如下;

        

            String[] arr = new String[100];
            String result = "";  //1个对象:共301个
            for(int i = 0; i < 100000; i++) {
                //result = new StringBuilder(result).append(str).toString();
                result += arr[i]; //没拼接一次,产生3个对象
            }
            
            //共:102个对象
            //产生一个对象
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i < 100000000; i++) {
                //每拼接一次,创建一个对象;一共产生了100个对象
                sb.append("a");
            }
            
            result = sb.toString(); //1个对象

          4. String类中,提供了一系列的字符串操作方法,但是都不改变原来字符串,都是产生一个新的字符串。

            例如查看获取子串函数的源码

     public String substring(int beginIndex, int endIndex) {
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            if (endIndex > value.length) {
                throw new StringIndexOutOfBoundsException(endIndex);
            }
            int subLen = endIndex - beginIndex;
            if (subLen < 0) {
                throw new StringIndexOutOfBoundsException(subLen);
            }
            return ((beginIndex == 0) && (endIndex == value.length)) ? this
                    : new String(value, beginIndex, subLen);
        }

            5. String字符串“+”在编译时和运行时的区别

            预编译是指编译器会在编译时检测是否存在字符串字面量,如果有字面量相加的情况,会提前将字面量字符串进行合并并存储到常量池中。   

    /**
     * 继续-编译期无法确定
     */
    public void test5(){
        String str1="abc";   
        String str2="def"; 
        String str3 = "abc"  +"def"
        String str4 = str1 + str2;
        System.out.println("===========test============");
        System.out.println(str4 == str3 ); //false
    }

      返回结果分析:因为str4指向堆中的"abcdef"对象,而"abcdef"是字符串池中的对象,所以结果为false。JVM对String str="abc"对象放在常量池中是在编译时做的,而String str4= str1+str2是在运行时刻才能知道的。new对象也是在运行时才做的。而这段代码总共创建了6个对象,字符串池中两个、堆中三个。+运算符会在堆中建立起来两个String对象,这两个对象的值分别是通过StringBuilder创建"abc"和通过append方法创建"abcdef",最后通过toString方法再建立对象str4,然后将"abcdef"的堆地址赋给str4,而堆中的“abcdef”地址指向常量池中的地址。

    步骤: 
    1) 栈中开辟一块空间存放引用str1,str1指向池中String常量"abc"。 
    2) 栈中开辟一块空间存放引用str2,str2指向池中String常量"def"。 
    3) 栈中开辟一块空间存放引用str3,str3指向常量池中String常量“abcdef”。
    4) str1 + str2通过StringBuilder的最后一步toString()方法还原一个新的String对象"abcdef",因此堆中开辟一块空间存放此对象。
    5) 引用str4指向堆中(str1 + str2)所还原的新String对象。 
    6) str4指向的对象在堆中,而常量str3对应的"abcdef"在池中,输出为false。

  • 相关阅读:
    类的使用(基础)
    <cf>Two Problems
    <cf>Walking in the Rain
    一些程序的整理
    <codeforces>Little Elephant and Sorting
    HDU 1172 猜数字(枚举)
    HDUOJ 1879 继续畅通工程
    error C2679: binary '<<' : no operator defined which takes a righthand operand of type 'class std::basic_s
    HDUOJ 1198 Farm Irrigation(并查集)
    HDU 1222 Wolf and Rabbit
  • 原文地址:https://www.cnblogs.com/chhyan-dream/p/10733834.html
Copyright © 2020-2023  润新知