需要了解的计算机基础知识:👉 存储单位 & 数值表示
- 存储单位:
- bit(位):描述电脑数据量的最小单位。
- byte(字节):计算机内存的最小存储单位(1byte = 8bit)。
- 数值表示:计算机中正负数的表示,即符号位的含义。
1、程序基本结构
针对 HelloWorld 程序,分析 Java 程序基本结构。
-
类:Java 程序的基本单位(即
class
关键字)。- 命名要求:英文字母开头,字母、数字、下划线组合。
- 命名规范:大驼峰命名。
-
方法:定义一组执行语句,代码会依次顺序执行。
- 命名要求:英文字母开头,字母、数字、下划线组合。
- 命名规范:小驼峰命名。
- main 方法:程序的主入口。
-
注释:标注代码,提高代码可读性。
-
单行注释
-
多行注释
-
文档注释:用于类和方法的定义处,可用于自动生成文档(JavaDoc)。
/** * 文档注释 * * @author Jaywee * @date 2022/12/12 */ public class Hello { public static void main(String[] args) { /* 多行注释 */ // 单行注释 System.out.println("Hello World!"); } }
-
2、变量 & 数据类型
2.1、变量
2.1.1、说明
- 类型:基本变量、引用变量。
- 使用:先定义后使用。
- 定义时可以赋初始值,定义后可以重新赋值。
- 可以赋值,也可以赋变量。
2.1.2、示例
针对以下代码,分析 JVM 执行操作。
// 定义变量
int x;
int y = 1;
// 修改变量值
x = 10;
y = x;
-
int x;
-
语句:定义变量 x,没有显式赋值(但 int 类型默认值 0);
-
JVM 操作:分配一个存储单元,变量 x 指向该存储单元。
x │ ▼ ┌───┬───┬───┬───┬───┬───┬───┐ │ │ 0 │ │ │ │ │ │ └───┴───┴───┴───┴───┴───┴───┘
-
-
int y = 1;
-
语句:定义变量 y,赋值 1;
-
JVM 操作:分配一个存储单元,变量 y 指向该存储单元。
x y │ │ ▼ ▼ ┌───┬───┬───┬───┬───┬───┬───┐ │ │ 0 │ │ 1 │ │ │ │ └───┴───┴───┴───┴───┴───┴───┘
-
-
x = 10;
-
语句:将变量 x 的值赋为 10;
-
JVM 操作:找到变量 x 的存储单元,移除原来的值 0,放入新的值10。
x y │ │ ▼ ▼ ┌───┬────┬───┬───┬───┬───┬───┐ │ │ 10 │ │ 1 │ │ │ │ └───┴────┴───┴───┴───┴───┴───┘
-
-
y = x;
-
语句:将变量 y 的值赋为变量 x 的值。
-
JVM 操作:找到变量 y 的存储单元,移除原来的值 1,放入新的值 10(还包含查找 x = 10 的过程)。
x y │ │ ▼ ▼ ┌───┬────┬───┬────┬───┬───┬───┐ │ │ 10 │ │ 10 │ │ │ │ └───┴────┴───┴────┴───┴───┴───┘
-
2.2、数据类型
2.2.1、八大基本类型
基本数据类型:CPU 可直接进行运算的类型。
含义 | 内存(byte) | 范围 | 默认值 | |
---|---|---|---|---|
byte | 字节 | 1 | [-27 , 27 -1] | 0 |
short | 短整型 | 2 | [-215, 215 - 1] | 0 |
int | 整型 | 4 | [-231, 231 - 1] | 0 |
long | 长整型 | 8 | [-263, 263 - 1] | 0 |
float | 单精度浮点型 | 4 | [1.4 & 10-45, 3.4 * 1038] | 0.0 |
double | 双精度浮点型 | 8 | [4.9 * 10-324, 1.79 * 10308] | 0.0 |
char | 字符 | 2 | [0, 216 - 1] | ''(空) |
boolean | 布尔 | 理论 1bit,实际通常 4byte | {true, false} | false |
使用
// byte
byte num1 = -100;
// short
short num2 = 300;
// int
int num3 = 200_000_000;
// long:赋值需要以 L 结尾
long num4 = 1L;
// float:赋值需要以 F 结尾
float num5 = -1.0F;
// double
double num6 = 3.14;
// char:以单引号赋值,即使是空字符
char ch1 = 'A';
char ch2 = '好';
char ch3 = '';
// boolean
boolean flag = true;
boolean isGreater = 10 > 9;
2.2.2、引用类型
除了八大基本类型,其它都是引用类型。
最常用的引用类型是 String(字符串)。
-
变量:存放在栈中,内部存储了对象实例在堆中的内存地址。
-
实例:存放在堆中。
引用类型 变量名 = new 对象实例(); Object obj = new Object();
2.2.3、说明
-
变量的作用域:
- 语句块:Java 中使用
{}
包围语句块,作为一组完整代码(如类、方法、控制语句)。 - 作用域:从变量的声明位置开始,到变量所在语句块结束。
- 说明:尽量将变量的作用域最小化,且避免使用重复的变量名。
- 语句块:Java 中使用
-
常量:需要显式初始化,并且无法再次赋值,否则报错。
- 定义:定义变量时使用
final
修饰符。 - 作用:提前声明变量名,避免在代码中使用魔法值(Magic Number)。
- 定义:定义变量时使用
-
var 关键字:省略变量类型,由编译器自动推断。
// 示例 var sb = new StringBuilder(); // 编译器自动推断为以下语句 StringBuilder sb = new StringBuilder();
3、运算
3.1、整数运算
整数:表示的是精确的数,其运算结果也是精确的整数。
3.1.1、运算符
① 基本运算符
-
运算符:
-
符号:加
+
,减-
,乘*
,除/
,取余%
。 -
除法
/
结果取整数部分,取余%
结果取余数部分。 -
除数若为
0
,编译通过但运行时报错。java.lang.ArithmeticException: / by zero
-
-
运算符简写:
-
符号:
+=
,-=
,*=
,/=
,%=
-
使用:
x += 10; // 等价于 x = x + 10;
-
-
自增/自减:本质也是运算符简写
-
符号:
++
,--
-
使用:符号可以在变量之前或之后,其位置表示自增的顺序。
-
符号在前:先自增,后引用。
-
符号在后:先引用,后自增。
int x = 1; int y = 2; // 情况1:y先自增成3,后引用;x=3+10=13 x = ++y + 10; // 情况2:先引用y,x=2+10=12;后y自增成3 x = y++ + 10; // 结论:y最终结果都是3,但运算符位置不同导致引用和自增顺序不同,因此x结果不同
-
-
② 特殊运算符
-
移位运算:将整数对应的二进制数进行移动。
-
左移:
<<
-
右移:
>>
(符号位不会移动) -
无符号右移:
>>>
(符号位也移动,高位自动补 0)// 右移 int n = -536870912; // 11100000 00000000 00000000 00000000 = -536870912 int a = n >> 1; // 11110000 00000000 00000000 00000000 = -268435456 int b = n >> 2; // 11111000 00000000 00000000 00000000 = -134217728 int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2 int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1 // 无符号右移 int n = -536870912; // 11100000 00000000 00000000 00000000 = -536870912 int a = n >>> 1; // 01110000 00000000 00000000 00000000 = 1879048192 int b = n >>> 2; // 00111000 00000000 00000000 00000000 = 939524096 int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7 int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1 // 对byte和short进行位移时,会先转换为int // 本质:左移即 * 2,右移即 / 2
-
-
位运算:将整数对应的二进制数按位对齐,依次运算。
- 与:全 1 才 1。
- 或:有 1 则 1。
- 非:01 互换。
- 异或:同 0 异 1。
3.1.2、说明
① 运算符优先级
()
:优先级最高,可用来改变运算顺序,提高可读性。!
~
++
--
*
/
%
+
-
<<
>>
>>>
&
|
+=
-=
*=
/=
%=
② 溢出
溢出:若计算结果超过了整数范围,不会报错,但会导致溢出。
- 原理:将整数转换为二进制运算,其最高位计算结果变成 1,导致符号改变。
- 解决方案:改用更大范围的整数类型,如 int 改成 long。
③ 转型
-
类型自动提升:若参与运算的两个数类型不一致,会先自动转型为较大类型。
short num1 = 1; int num2 = 10; // short先自动转型为int,再参与运算 System.out.println(num1 + num2);
-
强制转型:将大类型的整数强制转为小范围的整数。
-
使用:
(类型)
-
注意:以 int 和 short 为例,若 int 整数超出了 short 类型,强制转型时会丢弃 2 个高位字节,可能导致错误结果。
// 正确结果 int num1 = 10; short num2 = (short) num1; // 10 // 错误结果 int num1 = 12345678; short num2 = (short) num1; // 24910
-
3.2、浮点数运算
浮点数:通常无法精确表示,其运算结果也是如此。
3.2.1、运算符
-
支持的运算符:
- 四则运算:加
+
,减-
,乘*
,除/
(含简写形式+=
,-=
,*=
,/=
,%=
) - 自增/自减:
++
,--
- 四则运算:加
-
不支持的运算符:
- 取余
- 移位
- 位运算
3.2.2、说明
① 误差
-
由于浮点数无法精确表示,因此容易产生误差。
double num1 = 1.0 / 10; double num2 = 1 - 9.0 / 10; // 观察x和y是否相等 System.out.println(x); System.out.println(y);
-
由于误差的存在,判断浮点数是否相等的依据是二者之差小于一个很小的数。
// 二者之差的绝对值 double diff = Math.abs(x - y); if (r < 0.00001) { // 认为相等 }
② 溢出
-
整数运算
/ 0
会报错,浮点数运算/ 0
不会报错。 -
IEEE-754 规定,浮点数
/0
结果需要是NaN
(Not a Number)或Infinity
(无穷大)。double num1 = 0.0/0; // NaN double num2 = 1.0/0; // Infinity double num2 = -1.0/0 // -Infinity1
③ 转型
-
类型提升:整数与浮点数参与同一则运算时,整数通常会自动转型为浮点型。
// 情况1:自动转型 int num = 1; // int自动转为double,再参与运算 System.out.println(24.1 / num); // 情况2:不会自动转型 int num1 = 20, num2 = 4; /* num1和num2没有直接与浮点数运算,因此不会转型。 按整数运算得到结果5,5会自动转型为double */ System.out.println(3.4 + num1 / num2);
-
强制转型:浮点数可以强制转型为整数。
-
浮点数的小数部分会被丢弃。
-
四舍五入:强制转型之前,对浮点数加上 0.5。
-
若转型后超出整型的最大范围,将返回该整型的最大值。
// 丢弃小数部分 int n1 = (int) -3.4; // -3 int n2 = (int) 5.7; // 5 // 四舍五入:转型前+0.5 int n3 = (int) (5.7 + 0.5); // 6 // 超出范围:自动返回最大值 int n4 = (int) 1.2e20; // 2147483647
-
3.3、布尔运算
布尔运算:布尔运算是一种关系运算,结果只有 true 和 false。
3.3.1、运算符
-
比较运算符:
>
,>=
,<
,<=
,==
,!=
-
逻辑运算符:
-
与:
&
,&&
-
或:
|
,||
-
非:
!
boolean flag1 = 1 > 2; // false boolean flag2 = 2 >= 1; // true boolean flag3 = flag1 && flag2; // false boolean flag4 = flag1 || flag2; // true boolean flag5 = !flag4; // false
-
3.3.2、说明
① 优先级
!
>
,>=
,<
,<=
==
,!=
&&
||
② 短路运算
短路运算:若布尔运算的表达式能提前确定结果,则不会判断后续条件,直接返回结果。
-
短路运算符:
&&
,||
-
优点:提高性能。
// 非短路运算:计算后面的表达式时报错 boolean result1 = false & (7 / 0 > 1); // 短路运算:第一个false可确定结果为false,直接返回结果,不会报错 boolean result2 = false && (7 / 0 > 1);
④ 三目运算符
三目运算符:根据布尔表达式的结果,返回后续两个表达式之一的计算结果。
-
格式:
布尔表达式 ? 表达式1 : 表达式2
-
示例:
// 取两数中的最大值 int num1 = 20; int num2 = 10; int result = num1 >= num2 ? num1 : num2;
4、字符 & 字符串
4.1、char
char(character):字符,基本类型。
-
形式:单引号
''
-
Java 使用 Unicode 表示中/英文字符,一个
char
占用 2 个字节。 -
可将
char
赋值给整数,整数值为char
的十进制 Unicode 编码。 -
可用转义字符
u
及十六进制 Unicode 编码表示字符。char ch1 = 'A'; char ch2 = '好'; int num1 = ch1; // 65 int num2 = ch2; // 22909 char ch3 = 'u0041'; // A char ch4 = 'u597D'; // 好
4.2、String
4.2.1、说明
String:字符串,引用类型。
-
形式:双引号
""
-
底层实现:
String
本质是char
数组,一个String
可以存储任意个字符。 -
特点:不可变性,即字符串对象创建后无法改变。
- 含义:修改字符串时,JVM 会创建新的字符串,并修改变量的引用。
- 原因:String 底层的 char 数组用
final
修饰。
-
特殊值:
null
与""
-
null(空值):任何引用对象都可指向 null,表示不存在。
-
""(空串):值为空的有效字符串对象,不等同于 null。
String str1 = "ABC"; String str2 = null; String str3 = "";
-
4.2.2、转义字符
-
若字符串中存在特殊符号,则需要使用转义字符
,避免识别错误。
-
常用转义字符:
转义字符 真实含义 "
"
'
'
\
n
换行 r
回车 t
tab u####
Unicode 字符 -
示例:
// 三个字符:A 换行符 好 String str = "Anu597D";
4.2.3、相关操作
-
字符串拼接:使用
+
,可将String
与任意数据类型连接。- 好处:简化字符串处理操作。
- 说明:连接之前,其它数据类型会先自动转型为
String
。
-
多行字符串:两种形式。
-
使用
+
拼接。 -
使用一对
"""
包围(Java 13 引入)。String str1 = "SELECT * " + "FROM tb_user" + "WHERE id = 1;"; String str2 = """ SELECT * FROM tb_user WHERE id = 1; """;
-
5、数组
数组(array):相同类型数据的有序集合。
5.1、一维数组
5.1.1、语法
格式:数据类型[]
定义数组,new
关键字初始化。
-
动态初始化:指定数组长度。
-
静态初始化:指定内容,由 Java 推断数组长度。
// 动态 int[] nums1 = new int[3]; // 静态 int[] nums2 = new int[] {83, 97, 62}; int[] nums3 = {83, 97, 62};
5.1.2、访问数组
-
数组元素:
数组名[索引]
,索引从 0 开始。 -
数组长度:
数组名.length
。int[] nums = new int[5]; // 读 System.out.println(nums[0]); // 0 // 写 nums[0] = 100; // 长度 System.out.println(nums.length); // 5
5.1.3、特点
- 类型:数组是引用类型。
- 容量大小:数组一旦创建,容量不可变。
- 数组初始化后,所有元素都是默认值(基本类型对应默认值,引用类型 null)。
- 下标越界:若访问数组元素时索引超出范围,会报错
IndexOutOfBoundsException
。
5.2、引用关系
数组是引用类型。
变量保存了实例在堆中的内存地址,是对数组内存地址的引用。
5.3.1、基本类型
以 int 数组为例
int[] ns = new int[] {1, 2, 3, 4};
ns = new int[] {10, 20, 30};
-
初始化 ns:在堆内存开辟一块连续的内存空间,初始化数组。
ns │ ▼ ┌───┬───┬───┬───┬───┬───┐ │ │ 1 │ 2 │ 3 │ 4 │ │ └───┴───┴───┴───┴───┴───┘
-
重新赋值:在堆内存开辟另一块连续的内存空间,初始化数组。
(原数组没有改变,而是修改了变量引用)
ns ──────────────────────┐ │ ▼ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐───┐ │ │ 1 │ 2 │ 3 │ 4 │ │ │10 │20 │30 │ │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘───┘
5.3.2、引用类型
以 String 数组为例
String[] arrays = {"AA", "BB", "CC"}
arrays[1] = "XX";
-
初始化 arrays:在内存开辟一块连续的内存空间,初始化数组。
(数组元素是引用类型,指向内存中的对象实例)
┌─────────────┐ arrays │ ┌─────────┼───────────────────────┐ │ │ │ │ │ ▼ │ │ ▼ ▼ ┌───┬───┬─┴─┬─┴─┬───┬──────┬───┬──────┬───┬──────┬───┐ │ │░░░│░░░│░░░│ │ "BB" │ │ "AA" │ │ "CC" │ │ └───┴─┬─┴───┴───┴───┴──────┴───┴──────┴───┴──────┴───┘ │ ▲ └───────────────────────────┘
-
赋值:在内存中创建新的 String 实例,修改属性的引用。
┌─────────────────────────────────────────────┐ arrays │ ┌──────────────────────────────┐ │ │ │ │ │ │ ▼ │ │ ▼ ▼ ┌───┬───┬─┴─┬─┴─┬───┬──────┬───┬──────┬───┬──────┬───┬──────┬───┐ │ │░░░│░░░│░░░│ │ "BB" │ │ "AA" │ │ "CC" │ │ "XX" │ │ └───┴─┬─┴───┴───┴───┴──────┴───┴──────┴───┴──────┴───┴──────┴───┘ │ ▲ └───────────────────────────┘
Hint:数组本身是引用类型。
相比基本类型数组,引用类型数组多了一层指向关系。
5.3、多维数组
多维数组:一维数组的拓展,常见的是二维和三维数组。
语法、使用方式等基本相同。
5.3.1、二维数组
二维数组:一维数组的数组。
即每个数组元素是一维数组。
- 语法:
数组类型[][]
,支持动态、静态初始化。 - 访问数组:
- 数组元素:
数组名[行索引][列索引]
,索引从 0 开始。 - 数组长度:
- 行数:
数组名.length
- 列数:
数组名[行索引].length
- 行数:
- 数组元素:
- 特点:
- Java 高维数组,允许不同数组的长度不同。
- 其它特点同一维数组。
示例
-
代码:
// 初始化 int[][] nums = { {1, 2, 3}, {4, 5, 6, 7}, {8, 9} }; // 访问数组元素 System.out.println(nums[1][3]); // 7 // 数组长度 System.out.println(nums.length); // 3 System.out.println(nums[1].length); // 4
-
结构示意:
┌───┬───┬───┐ ┌───┐ ┌──▶│ 1 │ 2 │ 3 │ ns ─────▶│░░░│──┘ └───┴───┴───┘ ├───┤ ┌───┬───┬───┬───┐ │░░░│─────▶│ 4 │ 5 │ 6 │ 7 │ ├───┤ └───┴───┴───┴───┘ │░░░│──┐ ┌───┬───┐ └───┘ └──▶│ 8 │ 9 │ └───┴───┘
5.3.2、三维数组
三维数组:二维数组的数组。
即每个数组元素是二维数组。
- 语法:
数组类型[][][]
,支持动态、静态初始化。 - 访问数组:
- 数组元素:
数组名[索引][行索引][列索引]
,索引从 0 开始。 - 数组长度:类比二维数组。
- 遍历:推荐使用
Arrays.deepToString
- 数组元素:
- 特点:
- 在实际应用中,通常最多使用二维数组,很少使用更高数组。
- 其它特点同二维数组。
示例
-
初始化并访问:
int[][][] nums = { { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }, { {10, 11}, {12, 13} } }; // 访问数组元素 System.out.println(nums[0][1][2]); // 6 // 数组长度 System.out.println(nums.length); // 2
-
结构示意:
┌───┬───┬───┐ ┌───┐ ┌──▶│ 1 │ 2 │ 3 │ ┌──▶│░░░│──┘ └───┴───┴───┘ │ ├───┤ ┌───┬───┬───┐ │ │░░░│─────▶│ 4 │ 5 │ 6 │ │ ├───┤ └───┴───┴───┘ │ │░░░│──┐ ┌───┬───┬───┐ ┌───┐ │ └───┘ └──▶│ 7 │ 8 │ 9 │ ns ────▶│░░░│──┘ └───┴───┴───┘ ├───┤ ┌───┐ ┌───┬───┐ │░░░│─────▶│░░░│─────▶│10 │11 │ └───┘ ├───┤ └───┴───┘ │░░░│──┐ ┌───┬───┐ └───┘ └──▶│12 │13 │ └───┴───┘