4. 数据类型

空~2022年6月6日
  • java
大约 13 分钟

4. 数据类型

Java 是一种强类型语言。这就意味着必须为每一个变量声明一种类型。在 Java 中,一共有 8 种基本类型(primitive type),其中有 4 种整型、2 种浮点类型、1 种用于表示 Unicode 编码的字符单元的字符类型 char(请参见论述 char 类型的章节)和 1 种用于表示真值的 boolean 类型。

分类

Java 语言支持的类型分为两类:基本类型(Primitive Type)和引用类型(Reference Type)。

  1. 基本类型,四大类八小种:

    整数型:整型用于表示没有小数部分的数值,它允许是负数。Java 提供了 4 种整型:byteshortintlong

    浮点型:浮点类型用于表示有小数部分的数值。在 Java 中有两种浮点类型:floatdouble

    布尔型:boolean(布尔)类型有两个值:falsetrue,用来判定逻辑条件。整型值和布尔值之间不能进行相互转换。

    字符型(char 型):char 类型原本用于表示单个字符。不过,现在情况已经有所变化。如今,有些 Unicode 字符可以用一个 char 值描述,另外一些 Unicode 字符则需要两个 char 值。

    类型名称关键字占用内存取值范围缺省默认值
    字节型byte1 字节-128~1270
    短整型short2 字节-32768~327670
    整型int4 字节-2147483648~21474836470
    长整型long8 字节-9223372036854775808L~9223372036854775807L0L
    单精度浮点型float4 字节+/-3.4E+38F(6~7 个有效位)0.0f
    双精度浮点型double8 字节+/-1.8E+308 (15 个有效位)0.0
    字符型char2 字节ISO 单一字符集'\u0000'
    布尔型boolean1 字节truefalsefalse
  2. 引用类型:

    引用类型包括类、接口和数组类型,还有一种特殊的 null 类型。所谓引用数据类型就是对一个对象的引用,对象包括实例和数组两种。

    空类型(null type)就是 null 值的类型,这种类型没有名称。因为 null 类型没有名称,所以不可能声明一个 null 类型的变量或者转换到 null 类型。空引用(null)是 null 类型变量唯一的值。空引用(null)可以转换为任何引用类型。

    在实际开发中,程序员可以忽略 null 类型,假定 null 只是引用类型的一个特殊直接量。

    注意

    空引用(null)只能被转换成引用类型,不能转换成基本类型,因此不要把一个 null 值赋给基本数据类型的变量。

    前期最常见的引用类型:字符串类型:String

基本数据类型

整数型

int 是最常用的整数类型,因此在通常情况下,一个Java 整数常量默认就是 int 类型。除此之外,有如下两种情形必须指出。

  1. 如果直接将一个较小的整数常量(在 byteshort 类型的表数范围内)赋给一个 byteshort 变量,系统会自动把这个整数常量当成 byte 或者 short 类型来处理。

    byte b = 5;
    
  2. 如果使用一个巨大的整数常量(超出了 int 类型的表数范围)时,Java 不会自动把这个整数常量当成 long 类型来处理。如果希望系统把一个整数常量当成 long 类型来处理,应在这个整数常量后增加 l 或者 L 作为后缀。通常推荐使用 L,因为字母 l 很容易跟数字 1 搞混。

public class Test {
    public static void main(String[] args) {
        // 系统会自动把5转成byte类型
        byte b = 5;
        // 系统不会自动把999999999999999999转成long类型
        // 整数过大
        long errorValue = 999999999999999999;
        // 强制转换为long类型
        long trueValue = 99999999999999999L;
    }
}

Java 中整数常量有 4 种表示方式:二进制、八进制和十六进制,其中八进制的整数常量以 0 开头,十六进制的整数常量以 0x 或者 0X 开头,其中 10-15 分别以 a-f(此处的 a~f 不区分大小写)来表示。

从 Java 7 开始,加上前缀 0b 或 0B 就可以写二进制数。例如,0b1001 就是 9。另外,同样是从 Java 7 开始,还可以为数字字面量加下划线,如用 1_000_000(或 0b1111_0100_0010_0100_0000)表示一百万。这些下划线只是为了让人更易读。Java 编译器会去除这些下划线。

int i = 0b1001;
long l = 1_000_000;

一个 Java 整数常量默认就是 int 类型,因此使用二进制形式定义整数时,二进制整数默认占 32 位,其中第 32 位是符号位;如果在二进制整数后添加 l 或 L 后缀,那么这个二进制整数默认占 64 位,其中第 64 位是符号位。

当定义 32 位的二进制整数时,最高位其实是符号位,当符号位是 1 时,表明它是一个负数,负数在计算机里是以补码的形式存在的,因此还需要换算成原码。

public class Test {
    public static void main(String[] args) {
        // 8位的二进制数 105
        byte b = 0b01101001;
        // 32位的二进制数 -2147483645
        int i = 0b10000000_00000000_00000000_00000011;
        System.out.println(b);
        System.out.println(i);
    }
}

提示

所有数字在计算机底层都是以二进制形式存在的,原码是直接将一个数值换算成二进制数。但计算机以补码的形式保存所有的整数。补码的计算规则:正数的补码和原码完全相同,负数的补码是其反码加 1;反码是对原码按位取反,只是最高位(符号位)保持不变。

了解二进制open in new window

二进制、八进制、十六进制转换open in new window

反码、补码、原码open in new window

相关信息

在 C 和 C++中,int 和 long 等类型的大小与目标平台相关。在 8086 这样的 16 位处理器上整型数值占 2 字节;不过,在 32 位处理器(比如 Pentium 或 SPARC)上,整型数值则为 4 字节。类似地,在 32 位处理器上 long 值为 4 字节,在 64 位处理器上则为 8 字节。由于存在这些差别,这对编写跨平台程序带来了很大难度。在 Java 中,所有的数值类型所占据的字节数量与平台无关。

注意,Java 没有任何无符号(unsigned)形式的 intlongshortbyte 类型。

字符型

字符型通常用于表示单个的字符,字符常量必须使用单引号(')括起来。Java 语言使用 16 位的 Unicode 字符集作为编码方式,而 Unicode 被设计成支持世界上所有书面语言的字符,包括中文字符,因此 Java 程序支持各种语言的字符。

字符型常量有如下 3 种表示形式。

  1. 直接通过单个字符来指定字符型常量,例如'A'、'9'和'0'等。
  2. 通过转义字符表示特殊字符型常量,例如'\n'、'\t'等。
  3. 直接使用 Unicode 值来表示字符型常量,格式是'\uXXXX',其中 XXXX 代表一个十六进制的整数。

浮点型

double 表示这种类型的数值精度是 float 类型的两倍(有人称之为双精度数值)。绝大部分应用程序都采用 double 类型。在很多情况下,float 类型的精度很难满足需求。实际上,只有很少的情况适合使用 float 类型,例如,需要单精度数据的库,或者需要存储大量数据。

float 类型的数值有一个后缀 F 或 f(例如,3.14F)。没有后缀 F 的浮点数值(如 3.14)默认为 double 类型。当然,也可以在浮点数值后面添加后缀 D 或 d(例如,3.14D)。

相关信息

可以使用十六进制表示浮点数值。例如,0.125=2⁻³ 可以表示成 0x1.0p-3。在十六进制表示法中,使用 p 表示指数,而不是 e。注意,尾数采用十六进制,指数采用十进制。指数的基数是 2,而不是 10。

所有的浮点数值计算都遵循 IEEE 754 规范。具体来说,下面是用于表示溢出和出错情况的三个特殊的浮点数值:

● 正无穷大

● 负无穷大

● NaN(不是一个数字)

例如,一个正整数除以 0 的结果为正无穷大。计算 0/0 或者负数的平方根结果为 NaN。

提示

常量 Double.POSITIVE_INFINITYDouble.NEGATIVE_INFINITYDouble.NaN(以及相应的 Float 类型的常量)分别表示这三个特殊的值,但在实际应用中很少遇到。特别要说明的是,不能这样检测一个特定值是否等于 Double.NaN

image-20220601120133047

所有“非数值”的值都认为是不相同的。然而,可以使用 Double.isNaN 方法:

image-20220608191044077

使用

什么是变量

变量三要素

  1. 数据类型
  2. 变量名
  3. 直接量(字面量)

数据类型决定空间大小,变量名方便访问,值是变量保存的数据。

变量的分类

  1. 局部变量

    在方法体中声明的变量,局部变量没有默认值。

  2. 成员变量

    在方法体外,类体内声明的变量,成员变量有默认值。

  3. 静态变量

    有 static 关键字修饰的为静态变量

注意

变量的有效范围:作用域,出了大括号就不认识了。

直接量

在 java 语言中“数据”被称为字面量,如:10、1.23、true、false、'a'、"abc"。

直接量的分类:

int 类型的直接量:在程序中直接给出的整型数值,可分为二进制、十进制、八进制和十六进制 4 种,其中二进制需要以 0B 或 0b 开头,八进制需要以 0 开头,十六进制需要以 0x 或 0X 开头。例如 123、012(对应十进制的 10)、0x12(对应十进制的 18)等。

long 类型的直接量:在整型数值后添加 l 或 L 后就变成了 long 类型的直接量。例如 3L、0x12L (对应十进制的 18L)。

float 类型的直接量:在一个浮点数后添加 f 或 F 就变成了 float 类型的直接量,这个浮点数可以是标准小数形式,也可以是科学计数法形式。例如 5.34F、3.14E5f。

double 类型的直接量:直接给出一个标准小数形式或者科学计数法形式的浮点数就是 double 类型的直接量。例如 5.34、3.14E5。

boolean 类型的直接量:这个类型的直接量只有 true 和 false。

char 类型的直接量:char 类型的直接量有 3 种形式,分别是用单引号括起来的字符、转义字符和 Unicode 值表示的字符。例如'a'、'\n'和'\u0061'。

String 类型的直接量:一个用双引号括起来的字符序列就是 String 类型的直接量。

null 类型的直接量:这个类型的直接量只有一个值,即 null。

在上面的 8 种类型的直接量中,null 类型是一种特殊类型,它只有一个值:null,而且这个直接量可以赋给任何引用类型的变量,用以表示这个引用类型变量中保存的地址为空,即还未指向任何有效对象。

声明与赋值

声明变量的语法非常简单,只要指定变量的类型和变量名即可,如下所示:

type varName

例如:int i

注意

同一个作用域中变量名不能重名。

变量还可能使用其他修饰符。但不管是哪种变量,定义变量至少需要指定变量类型和变量名两个部分。

定义变量时的变量类型可以是 Java 语言支持的所有类型。

可以先声明后赋值,也可以在声明的同时进行赋值:

int i;
i = 10;
long l = 40000000L;
short s1, s2, s3;
byte b1 = 10, b2 = 20;

相关信息

  1. 在任何情况下,整数型的“字面量”默认被当做 int 处理。

  2. 当这个整数型字面量没有超出 byteshortchar 取值范围,那么这个整数型字面量可以直接赋值给 byteshortchar 类型的变量。

  3. 整数可以直接赋值给 char,会自动转换成 char 字符类型。

基本数据类型转换规则

  1. boolean 不能参与类型转换,其它都行。

  2. 没有超出 byte、short、char 的范围,整数可以直接赋值。

自动转换类型

当把一个表数范围小的数值或变量直接赋给另一个表数范围大的变量时,系统将可以进行自动类型转换。

就如同有两瓶水,当把小瓶里的水倒入大瓶中时不会有任何问题。

byte<short(char)<int<long<float<double

当把任何基本类型的值和字符串值进行连接运算时,基本类型的值将自动类型转换为字符串类型。

如果希望把基本类型的值转换为对应的字符串时,可以把基本类型的值和一个空字符串进行连接。

public class Test {
    public static void main(String[] args) {
        // 代码错误,5是一个整数,不能直接赋值给字符串
        // String str1 = 5;
        // 一个基本数据类型和字符串连接时,基本数据类型自动转换为字符串
        String str2 = 3.5f + "";
        // 结果:7Hello
        String str3 = 3 + 4 + "Hello";
        // 结果:Hello34
        System.out.println("Hello" + 3 + 4);
    }
}

3 + 4 + "Hello" 表达式:这个表达式先执行 3 + 4 运算,这是执行两个整数之间的加法,得到 7,然后进行 7+"Hello" 运算,此时会把 7 当成字符串进行处理,从而得到 7Hello!。

反之,对于 "Hello"+ 3 + 4 表达式,先进行 "Hello" + 3 运算,得到一个 Hello3 字符串,再和 4 进行连接运算,4 也被转换成字符串进行处理。

强制转换类型

当进行强制类型转换时,类似于把一个大瓶子里的水倒入一个小瓶子,如果大瓶子里的水不多还好,但如果大瓶子里的水很多,将会引起溢出,从而造成数据丢失。这种转换也被称为“缩小转换(Narrow Conversion)”;需要加强制类型转换符。

byteshortchar 做混合运算的时候,各自先转换成 int 再运算。

多种数据在做混合运算的时候,最终的结果是类型"最大容量"对应的类型。

public class Test {
    public static void main(String[] args) {
        int iValue = 233;
        // 强制转换
        byte bValue = (byte)iValue;
        // 输出:-23
        System.out.println(bValue);
        double dValue = 3.98;
        // 强制转换
        int tol = (int)dValue;
        // 输出:3
        System.out.println(tol);
    }
}

在上面程序中,把一个浮点数强制类型转换为整数时,Java 将直接截断浮点数的小数部分。除此之外,上面程序还把 233 强制类型转换为 byte 类型整数,从而变成了-23,这就是典型的溢出。

img

32 位 int 类型的 233 强制类型转换为 8 位的 byte 类型,则需要截断前面的 24 位,只保留右边 8 位,最左边的 1 是一个符号位,此处表明这是一个负数,负数在计算机里是以补码形式存在的,因此还需要换算成原码。将补码减 1 得到反码形式,再将反码取反就可以得到原码。最后的二进制原码为 10010111,这个 byte 类型的值为-(16+4+2+1),也就是-23。