目录
一. 前言
二. 基本数据类型
2.1. char(字符型)
2.2. byte(字节型)
2.3. short(短整型)
2.4. int(整型)
2.5. long(长整型)
2.6. float(单精度浮点型)
2.7. double(双精度浮点型)
2.8. boolean(布尔型)
三. 类型转换
3.1. 自动转换
3.2. 强制转换
3.3. 类型自动提升
3.4. 自动装箱和自动拆箱
四. 实例
五. 引用类型
六. Java 常量
Java 是一门强类型的编程语言,所有的变量必须显式声明数据类型。Java 中定义了多种数据类型,根据数据的特点,数据类型分为两大类:基本数据类型和引用数据类型(简称引用类型)。
Java 中数据类型的分类如下图所示:
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。
基本数据类型包括 4 类 8 种,4 类分别是整数类型、浮点类型、字符类型和布尔类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
数据类型 | 占用字节 | 默认值 | 包装类 |
---|---|---|---|
char(字符型) | 1 | \u0000 | Character |
byte(字节型) | 1 | 0 | Byte |
short(短整型) | 2 | 0 | Short |
int(整型) | 4 | 0 | Integer |
long(长整型) | 8 | 0L | Long |
float (单精度浮点型) |
4 | 0.0f | Float |
double (双精度浮点型) |
8 | 0.0d | Double |
boolean(布尔型) | 1 | false | Boolean |
注意:在 Java 中,整形和浮点型都是有符号的。
在 Java 中,将一种类型的值赋值给另一种类型是很常见的。boolean 类型与其他7种类型的数据都不能进行转换,char 类型不支持自动转换成它,只支持强制转换,除了这两种类型有些特殊以外,其他 6 种数据类型,它们之间都可以进行相互转换,只是可能会存在精度损失。
将 6 种数据类型按取值范围的大小顺序排列一下:
double > float > long > int > short > byte
取值范围从小转换到大,是可以直接转换的,而从大转成小,或转成 char 类型,则必须使用强制转换。
自动转换时会进行扩宽(widening conversion)。因为较大的类型(如 int)要保存较小的类型(如 byte),取值范围是足够的,不需要强制转换。
在自动类型转化中,除了以下几种情况可能会导致精度损失以外,其他类型的自动转换不会出现精度损失:
除了可能的精度损失外,自动转换不会出现任何运行时(run-time)异常。
如果要把大的转成小的,或者在 short 与 char 之间进行转换,就必须强制转换。这也被称作缩小转换(narrowing conversion),因为必须显式地使数值更小以适应目标类型。
强制转换的格式和实例如下:
(target-type) value;
// 实例
long lo = 99;
int i = (int) lo;
严格地说,byte 转为 char 不属于(narrowing conversion),因为从 byte 到 char 的过程其实是 byte --> int --> char,所以 widening 和 narrowing 都有。
强制转换可能会损失精度,主要有两种场景:
在表达式计算过程中会发生类型转换,这些类型转换就是类型提升,类型提升规则如下:
Java 是面向对象语言,其概念为一切皆为对象,但基本数据类型是个例外。基本数据类型大多是面向机器底层的类型,它是 “值” 而不是一个对象,对于声明在方法中的基本类型变量,它存放于“栈”中而不是存放于“堆”中。这有很多好处,例如不需要和对象一样需要在堆中分配内存,然后搞个引用指向它。不需要 GC,因为是直接在栈上分配空间,方法运行结束就出栈回收掉了。可以放心的使用最基本的运算符进行比较和计算等等。
没有什么是完美无缺的,数据类型也有缺点,例如会自动设置默认值(这是双刃剑,一些场景下会增加额外的处理逻辑),不支持泛型等。
Java 希望使用一切皆为对象的理念来统一语言设计,但基本类型确实有很多优点和使用场景,鱼和熊掌我就要兼得之!所以它为每一个基本类型都提供了相应的包装类,封装了很多实用的方法,最重要的是,提供了自动装箱和自动拆箱的语法糖,让开发者可以无感知的在包装类型和基础类型之间来回切换。
对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:
public class PrimitiveTypeTest {
public static void main(String[] args) {
// byte
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
System.out.println();
// short
System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
System.out.println("包装类:java.lang.Short");
System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
System.out.println();
// int
System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
System.out.println("包装类:java.lang.Integer");
System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
System.out.println();
// long
System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
System.out.println("包装类:java.lang.Long");
System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
System.out.println();
// float
System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
System.out.println("包装类:java.lang.Float");
System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
System.out.println();
// double
System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
System.out.println("包装类:java.lang.Double");
System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
System.out.println();
// char
System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
System.out.println("包装类:java.lang.Character");
// 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="
+ (int) Character.MIN_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最大值:Character.MAX_VALUE="
+ (int) Character.MAX_VALUE);
}
}
编译以上代码输出结果如下所示:
基本类型:byte 二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127
基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767
基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647
基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807
基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38
基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308
基本类型:char 二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535
Float 和 Double 的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"表示 E 之前的数字要乘以 10 的“数字”次幂。比如 3.14E3 就是 3.14×1000=3140,3.14E-3 就是3.14/1000=0.00314。
实际上,JAVA 中还存在另外一种基本类型 void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。
常量就是一个固定值。它们不需要计算,直接代表相应的值。
常量指不能改变的量。 在 Java 中用 final 标志,声明方式和变量类似:
final double PI = 3.1415927;
虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。
字面量可以赋给任何内置类型的变量。例如:
byte a = 68;
char a = 'A'
byte、int、long、和 short 都可以用十进制、16进制以及8进制的方式来表示。
当使用常量的时候,前缀0表明是8进制,而前缀 0x 代表16进制。例如:
int decimal = 100;
int octal = 0144;
int hexa = 0x64;
和其他语言一样,Java 的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:
"Hello World"
"two\nlines"
"\"This is in quotes\""
字符串常量和字符常量都可以包含任何 Unicode 字符。例如:
char a = '\u0001';
String a = "\u0001";
Java 语言支持一些特殊的转义字符序列:
符号 | 字符含义 |
---|---|
\n | 换行 (0x0a) |
\r | 回车 (0x0d) |
\f | 换页符(0x0c) |
\b | 退格 (0x08) |
\0 | 空字符(0x0) |
\s | 字符串 |
\t | 制表符 |
\" | 双引号 |
\' | 单引号 |
\\ | 反斜杠 |
\ddd | 八进制字符 (ddd) |
\uxxxx | 16进制Unicode字符 (xxxx) |