基本数据类型的包装类
Java将基本数据类型值封装成了对象。封装成对象有什么好处?——这样可以提供更多操作基本数值的功能
一般我们对数值的基本操作就是通过运算符来进行运算,但是如果我们想要获得一个数值的二进制,这个就是对数值的更多操作,需要将这些功能封装到对象中。
基本数据类型及其包装类分别为:
byte -> Byte; short -> Short; int -> Integer; long -> Long; float -> Float;
double -> Double; boolean -> Boolean; char -> Character;
以 Integer 为例,在对变量赋值时,Integer类进行了两步操作:
加包:Integer a = Integer.valueOf(10);
拆包:int a = a.intValue();
加包:
即 Integer a = 10;
相当于 Integer a = Integer.valueOf(10);
拆包:
即 int b = a;
相当于 int b = a.intValue();
针对基本数据类型的包装类采用 == 比较是否相等:
Integer内部缓存:-128~127;
Integer部份源码:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
public int intValue() {
return value;
}
如果 -128 < a,b <127 a == b 为 true;
如果a,b不在缓存中,则为 new 新Integer,则a == b 为false;
public class TestDemo{
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
System.out.println(a == b);
}
}
结果:
public class TestDemo{
public static void main(String[] args) {
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);
}
}
结果:
原因:Integer类中存在内部缓存:大小为 -128~127;当变量初始化的值在其中时,底层会直接将缓存中的地址赋给变量,所以在 == 比较时两者地址值是一样的,显示true;当变量初始化的值不在其中时,底层会new出新的Integer对象,将其赋值给变量,只要new对象,必有新内存的开辟,两者存储地址不同。在 == 比较时会显示flase;
String类
让我们先看看如何查看Java运行过程的变量值和包装类
javap -verbose用于查看java文件运行过程中的变量值以及包装类
1. 谈谈对String类的理解
String类:->字符串常量池(>= JDK1.7)
为了减少字符串常量在JVM中的创建。引用字符串常量池:
遇到字符串常量,先去字符串常量池中查找,找到返回常量的地址赋值给str1.如果没找到,给字符串常量池中添加一个新的常量,返回该地址。
-
String类 底层数据结构:<= JDK1.8 char[]
-
字符串比较方法:equals比较
== 比较字符串常量取值的地址
equals-> 字符串(char[]) -
字符串拼接方式:
- 常量+常量 -> 编译器优化 “tu”+“lun” ->“tulun”
- 变量+常量 -> String str4 = str+“lun”;
(1) StringBuilder s = new StringBuilder();
(2) s.append(str);//字符串拼接操作append
(3) s.append(“lun”);//s->所指对象"tulun" 对象类型StringBuilder
(4) String str4 = s.toString();
public class StringTest {
public static void main(String[] args) {
String str = "ab";
String str1 = "abc";
String str2 = "ab"+"c";
String str3 = new String("abc");
String str4 = str+"c";
System.out.println(str1 == str2);//true
System.out.println(str2 == str3);//false
System.out.println(str2 == str4);//false
System.out.println(str3 == str4);//false
}
}
由此可见,当常量 + 常量时,编译器对字符串进行了优化,发现常量池中已经存在该字符串,因此将常量池中该值的地址直接赋值给新对象,并未进行新内存的开辟,字符串存储地址未发生改变,所以显示true;而在变量 + 常量时,编译器使用StringBuilder 生成新对象,通过new对象开辟了新内存,其与原变量的地址不同,因此,显示false;
2. 谈谈String 和 StringBuilder 和 StringBuffer之间的区别
- 拼接方式:
String +
StringBuilder 、StringBuffer -> .append() - String 变量+常量 -> StringBuilder -> 效率考虑
(1)
String str = "";
for(int i=0;i<10000;i++){
str = str+"1";//new append() toString
}
(2)
StringBuilder s = new StringBuilder();
for(int i=0;i<10000;i++){
s.append("1");
}//s.toString()
public class StringTest {
public static void main(String[] args) {
long begin1 = System.currentTimeMillis();
String str = "";
for(int i=0;i<10000;i++){
str = str+"1";//new append() toString
}
long end1 = System.currentTimeMillis();
System.out.println(end1-begin1);
long begin2 = System.currentTimeMillis();
StringBuilder s = new StringBuilder();
for(int i=0;i<10000;i++){
s.append("1");
}
long end2 = System.currentTimeMillis();
System.out.println(end2-begin2);
StringBuffer stringBuffer = new StringBuffer();
for(int i=0;i<10000;i++){
stringBuffer.append("1");
}
}
}
结果:
由此可见使用String + 号拼接效率明显低于StringBuilder,因此,如果在进行字符串拼接时,变量+常量的情况下,String的+拼接会new StringBuilder 对象,此时如果拼接次数过多,应该选择StringBuilder,让其在一开始new对象,而不是每拼接一次new一次对象。这样可以提高程序执行的效率。
- String StringBuilder -> 多用在单线程环境
StringBuffer(synchronized) -> 多用在多线程环境
3. == 和 equals在字符串比较上的区别:
1) 对于==,比较的是值是否相等
- 如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
- 如果作用于引用类型的变量,则比较的是所指向的对象的地址。
2) 对于equals方法,
- 注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象。
- 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;
- 诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
而在字符串的比较上, == 比较的是两个字符串的地址是否相等,如果想比较字符串中的内容是否相等,此时应该使用equals。