Java 04 – Java的数据类型及数据类型之间的转换

Java 04 – Java的数据类型及数据类型之间的转换

1 数据类型

1.1 计算机的存储单元

变量是内存中的小容器, 用来存储数据.

(1) 计算机内存是怎么存储数据?

无论是内存还是硬盘, 计算机存储设备的最小信息单元位(bit), 又叫作“比特位”, 通常用小写字母b表示.

计算机的最小的存储单元字节(Byte), 通常用大写字母B表示, 字节由连续的8个比特位组成, 即1Byte=8bit.

(2) 常用的存储单位:

2^{10} = 1024 —— 这是计算机中的“整数”. 其他存储单位之间的换算方式如下:

1B(字节) = 8bit 1KB = 1024B
1MB = 1024KB 1GB = 1024MB
1TB = 1024GB 1PB = 1024TB

1.2 为什么要有数据类型?

Java语言是强类型语言, 对每一种数据都定义了明确的具体数据类型;

不同数据类型的变量/常量在内存中将占用不同大小的内存空间.

—— 这样一来就能更有效地利用内存.

1.3 Java语言中的数据类型

Java语言中的数据类型分为: ① 基本数据类型, ② 引用数据类型.

(1) 基本数据类型:

四类 八种 Byte数 能表示的数据范围 默认值
整型 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 -2^(63) ~ 2^(63) – 1 0
浮点型 float 4 -3.403E38 ~ 3.403E38 +0
double 8 -1.798E308 ~ 1.798E308 +0
字符型 char 2 0 ~ 65535 null code point (‘\u0000’)
布尔型 boolean 1 只有2个值: true 与 false false

注意: Java中整数默认是int, 小数默认是double.

1.4 Java虚拟机中的数据类型

在Java虚拟机中的数据类型只分为两类: 原始类型和引用类型.

1.4.1 原始类型(primitive type)

原始类型对应的数值称为原始值, 有如下3类:

① 数值类型, 包括byte、short、int、long、char、float、double.

② boolean类型, 只有truefalse两个值, 默认是false —— 虽然在JVM中定义了boolean类型, 但是却没有指令直接支持其操作, 所以 boolean类型的操作都需要在编译后用虚拟机中的int类型来表示 —— 1表示true, 0表示false.

③ returnAddress类型: 这种类型用在JVM中, 表示指向某个操作码opcode的指针, 此操作码与虚拟机指令相对应. 与原始类型不同, returnAddress类型不对应任何Java类型, 并且不能被运行中的程序修改.

1.4.2 引用类型(reference type)

引用类型的数值称为引用值, 包括:

① 类类型(class type), 指向动态创建的类实例.

② 数组类型(array type), 指向动态创建的数组实例.

③ 接口类型(interface type), 指向实现了某个接口的类/数组实例.

引用类型和Java中对象的引用有关, 在虚拟机中, 对象表示为某个类的实例或者某个数组, 指向此对象的引用就用引用类型(reference)来表示.

关于引用类型的值, 可以比作指向对象的指针, 每个对象可能存在多个指向它的引用reference, 对象的操作、传递和检查都通过引用它的reference类型数据来进行.

在引用类型中还有一个特殊的值null, 当一个引用不指向任何对象时, 它就用null表示. null作为引用类型的初始默认值可以转型成任意的引用类型.

1.5 扩展: 关于负数与进制

(1) 负数的二进制表现形式: 对应正数的二进制取反加1(二进制位都为1, 对应十进制的-1).

(2) 8进制以数字0开头表示, 16进制以0x开头表示.

2 数据类型转换

2.1 隐式数据类型转换

所谓隐式转换, 就是Java虚拟机内部自动实现, 对程序开发人员来说, 这个转换是透明的. 具体是指:

取值范围小的数据类型与取值范围大的数据类型进行运算, 会先将小的数据类型提升为大的,再运算.

—— 隐式数据类型转换又被叫作自动类型提升.

2.1.1 隐式转换规则

基本数据类型之间会发生隐式类型转换的情形:

(byte, short, char) -> int -> long -> float -> double

说明:

byte、short、char类型的值都会被提升为int类型, 再去计算;

② 只要有一个操作数是long类型, 计算结果就是long 类型;

③ 只要有一个操作数是float类型, 计算结果就是float类型;

④ 只要有一个操作数是double类型, 计算结果就是double类型.

2.1.2 char转换为int的示例

System.out.println('a');       // 结果是 a
System.out.println('a' + 1);   // 结果是 98

说明: 'a' + 1操作存在隐式数据类型转换 —— 由于字符'a'在ASCII码中对应的 int 值是97, 所以在执行'a' + 1操作后, 结果就是98.

2.1.3 其他示例代码

/**
 * "+" 是一个运算符, 做加法运算的. 运算时, 一般要求参与运算的数据的类型要保持一致
 * @author Heal Chow
 */
public class TypeCastDemo {
    public static void main(String[] args) {
        // 直接输出运算的结果
        System.out.println(3 + 4);   //7

        // 定义两个int类型的变量
        int a = 3;
        int b = 4;
        int c = a + b;
        System.out.println(c);       // 7

        // 定义一个byte类型, 定义一个int类型
        byte bb = 2;
        int cc = 5;
        System.out.println(bb + cc);  // 7

        // 能不能不直接输出, 而是用一个变量接收呢? 
        // 用变量接收, 这个变量应该有类型
        // byte dd = bb + cc;    // 可能损失精度: int型数据范围比byte型的大, byte表示不完整
        int dd = bb + cc;        // 不会损失精度
        System.out.println(dd);
    }
}

2.2 强制数据类型转换

2.2.1 强制转换的格式

目标类型 变量名 = (目标类型)(被转换的数据);

例如: int num = (int) 5.0; 就是把 float 类型的数值5转换为 int 类型.

2.2.2 强制转换注意事项

如果强制转换时, 被转换的数据超出了转换后数据类型的取值范围, 得到的结果会与期望的结果不同.

—— 也就是数据在表示精度上出现了损失, 所以不建议强制转换.

2.2.3 示例代码

/**
 * 强制类型转换的示例
 * @author Heal Chow
 */
public class ForceTypeCast {
    public static void main(String[] args) {
        int a = 3;
        // byte占1个8位, 而机器中所有的整数都是默认以int型存储的4个8位),
        // 编译器检查到3在byte可表示的范围内, 就回自动降低精确度
        byte b = 4; 

        // 这里b会自动类型提升
        int c = a + b;  

        // b + a是变量, 由于操作后的结果可能大于127(byte类型变量可表示的范围上限), 
        // 就不会自动类型提升, 表现为编译不通过
        // b = b + a;

        // 强制类型转换, 易出错
        byte d = (byte) (a + b); 

        // char的值在0-65535之间, 查表知97为'a'
        char ch1 = 97; 
        char ch2 = 'a';

        // "" + X表示将X拼接到""字符串之后, 变为一个更长的字符串
        System.out.println("ch1=" + ch);                // 结果是 ch1=a
        System.out.println("ch2=" + (ch2 + 1));         // 结果是 ch2=98
        System.out.println("ch2=" + (char)(ch2 + 1));   // 结果是 ch2=b
    }
}

2.3 扩展: 能把int值转换成byte吗

答案如下:

在Java虚拟机指令集的设计中, 由于操作码的长度为1字节, 这导致了指令的总数必须控制在256个以内.

而Java有8大基本数据类型 (byte,short,int,long,float,double,char,boolean), 如果给每种类型都设计一套指令, 那么就会产生8套重复冗余的指令, 指令总数肯定会超过256个.

为了避免这个问题, Java虚拟机指令集在设计时刻意避开了一些数据类型:

大部分的指令都没有直接支持byte、char、short、boolean类型的数据, 编译器会在编译期或运行期:

① 将byteshort类型的数据 通过类型转换指令 带符号拓展 为相应的int类型的数据;

② 将booleanchar类型的数据 通过类型转换指令 零位拓展 为相应的int类型的数据.

因此, 大多对 Java 中boolean、byte、short、char类型数据的操作, 实际上都是使用 JVM 中int类型作为运算类型来操作的.

也就是说, byte、short、char、boolean类型的数据在压入栈, 或者从Java堆中获取这些类型的数据的时候, 它们就已经被转化成了int类型.

说明: 示例代码可在 我的GitHub上 查看并下载.

参考资料

Java虚拟机—字节码指令初探

(全文完)

微信公众号
微信关注《马瘦风的南墙》 在移动端阅读文章

(感谢阅读, 转载请注明作者和出处 马瘦风的南墙 , 请勿用于任何商业用途)

——=== 访问 本站404页面 寻找遗失儿童 ===——

发表评论

你的个人信息不会被公开, 注意:标记为 * 的项必填。