[toc]
数据类型的出现是为了把数据分成所需内存大小不同的数据,合理、有效利用内存。数据类型在计算机语言里面,是对内存位置的一个抽象表达方式。
数据类型
在JAVA语言种将数据类型分为两类:基本数据类型
和引用数据类型
-
基本类型
:简单数据类型是不能简化的、内置的数据类型、由编程语言本身定义,它表示了真实的数字、字符和整数。 -
引用数据类型
:Java语言的引用数据类型包括类、接口和数组类型,还有一种特殊的null类型。引用数据类型是对一个对象的引用,对象有两种:数组和实例(面向对象的基本知识点,这篇文章主要以基本数据类型为主)
基本数据类型
基本数据类型分为4类,分别为整型、浮点型、字符型和布尔型
整型
1字节=8位
4种整型量都是有符号的,符号占一位(byte的取值范围本该是2的8次方,其他类似)。
Java整数常量默认是int类型
下表可以清楚的看出每个取值范围
内可以使用的数据类型
(这里没有把负数部分画出来)
取值范围 | 0~127 | 128 ~ 32767 | 32768 ~ 2147483647 | 2147483648 ~ 9223372...... |
---|---|---|---|---|
数据类型 | byte、short、int、long | short、int、long | int、long | long |
- byte、short可以使用在其取值范围内的整数常量,系统自动把这些整数常量当成byte或short类型处理。使用超出范围的整数常量会报错。
byte b = 127;
byte b2 = 128;//报错:Required:byte Found:int(默认是int类型)
- 如果把一个超出int取值范围的整数常量赋值给long类型,会报错;强制使用long类型需要在整数常量后加L(或l)
- 如果把一个int范围内的整数常量赋值给long类型,不会报错,是因为发生了自动类型转换(下面会具体说明)
int num1 = 2147483647;
long l1 = 2147483647;//正常,发生了自动类型转换
int num2 = 2147483648;//报错: Integer number too
large
long l2 = 2147483648;//报错: Integer number too
large
long l3 = 2147483648L;//正常
整数常量的四种表示方式
- 二进制:0b或0B开头(java7开始支持)
- 八进制:0开头
- 十进制:不做过多说明
- 十六进制:0x或0X开头
int num1=013;//八进制
int num2=0x13;//十六进制
重点说明二进制表示方式
所有数字在计算机底层都是以二进制形式表示的,原码是直接将一个数值换算成二进制数。计算机是以补码形式保存所有的整数。正数的补码和原码完全相同,负数的补码是原码的反码加1;反码是对原码按位取反,最高位符号位不变。
- 二进制整数的最高位是符号位,1表示负数,0表示正数
- java整数常量默认是int类型,所以二进制整数默认占32位,定义一个不足位的二进制整数时,会自动高位补齐(补0),第32位是符号位
- 系统对byte、short类型的使用与十进制一致
- 使用long类型需要在末尾加L(或l),表示占64位,第64位是符号位
关于进制间的转换请看
进制间的转换 这篇文章
浮点型
- float(单精度浮点型):第1位是符号位,接下来8位表示指数,其余23位表示尾数
- double(双精度浮点型):第1位符号位,接下来11位表示指数,其余52位表示尾数
浮点数的两种表示方式
-
十进制数形式,float浮点数后加F(或f),double浮点数后加D(或d),浮点数必须包含一个小数点,
如
float f=5.1200_001f;//可以使用_分隔,方便校对数位
科学计数法形式,只有浮点型的数值可以使用科学计数法形式表示。如:5.12E2(5.12✖️10^2)
字符型
- java语言使用16位的Unicode字符集(关于字符编码问题请看阮一峰的字符编码笔记)作为编码方式
- char类型的取值范围是0~2^16(65536),是无符号的整数(正数),可以被用作整数类型的值来使用,同样系统也会把整数类型的值当成char类型处理
字符型常量的三种表示方式
- 直接通过单个字符来指定,如 'a','0'
- 通过转义字符表示特殊字符型常量
常用转义字符
- 直接使用Unicode值表示字符型常量,范围'\u0000'-'\uFFFF',其中前256个('\u0000'-'\u00FF')字符和ASCII码表中的字符安完成重合
布尔型
用于表示逻辑上的”真“或”假“
基本类型的类型转换
自动类型转换
取值范围小的类型赋值给取值范围大的类型,系统自动进行类型转换,如图所示,可以根据箭头指向发生自动转换
强制类型转换
强制类型转换可能会丢失精度,所以需要使用()进行类型转换。
32位int型最高位0表示正数,转换为byte类型,高位精度丢失,第8位为byte最高位,1表示负数,因此正数的int型因丢失精度变成了负数的byte型。
表达式类型的自动提升
当一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升
- byte、short、char自动提升为int类型
- 整个表达式的数据类型自动提升到与表达式中最高等级操作数的数据类型
- 与字符串拼接规则:字符串在+号的左边,表示字符串拼接,如果字符串在+号的右边要具体看左边的情况
System.out.println("Hello" + 'a' + 7);//Helloa7
System.out.println('a' + 7 + "Hello");//104Hello
包装类
为什么还需要包装类
Java中的int类型在内存中只占用4个字节,而一个Object对象本身最少占用8个字节,另外还需要4个字节来引用它。除此之外CPU对基本类型的处理更加高效。
既然如此为什么还需要包装类?
Java是面向对象的编程语言,但这8种基本数据类型并不支持面试对象的特性。所以 Java 需要一个这样的包装类来使其面向对象的完整性,这样一来基本类型具有了对象的特性,例如:实现可空类型,完成字符串像基本类型的转换,另外Java 集合中只能放入包装类型,不支持基本类型。
//可为空
Integer integer=null;
//字符串转换为int型
int num=Integer.valueOf("123");
//java集合只能放入Integer类型,无法放入int类型
ArrayList arrayList=new ArrayList<>();
缓冲池——减少内存开销,提高程序性能
Integer integer = Integer.valueOf(127);
int intValue = integer.intValue();
- 通过静态方法valueOf将基本类型包装为Integer类型
- 通过intValue方法将包装类转换为int类型。
private final int value;
//intValue方法比较简单,直接返回value
public int intValue() {
return value;
}
//valueOf方法的内部实现
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以从valueOf
方法内部看到,传入进来的i并不是直接调用new Integer(i)
来创建对象的,而是通过IntegerCache.cache
缓存中获取的。
IntegerCache是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() {}
}
从上面的代码中,可以看到IntegerCache类中声明了Integer cache[]
数组来缓存Integer
对象的,并且IntegerCache.low
和IntegerCache.high
常量确定了cache
的范围从-128~127,也就是说不在这个范围内的值,会直接通过new Integer(i)
来创建对象。
Integer num1 = 127;
Integer num2 = 127;
System.out.println(num1 == num2);//引用的同一个对象所以输出为 true
Integer num1 = 128;
Integer num2 = 128;
System.out.println(num1 == num2);//超出了缓存范围输出false
Integer integer1 = new Integer(127);
Integer integer2 = new Integer(127);
System.out.println(integer1 == integer2);//直接new的对象,未走缓存输出false
在int和Integer类型之间转换其实不需要如此的麻烦,从java5开始为我们提供了装箱和拆箱的操作,简化转换过程。
自动装箱、拆箱——只是一种语法糖
Integer a= 123; // 装箱
int b = a; // 拆箱
装箱和拆箱可以看成一种java语法糖,javac编译器会自动把装箱转换为Integer.valueOf(),把拆箱转换为Integer.intValue(),因此我们在使用装箱时依然可以使用Integer
的缓存。
成员变量value的不可变性
包装类的成员变量value
都使用了final
关键字修饰,这表明它们是不可变类型(常量)。
对于成员变量value
只需要明白3点就可以了
- 它的访问修饰符是
pirvate
,所以外部无法修改它 - 使用了
final
关键字修饰,内部一旦赋值便无法再改变 - 基本数据类型的不可变性满足上面两点就可以了,不像引用类型,被final修改后,引用本身不可变,但具体的实例是可以通过引用改变的。这点在
String
类型中详细说明(String
是通过copyOf()
方法来保证不可变的)
之所以设计成不可变的的目的是为了保证基本的信息安全
和并发环境下的线程安全
其他基本类型的包装类
- Boolean,缓存了true/false对应实例
- Short,缓存范围与Integer一致
- Byte,全部缓存
- Character,缓存范围'\u0000' ~ '\u007F'
- Double和Float类型没有使用缓存池
运算
将运算符和操作数连接在一起就形成了表达式
这里只对两种运算做简要说明(其实主要是为了记录下)
- 逻辑运算符
//注释
& :逻辑与 &&:短路与
| :逻辑或 ||:短路或
! :逻辑非 ^:逻辑异或
- 位运算符(直接对二进制进行运算)