/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
所以Integer类中只有构造器和getter方法,并没有setter方法,原因就是value被final修饰不可再更改。因此我们想要修改Integer类的值只能新建Integer对象
* @deprecated
* It is rarely appropriate to use this constructor. The static factory
* {
@link #valueOf(int)} is generally a better choice, as it is
* likely to yield significantly better space and time performance.
*/
@Deprecated(since="9")
public Integer(int value) {
this.value = value;
}
这种构造器现已不推荐使用,建议换成静态方法public static Integer valueOf(int i)
关于该方法将在常量池中进一步说明
* @deprecated
* It is rarely appropriate to use this constructor.
* Use {
@link #parseInt(String)} to convert a string to a
* {
@code int} primitive, or use {
@link #valueOf(String)}
* to convert a string to an {
@code Integer} object.
*/
@Deprecated(since="9")
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
将传入的字符串按10进制进行解释。如"123",返回value为123的Integer对象
实际上利用static在类加载时,预先为[-128,127]区间整数建立Integer对象,并在日后需要时将引用指向这些预加载的对象。
下面我们看一下public static Integer valueOf(int i)
的源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
当传入参数IntegerCache.low<=i<=IntegerCache.high
时,返回IntegerCache.cache[]
中的对象,否则在堆中新建实例。
接着我们再看Integer
中的静态内部类IntegerCache
的部分源码
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
可以看到在类加载时会预先为某个区间缓存。(这个区间就是[-128,127],为啥看不懂)
我们可以用如下代码进行测试
Integer a1 = -127;
Integer a2 = -127;
Integer b1 = 128;
Integer b2 = 128;
System.out.println(a1==a2);
System.out.println(b1==b2);
hashCode
/**
* Returns a hash code for an {@code int} value; compatible with
* {@code Integer.hashCode()}.
*
* @param value the value to hash
* @since 1.8
*
* @return a hash code value for an {@code int} value.
*/
public static int hashCode(int value) {
return value;
}
将Integer中保存的int值作为hash code
equals
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
调用equals()
判断相等时,首先先进行类型匹配判断,如果传入了非Integer的实例,则直接判定为false。
如
Integer i = 10;
Byte b = 10;
System.out.println("i == b " + i.equals(b));\\i==b false
之后比较内部保存的int变量。
Integer a1 = 10;
Integer a2 = new Integer(10);
System.out.println(a1==a2);//false
System.out.println(a1.equals(a2));//true
note:关于其中==与equals方法的不同这里不做说明,详情百度
sum
在java里没有重载操作符的,所以
Integer a1 = 10;
Integer a2 = new Integer(10);
System.out.println(20==(a1+a2)); //true
上面这段代码实际上进行了自动拆箱(见下),因为Integer是不能相加的。最后是两个int类型的数据判断,而非地址判断。
自动拆装箱是一个语法糖。所谓语法糖就是让你在写代码的时候少写点,后期编译器帮你加上。我们来看这一段程序
```java
public static void main(String[] args) {
Integer a1 = 10;
Integer a2 = new Integer(10);
System.out.println(Integer.valueOf(20)==(a1+a2));
}
```
通过javap指令查看字节码指令我们可以看到,实际运行(部分)时是这样的
```
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
```
执行`Integer a1 = 10;`实际调用了Integer类的静态方法`Integer.valueOf`进行装箱
在判断`Integer.valueOf(20)==(a1+a2)`时
```
28: invokevirtual #6 // Method java/lang/Integer.intValue:()I
31: aload_1
32: invokevirtual #6 // Method java/lang/Integer.intValue:()I
35: aload_2
36: invokevirtual #6 // Method java/lang/Integer.intValue:()I
39: iadd
```
调用`Integer.intValue()`方法进行拆箱,最后是俩个基本类型做比较
官方文档 Java SE8 String
/**
* The value is used for character storage.
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*
* Additionally, it is marked with {@link Stable} to trust the contents
* of the array. No other facility in JDK provides this functionality (yet).
* {@link Stable} is safe here, because value is never null.
*/
@Stable
private final byte[] value;
存储字符串内容。
note:同Integer一样,也是final修饰。表示String一经创建则不可变
/**
* The identifier of the encoding used to encode the bytes in
* {@code value}. The supported values in this implementation are
*
* LATIN1
* UTF16
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*/
private final byte coder;
编码类型。支持LATIN1和UTF16,其中UTF16编码一个字符占2位
/** Cache the hash code for the string */
private int hash; // Default to 0
hash值缓存。关于hash将在下文详细说明
String s1 = "abc";
String s2 = new String("abc");
和字符串相关的常量池有三个,分别是:.class文件中的常量池,运行时常量池和字符串常量池
1.常量池
存放编译期生成的字面量及符号引用,详情请调用javap指令看一下,这里只放个截图
2. 运行时常量池
位于方法区,在类加载之后由常量池转换而来,还附加了部分符号引用的直接引用
3. 全局字符串常量池
存放String实例的引用
接下来我们看一看这几个常量池的关系:
运行时常量池由常量池转化而来,在程序初始 全局字符串常量池 沿用了 运行时常量池
为了验证这一结论,笔者设计这么一段程序 如有错误,还请务必指出
public class C1{
public static String s0 = "abc";
public String s1 = "abc";
}
public class language {
public static String s0 = "abc";
public static void main(String[] args) {
C1 tmp = new C1();
String s1 = "abc";
String s2 = new String("abc");
System.out.println(C1.s0==s0); //ture
System.out.println(s1==s0); //true
System.out.println(s1==tmp.s1); //true
}
}
首先,C1.s0==s0
说明运行时常量池不是私有的,而是共享的。
其次,下面两句表明:对于已出现的字面量,将直接引用运行时常量池中的实例对象,而不是在首次运行时创建
而对于没有出现在运行时常量池中的字符串,如下
public static String s0 = "abc";
public static String s3 = "abcabc";
public static void main(String[] args) {
String s1 = "abc";
String s2 = new String("abc");
System.out.println((s1+s1)==s3); \\false
}
则新建对象加入全局字符串常量池
在1.8以前,对字符串的相加会被编译器转换为StringBuilder的append,然后toString返回字符串;
在1.8以后,则是调用了方法`makeConcatWithConstants`进行字符串的拼接,执行过程较为复杂,暂不说明如果看到记得喊我回来天坑
intern
Returns a canonical representation for the string object.
When the intern method is invoked, if the pool already contains a string equal to this {@code String} object as determined by the {@link #equals(Object)} method, then the string from the pool is returned. Otherwise, this {@code String} object is added to the pool and a reference to this {@code String} object is returned.
根据JDK源码的注释,intern方法用于向全局字符串常量池(以下简称常量池,并非代表class文件中的常量池)中添加String实例。
调用重写的equals()
方法判断是否相同。
如果在常量池中已有相同的文本,则返回常量池中的引用;如果没有,则向常量池中添加该对象的引用,并返回该对象的引用
下面用一段程序验证前半句
public class C1{
public static String s1 = "abcabc";
}
public static void main(String[] args) {
String s0 = "abc";
String s1 = s0 + s0;
System.out.println(s1==C1.s1); //false
String s2 =s1.intern();
System.out.println(s2==C1.s1); //true
System.out.println(s1==s2); //false
}
在类加载时,会将字面量添加到常量池中,而根据s1==C1.s1
可知s1此并不在常量池中,调用intern方法向常量池中添加,由于常量池中已存在该文本实例,因此返回了C1.s1
的引用
equals
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] value, byte[] other) {
if (value.length == other.length) {
int len = value.length >> 1;
for (int i = 0; i < len; i++) {
if (getChar(value, i) != getChar(other, i)) {
return false;
}
}
return true;
}
return false;
}
首先比较地址,之后比较类型,最后遍历是否相同
hashcode
public static int hashCode(byte[] value) {
int h = 0;
int length = value.length >> 1;
for (int i = 0; i < length; i++) {
h = 31 * h + getChar(value, i);
}
return h;
}
就一散列码,没啥好说的,如果有对系数31感兴趣的可以看看底下知乎的链接
subString
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = length() - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
if (beginIndex == 0) {
return this;
}
return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
: StringUTF16.newString(value, beginIndex, subLen);
}
public static String newString(byte[] val, int index, int len) {
if (String.COMPACT_STRINGS) {
byte[] buf = compress(val, index, len);
if (buf != null) {
return new String(buf, LATIN1);
}
}
int last = index + len;
return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16);
}
注意:String类是不可变的,因此创建字串是构建了一个新的String实例,而并非对原有String改动。中间借助了byte数组作为缓存实现对String的改动。
参考文章