1.1.3.2 数据类型

127 阅读12分钟

[TOC]

3.2 数据类型

内容导视:

  • 整数类型
  • 浮点类型
  • 字符类型
  • 布尔类型
  • 基本数据类型转换
  • 基本数据类型与 String 类型的转换

数据类型 变量名 = 字面量;...变量的数据类型必须与字面量类型一致?

字面量讲了,变量名你可以随便取,接下来该说一说数据类型了。

Java 支持的数据类型有两种,基本数据类型引用数据类型

基本数据类型加上引用数据类型中的 String 类,正好与我们之前讲的字面量类型一一对应。

基本数据类型

1)整数类型

  • byte(字节):在内存中分配 1 个字节的空间。(8 位)
  • short(短整型):在内存中分配 2 个字节的空间。(16 位)
  • int(整型):在内存中分配 4 个字节的空间。(32 位)
  • long(长整型):在内存中分配 8 个字节的空间。(64 位)

2)浮点类型

  • float:在内存中分配 4 个字节的空间。
  • double:在内存中分配 8 个字节的空间。(double 精度比 float 更高)

3)布尔类型

  • boolean:在内存中分配 1 个字节(数组中)或 4 个字节的空间(单个定义时)。

4)字符类型

  • char:2 个字节。

引用数据类型

1)类(class)包括枚举

  • JDK 自带类库,如 java.lang.String、集合、包装类...
  • 用户自定义的类型,如我们之前写的 class Hello...
  • 第三方类库

2)接口(interface)包括注解

3)数组(array)

对于基本数据类型,如定义 int 类型的变量,就可以接收整数型的字面量:int i = 4;;定义 char 类型的变量,可以接收字符型的字面量:char c = '中';;定义 boolean 类型的变量可以接收 true 或 false:boolean b = true;...

对于引用类型的 String,定义 String 类型的变量,就可以接收字符串型的字面量:String str = "我是什么样";

正常情况下,占用字节空间越大,表示的数越多;如整数类型的 byte 只占一个字节,只能表示 -128 ~ 127 内的整数,所以才需要 int、long,用以存储更大的整数。当然存储的数很小时,没必要使用 long,太浪费空间。

引用类型可以赋值 null;等面向对象时讲,现在先看基本数据类型:

3.2.1 整数类型

整数类型用来存储整数类型的字面量。

以 1 个字节为例,1 个字节 8 位,每位是 0 或 1,那么就是有 2^8^ 种可能,即表示 256 个数。

下面是不同整数类型的取值范围:

类型占用存储空间取值范围
byte(字节)1 个字节[-128 ~ 127] 即 [-2^7^ ~ 2^7^ - 1]
short(短整型)2 byte[-32768 ~ 32767] 即 [-2^15^ ~ 2^15^ - 1]
int(整型)4 byte[-2147483648 ~ 2147483647] 即 [-2^31^ ~ 2^31^ - 1]
long(长整型)8 byte[-2^63^ ~ 2^63^ - 1]

int i = 3;
byte b = 45;
b = 34;
System.out.println(i);

定义了 int 类型的变量,变量名为 i,保存的值为 3。

定义了 byte 类型的变量,变量名为 b,保存的值为 45。

把 34 赋给 b 变量,原来保存的 45 被修改了。

System.out.println(i);表示把 i 保存的值输出到控制台(目前是 DOS 窗口)上。

由于现在你们可能还不懂二进制,于是使用十进制表示,同时也是为了方便,避免写太多的 0、1。


有人可能会问超过了整数类型的取值范围会怎么样?

byte b = 128;
Hello.java:4: 错误: 不兼容的类型: 从 int 转换到 byte 可能会有损失
    byte b = 128;

这下就更疑惑了,什么叫 int 转成 byte 会有损失?难道这个 128 也就是整数型字面量默认被当作 int 类型处理吗?

让我们试一试:

long num1 = 2147483648;

推测:就算 long 类型可以保存这么大的数,但如果后面的 2147483648 真的被当作 int 类型处理的话,那肯定由于超过 int 的范围,会报错。

Hello.java:4: 错误: 过大的整数: 2147483648
    long num1 = 2147483648;

好的,大概明白了。得出结论:

  • 整数型字面量默认被当作 int 类型处理。
  • 为了方便,当被当作 int 类型处理的字面量的值没超过整数类型的范围时,可以直接赋值。如 byte b = 1;

有人就问了,超出 int 范围的值?那能不能让整数型字面量被当作 long 类型处理?

答:在声明整数型字面量时在其后加 l 或 L。

long num1 = 2147483648L;
long num2 = 2147483648l;

但是你也看见了,由于小写的 l 感觉就像 1,为了避免混淆,统一使用大写表示。

这里插一条概念:

值传递:把变量保存的值重新复制一份,传递给另一个变量;而另一个变量修改自己保存的值,不会影响原来变量保存的值。

值传递也称值拷贝

int i1 = 100;
// 把 i1 保存的值 100 复制一份,传给 i2,此时 i2 保存的值为 100
int i2 = i1;

// 修改 i2 保存的值,不会对 i1 有影响
i2 = 55;

System.out.println("i1 = " + i1);// i1 = 100
System.out.println("i2 = " + i2);// i2 = 55

好,有了这个概念,让我们试着互换两个变量保存的值。

大家想一想,如果现在有两个杯子 a、b,都装满了水,该如何互换?

无标题

是不是要准备第 3 个杯子 c,先把 a 倒进 c 中,然后把 b 倒进 a 中,最后把 c 倒进 b 中。

int a = 22;
int b = 433;

int c = a;// c = 22
a = b;// a = 433
b = c;// b = 22

可以不借助第三个变量 c 吗?

有聪明的小伙伴想到了:让 a 保存两值之和(a + b),让 b = 两值之和 - b = a;

int a = 22;
int b = 433;

a = a + b;// a = 22 + 433
b = a - b;// b = (22 + 433) - 433 = 22
a = a - b;// a = (22 + 433) - 22 = 433

3.2.2 浮点类型

浮点类型的变量可以接收一个小数,如 6.2、32.2。

由于使用指数的形式表示值,表示的数比相同字节下的整数类型更大,但是精度有限,结果可能有误差,无法精确表示。

现在看看浮点类型的取值范围,的确比整数类型的取值范围大多了:

类型占用空间范围
float4 个字节[1.4E-45 ~ 3.4028235E38] 即 [2^-149^ ~ 2^128^]
double8 个字节[4.9E-324 ~ 1.7976931348623157E308] 即 [2^-1074^ ~ 2^1024^]

这段内容可以不看:


科学计数法

由于数太大了,为了更好表示数值不浪费空间,使用科学计数法表示。这个 E 大写小写都可以。

1.4E-45 = 1.4 * 10^-45^

3.4028235E38 = 3.4028235 * 10^38^

有人说为何使用科学计数法表示?以 2 的幂表示,如 2^-149^ 是不是更节省空间?

7777777 保留 2 位有效数字,使用科学计数法表示:7.78 * 10^6^(7.78E6);你此时可能还在算它是 2 的几次方吧?

Java 中,当小数超出 [-9999999,9999999] 范围时,会使用科学计数法表示。

System.out.println(-10000000.0);

控制台上会输出 -1.0E7

有聪明的伙伴可能注意到了两点,题干中的“小数”、字面量后的“.0”,注意啊这两个条件不能丢,否则会被当作 int 类型处理。那么下面这道题就可能做错:

思考控制台输出什么结果?

System.out.println(500e-2);

答:500e-2 = 500 * 10^-2^ = 500 / 10^2^ = 5.0

别说结果是 5 啊。

下面呢?

System.out.println(-10000000);
System.out.println(500E-7);

第 1 个明显不是小数,还是原样输出。第 2 个你可能会觉得这既然超过上述所说的范围,那使用科学计数法表示,还是原样输出。

我留的坑啊,没有使用这种方式 500E-7 表示的,而是 5.0E-5

举个例子,a * 10^n^,那么 |a| 是 [1 ~ 10) 之间的数。

-78937935.2 ,一看超过了范围,用科学计数法表示:-7.89379352E77.89379352 大于等于 1,小于 10。你要是这样表示就错了:-78.9379352E6


小数类型的字面量默认被当作什么类型处理?

回过头来,小数类型的字面量又被当作什么类型处理?

做个实验:

float f = 3.14;
Hello.java:8: 错误: 不兼容的类型: 从double转换到float可能会有损失
   float f = 3.14;

看来默认被当作 double 类型处理。同样想要指定字面量被当作 float 处理,需要在字面量后加上 f 或 F。

float f = 3.14F;

同理,指定字面量被当作 double 类型处理。(其实 D、d 去掉也可以,在基本类型的转换中有讲)

double d1 = 5D;
double d2 = 3d;

有时候你会看见这种写法:double d = .12; 不要疑惑,这等同于 double d = 0.12 ,这个 0 可以省略不写,但一般不要这么做,否则其他人可能会疑惑。

精度

浮点数存放形式:浮点数 = 符号位 + 指数位 + 尾数位,尾数部分很可能会丢失,造成精度损失。(小数都是近似值)(有兴趣去扩充知识了解,这里不赘述)

System.out.println(0.11111111111111111111111111111F);
System.out.println(0.11111111111111111111111111111D);
0.11111111
0.1111111111111111

大概可以这么理解:

float 的精度是保留 8 位有效数字。

double 的精度是保留 16 位有效数字。通常使用 double 类型。

有效数字是一个数从左边第一个不为 0 的数字起,直到末尾止的数字称为有效数字,如 0.009210,有效数字 4 位:9、2、1、0。

保留两位有效数字:0.0092。

由于浮点数运算得到的结果可能有误差,所以如下就成了错误做法:

double d1 = 9.9 / 3;
double d2 = 3.3;

if (d1 == d2) {
  System.out.println("它们相等");  
}

上面的意思是:如果 d1 等于 d2,就输出“它们相等”。

但试着运行,控制台什么都没有输出...

此时再试下访问 d1 的值:

System.out.println(d1);// 3.3000000000000003

看到没有?9.9 / 3 不等于 3.3,而是十分接近 3.3 的小数。

==当对运算结果是小数的进行相等判断时,应该以两个数的差值的绝对值,在某个精度范围类判断==。这个精度由自己决定,如人民币数值比较,人民币最低面额 1 分 = 0.01 元,只要两数差值小于 0.01,就认为它们相等。

现在该改一下了:

double d1 = 9.9 / 3;
double d2 = 3.3;

if (Math.abs(d1 - d2) < 1.0E-2) {
  System.out.println("它们相等");  
}

还记得如何查 API 文档吗?当导入 java.lang 包下的类时,可以不用 import。哦,对了,你们还不懂,这节在面向对象的包机制中。

那我直接说含义,Math.abs(a):求 a 的绝对值。当 d1 - d2 的绝对值小于 0.01 时,我们姑且认为它们相等。

现在收尾。我还是说说怎么找吧:

打开 JDK API 文档,如果知道 Math 在哪个包下,比如 Math 在 java.lang 包下,点击 java.lang;

如果不知道,点击索引找 M 开头的类:

找到后点击 Math,看看 abs 方法的介绍。

3.2.3 字符类型

输出、打印是一个意思。

char 类型的变量可以保存单个字符,占用 2 个字节,取值范围:[0 ~ 65535] 即 [0 ~ 2^16^ - 1]。

char c1 = '中';
char c2 = 'a';

// 字符类型可以直接存放数字,当输出 c2 时,会输出 97 代表的字符,扩充内容中有讲
char c2 = 97;

你们可能会在其他地方遇到如 char c = '\n';,这叫转义字符

一般使用 \ 开头,代表着将一个字符转义,本质还是单个字符。


\n:换行 \t:制表符 tab \r:回车 \u:把十六进制数转成对应的字符


1)\n:

什么叫换行?

System.out.println("你好\n我好\n大家好\n");
char c = '\n';
System.out.println(c);
System.out.println(2);

这里不得不提一下 ln:

System.out.print(2);
System.out.print(1);
System.out.println();// 代表换行
System.out.print(7);
21
7

ln 表示当前行结束输出,如果要继续输出,就要移到下行。

回过头来,"你好"换行,"我好"换行,"大家好"换行,ln 代表换行。 System.out.println(c);,c 是 '\n',代表换行,ln 代表换行。 结果如下:

你好
我好
大家好



2

2)\t:

System.out.println("狗\t猫\t鱼");
狗      猫      鱼

可以看到狗与猫、鱼之间隔了一个制表符的距离。

3)\r:

System.out.println("我是什么\r人呢");

回车:回到行首,输出文本。由此产生分歧,有人认为一个 \n 足以表示回车加换行,两个字符太浪费了,而 Windows 系统中还保留原来概念,Enter 键(我们通常叫回车键),实际用两个字符表示:“\r\n”;对应的 ASCII 码用十进制表示分别为 13、10。

Windows 系统换行 Carriage return and line feed(CRLF):回车加换行

Linux 系统换行 Line feed(LF):换行

回到这里,如果回车但不换行,\r 后的 “人呢” 还在同行首个位置上输出,把 “我是” 覆盖,最后结果:"人呢什么"。

4)\u:

System.out.println("\u5929");

16 进制的 5929 对应的字符为天,有兴趣请在扩充内容中的字符编码了解。

这个 \ 作用不止如此。

问想要使用 char 类型保存单个字符:英文单引号 ' 怎么做?

char c = ''';这样?

Hello.java:4: 错误: 未结束的字符文字
                char c = ''';

像这类具有特殊含义的字符,需要使用 \ 转成普通的字符,使其不再被认为是代表字符开始的单引号。

char c = '\'';

同理,打印 \、" 等特殊符号,都需要在字符前加上 \:

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

输出结果:

"
\

3.2.4 布尔类型

boolean 类型只有两个值:true 或 false,用于逻辑运算:2 < 3 为 true。

一般放在 if 、for 等语句的条件处,控制程序的流程,现在不用深究。