🌟 想系统化学习 Java 编程?看看这个:[编程基础] Java · 学习手册
0x01:Java · 初窥门径 —— 基本数据类型
Java 是一门强类型的编程语言,它对变量的数据类型有严格的限定。在定义变量时必须声明变量的类型,在为变量赋值时必须赋予和变量同一种类型的值,否则程序会报错。
在 Java 中,变量的数据类型分为两种,即基本数据类型和引用数据类型,Java 中的所有的数据类型如下图所示:

上图中的 4 种基本数据类型是 Java 内嵌的,在任何操作系统中都具有相同大小和属性;而引用数据类型是在 Java 程序中由编程人员自己定义的类型(数据结构吧)。
本章我们重点介绍的是 Java 的基本数据类型,引用数据类型我们将在后续章节中进行详解。
0x0101:整数类型 — byte & short & int & long
1. 整数类型 — 概念解析
整数类型的数据是没有小数点的数值,包括正整数、零和负整数。
1.1 整数类型 — 按进制划分
按照进制的不同,整数类型又可以分为十进制、八进制、十六进制、二进制(不同进制之间的转换需要自己去学习哦):
-
十进制:99,-500,0
-
二进制:要求以
0b或0B开头,如0b10,对应十进制的 2。 -
八进制:以
0开头,如010,对应十进制的 8。 -
十六进制:以
0x或者0X开头,如0x10,对应十进制的 16。
下面是一个进制的示例(以笔者目前编程来看,十进制用的最多,其它进制你知道有以及咋表达就行):
// FileName: printMotto.java
public class printMotto {
public static void main(String[] args) {
System.out.println(10); // 这个是十进制
System.out.println(0B10); // 这个是二进制
System.out.println(010); // 这个是八进制
System.out.println(0x10); // 这个是十六进制
}
}

1.2 整数类型 — 按取值范围和占用空间划分
按照取值范围和占用空间的不同,整数类型又可以分为以下四种:
| 类型 | 中文名称 | 占用的存储空间 | 取值范围 | 计算后范围 |
|---|---|---|---|---|
| byte | 字节型 | 8 位(1 字节) | ||
| short | 短整型 | 16 位(2 字节) | | |
| int | 整型 | 32 位(4 字节) | | |
| long | 长整型 | 64 位(8 字节) |
上表中 “占用的存储空间” 是指不同类型的变量分别占用的内存大小,比如,一个 int 类型的变量将会占用 4 字节大小的内存空间(涉及了计算机底层,做了解即可);取值范围则是指变量存储的值不能超出的范围,例如,一个 byte 类型的变量存储的值必须是 -128 ~ 127 之间的一个整数。
在前面的章节中,int 类型我们已经用的很熟练了。那么本章笔者就挑两个特殊的进行讲解。
2. 整数类型 — byte 解析
在前面的章节中我们已经教过了 Java 中变量的声明与使用流程,这里仅仅是换个变量类型而已:
基础回顾 🚀:Java · 初窥门径 —— 变量 & 常量
// FileName: printMotto.java
public class printMotto {
public static void main(String[] args) {
byte a = 126; // 声明一个 byte 类型的变量 a
System.out.println(a);
System.out.println(a + 1);
}
}

在上面那个案例中,我们创建了一个 byte 类型的变量 a,并将其赋值为 126。最后我们尝试打印了 a 与 a+1 的值,从显示结果来看,一切正常,似乎没啥特别的。
2.1 特殊示例 — 赋予 byte 超出范围的值
那么笔者就来个特殊的,看下面这个案例:
public class printMotto {
public static void main(String[] args) {
byte a = 128;
System.out.println(a);
}
}

如上,成功报错。证明我们前面介绍的没问题,当你赋予一个对象超出其范围的值时,因为存不下,java 会直接给你报错。
2.2 特殊示例 — 溢出(了解即可)
这部分涉及计算机底层,笔者并不打算讲为啥是这样(大部分小白可能听不懂,当然,如果强烈要求,笔者可以给你们推导一下)。但是为了给小白涨涨见识,笔者还是将案例放这里了:
// FileName: printMotto.java
public class printMotto {
public static void main(String[] args) {
byte a = (byte) 128; // 128 是 int 型变量,我们通过在其前面加上 (byte),强制将 128 转变为 byte 类型变量后赋值
byte b = (byte) 129; // 用于对比
System.out.println(a);
System.out.println(b);
}
}

3. 整数类型 — long 解析
long 型,我们一般叫其 “长整型”(主要是因为它能表示非常大的数字)。在为一个 long 类型的变量赋值时,我们需要在其后面加上字母 L(或者小写的 l,但不推荐),标明赋值为 long 类型。当然,也有特殊情况,就是如果赋的值未超出 int 型的取值范围,我们可以省略字母 L,看如下几个示例:
3.1 错误示例 — 赋值超出 int 范围但不加 L
当我们给 long 类型赋超过 int 范围的值时,需要在值得末尾加上 L 字符。这里我们整一个超出但不加 L 的示例看看(如果不超过 int 范围是不用加的):
public class printMotto {
public static void main(String[] args) {
long num1 = 123; // 所赋的值未超出 int 类型所取范围,可以不加 L
long num2 = 2200000000; // 所赋的值超出了 int 类型的取值范围,后面必须加上字母 L
}
}

如上,Java 编译器直接报错了。
3.2 正确示例 — 赋予超出 int 范围且加 L
解决上述报错非常简单,在大数末尾加个 L,标明这个数字是个 long 类型的即可:
public class printMotto {
public static void main(String[] args) {
long num1 = 123; // 所赋的值未超出 int 类型所取范围,可以不加 L
long num2 = 2200000000L; // 所赋的值超出了 int 类型的取值范围,后面必须加上字母 L
System.out.println(num1);
System.out.println(num2);
}
}

0x0102:浮点类型 — float & double
1. 浮点类型 — 概念解析
浮点类型就是含有小数的数值,比如 3.14 这种数值。
1.1 浮点类型 — 十进制 & 科学计数法表示
按照表示方式,浮点类型的值可以以十进制形式表示,也可以用科学计数法形式表示:
-
十进制形式: 3.14;314.0
-
科学计数法: 1e2;-1E2;1E-2
十进制形式在前面的案例中已经演示很多次了,那么笔者本节就演示一下科学计数法形式的使用:
public class printMotto {
public static void main(String[] args) {
double a = 1e2; // 1e2 => 1 x 10^2 = 100
double b = 1e-2; // 1e-2 => 1 x 10^-2 = 0.01
System.out.println(a);
System.out.println(b);
}
}

1.2 浮点类型 — 单精度 & 双精度
按照表达的精度划分,浮点类型可以分为单精度和双精度类型(它们所占的内存空间也是不一样的):
| 类型 | 中文名称 | 占用的存储空间 | 取值范围 |
|---|---|---|---|
| float | 单精度型 | 32 位(4 字节) | 1.4E-45~3.4E+38(有效位数为 6 - 7 位左右) |
| double | 双精度型 | 64 位(8 字节) | 4.9E-324~1.7E+308(有效位数为 15-15 位左右) |
在 Java 中,浮点数常量类型默认就是 double 类型,如 2.56,它默认为 double 类型常量,占用 8 字节内存。在浮点数类型常量后面加上 F 或 f,该数据就是 float 类型,如 2.56f,它为 float 类型,占用 4 字节内存。在为浮点数类型变量赋值时, float 类型变量值后面必须加上 F 或 f;double 类型变量值后面可以加上 D 或 d,也可以不加,具体示例如下:
float f = 123.4F; // 为一个 float 类型的变量赋值,后面必须加上字母 F
double d1 = 100.1; // 为一个 double 类型的变量赋值,后面可以省略字母 D
double d2 = 199.3D; // 为一个 double 类型的变量赋值,后面可以加上字母 D
在程序中也可以为一个浮点数类型变量赋予一个整数数值,JVM 会自动将整数数值转换为浮点数类型的值。例如,下面的写法也是可以的:
float f = 100; // 声明一个 float 类型的变量并赋予整数值
double d = 100; // 声明一个 double 类型的变量并赋予整数值
2. 浮点类型 — float 解析
float 即单精度型,它的精度有点低,具体怎么个低法,笔者下面会演示。
2.1 错误示例 — 给 float 型变量赋值不加 F
在下面这个示例中,我们尝试给 float 类型赋一个小数值,但是我们没有为该小数值添加 F 后缀:
public class printMotto {
public static void main(String[] args) {
float f1 = 1.0;
System.out.println(f1);
}
}

如上,显示 “从 double 转换到 float 可能会有损失”,这条报错提醒了我们两点:
-
Java 中默认浮点数就是 double 类型的。
-
将 double 类型的值强行赋值给 float 类型会有精度丢失的风险。(涉及底层原理)
2.2 正确示例 — 给 float 型变量赋值添加 F
解决上述报错的方法很简单,既然将 double 类型的值赋给 float 不行,那么我就将 float 型数据赋给 float 型变量。声明一个 float 型小数很简单,数值末尾加个 F 就行:
public class printMotto {
public static void main(String[] args) {
float f1 = 1.0F;
System.out.println(f1);
}
}

2.3 异常示例 — float 类型精度丢失(有效位数)
我们在介绍 float 型时,给了一个表格,里面标注了 “有效位数为 6-7 位左右”。那么啥是有效位数呢?简单而言就是我们可以相信的位数(有点抽象了)。
先来将怎么数有效位数吧,我么标注的有效位数是从左开始第一个不为 0 的数到最后一个数的范围,看下面这两个例子:
-
纯小数类型:0.123456789 => 有效数字就是 0.1234567
-
整数小数混杂型:123.456789 => 有效数字就是 123.4567
是不是有点懵逼,来看下面这个代码(float 的四舍五入与精度丢失,这部分涉及了计算机保存数据的底层原理,做了解即可):
public class printMotto {
public static void main(String[] args) {
float f1 = 0.123456789F;
float f2 = 123.456789F;
System.out.println(f1);
System.out.println(f2);
}
}

3. 浮点类型 — double
double 双精度类型,它能够精确到小数点后更多的位数。对于 double 的使用笔者是不准备讲的,因为它和前面讲的几种数据类型的使用方式都一样,没啥特别的。这里笔者想讲一个注意点:
3.1 注意 — 不推荐浮点数类型比较大小
由于计算机的底层设计原因,计算机展示的浮点数与实际存储的浮点数还是有些不同的。在介绍案例前,笔者先来引入一个判断大小的符号:
-
=:一个等于号,是赋值运算符,用于将等号右边的值赋值给等号左边。 -
==:两个等于号,是判断运算符,用于判断左右两侧的值是否相等。
首先先来一个正常的例子,这里我们使用两个 float 类型的数进行比较(笔者选择的精度比较小,暂时没发生精度丢失问题):
public class printMotto {
public static void main(String[] args) {
float f1 = 0.12345678F;
float f2 = 0.12345678F;
System.out.println(f1 == f2);
}
}

OK,上面这个案例因为我们挑选的数字还没有超过 float 的精度范围,所以结果是正常的,再来看看下面这个例子:
public class printMotto {
public static void main(String[] args) {
float f1 = 0.123456788F;
float f2 = 0.123456789F;
System.out.println(f1 == f2);
}
}

如上,我们利用 float 的精度丢失,成功让 0.123456788 和 0.123456789 相等了 !
3.2 注意 — double & float 最好不要一起比较
由于 double 与 float 的底层存储格式不同(两个内存占的位数都不一样),所以哪怕两个赋一样的值,也不建议放一起比较:
public class printMotto {
public static void main(String[] args) {
double f1 = 0.123;
float f2 = 0.123F;
System.out.println(f1 == f2);
}
}

float、double 类型的运算、比较往往都不准确, 所以在往后的工作中,我们可以通过 BigDecimal 提供的方法来处理浮点数相关的操作(这个是后话,给你们介绍一个解决思路而已)。
0x0103:字符类型 — char
字符类型,就是单个的文本字符,比如 A、a、啊、1、2、@ 等。
1. 字符类型 — 概念解析
在 Java 中,字符型变量用 char 表示,用于存储一个单一字符。Java 中每个字符型的变量都会占用 2 字节。在给字符型的变量赋值时,需要用一对英文半角格式的单引号('')把字符括起来,如 'a'。
在计算机的世界里,所有文字、数值都只是一连串的 0 和 1,这些 0 和 1 是机器语言,人类难以理解,于是就产生了各种方式的编码,用一个数值代表某个字符,如常用的字符编码系统 ASCII。
虽然各种编码系统有数百种之多,却没有一种包含足够的字符、标点符号及常用的专业技术符号。这些编码系统可能还会相互冲突。也就是说,不同的编码系统可能会使用相同的数值标识不同的字符,这样在数据跨平台时就会发生错误。
Unicode 字符码系统定义了绝大部分现存语言需要的字符,是一种通用的字符集,可以同时处理多种语言混合的情况。因此,在任何语言、平台、程序中都可以安心地使用 Unicode 字符码系统。Java 使用的就是 Unicode 字符码系统,在计算时,计算机会自动将字符转化为对应的数值。例如, Unicode 中的小写字母 a 是用 97 表示的,可以直接将 97 赋值给一个字符型变量。
定义字符型变量的具体示例如下:
char c = 'a'; // 为一个字符型的变量赋值为字符 a
char ch = 97; // 为一个字符型的变量赋值为整数 97,相当于赋值为字符 a
2. 字符类型 — char 详解
2.1 单引号引起的单个字符 — 详解
Java 中的字符,通常是单引号引起来的单个字符。我们首先来分析,单引号。下面这个案例,笔者使用双引号引起了字符 a,结果 IDE 直接报错:
public class printMotto {
public static void main(String[] args) {
char a = "1";
System.out.println(a);
}
}

继续,我们分析关键字 “单个字符”,我能不能只给它一个空字符(''),或者在单引号内多扩几个字符(123)?:
public class printMotto {
public static void main(String[] args) {
char a = '';
char b = '123';
}
}

如上,从 IDE 的标红显示可以推出,赋值给 char 类型的变量,无论是你往单引号中多赋值亦或是少值,均是不可以的。
那么我们的 char 真的真么矫情嘛,其实倒也不是,看下面这个示例,只要你符合它的规则,它几乎所有类型的单个字符都能给你接收了(特殊的转义字符也算单个字符哦):
知识回顾 🚀:Java · 初窥门径 —— Java 输出个性化签名
public class printMotto {
public static void main(String[] args) {
char ch1 = 'A'; // 赋予 char 单个英文字符
System.out.println(ch1);
char ch2 = '我'; // 赋予 char 单个中文字符
System.out.println(ch2);
char ch3 = '4'; // 赋予 char 单个数字字符
System.out.println(ch3);
char ch4 = '$'; // 赋予 char 单个特殊字符
System.out.println(ch4);
char ch5 = '\n'; // 赋予 char 转义字符
System.out.println(ch5);
}
}

2.2 字符 & 字符编码互转 — 拓展
在之前介绍转义字符的时候我们就简单说过编码表的事情。现在我们学了 char 类型,笔者就整点骚活。我们知道 A 在 ASCII 码表中的位置是 65,那么我们如何证明呢?
拿 A 过来做个计算不就行啦(下面的代码涉及到了类型转换):
public class printMotto {
public static void main(String[] args) {
char ch1 = 'A';
int a = ch1 + 1;
char ch2 = (char) a; // 将 int 型变量 a 强制转换为 char 型
System.out.println(a);
System.out.println(ch2);
}
}

除了上面的骚活,其实从 char 类型还衍生出来了一个 string 类型,即字符串类型。字符串,故名思意,就是将一个个字符串起来。比如 "Blue17 Says: Welcome To Hack3rX !!!" 这个被双引号包裹的内容就是字符串(这不是本章关注点,就先介绍这么多,等后面需要了再做详细的介绍)。
0x0104:布尔类型 — boolean
Java 中使用 boolean 定义布尔型变量,布尔型变量只有 true 和 false 两个值,且这两个值在内存中只占一位(不是一个字节哦)。
注意:Java 中的布尔类型不能用 0 或非 0 的整数来替代 true 和 false。
1. 正确示例 — boolean 赋值
boolean 类型可以用来做判断,以下写法都是可以使用的:
public class printMotto {
public static void main(String[] args) {
boolean flag1 = true;
boolean flag2 = false;
boolean flag3 = 5 == 9; // 判断 5 是否等于 9,并将结果赋值给 flag3
System.out.println(flag1);
System.out.println(flag2);
System.out.println(flag3);
}
}

2. 错误示例 — 用 0 或非 0 的整数来替代 true Or false
在其它编程语言中,我们经常会拿 0 来代替 false,拿非 0 值来代替 true。这在 Java 中是不允许的,看下面这个示例(涉及到了后面的流程控制):
public class printMotto {
public static void main(String[] args) {
boolean flag = 1;
boolean flag2 = (boolean) 1;
if (0) {
}
}
}

0x02:Java · 初窥门径 —— 基本数据类型的转换
0x0201:Java 基本数据类型转换 — 概念引入
在前面演示 Java 的基本数据类型时,我们其实已经遇到了很多次等号左侧和右侧的数据类型不一致的情况,那么本节,我们就来讨论一下当发生这个情况时,Java 会干些啥事呢?
笔者也不卖关子了,其实就是数据转换那些事,Java 的数据转换分为两种:
-
隐式类型转换 => 又称自动转换,这个是系统自己转的,你无需操作。
-
强制类型转换 => 通过在值前添加
(变量类型)手动进行转换。
0x0202:Java 基本数据类型转换 — 推导流程
1. 单个基本数据类型转换 — 隐式类型转换
先来看下面这个代码,我们尝试将一个 int 型值赋予一个 float 型变量:
public class printMotto {
public static void main(String[] args) {
float num = 1;
System.out.println(num);
}
}

如上,通过打印的结果可以发现,Java 自动将 int 型的 1 转换成了 float 型的 1.0 并打印。
2. 单个基本数据类型转换 — 强制类型转换
继续,看下面这个代码,我们尝试将 double 类型的数据,放到 int 类型的变量中:
public class printMotto {
public static void main(String[] args) {
int num = 1.0;
System.out.print(num);
}
}

如上,Java 直接报错,说 “将 double 类型的数据转换到 int 类型可能会有损失”。注意它的语义,它是说了将 double 类型的数据转换到 int 类型可能有损失是吧,没说不能转是把。
OK,问题来了,咋转?此时我们就需要引入强制类型转换了,强制类型转换我们只需要在要转换的值前通过 (转换类型)声明要强制转换的类型即可,比如,强制将 double 转换成 int:
public class printMotto {
public static void main(String[] args) {
int num = (int) 1.0;
System.out.print(num);
}
}

如上,我们成功通过强制类型转换,将 double 类型转换成了 int 类型,并赋予给了 int 类型变量。
3. 多个基本数据类型转换 — 隐式类型转换 - 结论 1
继续剖析,在上面的演示中,我们都是单个数据类型的转换,假设多个不同的数据类型混杂在一起运算时,又该如何呢?看下面这个例子:
public class printMotto {
public static void main(String[] args) {
double num = 1 + 1.0F + 1.0D + 'A' + true;
System.out.print(num);
}
}

如上,java 报错,说我们二元运算符 + 的操作数类型错了,结合报错的列号(笔者没截进去),可以推测出是 boolean 类型有问题。因此,我们可以推出第一个结论:
多种数据类型混杂运算时,整数、浮点数、字符型都能参数运算,唯独布尔类型不可以!
4. 多个基本数据类型转换 — 隐式类型转换 - 结论 2
在继续试验前,笔者先导入一个小的知识点,即类型级别(按从低到高排列):
-
byte,short,char => int => long => float => double
在 Java 中,当一个表达式中有多种数据类型参数运算的时候,系统会找出当前表达式中级别最高的那个类型,然后其余的所有类型都转换为当前表达式中级别最高的那个类型来进行计算。
有点抽象?读者可以去回顾一下上面几个类型占据的内存大小,在联想一下我们生活中的套娃,是不是把小盒子放到大的盒子中不会出现问题,而当我们尝试把大的盒子放小的盒子中时,要么放不了,如果强行放,就会把大盒子弄坏(精度丢失)。
来看下面这个例子,在下面的例子中,有 double 类型参与运算,所以最终结果的值也是 double 类型,将 double 类型的值赋予给 double 类型的变量,一点毛病没有:
public class printMotto {
public static void main(String[] args) {
double num = 1 + 1.0F + 1.0D + 'A';
System.out.print(num);
}
}

继续,为了作证前面我们的观点,我们将变量类型改为 int,此时由于表达式中有 double 参与,所以表达式的结果值应该也是 double 类型的,而将 double 值赋予给 int 必然会报错:
public class printMotto {
public static void main(String[] args) {
int num = 1 + 1.0F + 1.0D + 'A';
System.out.print(num);
}
}

如上,Java 在编译时确实报错了,从报错结果来看,也更加佐证了我们前面的结论,即等号右边的值的类型是 double,至于如何把 double 类型赋值给 int,是不是又回到我们前面讲解的强制类型转换啦(当然,写法跟单个有点不一样,要加上 (),笔者相信聪明如你,肯定能悟出来)。
5. 单个基本数据类型转换 — 特殊情况?
来看下面这个情况,我们尝试将 int 型的值赋予给 byte 型的变量(int 型所占空间大小大于 byte 型变量所占空间大小):
public class printMotto {
public static void main(String[] args) {
byte a = 12;
System.out.print(a);
}
}

如上,可以看到,我们并没有使用强制类型转换,照样把 int 型值赋值给了 byte 型。这是啥特殊情况嘛?
其实呢,说特殊也特殊,说不特殊也不特殊。对于 byte、short、char 类型来说,只要在它们的表数范围内(就是不超过它们的取值范围),我们在赋值的时候就不需要进行强行转换,直接赋值即可(其实笔者觉得,其底层还是隐式类型转换的功劳)。
142

被折叠的 条评论
为什么被折叠?



