数据类型

概述

Java 是强类型语言,每一种数据都定义了明确的数据类型,在内存中分配了不同大小的内存空间(字节)。

Java 的数据类型分为两大类:基本数据类型(primitive type)和引用数据类型。基本数据类型又分为四类八种。八种基本类型包括 4 种整型、2 种浮点型、1 种字符类型(用于表示 Unicode 编码的代码单元)和 1 种用于表示真值的 boolean 类型。


图 1 Java 基本数据类型

八种基本类型占用字节数及取值范围对照表:

数据类型 字节数 取值范围 默认值 备注
byte 1 -128 ~ 127 0 最小的整数类型
short 2 -32,768 ~ 32,767 0 较少使用
int 4 -2,147,483,648 ~ 2,147,483,647 0 最常用整数类型
long 8 -9,223,372,036,854,775,808 ~
9,223,372,036,854,775,807
0L 数值后需加 L / l
float 4 约 ±3.40282347E+38F(6-7 位有效小数) 0.0f 数值后需加 F / f
double 8 约 ±1.79769313486231570E+308(15 位有效小数) 0.0d 默认浮点数类型(d / D 可省略)
char 2 0 ~ 65,535(Unicode 字符) '\u0000' 采用 Unicode 编码,可表示汉字
boolean 不明确(通常 1 字节) true / false false Java 虚拟机规范未强制规定 boolean 大小,大小依赖 JVM 实现,常见实现中占 1 个字节

除了这四类八种基本数据类型,其他类型都是引用数据类型。

与 C 和 C++ 不同,Java 规范中没有 “依赖具体实现” 的地方。基本数据类型的大小以及有关运算的行为都是明确的。各个数据类型有固定的范围和字段长度,不受具体操作系统的影响,以保证程序的可移植性。

例如,Java 中的 int 总是 32 位整数,而在 C 和 C++ 中,int 可能是 16 位或 32 位整数,也可能是编译器开发商指定的任何其他大小。唯一的限制是 int 类型的字节数不能低于 short int,不能高于 long int。在 Java 中基本数据类型有固定的字节数,这消除了代码移植时一个令人头痛的问题。二进制数据以固定的格式进行存储和传输,消除了有关字节顺序的困扰。字符串则采用标准的 Unicode 格式存储。

作为系统组成部分的类库定义了可移植的接口。例如,有一个抽象 Window 类,并给出了面向 UNIX、Windows 和 Macintosh 环境的不同实现。

Java 中的整型和浮点型都是有符号的,Java 中没有无符号数,可使用包装类中的方法实现无符号数的功能。

Java 的引用数据类型主要分为以下 4 类:

1️⃣ 类(Class):包括自定义类、系统类(如 String、Date)、抽象类等。例如:String str = "Hello";

2️⃣ 接口(Interface):如 List、Map、Comparable 等。例如:List<String> list = new ArrayList<>();

3️⃣ 数组(Array):存储同类型数据的容器,包括基本类型数组(int[])和引用类型数组(String[])。例如:int[] arr = {1, 2, 3};

4️⃣ 枚举(Enum):用于定义常量的特殊类。例如:enum Color {RED, GREEN, BLUE}

注解(Annotation) 在某种程度上也属于引用类型,但通常不单独列为与以上并列的大类。

引用类型与基本类型的区别:引用类型存储的是对象的 “内存地址”(引用),默认值为 null;基本类型直接存储值(如 int 默认 0)。

可以在一行声明相同类型的多个变量,但是不提倡这种写法,分别声明每一个变量可以提高程序的可读性。

程序示例:

public class Test {
    public static void main(String[] args) {
        int a, b;  // 可以在一行声明相同类型的多个变量
        int c;    // 分别声明每一个变量可以提高程序的可读性
        int d;
    }
}
public class Test {
    public static void main(String[] args) {
        // 一条语句中定义多个变量
        int a = 10, b = 20, c = 30;
        // 一条语句中定义多个变量, 只给部分变量初始化
        int d, e = 100, f;
    }
}

不要使用未初始化的变量的值。

程序示例:

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student();
        Student student2; // 没有被初始化
    }
}

class Student {
    String name;
    int age;
}

Java 中可以将声明放在代码中的任何地方,但是建议变量的声明要尽可能靠近第一次使用这个变量的地方,且尽量将变量的声明和初始化放在同一行中,这是一种很好的编程风格。

程序示例:

public class Test {
    public static void main(String[] args) {
        int a;                      // 声明
        a = 10;                     // 初始化
        System.out.println(a);      // 10
    }
}
public class Test {
    public static void main(String[] args) {
        int a = 10;                 // 声明和初始化放在同一行
        System.out.println(a);      // 10
    }
}

当声明和初始化放在同一行时,其实是两条语句,可以这样证明:

public class Test {
    public static void main(String[] args) {
        boolean flag = true;
        if (flag) 
            int a = 10;  // 程序报错
    }
}

如果只是一条语句,if 是可以不加花括号的,但是此时声明和初始化放在了同一行,其实是两条语句。

从 Java 10 开始,局部变量如果可以从变量的初始值推断出它的数据类型,就不再需要声明数据类型,只需要使用关键字 var 而无须指定类型。

程序示例:

public class Test {
    public static void main(String[] args) {
        var a = 10;                 // 使用关键字 var 而无须指定类型
        System.out.println(a);      // 10
        {
            var b = 100;            // 使用关键字 var 而无须指定类型
            System.out.println(b);  // 100
        }
    }
}

整型

整数类型中,int 类型最为常用,但是如果 int 类型的范围不够,则需要使用 long 类型。byte 和 short 两种类型主要用于特定的场合,比如底层的文件处理或者存储空间有限时的大数组。

Java 的整型常量或字面量默认为 int 型。如果超过了 int 的范围,就要在数据的后面需要加一个 L 或 l 作为后缀,表示是 long 类型的整数而不是默认的 int 类型。

程序示例:

public class Test {
    public static void main(String[] args) {
        long a = 10l;               // 加后缀 l
        long b = 10L;               // 加后缀 L
        // int c = 10L;             // 报错: java: 不兼容的类型: 从 long 转换到 int 可能会有损失
        System.out.println(a);      // 10
        System.out.println(b);      // 10
        // System.out.println(c);
    }
}
public class Test {
    public static void main(String[] args) {
        long a = 999999999;    // 正常输出
        long b = 9999999999L;  // 加了后缀 L 后才能正常输出
        System.out.println(a);
        System.out.println(b);
        long c = 10L;          // 就算一个整数在 int 范围内, 也可以通过后缀 L 让它变成 long 类型
        System.out.println(c);
    }
}

不同进制的整数要加前缀。

  • 十进制整数不加前缀

  • 二进制整数加前缀 0b 或 0B

  • 八进制整数加前缀 0

  • 十六进制整数加前缀 0x 或 0X

用 System.out.println 进行输出时都是默认以十进制的形式进行输出。

程序示例:

public static void main(String[] args) {
    System.out.println(10);         // 10
    System.out.println(010);        // 8
    System.out.println(0b10);       // 2
    System.out.println(0x10);       // 16
}

可以为整型字面量加下划线,这样可以提高可读性,编译器会去除这些下划线,以多少个数字为一组进行分割是随意的,一般都是三个数字为一组。

程序示例:

public class Test {
    public static void main(String[] args) {
        long a = 1_000_000_000_000_000L;
        int b = 0B1111_0000_1111_0000;
        System.out.println(a);      // 1000000000000000
        System.out.println(b);      // 61680
    }
}

浮点型

关于浮点数在机器中存放形式的简单说明:浮点数 = 符号位 + 指数位 + 尾数位

尾数部分可能丢失,造成精度损失(小数都是近似值)

与整数类型类似,Java 浮点类型也有固定的范围和字段长度,不受具体操作系统的影响。float 是 4 个字节,double 是 8 个字节。float 是 6~7 位有效数字,double 是 15 位有效数字。

double 表示这种数值的精度是 float 类型的两倍,所以有人称之为双精度 double precision。

通常情况下都应该使用 double,因为它比 float 更精确。

Java 的浮点型常量默认为 double 型。在一个数值字面量后面加 d 或 D 可以将其视为 double 类型,一般都不需要这么做,但是不会报错,如 3d 是 double 类型。

如果要将一个浮点型字面量视为 float 类型,必须加后缀 f 或 F。

程序示例:

public static void main(String[] aStrings) {
    double a = 11.1;
    // float b = 11.1;              // java: 不兼容的类型: 从 double 转换到 float 可能会有损失
    float c = 11.1F;
    System.out.println(a);          // 11.1
    // System.out.println(b);
    System.out.println(c);          // 11.1
}
public static void main(String[] args) {
    double d1 = 3.3d;
    double d2 = 3D;             // 加后缀 D 将 int 类型数值变为 double 类型
    System.out.println(d1);     // 3.3
    System.out.println(d2);     // 3.0
}
public class Test {
    public static void main(String[] args) {
        // float a = 10.53; // Type mismatch: cannot convert from double to float
        float a = 10.53F; // 正常输出
        System.out.println(a);  // 10.53
        double b = 10.99F;  // 允许将 float 赋给 double
        System.out.println(b);  // 10.989999771118164
    }
}

浮点数字面量有三种写法:

1️⃣ 十进制,如 5.12,512.0f,.512(小数点不能省略,必须有小数点,0 可以省略,即 0.512 = .512)

2️⃣ e 计数法,如 5.12e2,5.12E-2

3️⃣ p 计数法,这是用十六进制表示浮点型字面量,用 p 表示指数,尾数采用十六进制,指数采用十进制,指数的基数是 2 而不是 10。

十六进制中使用 p 表示指数而不是 e,因为 e 是一个十六进制数。

浮点数值不适用于无法接受舍入误差的金融计算,程序示例:

public class Test {
    public static void main(String[] args) {
        System.out.println(2.0 - 1.1);      // 0.8999999999999999
    }
}

一个死循环:

public class FloatTest {
    public static void main(String[] args) {
        for (double a = 1; a != 10; a += 0.1) {
            System.out.println(a);
        }
    }
}

这种舍入误差的主要原因是浮点数值采用二进制表示,而二进制系统中无法准确地表示分数 1/10。这就好像十进制无法精确地表示分数 1/3 一样。如果需要精确的数值计算,不允许有舍入误差,则应该使用 BigDecimal 类。

所有的浮点数的计算都遵循 IEEE 754 规范。

有 3 个特殊的浮点数值表示溢出和出错情况:

🔹 正无穷大

🔹 负无穷大

🔹 NaN(不是一个数)

负数的平方根计算结果为 NaN。

表示这三个特殊值的常量:

public class Test {
    public static void main(String[] args) {
        System.out.println(Double.POSITIVE_INFINITY);  // Infinity
        System.out.println(Double.NEGATIVE_INFINITY);  // -Infinity
        System.out.println(Double.NaN);                // NaN
        System.out.println(Float.POSITIVE_INFINITY);   // Infinity
        System.out.println(Float.NEGATIVE_INFINITY);   // -Infinity
        System.out.println(Float.NaN);                 // NaN
        System.out.println(Math.sqrt(-4));  // NaN
    }
}

整数被 0 除将产生一个异常,而浮点数被 0 除将会得到一个无穷大或 NaN,正的浮点数被 0 除会得到正无穷大,负的浮点数被 0 除会得到负无穷大,浮点数 0.0 被 0 除会得到 NaN。

整数或浮点数被 0 或 0.0 除,结果是一样的,即分母不区分是 0 还是 0.0。

程序示例:

public class Test {
    public static void main(String[] args) {
        // System.out.println(10 / 0);  // Exception in thread "main" java.lang.ArithmeticException: / by zero
        System.out.println(10.0 / 0);   // Infinity
        System.out.println(-10.0 / 0);  // -Infinity
        System.out.println(0.0 / 0);    // NaN
        System.out.println(-0.0 / 0);   // NaN
        System.out.println(0.0 / 0.0);  // NaN
        System.out.println(0 / 0.0);    // NaN
    }
}

所有 NaN 的值都认为是不相同的,包括它自己,因此不能这样检测一个特定结果是否等于 Double.NaN:

if (x == Double.NaN) // is never true

可以使用 Double.isNaN 方法来判断:

if (Double.isNaN(x)) // check whether x is "not a number"

这适用于 x 是 double 类型的,如果 x 是 float 类型,则使用 Float.isNaN()。

System.out.println(Double.NaN == Double.NaN);  // false

浮点型字面量也可以加下划线。

程序示例:

public class Test {
    public static void main(String[] args) {
        double a = 1.000_000_999;
        System.out.println(a);  // 1.000000999
    }
}

char

char 是无符号的,char 的本质是用来表示 Unicode 字符,不是用来做数值计算的。

单个字符用 char,多个字符用 String。

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

在 Java 中 char 的本质是一个整数,在输出时输出的是 Unicode 码对应的字符。可以直接给 char 赋一个整数,然后输出时会输出对应的 Unicode 字符。char 类型是可以进行运算的,相当于一个整数,因为它有对应的 Unicode 码。

char 类型的字面量值要用单引号括起来。例如:'A' 是编码值为 65 的字符常量,它与 "A" 不同,"A" 是包含一个字符的字符串。

char 类型的值可以表示为十六进制值,其范围从 \u0000 ~\uFFFF。例如 \u2122 表示商标符号(™),\u03C0 表示希腊字母 π。

字符在计算机中的存储:根据码表找到这个字符的编码,再将编码转为二进制,在计算机中存储这个二进制。读取字符时要由二进制根据编码转换为字符。

编码转换的网址:https://tool.chinaz.com/Tools/Unicode.aspx

程序示例:

public class Test {
    public static void main(String[] args) {
        char ch1 = 'a';
        char ch2 = 97;
        System.out.println(ch1);       // a
        System.out.println(ch2);       // a
        System.out.println(ch1 + 100); // 字符型参与运算, 输出 197
    }
}

字符型字面量要用单引号括起来,内容有且只能有一个,即内容不能为空。字符串类型的字面量用双引号括起来,内容可以为空,比如: ""

转义字符

转义字符是字符型常量。可以在加引号的字符字面量或字符串中使用这些转义序列,例如 '\u2122' 或 "Hello\n" 或 "\t"。

\u:后面跟随四位十六进制数

\t:制表符,在打印的时候将前面的字符串的长度补齐到 8 或者 8 的倍数,最少补 1 个空格,最多补 8 个空格

\n:换行符

\r:回车

\\:一个 \

\\\\:两个 \,即 \\

\":一个 "

\':一个 '

程序示例:

public class Test {
    public static void main(String[] args) {
        System.out.println("************************");
        System.out.println("abc" + '\t' + "def");    // \t 也可以用双引号括起来
        System.out.println("12345678" + "\t" + "abc");  // 输出: 12345678        abc, 因为最少要补 1 个空格, 最多补 8 个空格
    }
}

执行结果:

************************
abc     def
12345678        abc

程序示例:

public class Test {
    public static void main(String[] args) {
        // \t: 制表位
        System.out.println("天津\t上海\t北京");
        // \n: 换行符
        System.out.println("天津\n上海\n北京");
        // \\: 一个 \
        System.out.println("Hello\\ok");
        System.out.println("C:\\Windows\\apppatch");
        // \ 会把后面那个字母当成是一个转义符的一个组合,
        // 如果组合起来之后是一个合法的转义符, 就正常编译, 否则报错: 非法转义符
        // 反正就一定会当成是一个转义符, 然后去判断是否是合法的转义符
        // 输出两个 \\:
        System.out.println("C:\\\\Windows\\\\apppatch");
        // \": 一个"
        System.out.println("他说:\"你好.\""); // 输出: 他说:"你好."
        // \': 一个'
        // \r: 一个回车
        System.out.println("好的可以\r你好"); // 输出: 你好可以
        System.out.println("好的可以\r\n你好");
    }
}

执行结果:

天津    上海    北京
天津
上海
北京
Hello\ok
C:\Windows\apppatch
C:\\Windows\\apppatch
他说:"你好."
你好可以
好的可以
你好

转义序列 \u 还可以在加引号字符常量或字符串之外使用(而其他所有转义序列不可以),Unicode 转义序列会在解析代码之前处理。例如:

public static void main(String\u005B\u005D args)

是完全合法的,\u005B 和 \u005D 分别是 [ 和 ] 的编码。

例如,"\u0022+\u0022" 当中,\u0022 会在解析之前转换为 ",这会得到 ""+"",也就是一个空串。

更隐秘地,一定要当心注释中的 \u。以下注释:

// \u000A is a newline

会产生一个语法错误,因为读程序时 \u000A 会替换为一个换行符。

下面这个注释:

// look inside c:\users

也会产生一个语法错误,因为 \u 后面并没有跟着 4 位十六进制数。

下面这个注释:

// look inside c:\\users

则不会报错。

字符编码

1️⃣ ASCII 码表

用一个字节表示,一共 128 个字符。扩展的 ASCII 码表一共 256 个字符。

2️⃣ Unicode 码表

Unicode 编码表的编码大小固定,使用两个字节来表示字符,字母和汉字统一都是占用两个字节,这样浪费空间。Unicode 的好处是使用一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,使用 Unicode 没有乱码的问题。

2 的 16 次方是 65536,所以最多可以表示 65536 个字符。

编码 0-127 的字符与 ASCII 的编码一样。比如 'a' 在 ASCII 码是 0x61,在 Unicode 码是 0x0061,都对应 97。因此 Unicode 码兼容 ASCII 码。

3️⃣ UTF-8 码表

UTF-8 是 Unicode 是一种改进,是在互联网上广泛使用的码表。UTF-8 是一种变长的编码方式,可以使用 1-6 个字节表示一个符号,根据不同的符号而变化字节长度,字母使用 1 个字节,汉字使用 3 个字节。

4️⃣ GBK 码表

可以表示汉字,而且范围广,字母使用 1 个字节,汉字 2 个字节。

5️⃣ GB2312 码表

可以表示汉字,GB2312 可以表示的汉字个数少于 GBK。

6️⃣ Big5 码表

可以表示繁体中文,多用于台湾、香港。

boolean

boolean 类型的字面量只有 true 和 false 两个值。

Java 中 int 与 boolean 两种类型并不相容,不可以用 0 或非 0 的整数代替 false 和 true。

例如下面的代码是错误的,不能用整数作为测试条件,只能用 boolean 值作为测试条件。

int x = 1;
while(x)
{}

下面的代码是正确的:

boolean isOK = true;
while(isOK)
{}
// 或者
int x = 1;
while(x == 1)
{}

打印 boolean 字面量:

public class Test {
    public static void main(String[] args) {
        // 打印布尔型字面量
        System.out.println(true);
        System.out.println(false);
    }
}

执行结果:

true
false

不可以将处于 boolean 范围内的 int 类型的常量赋值给 boolean 类型的变量,boolean 值只有 true 和 false。

程序示例:

public class Test {
    public static void main(String[] args) {
        // boolean a = 10;  // Type mismatch: cannot convert from int to boolean
    }
}

自动类型转换

自动类型转换是把精度小的类型自动转换为精度大的数据类型。自动类型转换也叫隐式转换或自动类型提升。

总的原则:当 Java 程序进行赋值或算术运算时,精度小的类型自动转换为精度大的数据类型,这就是自动类型转换。

数据类型按精度(容量)大小排序为:

char ➡️ int ➡️ long ➡️ float ➡️ double

byte ➡️ short ➡️ int ➡️ long ➡️ float ➡️ double


图 3

图中有 6 个实线箭头,表示无信息丢失的转换;另外有 3 个虚线箭头,表示可能有精度损失的转换。例如,123456789 是一个大整数,它包含的位数多于 float 类型所能表示的位数。将这个整数转换为 float 类型时,数量级是正确的,但是会损失一些精度。

其他规则:

🟢 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。

🟢 当把精度(容量)大的数据类型赋值给精度(容量)小的数据类型时就会报错,反之就会进行自动类型转换。

🟢 (byte,short)和 char 之间不会相互自动转换。

🟢 byte,short,char 它们三者可以计算,在计算时首先转换为 int 类型。

🟢 boolean 不参与自动转换。

当用一个二元运算符连接两个值时,先要将两个操作数转换为同一种类型,然后再进行计算。

如果两个操作数中有一个是 double 类型,另一个操作数就会转换为 double 类型;

否则,如果其中一个操作数是 float 类型,另一个操作数将会转换为 float 类型;

否则,如果其中一个操作数是 long 类型,另一个操作数将会转换为 long 类型;

否则,两个操作数都将被转换为 int 类型。

程序示例:

int n1 = 10;
// 有多种类型的数据混合运算时, 系统首先自动将所有数据转换成容量最大的那种数据类型, 然后再进行计算. 
// float f1 = n1 + 1.1; // Type mismatch: cannot convert from double to float
float f1 = n1 + 1.1F;
System.out.println(f1); // 11.1

程序示例:

// 当把精度 (容量) 大的数据类型赋值给精度 (容量) 小的数据类型时, 就会报错, 反之就会进行自动类型转换.
// int a = 1.1; // Type mismatch: cannot convert from double to int
// 把一个常数 (必须是整数类型) 赋值给 byte 时, 先看这个常数是否在 byte 范围内 (-128~127)
// 如果在范围内, 则可以赋值
byte b1 = 10;
System.out.println(b1); // 10
// byte b3 = 1.1;  // Type mismatch: cannot convert from double to byte
// 把一个变量赋值给 byte 时, 要求变量必须也是 byte 类型
// int a1 = 10;
// byte b2 = a1; // Type mismatch: cannot convert from int to byte

程序示例:

// byte, short, char 他们三者可以计算, 在计算时首先转换为 int 类型.
byte b6 = 10;
char c1 = 2;
// byte b7 = b6 + c1; // Type mismatch: cannot convert from int to byte
int n2 = b6 + c1;
System.out.println(n2); // 12

程序示例:

// byte, short, char 他们三者可以计算, 在计算时首先转换为 int 类型.
byte b8 = 10;
byte b9 = 10;
byte b10 = b8 + b9; // Type mismatch: cannot convert from int to byte

隐式类型转换就是在补码前面加零:

public class test4 {
    public static void main(String[] args) {
        byte a = 10;    // 0000 1010
        int b = a;      // 0000 0000 0000 0000 0000 0000 0000 1010
        System.out.println(b);
    }
}

整型字面量在范围内时可以降级赋值,用后缀 L 或 l 指定的常量除外。但是用变量赋值时即便值在范围内也不可以降级赋值。浮点型不论是字面量还是变量都不允许降级赋值。总结起来一句话,int 类型的字面量在 short,byte 和 char 的范围内时,可以降级赋值,其余情况均不允许降级赋值。

程序示例:

public static void main(String[] args) {
    short a = 100;                 // 没问题, 是字面量降级赋值, int → short, 在范围内
}

程序示例:

public static void main(String[] args) {
    int a = 100L;                 // 报错: java: 不兼容的类型: 从 long 转换到 int 可能会有损失
}

程序示例:

public static void main(String[] args) {
    float a = 100;               // 没问题, 可以将 int 赋值给 float, 同样, 也可以将 int 赋值给 double
    System.out.println(a);       // 100.0
}

程序示例:

public static void main(String[] args) {
    // 加了一点, 变成了 double 类型
    // 报错: java: 不兼容的类型: 从 double 转换到 float 可能会有损失
    float a = 100.;               
    System.out.println(a);        // 100.0
}

程序示例:

public class Test {
    public static void main(String[] args) {
        byte b1 = 10;   // 可行, 在范围内的 int 类型常量可以赋值
        byte b2 = 10L;  // 报错
        int a = 10;
        byte b3 = a;    // 报错, 在范围内的 int 类型变量不可以赋值
 
        short s1 = 10;   // 可行, 在范围内的 int 类型常量可以赋值
        short s2 = 10L;  // 报错
        int b = 10;
        short s3 = a;    // 报错, 在范围内的 int 类型变量不可以赋值
 
        char c1 = 10;   // 可行, 在范围内的 int 类型常量可以赋值
        char c2 = 10L;  // 报错
        int c = 10;
        char c3 = a;    // 报错, 在范围内的 int 类型变量不可以赋值
    }
}

强制类型转换

强制类型转换是自动类型转换的逆过程,是将精度大的类型转换为精度小的数据类型。

当需要精度高的数据类型转换为精度低的数据类型时,就要用强制类型转换。

使用强制类型转换时要加上强制转换符 ( ),在圆括号中指定想要转换的目标类型,后面紧跟待转换的变量名。但可能造成精度降低或溢出,因此要格外要注意。

强制类型转换只针对最近的操作数有效,可以使用小括号修改优先级。

程序示例:

public class Test {
    public static void main(String[] args) {
        int i1 = (int) 1.9;
        System.out.println(i1); // 1, 非四舍五入, 直接丢弃小数点后的内容, 精度损失
        byte b1 = (byte) 2000;
        System.out.println(b1); // -48, 数据溢出
        int n1 = 2000;
        byte b2 = (byte) n1;
        System.out.println(b2); // -48, 数据溢出
        double d1 = 121.42;
        double d2 = 121.52;
        System.out.println((int) d1);  // 121
        System.out.println((int) d2);  // 121
    }
}

byte,char 和 short 可以接收处于范围内的 int 类型的整数,但是不能接收 int 类型的变量,如果需要则要进行强制类型转换。

程序示例:

// 强制类型转换只针对最近的操作数有效, 可以使用小括号修改优先级.
public class ForceConvertDetail {
    public static void main(String[] args) {
        // int x = (int) 10 * 3.5 + 6 * 1.5; // Type mismatch: cannot convert from double to int
        // System.out.println(x);
        int y = (int) (10 * 3.5 + 6 * 1.5); // 44
        System.out.println(y);
    }
}
public class ForceConvertDetail {
    public static void main(String[] args) {
        byte b1 = 1, b2 = 2;
        byte b3 = b1 + b2;            // 报错: java: 不兼容的类型: 从 int 转换到 byte 可能会有损失
        byte b3 = (byte) (b1 + b2);   // 正确
    }
}
public class Test {
    public static void main(String[] args) {
        int a = 97;
        char c = (char) a;
        char b = 97;
        System.out.println(c);   // a
        System.out.println(b);   // a, 加不加强制类型转换都是一样的, 因为 97 在 0-65535 之间, 可以接收
        char d = a;              // 报错, 用变量赋值需要强转
    }
}

强制类型转换就是删除补码前面多出来的位:

public class Test {
    public static void main(String[] args) {
        int a = 300;            // 0000 0000 0000 0000 0000 0001 0010 1100
        byte b = (byte) a;      // 0010 1100
        System.out.println(b);  // 44
    }
}
public class Test {
    public static void main(String[] args) {
        int a = 200;            // 0000 0000 0000 0000 0000 0000 1100 1000
        byte b = (byte) a;      // 1100 1000
        System.out.println(b);  // -56
    }
}

不要在 boolean 类型与任何数值类型之间进行强制类型转换,这样可以防止发生一些常见的错误,只有极少数的情况下需要将一个 boolean 值转换为一个数,此时可以使用条件表达式 b ? 1 : 0

posted @ 2026-03-22 18:12  YouKong  阅读(11)  评论(0)    收藏  举报