String类详解01

    工具类在我们开发中经常使用,这里好好掌握总结起来!

String

 描述:

    API中,String类代表字符串,且字符串是常量,值在创建之后不能更改,

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

java。lang.String :字符串类型

* Strings are constant; their values cannot be changed after they
 * are created.

分析源码:

  1)成员变量

/** String的属性值 */  
    private final char value[];

    /** The offset is the first index of the storage that is used. */
    /**数组被使用的开始位置**/
    private final int offset;

    /** The count is the number of characters in the String. */
    /**String中元素的个数**/
    private final int count;

    /** Cache the hash code for the string */
   /**String类型的hash值**/
    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];

String的成员变量

    上述源码中可以看出:String底层是使用者一个字符数组维护,且String类的值时final类型的,不能被改变,因此只要一个值改变就会生成一个新的String类型对象,存储String数据也不一定从数组的第0个元素快开始,而是从offset所指的元素开始 

  2)String的构造方法

String() 
          初始化一个新创建的 String 对象,使其表示一个空字符序列。 
String(byte[] bytes) 
          通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。 
String(byte[] bytes, Charset charset) 
          通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。  
String(byte[] bytes, int offset, int length) 
          通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。 
String(byte[] bytes, int offset, int length, Charset charset) 
          通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。 
String(byte[] bytes, int offset, int length, String charsetName) 
          通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String。 
String(byte[] bytes, String charsetName) 
          通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。 
String(char[] value) 
          分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。 
String(char[] value, int offset, int count) 
          分配一个新的 String,它包含取自字符数组参数一个子数组的字符。 
String(int[] codePoints, int offset, int count) 
          分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。 
String(String original) 
          初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。 
String(StringBuffer buffer) 
          分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。 
String(StringBuilder builder) 
          分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列。

  String的构造方法创建方式

①  直接赋值(方法区的常量池)

扫描二维码关注公众号,回复: 1840187 查看本文章
String zjc = "hello";

②  构造方法(堆内存)

String zjc = new String("hello");

  实例对比:

package test;

import javax.sound.midi.Soundbank;

/**
 * 
 * @author zjc
 *
 */
public class StringTest {

	/*
	 * java.lang.String:是字符串类型 ①字符串一旦创建不可再更改,。"abc"字符串对象一旦创建,不可再更改为”"abcd"
	 * 
	 */

	public static void main(String[] args) {
		
		// 创建一个'abc'字符串,改对象的内存地址,让zjc变量存储;
		// zjc是一个引用,zjc指向"abc"对象
		String zjc = "abc";
		String zjc1 = new String("abc");
		String zjc2 = zjc1;//引用地址,zjc2直接指向zjc1堆内地址
		String zjc3 = "abc";

		/**
		 * 基本数据类型:比较的是基本数据类型的值是否相同 引用数据类型:比较的是引用数据类型的地址值是否相同
		 * 因此:String类对象==标记,比较的是地址而不是内容
		 */
		System.out.println(zjc == zjc1);
		System.out.println(zjc == zjc2);
		System.out.println(zjc2 == zjc1);
		System.out.println(zjc == zjc3);
	}
}

内存分析:

             1>String str = new String("Hello"); 当我们再一次的new一个String对象时:

       

             2>z字符串常量池

    在字符串中,如果采用直接赋值的方式(String str="hello")进行对象的实例化,则会将匿名对象“hello”放入对象池每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象,因此所有直接赋值的String对象,如果利用相同的“hello”,则String对象==返回true;例如:手工将对象入池---->

public class TestString {
    public static void main(String args[]){
     String str =new String("ZJC").intern();//对匿名对象"zjc"进行手工入池操作
     String str1="ZJC";
     System.out.println(str==str1);//true
    }
}

  String类被final修饰,官方注释说明创建后不能被改变,但是为什么String要使用final修饰呢? 

区别:

1)直接赋值(String str = "hello"):只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。

2)构造方法(String str=  new String("hello");):会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要通过public  String intern();方法进行手工入池。

3)避免空指向: == 和public boolean equals()比较字符串的区别;==在对字符串比较的时候,对比的是内存地址,而equals比较的是字符串内容,在开发的过程中,equals()通过接受参数,可以避免空指向

    

String str = null;
      if(str.equals("hello")){//此时会出现空指向异常
        ...
      }
      if("hello".equals(str)){//此时equals会处理null值,可以避免空指向异常
         ...
      }

public class StringTest {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a==b);  //true
        System.out.println(a.equals(b));  //true
        System.out.println(a==c);  //false
        System.out.println(a.equals(c));  //true
    }
}

 内存图:

    

分析:

    因为String太过常用,JAVA类库的设计者在实现时做了个小小的变化,即采用了享元模式,每当生成一个新内容的字符串时,他们都被添加到一个共享池中,当第二次再次生成同样内容的字符串实例时,就共享此对象,而不是创建一个新对象,但是这样的做法仅仅适合于通过=符号进行的初始化。  

  需要说明一点的是,在object中,equals()是用来比较内存地址的,但是String重写了equals()方法,用来比较内容的,即使是不同地址,只要内容一致,也会返回true,这也就是为什么a.equals(c)返回true的原因了。


好处:

        可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销

  我们的程序中大量使用了String字符串,有可能是出于安全性考虑

  大家都知道HashMap中key为String类型,如果可变将变的多么可怕

  当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失


猜你喜欢

转载自blog.csdn.net/z15732621582/article/details/80469892