入门篇-其之三-基本数据类型及其转换

文章结构如下

Java的数据类型分为基本数据类型和引用数据类型,具体分类如下图:

Java数据类型

对于初学者而言,认为字符串类型String也属于基本数据类型,事实上String属于类,即引用数据类型。从String的源码中,我们就可以看出其使用的class关键字进行修饰:

image-20230906093750819

观察上述结构图我们可以发现Java的八种基本类型又可以细分成四类:整数类型、浮点类型、字符类型和布尔类型。本文将会对这四大类型一一进行讲解。

整数类型

整数类型,简称整型。Java中存储整型由四个类型组成:byteshortintlong。其中int类型最常用。这四个数据类型的对照表如下所示:

计算机存储大小 存储范围(使用数学开闭区间表示) 默认值
byte 8位,1字节 [-128, 127] 0
short 16位,2字节 [-216-1,216-1-1] 0
int(默认) 32位,4字节 [-232-1,232-1-1] 0
long 64位,8字节 [-264-1,264-1-1] 0L

在定义这四个类型的变量时,需要注意定义的整数不要超过其存储范围(尤其是byte类型,因为它的存储范围最小)。

例如:我想定义一个byteshortint类型的变量并赋值,其代码如下:

/**
 * 基本数据类型之整数类型byte、short、int
 *
 * @author iCode504
 * @date 2023-09-06 09:44:46
 */
public class BasicType {
    public static void main(String[] args) {
        byte b = 20;
        short s = 30;
        int i = 40;
        System.out.println(b);
        System.out.println(s);
        System.out.println(i);
    }
}

运行结果:

image-20230906094554236

在使用long类型的时候需要注意:long类型的数据后面需要加上Ll(不推荐小写l,因为小写l很有可能和数字1或者大写字母I混淆)。示例代码如下:

/**
 * 基本数据类型之整数类型long的使用
 *
 * @author iCode504
 * @date 2023-09-06 09:53:04
 */
public class BasicTypeLong {
    public static void main(String[] args) {
        long num1 = 88;
        long num2 = 6666666666666666666L;
        System.out.println(num1);
        System.out.println(num2);
    }
}

运行结果:

image-20230906095442555

细心的小伙伴会发现,为什么变量num1的值88后面没有加上后缀L呢?

由于整型的默认使用的int类型,而long类型的范围比int大,因此数字88会由int类型自动提升为long类型,这种现象称作自动类型提升(本文后面会讲到自动类型提升)。因此long num1 = 88;并不会报语法错误。

而变量num2的值6666666666666666666已经超出了int类型的最大范围,但是这个数字在long范围内,此时就必须要加上后缀L

以下是对定义long类型变量的总结说明:

说明 案例
int范围内的数字,可以用Ll表示long类型,也可以不使用后缀。 long num1 = 32;
long num2 = 43L;
如果表示的数字在int范围之外,但是在long的范围之内,则必须使用Ll作为后缀。 long num = 66666666666666L

如果你并不能确定所定义的整数是否在int范围,我个人的建议就是只要定义long类型的整数,就在数字后面加个后缀L

浮点类型

浮点类型,其实就是我们说的小数类型。浮点类型主要由floatdouble类型组成。其中,float类型的数值后必须要加fF为后缀,二者对照表如下所示:

计算机存储位数 存储范围(使用数学开闭区间表示) 数字后缀 默认值 精度
float 32位,4字节 [-2128,2128] fF(必须写后缀) 0.0f或0.0F 7位小数
double 64位,8字节 [-21024,21024] dD(非强制要求,一般不写后缀) 0.0 15位小数

示例代码如下:

/**
 * 基础数据类型float和double
 *
 * @author iCode504
 * @date 2023-09-06 15:25:05
 */
public class BasicTypeFloatAndDouble {
    public static void main(String[] args) {
        float f1 = 3.88f;
        float f2 = 6.44F;   // 使用F或f最为后缀都可以
        float f3 = 6;
        float f4 = 10.0f / 3f;
        System.out.println(f1);
        System.out.println(f2);
        System.out.println(f3);
        System.out.println(f4);

        double d1 = 6.66D;  // double的后缀可有可无
        double d2 = 8;
        double d3 = 10.0 / 3;

        System.out.println(d1);
        System.out.println(d2);
        System.out.println(d3);
    }
}

运行结果为:

image-20230906152845268

为什么d2的输出结果是8.0?由于8默认为int类型,给变量d2赋值时,int类型的数值会向范围更大的double转换(自动类型提升,在后面文章会讲到),而double是浮点类型,后面需要跟随小数点,默认会在后面加上.0(一位小数),即输出结果为8.0。同理,f3的输出结果为6.0。

从输出结果中我们还能看出,10 / 3得到的是无限循环小数,但是float类型变量f4输出结果保留了7位小数,而double类型变量d3输出结果保留了15位小数。由这两个输出结果可以印证两个浮点类型的精度大小。

在日常使用过程中,使用double的次数要比float多,个人总结有如下三点:

  1. float类型数值需要在必须其后面加上fF,而double不需要在值后面加后缀符。
  2. double存储范围比float的大,并且浮点类型数值默认类型就是double
  3. double的精度要比 float的高,表示的数值更加准确。

字符类型

字符类型,即char类型,用来存储单个字符,使用单引号和单个字符表示,因此在单引号中写多个字符是错误写法:

char ch1 = 'i';		// 正确写法
char ch2 = 'ijk';	// 错误写法,单引号中只能写一个字符!

char是一个单一的16位的Unicode字符,它的存储范围是[0,65535],即'\u0000''\uffff'

这里会有小伙伴问:char不是只能表示单个字符吗?这就要说到Unicode字符表了,这个表存储了所有的字符(各种符号、中文英文等各种字符),Unicode字符表中的每个字符默认使用的是以\u和十六进制数组合表示,也就是说\u0000就是一个Unicode值,这个Unicode值对应着字符表中的一个字符。

Unicode字符表中存储了所有的可用的字符,\u0000其实表示的时候Unicode字符表中第一个字符,编写测试代码如下:

/**
 * 基本数据类型之char
 *
 * @author iCode504
 * @date 2023-09-06 16:18:20
 */
public class BasicTypeChar1 {
    public static void main(String[] args) {
        // 使用Unicode字符表中的字符来初始化char类型的变量
        char ch1 = '\u0000';
        // 会输出,但是无法在控制台显示出来
        System.out.println(ch1);
        // 利用if方法判断ch1是否是Unicode字符表中的第一个字符
        if (ch1 == '\u0000') {
            System.out.println("ch1是Unicode字符表中的第一个字符");
        } else {
            System.out.println("ch1不是第一个字符");
        }
    }
}

运行结果如下:

image-20230906162315218

代码中我写了三个输出语句,其中第一个直接输出这个字符,但是从运行结果中我们发现这个语句确实输出了,但是控制台无法显示这个字符。

为了进一步验证输出的字符是否是Unicode字符表第一个字符,这里我使用了一个if判断。如果我们定义变量和\u0000相等时,输出ch1是Unicode字符表中的第一个字符,此时也就说明了第一个字符确实在计算机中存在,只是无法正常显示;相反,\u0000并不是Unicode字符表中的第一个字符。运行结果正如我们所料,输出的内容是ch1是Unicode字符表中的第一个字符

布尔类型

boolean类型,即布尔类型,它只有两个值:true(真)和false(假)。通常用于条件表达式的判断(条件表达式后续文章会讲到),例如:我们都知道20 > 30是假,即判断结果为false

/**
 * 基本数据类型之布尔类型
 *
 * @author iCode504
 * @date 2023-09-06 15:33:15
 */
public class BasicTypeBoolean {
    public static void main(String[] args) {
        boolean bool1 = true;
        boolean bool2 = false;
        System.out.println(bool1);
        System.out.println(bool2);
        System.out.println(20 > 30);
    }
}

运行结果:

image-20230906153456679

数字的进制表示(了解)

在中学期间我们学过数字有二进制、八进制、十进制和十六进制。

  • 二进制数字是由0、1组成,满二进一。
  • 八进制数字是由0~7组成,满八进一。
  • 十六进制是由0~9、A、B、C、D、E、F组成,满十六进一

日常我们表示数字都是采用十进制,Java程序表示数字亦是如此。那么,如何表示二进制、八进制、十六进制的数字?

以十进制的数字22为例,转换为各个进制的数字如下:

二进制 八进制 十六进制
10110 26 16

在Java中,表示二进制数字,需要在数字前面加上0B0b;如果表示八进制数字,需要在数字前面加上0即可;如果是十六进制的数字,需要在数字前面加上0X或者0x,以下是示例代码:

/**
 * 基本数据类型之整数类型byte、short、int
 *
 * @author iCode504
 * @date 2023-09-06 10:32:17
 */
public class BaseRepresentation {
    public static void main(String[] args) {
        // 十进制数字
        int decimal = 22;
        // 二进制数字
        int binary = 0B10110;
        // 八进制数字
        int octal = 026;
        // 十六进制数字
        int hexadecimal = 0x16;
        
        System.out.println(decimal);
        System.out.println(binary);
        System.out.println(octal);
        System.out.println(hexadecimal);
    }
}

运行结果:

image-20230906104002433

从运行结果我们可以看出:输出的数字无论是哪一种进制,默认都会转换为十进制的数字22

如果我想直接将十进制数字22转换为各个进制并进行输出。

例如:我想定义的变量是int类型,可以使用int的包装类Integer,在Integer类中有和进制转换相关的方法:

  • toBinaryString(num):将十进制数字转换为二进制数字并表示。
  • toOctalString(num):将十进制数字转换为八进制数字并表示。
  • toHexString(num):将十进制数字转换为十六进制数字并表示。
/**
 * 十进制数字转换为各个进制并输出
 *
 * @author iCode504
 * @date 2023-09-06 10:43:53
 */
public class BaseRepresentationParse {
    public static void main(String[] args) {
        // 十进制数字
        int number = 22;
        // 转换为二进制数字并输出
        System.out.println(Integer.toBinaryString(number));
        // 转换为八进制数字并输出
        System.out.println(Integer.toOctalString(number));
        // 转换为十六进制数字并输出
        System.out.println(Integer.toHexString(number));
    }
}

运行结果:

image-20230906104624146

原码、反码、补码(了解)

原码、反码、补码是计算机中表示数值的一种方式,主要应用于计算机的加减运算。

原码是最基本的表示方法, 直接将数值以二进制的形式表示,原码就是符号位加上真值的绝对值,即第一位表示正负号(0为整数,1为负数),其他位表示值。

例如:127的原码是01111111-127的原码是11111111

原码的优点就是直观,容易理解。

反码:正数的反码就是其原码本身,负数的反码在其原码的基础上保持符号位不变,其他位取反。

例如:-127的反码是10000000127的反码是01111111

补码:正数的补码就是其原码本身,负数的补码需要在反码的基础上加1。

例如:-127的补码就是10000001

想深入了解此方面的内容的小伙伴,详见这篇文章:《原码、反码、补码的基本概念》,我个人觉得写的很棒!

自动类型提升

前面我们已经讲过了8种基本数据类型,按照数据存储范围来比较:double > float > long > int > short 、char > byte

自动类型提升是指小范围的数据类型向大范围的数据类型进行转换

boolean类型不能进行自动类型提升或强制类型转换。

例如:short的存储范围比int小,因此,short类型的值赋值给int类型的变量时,short类型的值自动转换为int类型,代码如下:

/**
 * 自动类型提升
 *
 * @author iCode504
 * @date 2023-09-06 15:42:38
 */
public class TypePromotion1 {
    public static void main(String[] args) {
        short s1 = 16;
        // short类型自动类型提升为int类型
        int i1 = s1;
        System.out.println(i1);
    }
}

运行结果:

image-20230906154452844

上述案例可以看出,s1赋值给i1的时候并没有报错,原因就在于s1自动转换为int类型的值赋给i1

自动类型提升可能存在的特殊情况:

情况一:byteshortchar三者互相参与运算时,默认转为int类型。

/**
 * 自动类型提升:参与运算
 *
 * @author iCode504
 * @date 2023-09-06 15:45:36
 */
public class TypePromotion2 {
    public static void main(String[] args) {
        byte num1 = 20;
        short num2 = 30;
        char ch1 = 'a';

        // 以下三种运算均为错误写法,因为运算过程中byte、short、char类型的变量num1、num2和ch1会自动提升为int类型进行运算
        // 得到的结果和左侧变量的数据类型不匹配而导致报错
        // byte num3 = num1 + num2;
        // short num4 = num1 + ch1;
        // char ch3 = num1 + num2;
    }
}

当我们解除一个错误写法的注释(例如byte num3 = num1 + num2;)。我们可以执行javac命令查询错误信息:

image-20230906155323864

情况二:整数类型向浮点类型转换时,默认后面会带.0

/**
 * 自动类型提升:整型向浮点类型进行转换
 *
 * @author iCode504
 * @date 2023-09-06 15:56:40
 */
public class TypePromotion3 {
    public static void main(String[] args) {
        int num1 = 20;
        // num1由int类型自动提升为float类型
        float num2 = num1;
        System.out.println(num1);
        System.out.println(num2);
    }
}

运行结果:

image-20230906155902924

情况三:char类型向更高数据范围(例如:intlong等)转换时,以数字的形式输出。

/**
 * 自动类型提升:字符类型char转换成其他类型
 * 
 * @author iCode504
 * @date 2023-09-06 16:00:43
 */
public class TypePromotion4 {
    public static void main(String[] args) {
        // char类型转换成int类型
        char ch1 = 'i';
        int num1 = ch1;
        System.out.println(num1);

        // char类型转换成long类型
        char ch2 = 'C';
        long num2 = ch2;
        System.out.println(num2);

        // char类型转换成double类型
        char ch3 = 'O';
        double num3 = ch3;
        System.out.println(num3);
    }
}

image-20230906160250575

强制类型转换

和自动类型提升相比,强制类型转换正好相反,由大范围的数据类型向小范围的数据类型进行转换,转换格式如下:

小数据类型 变量名 = (小数据类型) 大类型数据

如果我想将long类型的数据转换为byteshortint类型的数据,由于long是大范围的数据类型,向这三个小范围数据类型转换时需要进行强制类型转换。以下是示例代码:

/**
 * 强制类型转换:long类型转换为byte、short、int类型
 *
 * @author iCode504
 * @date 2023-09-06 11:16:21
 */
public class CastType {
    public static void main(String[] args) {
        // 定义一个long类型变量
        long longValue = 108L;
        // 强制把long类型转换为int类型
        int intValue = (int) longValue;
        // 输出int变量
        System.out.println(intValue);
        // 强制把long类型转换为short类型
        short shortValue = (short) longValue;
        // 输出short变量
        System.out.println(shortValue);
        // 强制把long类型转换为byte类型
        byte byteValue = (byte) longValue;
        // 输出byte变量
        System.out.println(byteValue);
    }
}

运行结果:

image-20230906111843181

当然,强制类型转换也会存在如下的情况:

情况一:浮点类型转换成整数类型时,会出现精度损失,即小数点会被截断(不会四舍五入),只保留整数部分。

/**
 * 强制类型转换情况一:浮点类型转换为整数类型
 *
 * @author iCode504
 * @date 2023-09-06 11:21:54
 */
public class CastType1 {
    public static void main(String[] args) {
        // 定义一个double类型变量
        double doubleValue = 9.06;
        // 强制把double类型转换为int类型
        int intValue = (int) doubleValue;
        // 输出int变量
        System.out.println(intValue);
        // 定义一个float类型变量
        float floatValue = 5.06f;
        // 强制把float类型转换为int类型
        intValue = (int) floatValue;
        // 输出int变量
        System.out.println(intValue);
    }
}

运行结果:

image-20230906112439981

情况二:要转换的数字超出目标类型的范围,Java会自动对整数进行溢出处理,不会得到预期的值。

例如:定义一个int类型的变量130,将其转换成byte类型,而byte类型的存储范围是[-128,127]130很明显超出了这个范围,强制转换的结果不会符合我们的预期,示例代码如下:

/**
 * 强制类型转换情况二:要转换的数字超出目标类型的范围,得到的结果不符合预期
 *
 * @author iCode504
 * @date 2023-09-06 11:28:48
 */
public class CastType2 {
    public static void main(String[] args) {
        // 定义一个int类型变量,值是超出byte类型的范围
        int intValue = 130;
        // 强制把int类型转换为byte类型
        byte byteValue = (byte) intValue;
        // 输出byte变量
        System.out.println(byteValue);
    }
}

image-20230906150905591

很明显,输出结果并不符合我们的预期,而是得到了值-126,接下来我们从底层角度进行分析:

由于int为4字节32位,每一位是由二进制的0和1表示,因此130转换成二进制数(32位)为:

image-20230906134926115

int类型强制转换成byte类型以后,只保留后八位,结果如下:

image-20230906135029377

得到的10000010是源码,8位的byte第一位是符号位,0表示正号,1表示负号。很明显这个数是负数,表示负数需要先将原码转换成反码,反码变成补码,补码再转换成十进制数字以后就是byte类型的结果。首先我们先将其转换成反码(符号位除外):

image-20230906135415985

将反码加1之后,就得到补码:

image-20230906135507695

11111110转换成十进制数为(第1位是符号位,是负数):

\[-(1\times2^{6}+1\times2^{5}+1\times2^{4}+1\times2^{3}+1\times2^{2}+1\times2^{1}+0\times2^{0})=-126 \]

因此强制类型转换得到的结果是-126

情况三:byteshortchar进行运算时,会被提升为int类型,然后再进行计算。要想转换成小范围数据类型,需要进行强制类型转换。

以下写法无法通过编译而报错:

/**
 * 强制类型转换情况一:byte、short、char进行运算时,会自动提升为int类型
 *
 * @author iCode504
 * @date 2023-09-06 14:17:31
 */
public class CastType3 {
    public static void main(String[] args) {
        byte byteValue = 20;
        short shortValue = 30;
        char charValue = 'A';

        // 以下写法都是错误的,因为运算过程中byte、short、char会自动提升为int类型
        byte byteResult = byteValue + shortValue;
        short shortResult = shortValue - charValue;
        char charResult = charValue + 10;
        System.out.println(byteResult);
        System.out.println(shortResult);
        System.out.println(charResult);
    }
}

无法通过编译,因为进行加减法运算时,变量会自动提升为int类型,得到的结果也是int类型,和左侧原有的数据类型不匹配而报错:

image-20230906142228688

正确的写法是:将得到的结果进行强制类型转换:

/**
 * 强制类型转换情况一:byte、short、char进行运算时,会自动提升为int类型
 *
 * @author iCode504
 * @date 2023-09-06 14:17:31
 */
public class CastType3 {
    public static void main(String[] args) {
        byte byteValue = 20;
        short shortValue = 30;
        char charValue = 'A';

        // 以下写法都是错误的,因为运算过程中byte、short、char会自动提升为int类型
        // byte byteResult = byteValue + shortValue;
        // short shortResult = shortValue - charValue;
        // char charResult = charValue + 10;
        byte byteResult = (byte) (byteValue + shortValue);
        short shortResult = (short) (shortValue - charValue);
        char charResult = (char) (charValue + 10);
        System.out.println(byteResult);
        System.out.println(shortResult);
        System.out.println(charResult);
    }
}

运行结果符合预期:

image-20230906142626685

posted @ 2023-09-06 22:23  iCode504  阅读(52)  评论(0编辑  收藏  举报