public final class String implements java.io.Serializable, Comparable, CharSequence { /** The value is used for character storage. */ private final char value[]; private int hash; // Default to 0 public String() { //无参构造器 this.value = new char[0]; } public String(String original) { this.value = original.value; this.hash = original.hash; }/*****传入一个字符数组的构造函数,使用java.utils包中的Arrays类复制******/ public String(char value[]) { this.value = Arrays.copyOf(value, value.length); }/*******传入一个字符串数字,和开始元素,元素个数的构造函数******/ public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = Arrays.copyOfRange(value, offset, offset+count); }/*********************类似方法不介绍了**************************/ public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } } 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; }/***********s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]*********/ public int hashCode() { int h = hash;/***如果hash没有被计算过,并且字符串不为空,则进行hashCode计算*****/ 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; }
情景一:字符串池 * JAVA虚拟机(JVM)中存在着一个字符串池,其中保存着很多String对象; * 并且可以被共享使用,因此它提高了效率。 * 由于String类是final的,它的值一经创建就不可改变。 * 字符串池由String类维护,我们可以调用intern()方法来访问字符串池。 /***intern方法是Native调用,它的作用是在方法区中的常量池里通过equals方法寻找等值的对象,如果没有找到则在常量池中
开辟一片空间存放字符串并返回该对应String的引用,否则直接返回常量池中已存在String对象的引用。*****/ public native String intern();
举例:
String a = "abc";String b = new String("ab1").intern();if ( a == b ) { System.out.println("a == b"); } else { System.out.println("a不等于b");}打印出:a == b
//String的compareTo其实就是依次比较两个字符串ASC码。如果两个字符的ASC码相等则继续后续比较,否则直接返回两个ASC的差值。如果两个字符串完全一样,则返回0public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length; int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } return len1 - len2; }
1. 什么是不可变类
所谓不可变类,就是创建该类的实例后,该实例的属性是不可改变的,提供的包装类和java.lang.String类都是不可变类。当创建它们的实例后,其实例的属性是不可改变的。
需要注意的是,对于如下代码
String s="abc";s="def";
你可能会感到疑惑,不是说String是不可变类吗,这怎么可以改变呢,平常我也是这样用的啊。请注意,s是字符串对象的”abc”引用,即引用是可以变化的,跟对象实例的属性变化没有什么关系,这点请注意区分。
2.String类被设计成不可变的原因
- 字符串常量池的需要
- 允许String对象缓存HashCode
Java中String对象的哈希码被频繁地使用, 比如在hashMap 等容器中。
字符串不变性保证了hash码的唯一性,因此可以放心地进行缓存.这也是一种性能优化手段,意味着不必每次都去计算新的哈希码.
-
安全性:String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。
- 线程安全:因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
3. 如何实现一个不可变类
既然不可变类有这么多优势,那么我们借鉴String类的设计,自己实现一个不可变类。
不可变类的设计通常要遵循以下几个原则:-
- 将类声明为final,所以它不能被继承。
- 将所有的成员声明为私有的,这样就不允许直接访问这些成员。
- 对变量不要提供setter方法。
- 将所有可变的成员声明为final,这样只能对它们赋值一次。
- 通过构造器初始化所有成员,进行深拷贝(deep copy)。
- 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝。