数据类型
Java 中的数据类型有哪些?
Java支持数据类型分为两类:基本数据类型
和引用数据类型
。
- Java 中有 8 种基本数据类型,分别为:
基本类型 | 位数 | 字节 | 默认值 | 包装类 | 取值范围 |
---|---|---|---|---|---|
byte | 8 | 1 | 0 | Byte | -128 ~ 127 |
short | 16 | 2 | 0 | Short | -32768 ~ 32767 |
int | 32 | 4 | 0 | Integer | -2147483648 ~ 2147483647 |
long | 64 | 8 | 0L | Long | -9223372036854775808 ~ 9223372036854775807 |
char | 16 | 2 | ‘u0000’ | Character | 0 ~ 65535 |
float | 32 | 4 | 0f | Float | 1.4E-45 ~ 3.4028235E38 |
double | 64 | 8 | 0d | Double | 4.9E-324 ~ 1.7976931348623157E308 |
boolean | 1 | false | Boolean | true、false |
其中:
char a = ‘hello’; 单引号
String a = “hello” ;双引号
常见类型的字节大小,需要牢记:1字节(byte、boolean)、2字节(short、char)、4字节(int、float)、8字节 (long、double)
还有就是,Java里使用long
类型的数据一定要在数值后面加上 L,否则将作为整型解析:
long g = (long)33333372036854766666;//报错
long g = 33333372036854766666L;//正确
- 引用数据类型:类(Class)、接口(Interface)、数组(Array)
基本数据类型与引用数据类型区别
- 存储方式:基本数据类型
直接存储值
,而引用数据类型存储的是对象的引用(内存地址)
- 内存分配:基本数据类型在
栈
上分配内存,引用数据类型在堆
上分配内存(具体内容存放在堆中,栈中存放的是其具体内容所在内存的地址)栈上的分配速度较快,但是内存空间较小,而堆上的分配速度较慢,但可以分配更大的内存空间 - 默认值:基本数据类型会有默认值,例如int类型的默认值是0,boolean类型的默认值是false。而引用数据类型的默认值是null,表示没有引用指向任何对象
- 复制操作:基本数据类型进行复制时,会复制该变量的值。而引用数据类型进行复制时,只会复制对象的引用,两个变量指向同一个对象
- 参数传递:基本数据类型作为方法的参数传递时,传递的是值的副本,不会修改原始值。而引用数据类型作为方法的参数传递时,传递的是对象的引用,可以修改对象的属性或状态
- 比较操作:基本数据类型使用
==
进行比较时,比较的是值是否相等。而引用数据类型使用==
进行比较时,比较的是引用是否指向同一个对象,如果要比较对象的内容是否相同,需要使用equals()
方法
什么是自动拆箱/装箱?
-
装箱 :将基本类型⽤它们对应的引⽤类型包装起来
-
拆箱 :将包装类型转换为基本数据类型
Java可以⾃动对基本数据类型和它们的包装类进⾏装箱和拆箱。我们来看一下基本类型跟封装类型之间的对应关系:
数据类型 | 封装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
我们来看一个例子:
// 自动装箱
1. Integer a = 100;
// 自动拆箱
2. int b = a;
自动装箱,相当于Java编译器替我们执行了Integer.valueOf(XXX);
自动拆箱,相当于Java编译器替我们执行了Integer.intValue(XXX);
包装类型的缓存机制
Java 基本数据类型的包装类型Byte,Short,Integer,Long
这 4 种包装类内置缓存机制,默认创建了数值 [-128,127] 的相应类型的缓存数据,如果超出对应范围才会去创建新的对象。
另外两种浮点数类型的包装类 Float,Double
并没有实现缓存机制。
Integer i1 = 10;
Integer i2 = 10;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1 == i2);// 输出 true
System.out.println(i3 == i4);// 输出 false
什么是自动类型转换、强制类型转换?
Java 数值型变量经常需要进行相互转换,当把⼀个表数范围⼩
的数值或变量直接赋给另⼀个表 数范围⼤
的变量时,可以进⾏⾃动类型转换
,即隐式转换
long l = 100;
int i = 200;
long ll = i;
反之,需要强制类型转换(显式转换)
short s = 199;
int i = s;// 199
double d = 10.24;
long ll = (long) d;// 10, 精度丢失
自动类型转换规则如下:
- 数值型数据的转换:byte→short→int→long→float→double
- 字符型转换为整型:char→int
一些常见的易错题:
float f=3.4;
程序正确吗?
3.4 是单精度数,将双精度型(double)赋值给浮点型(float)属于下转型会造成精度损失,因此需要强制类型转换
float f =(float)3.4;
或 者写成float f =3.4F;
short s1 = 1; s1 = s1 + 1;
对吗?short s1 = 1; s1 += 1;
对吗?
对于
short s1 = 1; s1 = s1 + 1;
编译出错,由于 1 是 int 类型,因此 s1+1 运算结果也是 int型, 需要强制转换类型才能赋值给 short 型。⽽
short s1 = 1; s1 += 1;
可以正确编译,因为 s1+= 1;相当于s1 = (short(s1 + 1);
其中有隐含的强 制类型转换。
int count = 100000000; int price = 1999; long totalPrice = count * price;
正确吗?
不正确,编译没任何问题,但结果却输出的是负数,这是因为两个 int 相乘得到的结果是 int, 相乘的结果超出了 int 的代表范围。这种情况,一般把第一个数据转换成范围大的数据类型再和其他的数据进行运算。
Integer a= 127 与 Integer b = 127相等吗
我们首先得知道,对于对象引用类型:==
比较的是对象的内存地址;而对于基本数据类型:==
比较的是值
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
System.out.println(b == c); // true
Integer a1 = 128;
Integer b1 = 128;
System.out.println(a1 == b1); // false
Integer a2 = 127;
Integer b2 = 127;
System.out.println(a2 == b2); // true
}
上述结果不一样,这是因为Java的Integer类内部实现了一个静态缓存池,用于存储特定范围内的整数值对应的Integer对象。如果整型字面量的值在-128到127
之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,不需要新建一个对象。若超过范围,则a1==b1的结果是false
为什么金额一般用bigDecimal而不是double?
因为double类型,在计算时会出现丢失精度的问题,比如:
System.out.println(12.3 + 45.6); // 57.900000000000006
System.out.println(12.3 / 100); // 0.12300000000000001
另外等于判断在使用时还需要注意
double a = 2.333111111111111111111111112;
double b = 2.333111111111111111111111113;
// duoble超过15位以后就会不对了
System.out.println(a == b); // true
一旦涉及到钱,就是丢失精度是一个很严重的问题,所以我们一般使用bigDecimal来计算金额
bigDecimal
是高精度计算,且因为是不可变类和对象类型,做计算时没那么方便,效率比Double
低
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = BigDecimal.valueOf(0.2);//是静态工厂类,永远优先于直接使用构造函数!
System.out.println(bd1.subtract(bd2));//0.1