Java 笔记 04:Java 数据类型基础,数据类型转换,及其相关场景拓展

一、前言

记录时间 [2024-04-22]

系列文章简摘:
Java 笔记 01:Java 概述,MarkDown 常用语法整理
Java 笔记 02:Java 开发环境的搭建,IDEA / Notepad++ / JDK 安装及环境配置,编写第一个 Java 程序
Java 笔记 03:Java 基础知识,使用 IDEA 创建 Java 项目、设置注释颜色,以及自动生成 JavaDoc

本文介绍了 Java 数据类型和数据类型转换,包括数据类型的八大分类,以及各种数据类型的问题拓展,介绍其在不同应用场景下可能存在的问题。


二、数据类型基础

1. 强类型语言

强类型语言要求变量的使用必须严格符合规定,且所有变量都必须先定义后使用,因而安全性高。

Java 就是一种强类型语言。

弱类型语言,如,VB、JavaScript,它们的要求就相对来说没有那么高。

例如,String 是字符串类型,那么它的内容只能是字符串;int 是整数类型,那么它的内容只能是整数。否则就会报错。

// 这段用于说明Java是强类型语言
String a = "hello";
int num = 10;

2. 数据类型分类

  • 基本类型(primitive type)
  • 引用类型(reference type)

在这里插入图片描述


基本类型(primitive type)

Java 基本数据类型分为 8 类,分别是:byte;short;int;long;float;double;boolean;char;表示的类型和范围如上图所示。

基本数据类型在 Java 中的定义方式:

注意:字符串 String 不是关键字,String 是一个类。

// 八大基本数据类型
public class Demo02 {

    public static void main(String[] args) {

//        整数
        int num1 = 10;  // 最常用
        byte num2 = 20;
        short num3 = 30;
        long num4 =30L;     // Long 类型要在数字后面加 L

//        小数:浮点数
        float num5 = 50.1F;     // float 类型要在数字后面加 F
        double num6 = 3.141592653589793;

//        字符
        char name = 'A';		// char 里面只能写一个字符
//        字符串,String 不是关键字,String 是一个类
//        String namea = "Ahhh";

//        布尔值:是非
        boolean flag = true;
        boolean flag1 = false;

    }

}

如何查看数据类型的取值范围?以 int 举例:

在 Java 中存在这样一个类:Integer,其中规定了 int 类型的取值范围。

public final class Integer extends Number implements Comparable<Integer> {

    // MIN_VALUE 最小值 -2^31
    @Native public static final int   MIN_VALUE = 0x80000000;

    // MAX_VALUE 最大值 2^31
    @Native public static final int   MAX_VALUE = 0x7fffffff;

    @SuppressWarnings("unchecked")
    public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
	// ...
}

引用类型(reference type)

引用类型包括类、接口和数组,除了基本数据类型,基本都是引用类型。


3. 什么是字节

字节概述

  • 位(bit):是计算机内部数据储存的最小单位,11001100 是一个 8 位二进制数;
  • 字节(byte):是计算机中数据处理的基本单位,习惯上用大写 B 来表示;
  • 字符:是指计算机中使用的字母、数字、字、符号。

字节单位转换

常见的字节单位有:bit;B;KB;MB;GB;TB 等。

  • 1 bit 表示 1 位,可以表示二进制的 0 或者 1
  • 1 Byte 表示 1 个字节

基本换算单位如下:

1 B = 8 b(bit)
1024 B = 1 KB
1024 KB = 1 MB
1024 M = 1 G
1024 G = 1 T

计算机 32 /64 位的区别

这里的,就是字节单位中的位(bit)

32 位的计算机只能安装 32 位的操作系统;64 位的计算机 32 / 64 位的操作系统都能安装。


三、数据类型拓展

1. 整数拓展:进制问题

在生活中,我们常用十进制 0~9来进行数字计算,逢十进一。

在计算机中,我们还将接触到:二进制、八进制、十六进制

各种进制在计算机中的表示如下:

  • 二进制:0b,数字前面加一个 0b
  • 八进制:010,数字前面加一个 0
  • 十进制:正常写
  • 十六进制:0x10,数字前面加一个 0x

例如,在 Java 中各类进制的定义方式:

//        整数拓展:     进制
//        二进制 0b   十进制    八进制 0   十六进制 0x

int i = 10;
int i2 = 010;	// 八进制
int i3 = 0x10;  // 十六进制  0~9 A~F

// 输出查看,是以十进制形式输出的
System.out.println(i);	// 10
System.out.println(i2);	// 8
System.out.println(i3);	// 16

2. 浮点数拓展:银行业务

思考一个问题:银行业务,比如计算金额,能使用浮点数吗?

案例分析

先来查看这样一个案例:

定义了两个浮点数,分别是 float 类型的 f,和 double 类型的 d,给它们的赋值都为 0.1,正常情况下,f 和 d 的值应该是相等的。

public class Demo03 {

    public static void main(String[] args) {

        // 定义 f 和 d
        float f = 0.1f;     //0.1
        double d = 1.0/10;  //0.1

        // 输出 f 和 d 的值进行查看
        System.out.println( "f = " + f);
        System.out.println( "d = " + d);

        // 判断 f 和 d 是否相等
        System.out.println( "f == d ? " + (f==d) );   //false

    }

}

程序输出的结果如下:显示 f 和 d 不相等。

f = 0.1
d = 0.1
f == d ? false

再看下面这个案例:

定义了两个浮点数 d1 和 d2,都是 float 类型的,我们把 d2 的值设置成 d1+1 的值,然后判断 d1 和 d2 是否相等,事实上它们是不相等的。

public class Demo03 {

    public static void main(String[] args) {

        // 定义浮点数 d1 和 d2
        float d1 = 2323233333333333333f;
        float d2 = d1 + 1;	// 让 d2 等于 d1+1
        
        // 输出 d1 和 d2 的值进行查看
        System.out.println( "d1 = " + d1);
        System.out.println( "d2 = d1 + 1 = " + d2);
        
        // 判断 d1 和 d2 是否相等
        System.out.println( "d1 == d2 ? " +  (d1==d2) ); //true

    }

}

程序输出的结果如下:显示 d1 和 d2 相等。

d1 = 2.32323343E18
d2 = d1 + 1 = 2.32323343E18
d1 == d2 ? true

误差背后的原因

由于取值范围的约束,浮点数类型表示的小数位数有限,是离散的。而计算中经常会出现计算值为无限小数的情况,浮点数不能把无限小数全部表示出来,就会进行舍入,因而存在舍入误差,出现了上面那些数值比较的问题。

可以说,浮点数类型表示数值,只能是大约,接近但不等于

最好完全避免使用浮点数进行比较,而是使用 BigDecimal 数学工具类。

import java.math.BigDecimal;

解决方案

如何正确比较两个浮点数呢?除了使用 BigDecimal 数学工具类,还可以使用作差的方式。

简单介绍下作差的方式:如果两个浮点数之差的绝对值小于 0.0001,那么判断它们是相等的。

public class Demo03 {

    public static void main(String[] args) {

        // 定义 f 和 d
        float f = 0.1f;     //0.1
        double d = 1.0/10;  //0.1

        // 输出 f 和 d 的值进行查看
        System.out.println( "f = " + f);
        System.out.println( "d = " + d);

        // 作差绝对值,判断 f 和 d 是否相等
        System.out.println( "Math.abs(f-d) < 0.0001 ? " +  (Math.abs(f-d) < 0.0001) );    //true
       
    }

}

程序输出的结果如下:显示 f 和 d 相等。

f = 0.1
d = 0.1
Math.abs(f-d) < 0.0001 ? true

3. 字符拓展

关于 ASCII 和 Unicode

ASCII 和 Unicode 是计算机中的两种字符编码标准。详细的编码放置在文末附录部分,需要的朋友可以参考。

  • ASCII(American Standard Code for Information Interchange)使用 7 位表示 128 个字符,包括基本的拉丁字母、数字、标点符号等。ASCII 编码主要适用于英文字符,不支持多字节字符,因此无法表示汉字和其他非英文字符
  • Unicode 旨在为世界上的所有字符提供一个唯一的编码。Unicode 使用16 位(基础的 BMP 区域)或更多位来表示字符,因此可以表示全球各种语言中的字符,包括汉字、日文字、韩文字等。

关于字符类型

所有的字符本质上是数字,字符有对应的编码。

在 Java 中,字符类型 char ,占 16 位,存储形式为 Unicode 编码。例如,当在 Java 中声明一个字符变量并赋予一个字符(如汉字)时,该字符实际上是使用 Unicode 编码存储的。

当强制将 char 类型的变量转换为 intbyte 类型时,它实际上是将 Unicode 编码值转换为对应的数值。对于英文字符而言,这个数值就是其 ASCII 编码值。

例如下面这个例子:

char ch = 'A';  // ch 的 ASCII 值是 65, Unicode 值是 U+0041
char zh = '中'; // zh 的 Unicode 编码值是 U+4E2D

int asciiValue = (int) ch; // asciiValue 的值是 65
int unicodeValue = (int) zh; // unicodeValue 的值是 20013
// 4E2D 是十六进制,转换成十进制就是 20013

可能会存在这样一个疑问,字符 A 的 ASCII 值是 65, Unicode 值是 U+0041,为什么说 ASCII 和 Unicode 值是一样的呢?这主要是进制转换问题,0041 是十六进制,转换成十进制就是 65。

char 中使用 Unicode 编码(十六进制)形式存储字符,强制类型转换为 int 时,输出为十进制。

我们还可以直接将 Unicode 编码赋值给 char 类型变量:

char c3 = '\u0061';
System.out.println(c3);     //a

由编码表可知,0061 是 a 的编码,故 c3 的值为 a;其中,\u 是转义字符。


转义字符

如上述所说,\u 是转义字符。

在编程中,转义字符是一种特殊的字符序列,用于表示一些不可见的字符或特殊字符,例如换行符、制表符或引号等。在 Java 中,转义字符以反斜杠 \ 开头。

通过使用这些转义字符,可以在字符串和字符常量中插入或表示特殊的字符。

以下是一些常见的 Java 转义字符:

  • \n:换行符(newline),将光标移到下一行开头。
  • \r:回车符(carriage return),将光标移到当前行的开头。
  • \t:制表符(tab),在文本中插入一个制表符。
  • \\:反斜杠自身,用于表示一个单独的反斜杠字符。
  • \':单引号,用于在字符或字符常量中表示单引号。
  • \":双引号,用于在字符串中表示双引号。
  • \uXXXX:Unicode 转义序列,其中 XXXX 是一个 4 位十六进制数,用于表示 Unicode 字符。

例如,可以使用 \n 来插入一个换行符:

System.out.println("Hello,\nWorld!");

其输出形式为:

Hello,
World!

或者使用 \t 插入一个制表符:

System.out.println("Name\tAge");

其输出形式为:

Name    Age

判断字符串相同

先来分析这样一个案例:我们使用 String 类定义了 4 个字符串变量,它们的内容都是 hello world,但 stra 和 strb 使用的是构造对象的方式,strc 和 strd 是直接赋值的方式。

判断:stra 和 strb 是否相同?strc 和 strd 是否相同?

public class Demo03 {

    public static void main(String[] args) {

        // 对象  从内存分析
        String stra = new String("hello world");
        String strb = new String("hello world");
        System.out.println(stra == strb);   //false

        String strc = "hello world";
        String strd = "hello world";
        System.out.println(strc == strd);   //true

    }

}

产生疑问:根据判断的结果,stra 和 strb 不相同;strc 和 strd 相同。

为什么呢?明明 stra 和 strb 中都是 hello world,怎么不同呢?

原因在于:在 Java 中,stra 和 strb 是 String 类构造的两个对象,使用 == 运算符比较两个对象引用时,它比较的是对象的引用地址,而不是对象的内容。

在上述代码中,strastrb 是两个不同的对象,即使它们包含相同的字符串内容 hello world。因此,使用 == 运算符比较它们会返回 false因为它们的引用地址不同

如果想比较两个字符串对象的内容是否相同,应该使用 equals() 方法。

例如:在下面的代码中,equals() 方法比较的是两个字符串对象的内容,因此输出为 true,表示两个字符串的内容相同。

String strb = new String("hello world");
System.out.println(stra.equals(strb)); // 输出 true

4. 布尔值拓展

在 Java 中,布尔表达式本身已经是 truefalse,所以在检查布尔变量时,你可以直接使用变量名而不是与 true 进行比较。

  • if (flag == true) {}:这是一种显式的写法,对于初学者可能更容易理解,但是相对冗长。
  • if (flag) {}:这是一种简洁的写法,对于有经验的开发者来说,这种写法更为常见和推荐。
// 布尔值扩展
boolean flag = true;

if (flag == true) {}    // 新手
if (flag) {}            // 老手
// Less is More  代码要精简易读

两者在功能上是等效的,都是检查 flag 是否为 true。然而,第二种写法更为简洁,更符合 Java 的编码习惯和约定。


四、类型转换

由于 Java 是强类型语言,运算时会涉及类型转换问题。

数据类型的优先级,从低到高依次为:byte, short, char -> int -> long -> float -> double

小数优先级大于整数

运算中,不同类型的数据要先转化为同一类型,然后进行运算。


1. 强制类型转换

强制类型转换,是数据类型优先级从高转向低,使用时需要在变量前面加 (类型)

来看这样一个案例:将一个 int 类型的变量 i 的值,通过强制类型转换赋值给一个 byte 类型的变量 b,那么 b 的值是什么?

// i 是 int 类型的
int i = 128;

// 把 i 强制转换为 byte 类型
byte b = (byte) i;  // b 的值实际上是 -128,而不是 128

b 的值实际上是 -128,而不是 128,这是由于数据溢出导致的。因为 byte 类型的取值范围是 -128 ~ 127,当将一个大于 byte 类型最大值或小于最小值的整数值赋给 byte 类型变量时,会发生数据溢出。

在上述例子中,int i = 128; 的值超出了 byte 类型的范围。将这个 int 值转换为 byte 类型时,会发生数据溢出。数据溢出会导致结果不可预测,通常是截断最高有效位,得到一个不正确的值。

在进行强制类型转换之前,最好检查值是否在目标数据类型的有效范围内。


2. 自动类型转换

自动类型转换,是数据类型优先级从低转向高,使用时不需要做处理。

比如,int 类型可以自动转换成 double 类型。

int i = 128;
double a = i;

3. 注意点

  • 不能对布尔类型进行转换;
  • 不能把对象类型转换为不相干的类型;
  • 在把高容量转换到低容量时,是强制转换;
  • 转换时可能存在内存溢出,或者精度问题;
  • char 类型也可以和数值类型互相转换。

精度问题

在 Java 中,当将一个浮点数转换为整数类型时,会执行向下取整操作。这意味着小数部分会被舍弃,只保留整数部分。

// 将浮点数转换成整数,转换时遇到精度问题
System.out.println( (int)23.7 );    //23
System.out.println( (int)-45.89f );     //-45

char 和数值类型互转

在 Java 中,当对 char 类型的字符进行数学运算时,字符会被隐式转换为其对应的 Unicode编码值。

char 类型也可以和数值类型互相转换。

// char 类型也可以和数值类型互相转换
char c = 'a';
int d = c + 1;
System.out.println(d);  //98
System.out.println( (char)d );  //b

在上面的代码中:

  • char c = 'a';:这里 c 的 Unicode 编码值是 97,因为小写字母 a 的 Unicode 编码值是 97。
  • int d = c + 1;:在这里,字符 a 被转换为其 Unicode 编码值 97。然后,这个值加上 1,得到的结果是 98。
  • 所以,d 的值是 98,这是字符 a 的下一个字符 b 的 Unicode 编码值。

溢出问题

操作比较大的数的时候,注意溢出问题。

分析这样一个案例:给 money 赋值为 10 亿,year 赋值为 20,money 和 year 均为 int 类型。

当 money 和 year 相乘时,是否能得到 200 亿?

public class Demo06 {

    public static void main(String[] args) {

        // JDK7 新特性,数字之间可以用下划线分割,下划线不会被输出。
        int money = 10_0000_0000;
        int years = 20;
        int total1 = money * years;
        System.out.println("long total1 = " + total1);  //-1474836480 , 计算时溢出了

        long total2 = money * years;  // 默认是int,转换之前就已经出问题了
        System.out.println("long total2 = " + total2);  //-1474836480

        // 解决问题:先把一个转换为 Long
        long total3 = money * ((long)years);
        System.out.println("long total3 = " + total3);

    }

}

在上述案例中,moneyyears 都是 int 类型,所以当执行 money * years 运算时,结果会是 int 类型。结果超过了 int 类型的最大值(Integer.MAX_VALUE,约为 2^31 - 1,即 2147483647),所以会发生整数溢出。即使使用 long 类型来存放结果,也无济于事。

这导致结果 total1total2 的值是负数 -1474836480

正确的做法是:先将其中一个操作数转换为 long 类型,然后执行乘法运算:首先将 moneyyear 强制转换为 long 类型,然后执行乘法运算,这样可以避免整数溢出,并得到正确的结果。


五、总结

本文介绍了 Java 数据类型和数据类型转换,包括数据类型的八大分类,以及各种数据类型的问题拓展,介绍其在不同应用场景下可能存在的问题。

1. 附录:ASCII 码表

在这里插入图片描述


2. 一些参考资料

狂神说 Java 零基础:https://www.bilibili.com/video/BV12J41137hu/
TIOBE 编程语言走势: https://www.tiobe.com/tiobe-index/
Typora 官网:https://www.typoraio.cn/
Oracle 官网:https://www.oracle.com/
Notepad++ 下载地址:https://notepad-plus.en.softonic.com/
IDEA 官网:https://www.jetbrains.com.cn/idea/
Unicode 汉字编码表:https://max.book118.com/html/2017/0125/86765015.shtm

  • 25
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值