Java 的基础数据类型

从上节中, 我们了解到, 变量是用来指向分配到内存里的值的. 因为内存是有限的, 所以当要将值存放到内存中时, Java 中的内存管理系统需要尝试申请一定的空间来满足对应数据的存放.

我们可以通过 Java 在内存中存放数字, 字符以及引用对象. 按照类型划分, 我们可以将 Java 的数据类型分为两类 基础数据类型引用数据类型 .

基础数据类型

基础类型就是类型的基础, 它不是类 (Class) 只是一种数据类型. 像C/C++, 当然每种基础类型都有其对应的包装类(将基础类型包装成一个Class), 这个我们后面会说到, 现在我们来看看 Java 中的基础数据类型有哪些吧.

Java 的基础数据类型分为3类, 数值型, 布尔型字符型 :

1. 数值型

数值型又分为 整数型浮点型, 类似对应数学上的 整数小数, 为了便于对比和记忆, 我们他们列到一个表里:

类型 类型 占用存储空间 取值范围 默认值
byte 整数型 1字节 -2^7 ~ 2^7-1 0
short 整数型 2字节 -2^15 ~ 2^15 - 1 0
int 整数型 4字节 -2^31 ~ 2^31 - 1 0
long 整数型 8字节 -2^63 ~ 2^63 - 1 0L
float 浮点型 4字节 32位 IEEE 754 单精度范围 0.0f
double 浮点型 8字节 64位 IEEE 754 双精度范围 0.0d

 

这里需要注意的是:

  1. 所有不带L的字面整型字面常量都是 int 型, 因此 int 型字面常量不能直接赋值给 long, 必须使用 l L 尾缀, 但是取值区间内的整型字面常量可以赋值给 byte short.
  2. long 的声明方式是数字+字母L, 为了避免在不同字体下 1 和 l 混淆, 我们建议使用大写的 L 表示.
  3. 基础类型都有取值范围
  4. 直接给类型赋值超过取值范围会报错, 就算使用字符型常量也不行.
  5. 对于数值型的值来说, 存在超过取值范围的可能性, 但是常量的值超过了取值范围会报错.
  6. int 类似, 小数的字面常量, 默认类型是 double, 这就意味这 float 小数常量必须以 f 结尾.

以下是关于数值型基本类型的使用的代码, 请仔细阅读注释, 帮助理解, 有些注释的代码可以去掉注释现, 尝试能否运行:

class Yi21Runner {
    public static void main(String[] args) {
        // byte 的最大值
        byte b = 127;
        //因为超过取值范围, 下行注释中的代码是不合法的
        //byte b = 128;
        //而下行代码中的计算是允许的, 因为编译器无法直接判断 b 的值,计算结果会越过取值边界
        b += 1;
        System.out.println("byte: 127 + 1 = " + b);
        // short 最小值
        short s = -32768;
        s -= 1;
        System.out.println("short: -32768 - 1 = " + s);
        int i = -32768;
        i -= 1;
        System.out.println("int i: " + i);
        long l = 2147483648L;
        System.out.println("long l: " + l);
    
        // 因为不带 L 尾缀, 下面注释中的代码是不合法的
        // long l = 2147483648;
    
        float f = 52.01f;
        System.out.println("float f: " + f);

        // 因为不带 f 尾缀, 下面注释中的代码是不合法的
        // float = 52.01;
        
        double d = 3.1415926d;
        System.out.println("double : " + d);
        //因为小数字面常量类型就是 double 所以下行代码也是正确的
        d = 3.14;
        System.out.println("double : " + d);
    }
}

执行结果如下:

Java 的数字型基础类型的计算

其中 127 + 1 得到 -128, 是越过了 byte 的取值上限, 进入取值下限, 这里的计算需要注意.

2. 布尔型

布尔值表示逻辑上的真(true) 和假(false), 取值也就这两种, 默认值是 false, 占用 1 个字节, 示例代码:

class Yi21Runner {
    protected static boolean bs;
    public static void main(String[] args) {
        System.out.println(bs);
        boolean b = true;
        System.out.println(b);
    }
}

在这里, 我们没有给bs复制, 但是在使用的时候, 实际上他已经被初始化被赋予了一个默认值 false, 而在局部变量中, 未初始化的变量 b 是没法使用的.

这个机制的存在应该是, 类变量会随时被调用, 如果不赋值, 在使用时会报错, 而在局部变量中, 不赋值就不分配值用完就销毁, 以达到节约内存使用的目的.

上述代码执行结果如下:

PS D:\21yi.com\course\basic_types> java Yi21Runner
false
true

3. 字符型

字符行是存放字符值的变量, 默认值是 '\u0000', 一个字符占用两个字节, 可以存放 Unicode 编码字符(包括中文). 实例代码如下:

class Yi21Runner {
    protected static char cs;
    public static void main(String[] args) {
        System.out.println(cs);
        char c = '2';
        System.out.print(c);
        System.out.print('1');
        System.out.print('y');
        System.out.print('i');
        System.out.print('.');
        System.out.print('c');
        System.out.print('o');
        System.out.print('m');
        //输出回车
        System.out.print('\n');
        System.out.print('O');
        System.out.print('K');
        System.out.println('!');
    }
}

这里我们使用了 System.out.print 的方法, 该方法不会输出换行, 同时 我们输出了 '\n' 这也表示换行, 执行结果如下:

PS D:\21yi.com\course\basic_types> javac -encoding UTF-8 Yi21Runner.java
PS D:\21yi.com\course\basic_types> java Yi21Runner

21yi.com
OK!

我们发现输出结果中在 21yi.com 上面有一个空行, 实际上就是输出了 char 的默认值的结果.

基础类型的包装类

每一种基础的类都有其对应的包装类, 经过包装, 原有的基础类型就可以作为对象使用了, 便于一些面向对象编程的操作, 一下是各基础类对应的包装类的列表:

基础类型 对应的包装类
byte java.lang.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
char java.lang.Character
bool java.lang.Boolean

 

一下是一些简单的使用示例:

class Yi21Runner {
    public static void main(String[] args) {
        Integer i = 130;
        System.out.println(String.format("i 的类型是 %s, 值的为 %d, Byte 值为 %d", i.getClass().getName(), i, i.byteValue()));
        Long l = 130L;
        System.out.println(String.format("l 的类型是 %s, 值的为 %d, Byte 值为 %d", l.getClass().getName(), l, l.byteValue()));
        Character c = '字';
        System.out.println(String.format("c 的类型是 %s, 值的为 %c", c.getClass().getName(), c));
        Integer i200 = Integer.valueOf("200");
        System.out.println(String.format("i 的类型是 %s, 值的为 %d", i200.getClass().getName(), i200));
    }
}

以上代码中

String.format 是一种格式化输出的方式, 具体使用方法, 请点击这里 , 显然这是 String 对象的一种静态方法.

我们可以看到, 当我们使用了封装类之后, 我们就可以将基础值作为对象来使用了, 同时赋值的过程是没有变化的, 我们并不需要初始化一个对象来给封装类的对象, 从这一点来说, 封装类也是一种受到语法优待(语法支持)的特殊的类.

同时我们还可以发现, 我们甚至可以使用对象让其在类型直接相互转换, 比如上例中, 我们显示将 Integer Long 转换成了 byte 类型了, 然后通过 Integer.valueOf() 的方法将一个字符串的 "200" 解析成了整数.

类型之间的转换

大部分情况下 Java 是严格区分类型的, 不同类型的数据往往不能直接相互操作 比如 1 / "2", 但是也存在某些特殊的情况, 让不同类型的数据可以直接相互操作. 因此Java中的类型转换又分为隐式转换(自动转换)显式转换(强制转换) .

隐式转换

隐式转换又叫自动转换, 是不需要特殊操作的转换形式, 他发生在基础类型之上, 它的转换具有以下规则:

  1. 除 boolean 类型外, 其他类型可以相互转换
  2. 容量(容量不是存储空间的占用)小的类型可以自动转换为大的类型, 数据类型按容量大小排序为: byte, short, char < int < long <float < double
  3. 容量大转小, 必须加上类型强制转换符
  4. 无论是大转小, 还是小转大, 都有可能出现精度丢失或者溢出问题.
  5. 有多种数据类型混合计算的时候,系统首先自动转换为容量最大的那个类型再来继续计算

以下为代码示例:

class Yi21Runner {
    public static void main(String[] args) {
        //普通的转换
        int i = '2';
        System.out.println("char a -> int    : " + i);
        byte b = '1';
        System.out.println("char a -> byte   : " + b);
        short s = b;
        System.out.println("byte b -> short  : " + s);
        //以下是大容量强制转小容量溢出的例子
        i = 100000;
        s = (short) i;
        System.out.println("int i -> short   : " + s);
        //以下是小容量转大容量精度丢失是例子
        long l = Long.MAX_VALUE;
        System.out.println("Original long    : " + l);
        float f = l;
        System.out.println(String.format("Long Max -> float: %f", f));
        //long 的最大值, 等于 Long.MAX_VALUE
        f = 9223372036854775807f;
        System.out.println(String.format("Long Max -> float: %f", f));
    }
}

结果如下:

> javac -encoding UTF-8 Yi21Runner.java
> java Yi21Runner
char a -> int    : 50
char a -> byte   : 49
byte b -> short  : 49
int i -> short   : -31072
Original long    : 9223372036854775807
Long Max -> float: 9223372036854776000.000000
Long Max -> float: 9223372036854776000.000000

其中在 long -> float 时, 我们看到了, 值在精度上出现了丢失 5807 直接被约为了 6000

显式转换

显式转换又称为强制转换, 是需要在代码中说明的转换形式除了上面代码中的例子. 数字型封装类还可以对字符串进行转换, 简单示例代码如下:

int i = Integer.valueOf("25945");
System.out.println("String 100 -> int: " + i);
char c = (char) i;
char c2 = (char) 31243;
System.out.println("" + (char) 50 + (char) 49 + (char) 121 + (char) 105 + ".com Java " + c + c2);

教程结果示例如下:

Java 的基础数据类型运行结果2

引用数据类型

引用数据类型是数据的类型, 表示该数据是引用的. 还有种东西叫引用类型是JVM里引用的类型, 是给引用分类的不是给数据类型分类的, 需要区分.

引用数据类型通常分为5种: 类 接口 数组 枚举 标注 . 我们在后面再做详细的介绍, 这里我们只看看几个常见的引用数据类型:

常见的引用数据类型

字符串

字符串是非常常见的类型, 是Java内置支持的类型之一. 其实我们在Java教程的开篇就用到了字符串, 所有用双引号引用的字符, 就是字符串, 我们看一些简单的处理字符串的方法:

String hey = "Hey, Baby";
System.out.println(hey);
System.out.println(hey.length());
System.out.println(hey.split(",")[0]);

以上代码, 分别输出了 字符串 hey, hey的长度, 按,分割字符串hey生成的数组的第一个元素, 结果如下:

Hey, Baby
9
Hey

数组

public static void main(String[] args) {
    System.out.println("字符串数组的类型名称是: " + args.getClass().getName() + ", 其简化形式为: " + args.getClass().getSimpleName());
int[] ids = {1, 2, 3}; System.out.println("整型数组的类型名称是: " + ids.getClass().getName() + ", 其简化形式为: " + ids.getClass().getSimpleName());
}

输出结果如下:

字符串数组的类型名称是: [Ljava.lang.String;, 其简化形式为: String[]
整型数组的类型名称是: [I, 其简化形式为: int[]

由此可见, 能过通过方法获得类名, 数组是一种类, 但是他和普通类又不太一样, 他没有对应的 class 文件, 这个类是在 JVM 运行时创建的, 数组类中不包含任何成员和变量, 数组的长度 length 也是通过 JVM 的指令 arraylength 得到的而不是 数组对象具备 length 这个属性, 因此数组是一种特殊的类.

以上就是 Java 基础数据类型的全部内容了, 下面请看 Java 的面向对象编程