JAVA--String

      在编程中,字符串是最常用的一种数据格式,这里以JAVA8为例,主要讲解一下源码中的重点。

String

public final class String
  implements Serializable, Comparable<String>, CharSequence
{
  private final char[] value;
  private int hash;
  private static final long serialVersionUID = -6849794470754667710L;
  private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
  public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(null);
  
}
  
  • 主要特点,也是最重要的特点,String被final修饰,表名String类型不能被继承。即每次对字符串的操作都是产生一个新的对象,而不是在原先对象上进行操作。
  • 成员变量value的类型为字符数组,用于存储值,特点:查找快,增删慢。简单的理解就是数组有下标,查询时根据下标直接查找,可以称为“直接地址索引(个人见解)”。增删操作,都伴随着下标的维护。
  • String是线程安全的,这个正式因为他的不可变性。
          了解过JVM体系结构的都知道常量池,常量池简单的可以分为静态常量池与运行时常量池,这里重要说一下字符串常量池
          字符串常量池存在于运行常量池中(注意在JDK1.7是已经转移到堆中),字符串常量池的存在使JVM提高了性能并且减少了内存开销。
          每当我们使用字面量创建一个字符串对象时(String s=“demo”),JVM首先会检查字符串常量池,如果在常量池中已存在该字符串,那么就将该字符串对象的地址赋值给引用‘s’,如果字符串不存在与常量池中,就会实例化该字符串并且放在常量池中,并将该字符串对象的地址赋值给引用‘s’。
          每当我们使用关键字new(String s = new String(“1”))创建字符串常量时,JVM首先会检查字符串常量池,如果在常量池中已存在该字符串,直接在堆中复制该对象的副本,然后就将堆中字符串对象的地址赋值给引用‘s’,如果字符串不存在与常量池中,就会实例化该字符串并且放在常量池中,在堆中复制该对象的副本,并将堆中字符串对象的地址赋值给引用‘s’。
          由于String的不可变性,我们可以十分的肯定在常量池中不存在相同的字符串。
    下面是String的一个方法实现:
//截取字符串
public String substring(int paramInt1, int paramInt2)
  {
    if (paramInt1 < 0) {
      throw new StringIndexOutOfBoundsException(paramInt1);
    }
    if (paramInt2 > value.length) {
      throw new StringIndexOutOfBoundsException(paramInt2);
    }
    int i = paramInt2 - paramInt1;
    if (i < 0) {
      throw new StringIndexOutOfBoundsException(i);
    }
    																//注意这里
    return (paramInt1 == 0) && (paramInt2 == value.length) ? this : new String(value, paramInt1, i);
  }

      从最后一句可以看到,最初传入的String并没有改变,其返回的是一个new String(),即创建一个新的字符串对象。其他的String的方法也是同样如此,不会改变原字符串,这也是String的不可变性。
String的经典案例
      关于"==和equals"
A:对于==,如果作用于基本数据类型的变量(boolean,byte,char,short,int,long,double,float),则比较的是存储的‘’;如果作用于引用类型String,则比较的是指向对象的地址(即是否指向的是同一个对象)。
B:equals方法是基类object中的方法,所以所有继承object的类都会拥有该方法。在object类中,equals比较的是两个对象的引用是否相等。

public class Object
{
  private static native void registerNatives();
  
  public final native Class<?> getClass();
  
  public native int hashCode();
  //可以看到这里的equals方法,比较的是对象的引用
  public boolean equals(Object paramObject)
  {
    return this == paramObject;
  }
}

C:对于equals方法,不能作用于基本数据类型变量。如果没有对equals进行重写,则比较的是基本数据变量的所指向对象的地址;String类对equals进行了重写,用来比较所指向的字符串对象所存储的值是否相等。其他的一些类,如Date,Integer等都对equals进行了重写用来比较所指向的字符串对象所存储的值是否相等。

//String对equals方法进行了重写
  public boolean equals(Object paramObject)
  {
    if (this == paramObject) {
      return true;
    }
    if ((paramObject instanceof String))
    {
    //下面进行循环比较每个char值是否相等
      String str = (String)paramObject;
      int i = value.length;
      if (i == value.length)
      {
        char[] arrayOfChar1 = value;
        char[] arrayOfChar2 = value;
        for (int j = 0; i-- != 0; j++) {
          if (arrayOfChar1[j] != arrayOfChar2[j]) {
            return false;
          }
        }
        return true;
      }
    }
    return false;
  }

例子:

public class StringDemo1 {
	public static void main(String[] args) {
		/**
		 * 字符串常量池
		 */
		String str1="demo";  //在常量池创建一个对象 demo
		String str2="demo";	//常量池已经存在对象“demo”,所以不再创建对象。
		System.out.println("str1==str2:"+(str1 == str2));//(引用类型)true 指向的是同一个对象
		System.out.println("str1.equals(str2):"+(str1.equals(str2)));//true,值相等。
		/**
		 * 关键字new String()
		 */
		String str3 = new String("abc");//创建两个对象(字符串池,堆中),一个引用(栈中)
		String str4 = new String("abc");//字符串池已有对象,所以只在堆中创建一个。
		System.out.println("str3==str4:"+(str3 == str4));//false str3和str4指向的地址不同(栈区的地址不同)
		System.out.println("str3.equals(str4):"+(str3.equals(str4)));//true str3和str4值相等
		System.out.println("str1==str3:"+(str1 == str3));//false str1在栈中,str3在堆中
		System.out.println("str1.equals(str3):"+(str1.equals(str3)));//true 值相等
	}
}

      所以String被设计为不可变和不被继承是为了效率以及安全。只有当字符串不可变时,字符串池才有可能实现,同时也可以在运行时节省很多堆空间,因为不同的字符串变量都指向的是字符串池的同一个对象。假若字符串允许改变,那么就会出现改变一个对象,其他指向这个对象的都会随之改变。
      字符串的不可变性使得同一字符串对象被多个线程共享,所以保障了多线程的安全性。
(未完待续)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值