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 |
这里需要注意的是:
- 所有不带L的字面整型字面常量都是 int 型, 因此 int 型字面常量不能直接赋值给 long, 必须使用 l 或 L 尾缀, 但是取值区间内的整型字面常量可以赋值给 byte 和 short.
- long 的声明方式是数字+字母L, 为了避免在不同字体下 1 和 l 混淆, 我们建议使用大写的 L 表示.
- 基础类型都有取值范围
- 直接给类型赋值超过取值范围会报错, 就算使用字符型常量也不行.
- 对于数值型的值来说, 存在超过取值范围的可能性, 但是常量的值超过了取值范围会报错.
- 与 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); } }
执行结果如下:
其中 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中的类型转换又分为隐式转换(自动转换)和显式转换(强制转换) .
隐式转换
隐式转换又叫自动转换, 是不需要特殊操作的转换形式, 他发生在基础类型之上, 它的转换具有以下规则:
- 除 boolean 类型外, 其他类型可以相互转换
- 容量(容量不是存储空间的占用)小的类型可以自动转换为大的类型, 数据类型按容量大小排序为: byte, short, char < int < long <float < double
- 容量大转小, 必须加上类型强制转换符
- 无论是大转小, 还是小转大, 都有可能出现精度丢失或者溢出问题.
- 有多种数据类型混合计算的时候,系统首先自动转换为容量最大的那个类型再来继续计算
以下为代码示例:
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);
教程结果示例如下:
引用数据类型
引用数据类型是数据的类型, 表示该数据是引用的. 还有种东西叫引用类型是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 的面向对象编程