数据类型,包装类
数据类型
常量
1 |
|
Java的数据类型分为两大类
Java 中有 8 种基本数据类型,分别为:
- 6 种数字类型:
- 4 种整数型:
byte
、short
、int
、long
- 2 种浮点型:
float
、double
- 4 种整数型:
- 1 种字符类型:
char
- 1 种布尔型:
boolean
。
这 8 种基本数据类型的默认值以及所占空间的大小如下:
基本类型 | 位数 | 字节 | 默认值 | 取值范围 |
---|---|---|---|---|
byte |
8 | 1 | 0 | -128 ~ 127 |
short |
16 | 2 | 0 | -32768(-2^15) ~ 32767(2^15 - 1) |
int |
32 | 4 | 0 | -2147483648 ~ 2147483647 |
long |
64 | 8 | 0L | -9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1) |
char |
16 | 2 | ‘u0000’ | 0 ~ 65535(2^16 - 1) |
float |
32 | 4 | 0f | 1.4E-45 ~ 3.4028235E38 |
double |
64 | 8 | 0d | 4.9E-324 ~ 1.7976931348623157E308 |
boolean |
1 | false | true、false |
char:Unicode编码的字符,或字符的整数编码,必须用单引号
float:默认值是0.0f;
double:默认值是0.0d;
基本类型字面值规则
- 整数字面值是int类型,如果右侧赋值超出int范围,需要做转型处理
- byte,short,char 三种比int小的整数,在自身范围内可以直接赋值。byte d=1+3 正确,1+3编译器会自动转成4
- 浮点数字面值是double;浮点数转成整数会直接舍弃小数点后位数。
- 字面值后缀,L长整型 D双精度浮点型 F单精度浮点型
- 字面值前缀,0b 二进制;0x 十六进制;0 八进制; \u char 类型十六进制
基本类型的类型转换
转换规则
范围小的类型向范围大的类型提升, byte、short、char 运算时直接提升为 int 。
byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double
自动转换
1 |
|
强制类型转换
将取值范围大的类型强制转换成取值范围小的类型
比较而言,自动转换是Java自动执行的,而强制转换需要我们自己手动执行
1 |
|
类型互转会出现什么问题吗?
**数据丢失:**当将一个范围较大的数据类型转换为一个范围较小的数据类型时,可能会发生数据丢失。例如,将一个 long 类型的值转换为 int 类型时,如果 long 值超出了 int 类型的范围,转换结果将是截断后的低位部分,高位部分的数据将丢失。
**数据溢出:**与数据丢失相反,当将一个范围较小的数据类型转换为一个范围较大的数据类型时,可能会发生数据溢出。例如,将一个 int 类型的值转换为 long类型时,转换结果会填充额外的高位空间,但原始数据仍然保持不变。
**精度损失:**在进行浮点数类型的转换时,可能会发生精度损失。由于浮点数的表示方式不同,将一个单精度浮点数( float )转换为双精度浮点数( double )时,精度可能会损失。
**类型不匹配导致的错误:**在进行类型转换时,需要确保源类型和目标类型是兼容的。如果两者不兼容会导致编译错误或运行时错误。
为什么浮点数运算的时候会有精度丢失的风险?
浮点数运算精度丢失代码演示:
1 |
|
为什么会出现这个问题呢?
这个和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。这也就是解释了为什么浮点数没有办法用二进制精确表示。
就比如说十进制下的 0.2 就没办法精确转换成二进制小数:
1 |
|

如何解决浮点数运算的精度丢失问题?
BigDecimal
可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal
来做的。
1 |
|
BigDecimal
类的底层实现主要依赖于整数数组来表示十进制数。每个数组元素都包含一段数字,通常是一个或多个十进制位。这种表示方式允许 BigDecimal
类可以处理任意长度的数值,并且能够保持精度。
超过 long 整型的数据应该如何表示?
基本数值类型都有一个表达范围,如果超过这个范围就会有数值溢出的风险。
在 Java 中,64 位 long 整型是最大的整数类型。
1 |
|
BigInteger
内部使用 int[]
数组来存储任意大小的整形数据。
相对于常规整数类型的运算来说,BigInteger
运算的效率会相对较低。
包装类
Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:
基本类型–>包装类(引用类型,包装类都位于java.lang包下)
byte Byte
short Short
int Integer 【特殊】
long Long
float Float
double Double
char Character 【特殊】
boolean Boolean
装箱与拆箱
基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“
装箱: 基本数值—->包装对象(装箱)
构造方法
Integer(int value) :构造一个新分配的 Integer 对象,它表示指定的 int 值
Integer(String s) :构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。传递的字符串,必须是基本类型的字符串,否则会抛出异常 “100” 正确 “a” 抛异常
静态方法
static Integer valueOf(int i):返回一个表示指定的 int 值的 Integer 实例
static Integer valueOf(String s):返回保存指定的 String 的值的 Integer 对象
1 |
|
拆箱: 包装对象—->基本数值(拆箱)
int intValue() :以 int 类型返回该 Integer 的值。
1 |
|
自动装箱与自动拆箱
由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:
1 |
|
装箱其实就是调用了 包装类的valueOf()
方法,拆箱其实就是调用了 xxxValue()
方法。
因此,
Integer i = 10
等价于Integer i = Integer.valueOf(10)
int n = i
等价于int n = i.intValue()
;
注意:如果频繁拆装箱的话,也会严重影响系统的性能。我们应该尽量避免不必要的拆装箱操作。
包装类型的缓存机制
Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False
。
Integer 缓存源码:
1 |
|
Character
缓存源码:
1 |
|
Boolean
缓存源码:
1 |
|
如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
两种浮点数类型的包装类 Float
,Double
并没有实现缓存机制。
下面我们来看一个问题:下面的代码的输出结果是 true
还是 false
呢?
1 |
|
Integer i1=40
这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40)
。因此,i1
直接使用的是缓存中的对象。而Integer i2 = new Integer(40)
会直接创建新的对象。
因此,答案是 false
。
记住:所有整型包装类对象之间值的比较,全部使用 equals 方法比较
引用类型
实际的开发中,引用类型的使用非常重要,也是非常普遍的。我们可以在理解基本类型的使用方式基础上,进一步 去掌握引用类型的使用方式。基本类型可以作为成员变量、作为方法的参数、作为方法的返回值,那么当然引用类型也是可以的。
1 |
|
interface作为成员变量
1 |
|
interface作为方法参数和返回值类型
当接口作为方法的参数时,需要传递什么呢?当接口作为方法的返回值类型时,需要返回什么呢?对,其实都是它的 子类对象。 ArrayList 类我们并不陌生,查看API我们发现,实际上,它是java.util.List 接口的实现类。所以,当我们看见 List 接口作为参数或者返回值类型时,当然可以将 ArrayList 的对象进行传递或返回。
1 |
|