Java基础提升2
今天是第二天,感觉有很多东西需要加强,每次看到那些技术大神洋洋洒洒的博客,内心除了崇拜,更多的是鄙视自己,感慨差距如此之大。我要弥补自己的差距。
今天内容是一篇关于基础类型与之对应包装类型。
Java基础数据类型总结
- 首先,我们必须明确的是,Java是一种强类型语言,因此,第一次声明变量必须说明数据类型,第一次变量赋值称之为变量的初始化。
- 按类型分,可以分为4类,字符类型char,布尔类型boolearn,整数类型byte,short,int,long,浮点数类型float,double。
数据类型 | 大小/位 | 分装类 | 默认值 |
---|---|---|---|
byte | 8 | Byte | 0 |
short | 16 | Short | 0 |
int | 32 | Integer | 0 |
long | 64 | Long | 0L |
float | 32 | Float | 0.0F |
double | 64 | Double | 0.0D |
char | 16 | Character | 空 |
boolean | 待定 | Boolean | false |
注意事项
- 慎用基本类型处理货币存储。如采用double常会带来差距,常采用BigDecimal、整型(如果要精确表示分,可将值扩大100倍转化为整型)解决该问题。
- 优先使用基本类型。原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合。
- 如果有线程安全的计算需要,建议考虑使用类型AtomicInteger、AtomicLong 这样的线程安全类。部分比较宽的基本数据类型,比如 float、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值。
- 关于boolean的长度是有争议的,不同条件下,存储会不同,具体官方文档的定义,boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its "size" isn't something that's precisely defined.
基本数据类型的转换
- boolean不可以转换为其他的数据类型
- 整数型,浮点类型,字符类型可以相互转换。
- 容量小的类型自动转换为大的类型,数据类型按容量大小排序为:
byte, short, char < int < long < float < double- byte, short, char 三种类型不会相互自动转换,在计算时,会首先转换为int类型
- 大的类型转换为小的类型,必须加上强制转换符,并且此时可能会造成精度降低或者溢出的问题
- 混合类型计算时,首先自动转换为最大的那个类型,再继续计算
- 浮点数默认类型是double类型;整数类型默认为int
基本类型和包装类型的区别
- 包装类是对象,拥有方法和属性,对象的调用都是通过引用对象的地址
- 包装类创建的是对象,拥有方法和属性,对象的调用都是通过引用对象的地址
- 包装类是引用传递,基本类型是值传递(其实本质都是传递一个值,只不过值的形式不同而已)
- 基本类型的值存储在栈中,对象存储在堆中。举个例子:
int a = 2;
int b = 2;
在常量区中,相同的常量只会存在一个。当执行第一句代码时。先查找常量区中有没有2,没有,则开辟一个空间存放2,然后在栈中存入一个变量a,让a指向2;执行第二句的时候,查找发现2已经存在了,所以就不开辟新空间了。直接在栈中保存一个新变量b,让b指向2。
- 对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。
- int的默认值为 0,而 Integer的默认值为 null,即Integer可以区分出未赋值和值为 0 的区别,int 则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为 0 的区别,则只能使用 Integer。
Java中的装箱与拆箱
- 先看基本操作
Java SE5之前的装箱的操作
Integer i = new Integer(5);
Integer i = 5; // 自动装箱
int n = i; //自动拆箱
- 那么是怎么实现的,请看代码
public class Main {
public static void main(String[] args) {
Integer i = 10;
int n = i;
}
}
从图中反编译的内容看,在装箱的时候是自动调用Integer的valueOf()方法,在拆箱的时候自动调用Integer的intValue()方法。因此,可以总结装箱和拆箱的实现过程:装箱的过程是通过调用包装类的valueOf()方法实现的,而拆箱过程是调用包装类的xxxValue()方法实现的。
常见的问题
先看代码
/**
* @ClassName: IntegerTest
* @Description: TODO
* @Author: kevin
* @Date: 2019-03-21 21:14
* @Version: 1.0
**/
public class IntegerTest {
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println(a == b);
System.out.println(c == d);
}
}
也许答案和自己想的不一样!
true
false
这里涉及到了IntegerCache类的实现,进入到Integer源码(JDK1.8版本)
从图中可以看到Integer在创建对象时,如果数值在[-128-127]之间,便指向IntegerCache.cache已经存在的对象的引用。
Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别
- 第一种方式不会触发自动装箱的过程;而第二种方式会触发;
- 在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。
/**
* @ClassName: IntegerTest2
* @Description: TODO
* @Author: kevin
* @Date: 2019-03-21 21:49
* @Version: 1.0
**/
public class IntegerTest2 {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d);
System.out.println(e==f);
System.out.println(c==(a+b));
System.out.println(c.equals(a+b));
System.out.println(g==(a+b));
System.out.println(g.equals(a+b));
System.out.println(g.equals(a+h));
}
}
当 "=="运算符的两个操作数都是包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。
第一个和第二个输出结果没有什么疑问。第三句由于 a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。对于a+h,先自动触发拆箱,就变成了int类型和long类型相加,这个会触发类型晋升,结果是long类型的,然后会触发装箱过程,就变成Long了。因此比较结果是true。
true
false
true
true
true
false
true
参考链接
深入剖析Java中的装箱和拆箱
int 和 Integer 有什么区别
Java支持的8种基本数据类型
Java基础类型总结
Java基本数据类型总结
java基本类型与包装类型区别
int和Integer的比较
int 和 Integer 有什么区别