# Java面向对象和基础
# 1. Java语言基础
# JDK下载
Adoptium的OpenJDK:https://adoptium.net/zh-CN/
Oracle官网下载地址:https://www.oracle.com/java/technologies/downloads/
# Java语言及类库的演进
| 版本 | 年份 | 新语言特性 | 类与接口的数量 |
|---|---|---|---|
| 1.0 | 1996 | 语言本身 | 211 |
| 1.1 | 1997 | 内部类 | 477 |
| 1.2 | 1998 | strictfp修饰符 | 1524 |
| 1.3 | 2000 | 无 | 1840 |
| 1.4 | 2002 | 断言 | 2723 |
| 5.0 | 2004 | 泛型类、"for each"循环、可变参数、自动装箱、元数据、枚举、静态导入 | 3279 |
| 6 | 2006 | 无 | 3793 |
| 7 | 2011 | 基于字符串的Switch语句、菱形运算符、二进制字面量、异常处理增强 | 4024 |
| 8 | 2014 | Lamda表达式、包含默认方法的接口、流和日期/时间库 | 4240 |
| 9 | 2017 | 模块、其他的语言和类库增强 | 6005 |
| 11 | 2018 | 局部变量类型推导(var)、Http客户端,移除Java FX、JNLP、Java EE重叠模块和CORBA | 4410 |
| 17 | 2021 | Switch表达式、文本块、instanceof模式匹配、记录、密封类 | 4859 |
| 21 | 2023 | 虚拟线程(Virtual threads),模式匹配(pattern matching) | 4882 |
# 基本数据类型
Java中数据类型分为基本数据类型和引用数据类型。这些基本数据类型的数据变量在声明之后就会立刻在栈上被分配内存空间。
| 基本类型 | 大小 | 默认值 | 最小值 | 最大值 | 包装类型 |
|---|---|---|---|---|---|
| boolean | ---- | ---- | ---- | ---- | Boolean |
| char | 16bits | \u0000 | \u0000 | \uffff | Character |
| byte | 8bits | 0 | -128(-2^7) | +127(2^7-1) | Byte |
| short | 16bits | 0 | -32768(-2^15) | +32767(2^15-1) | Short |
| int | 32bits | 0 | -2,147,483,648(-2^31) | 2,147,483,647(2^31-1) | Integer |
| long | 64bits | 0L | -9,223,372,036,854,775,808(-2^63) | 9,223,372,036,854,775,807(2^63-1) | Long |
| float | 32bits | 0.0f | IEEE 754 | IEEE 754 | Float |
| double | 64bits | 0.0d | IEEE 754 | IEEE 754 | Double |
char类型的字面值要用单引号括起来,长整型数值有一个后缀L或l,十六进制数值有一个前缀0x或0X,八进制有一个前缀0,0b或0B标识二进制,float类型的数值有一个后缀F或f,没有F或f的数值默认为double,也可在double数值后面添加后缀D或d。所有浮点数计算都遵循IEEE754规范。如果需要精确的数值计算,不允许有舍入误差,则应该使用BigDecimal类。整型值和布尔值之间不能进行相互转换。
类(默认值null)、接口(默认值null)、数组(默认值null)。
# Java注释有三种
单行注释,//注释本行
/* */两个注释界定符将一段很长的注释括起来,可换行,但不能多个/* */嵌套
/** */可以用来自动生成文档
# Java变量与命名
Java是一种强类型语言,声明一个变量时,先指定变量的类型。作为变量名的标识符由字母、数字、货币符号以及"标点连接符"组成。第一个字符不能是数字。'+'和'©'之类的符号不能出现在变量名中,空格也不行。字母区分大小写。标识符的长度基本上没有限制。不能使用Java关键字作为变量名。在Java9中,单下划线_是一个保留字,将来的版本可能使用_作为通配符。
Java区分大小写。类名必须以字母开头,后面可以跟字母和数字的任意组合,长度基本上没有限制。标准命名约定为,类名是以大写字母开头的名词,如果名字由多个单词组成,每个单词的第一个字母都应该大写,也就是驼峰命名法。源代码的文件名必须与公共类的类名相同,并用.java作为扩展名。
# 常量
可以用关键字final指示常量,关键字final表示这个变量只能被赋予一次,一旦赋值,就不能再更改了。习惯上,常量名使用全大写。可以使用关键字static final设置一个类常量。
# 数值类型之间的转换

# 位运算符
& (and) | (or) ^ (xor) ~ (not) >> (左移,会用符号位填充高位) << (右移) >>>(左移,运算符会用0填充高位)
不存在<<<运算符
&&的优先级比||的优先级高
# 字符串
Java字符串就是Unicode字符序列,每个用双引号括起来的字符串就是String类的一个实例。当将一个字符串与一个非字符串的值进行拼接时,后者会转换成字符串。如果需要把多个字符串放在一起,用一个界定符分隔,可以使用静态join方法。字符串不可变,编译器可以让字符串共享。原始字符串在堆中分配。可以使用equals方法检测两个字符串是否相等,不要使用==运算符检测两个字符串是否相等,这个运算符只能够确定两个字符串是否存放在同一个位置上。如果虚拟机总是共享相等的字符串,则可以使用==运算符检测字符串是否相等。但实际上只有字符串字面量会共享,而+或substring等操作得到的字符串并不共享。空串""是长度为0的字符串。空串是一个Java对象,有自己的串长度(0)和内容(空)。不过,String变量还可以存 放一个特殊的值,名为null ,表示目前没有任何对象与该变量关联。如果采用字符串拼接的方式来构建字符串,效率会比较低。每次拼接字符串时,都会构建一个新的 String对象,既耗时 ,又浪费空间 。使用StringBuilder类就可以避免这个问题。StringBuffer)类的效率不如StringBuilder类,不过它允许采用多线程的方式添加或删除字符。
Java15新增的文本块(textblock)特性,可以很容易地提供跨多行的字符串字面量。 文本块以"""开头 (这是开始"""),后面是一个换行符,并以另一个"""结尾(这是结束""")。
# switch语句
case标签可以是byte,short,int,char。JDK5以后可以是枚举,JDK7以后可以是String。多个字符串,用逗号分隔。不能在一个swit ch语句中混合使用冒号和箭头。在Java中,switch表达式直通式是指当switch块中的一个case匹配并执行后,如果没有遇到break语句,那么程序会继续执行下一个case中的语句,而不是立即退出switch结构。这种行为称为直通式。
//旧语法
int number = 1;
switch (number){
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
break;
case 3:
System.out.println("3");
break;
default:
System.out.println("");
break;
}
//新语法糖,break可以省略,也没有直通式行为
int number = 2;
switch (number){
case 1 -> {
System.out.println("1");
}
case 2 -> {
System.out.println("2");
}
case 3 -> {
System.out.println("3");
}
default -> {
System.out.println("");
}
}
//若语句体中只有一条语句,大括号也可省略
int number = 1;
switch (number){
case 1 -> System.out.println("1");
case 2 -> System.out.println("2");
case 3 -> System.out.println("3");
default -> System.out.println("");
}
//旧语法,有直通式行为
int number 4;
switch (number){
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("weekday");
break;
case 6:
case 7:
System.out.println("weekend");
break;
default:
System.out.println("error");
break;
}
//新语法糖,有直通式行为
int number = 7;
switch (number){
case 1,2,3,4,5 -> System.out.println("weekday");
case 6,7 -> System.out.println("weekend");
default -> System.out.println("error");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
注意switch表达式中的yield关键字。与break类似,yield会终止执行。但与break不同的是,yield还会生成一个值,这就是表达式的值。要在switch 表达式的一个分支中使用语句而不想有直通行为,就必须使用大括号和 yield。不能在switch表达式中使用return、break或continue语句。
# 大数
java.nath包中两个很有用 的类:BigIntegey和BigDecimal,这两个类可以处理包含任意长度数字序列的数值。BIgInteger 类实现任意精度的整数运算,BigDecinaL实现任意權度的浮点数运算。不能使用人们熟悉的算术运算符(如:+和*)来组合大数,而需要使用大数类中的add和multiply方法。
# 类之间的关系
| 类关系 | 描述 | 权力强侧 | 举例 |
|---|---|---|---|
| 继承 | 父子类之间的关系:is-a | 父类 | 小狗继承于动物 |
| 实现 | 接口和实现类之间的关系:can-do | 接口 | 小狗实现了狗叫接口 |
| 组合 | 比聚合更强的关系:contains-a | 整体 | 头是身体的一部分 |
| 聚合 | 暂时组装的关系:has-a | 组装方 | 小狗和绳子是暂时的聚合关系 |
| 依赖 | 一个类用到另一个:depends-a | 被依赖方 | 人养小狗,人依赖于小狗 |
| 关联 | 平等的使用关系:links-a | 平等 | 人使用卡消费,卡可以提取人的信息 |
# 用var声明局部变量
在Java10中,如果可以从变量的初始值推导出它们的类型,那么可以用var 关键字声明 局部变量,而无须指定类型。var关键字只能用于方法中的局部变量。
Java允许重载任何方法,而不只是构造器方法。要完整地描述一个方法,需要指定方法名以及参数类型,这叫作方法的签名。返回类型不是方法签名的一部分。也就是说,不能有两个名字相同、参数类型也 相同却有不同返回类型的方法。返回类型不是方法签名的一部分。也就是说,不能有两个名字相同、参数类型也 相同却有不同返回类型的方法。
如果你写的类没有构造器,就会为你提供 一个无参数构造器。这个构造器将所有的实例 字段设置为相应的默认值。如果类中提供了至少一个构造器,但是没有提供无参数构造器,那么构造对象时就必须 提供参数,否则就是不合法的。尚类没有任何其他构造器的时候,你才会得到一个默认的无参数构造器。this指示隐式参数。
# 记录
JDK14引入了一个预览特性:"记录"。最终版本在JDK16 中发布。记录(record)是一种特殊形式的类,其状态不可变,而且公共可读。在Java语言规范中,一个记录的实例字段称为组件(component)。记录的实例字段自动为final字段。对于完全由一组变量表示的不可变数据,要使用记录而不是类。如果教据是可变的,或者数据表示可能随时间改变,则使用类。记录更易读、更高效,而且在形发程序中更安全
# 2. Java 面向对象 (OOP) 深度解析
面向对象编程(Object-Oriented Programming, OOP)不是一种语法,而是一种思维方式。
- 面向过程 (C语言风格):像是炒饭。所有步骤(切菜、打蛋、炒饭)都在一个大锅里按顺序完成。
- 面向对象 (Java风格):像是盖浇饭。饭是对象,菜是对象,把它们组合在一起。如果想换口味,只需要换掉“菜”这个对象,而不用重新煮“饭”。
# 2.1 两大基石 —— 类与对象
这是 OOP 的起点。
1. 概念
- 类 (Class):是图纸(或者模具)。它定义了某类东西长什么样(属性)和能干什么(方法)。
- 对象 (Object):是实物。是根据图纸造出来的具体东西。
2. 举例
- 类:
Cat(猫)。定义了猫都有名字、颜色,都会喵喵叫。 - 对象:你家养的那只叫 "咪咪" 的白猫,或者是邻居家叫 "由于" 的黑猫。
3. 代码实现
// 1. 定义类 (图纸)
public class Cat {
// 属性 (状态)
String name;
String color;
// 构造器 (初始化对象的方法)
public Cat(String name, String color) {
this.name = name; // this 指代当前对象
this.color = color;
}
// 方法 (行为)
public void meow() {
System.out.println(name + " 正在喵喵叫...");
}
}
// 2. 使用类创建对象 (实物)
public class Main {
public static void main(String[] args) {
// new 关键字就是“制造”的过程
Cat myCat = new Cat("咪咪", "白色");
myCat.meow(); // 调用行为 -> 输出:咪咪 正在喵喵叫...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 2.2 三大特征 (OOP 的灵魂)
如果面试问 Java 基础,这三个词必问:封装、继承、多态。
1. 封装 (Encapsulation) —— "保护自己,隐藏细节"
- 通俗理解:就像插座。你不需要知道插座里面电线怎么接的(隐藏细节),你只需要把插头插进去就有电了(提供公开接口)。同时,插座外壳保护了内部电路不被你乱摸(安全性)。
- 关键词:
private(私有),public(公开),Getter/Setter。
public class User {
// 1. 私有化属性:不让外部直接修改,防止设置非法值(比如年龄-100岁)
private int age;
// 2. 提供公开的方法来控制访问
public void setAge(int age) {
if (age > 0 && age < 120) {
this.age = age;
} else {
System.out.println("年龄不合法!");
}
}
public int getAge() {
return this.age;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2. 继承 (Inheritance) —— "子承父业,青出于蓝"
- 通俗理解:儿子继承父亲的财产(代码复用),但儿子也可以有自己的爱好(扩展功能)。
- 关键词:
extends。Java 是单继承的(一个儿子只能有一个亲爹)。
// 父类:动物
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
// 子类:狗 (继承了动物)
class Dog extends Animal {
// 它可以直接使用 eat(),也可以定义自己的方法
public void bark() {
System.out.println("汪汪汪!");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
3. 多态 (Polymorphism) —— "一种定义,多种形态"
这是 OOP 中最难理解但也最强大的部分。
- 通俗理解:我在买单时说“支付”(一个动作)。如果你用支付宝,就扫码;如果你用现金,就给钱。同一个“支付”指令,根据对象的不同,表现出不同的行为。
- 核心条件:继承 + 方法重写 (Override) + 父类引用指向子类对象。
class Animal {
public void shout() {
System.out.println("动物叫...");
}
}
class Cat extends Animal {
@Override // 重写父类方法
public void shout() {
System.out.println("喵喵喵");
}
}
class Dog extends Animal {
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
public class Test {
public static void main(String[] args) {
// 多态的体现:类型是 Animal,但实际装的是 Cat
Animal myPet = new Cat();
myPet.shout(); // 输出:喵喵喵 (自动调用了实际对象的方法)
myPet = new Dog();
myPet.shout(); // 输出:汪汪汪
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
核心关键字详解
1. static
- 含义:它属于类,而不属于某个具体的对象。
- 比喻:就像班级里的饮水机。它不属于张三,也不属于李四,它是大家共享的。
- 注意:
static方法(如main)里不能使用this关键字,因为它不依赖于对象存在。
2. final
- 修饰变量:变成了常量,一旦赋值不能修改(如圆周率 PI)。
- 修饰方法:不能被子类重写(Override)。
- 修饰类:不能被继承(太监类,断子绝孙),例如 Java 中的
String类就是 final 的。
3. this 和 super
this:指代当前对象(我自己)。super:指代父类对象(我爹)。
# 2.3 抽象与接口 —— 系统的骨架
随着代码越来越复杂,我们需要更高级的设计。
1. 抽象类 (Abstract Class)
- 关键字:
abstract - 含义:这是一个半成品图纸。有些方法没写完(抽象方法),留给子类去实现。
- 特点:不能被
new(因为是半成品)。 - 场景:比如“图形”类,它有一个
calculateArea()(算面积) 方法。但图形本身不知道怎么算,必须等子类(圆形、方形)确定了才能算。
2. 接口 (Interface)
- 关键字:
interface,implements - 含义:这是一种契约或标准。
- 比喻:USB 接口。电脑定义了 USB 标准,无论是鼠标、键盘还是 U 盘,只要实现了这个接口,就能插进去用。
- 区别:
- 抽象类是
is-a关系(狗是动物)。 - 接口是
can-do关系(狗会游泳,鸟会飞)。一个类可以实现多个接口(技多不压身)。
- 抽象类是
# 2.4 Object 类 —— 万物之祖
在 Java 中,所有的类默认都继承自 Object 类。有两个方法必须搞懂(面试高频):
1. equals() vs ==
==:比较的是地址。这俩是不是内存里的同一个东西?equals():默认也是比地址,但通常我们需要重写它,用来比较内容。- 例子:你手里有一张 100 元,我手里也有一张 100 元。用
==比较是false(不是同一张纸),用equals比较应该是true(购买力相同)。
- 例子:你手里有一张 100 元,我手里也有一张 100 元。用
2. hashCode()
- 对象的身份证号。通常配合
HashMap使用。如果两个对象equals为 true,它们的hashCode必须相等。
总结与学习建议
作为初学者,建议按以下顺序攻克:
- 先动手:把上面的代码敲进 IDEA 里运行一遍,修改参数看看效果。
- 懂逻辑:彻底理解“封装、继承、多态”是为了解决什么问题(降低复杂度、提高复用)。
- 再深挖:当你熟练使用后,再回头看关于
static执行顺序、对象内存布局的内容,你会豁然开朗。
# 3. Java 进阶:五大核心机制详解
泛型、注解、异常、反射、SPI属于 Java 的 "核心机制"。如果说之前的 OOP 是教你如何写出基本的代码,那么这一部分就是教你如何看懂框架(如 Spring, MyBatis)的底层源码,以及如何写出架构级别的代码。我们不再局限于“怎么定义一个类”,而是要深入到 Java 语言设计的哲学中去。
# 3.1 泛型 (Generics) —— “给数据贴标签”
在没有泛型之前,Java 的集合(比如 List)就像一个垃圾桶,什么都能往里扔(String, Integer, Dog...),但取出来的时候如果不小心,程序就会崩溃。
1. 通俗理解
泛型就是给容器贴标签。
- 不带泛型:像个杂物箱。你以为里面是糖,拿出来直接吃,结果是个图钉(报错)。
- 带泛型:像药房的药柜。柜子上写着
<阿莫西林>,那么里面只能放阿莫西林。编译器(安检员)在编译时就会帮你检查,绝对不让你放错。
2. 为什么要用?
- 类型安全:把运行时的错误(Crash)提前到编译时(画红线报错)。
- 消除强制转换:不用麻烦地写
(String) obj。
3. 代码实战
// 1. 没有泛型的世界 (危险)
List list = new ArrayList();
list.add("Hello");
list.add(100); // 居然能放进去!
// 取数据时:如果不小心把 100 当成 String 取,程序直接炸裂 (ClassCastException)
String s = (String) list.get(1);
// 2. 泛型的世界 (安全)
// <String> 就是标签,规定只能放字符串
List<String> safeList = new ArrayList<>();
safeList.add("Hello");
// safeList.add(100); // 编译器直接报错,不让你运行!
// 取数据:不需要强制转换,一定是 String
String safeS = safeList.get(0);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4. 进阶:自定义泛型 <T>
如果你看源码,经常看到 <T>, <E>, <K, V>,它们其实就是代号(占位符)。
T(Type): 具体的类型E(Element): 元素K/V: 键值对
// 定义一个盒子,T 代表里面放什么类型,由使用者决定
public class Box<T> {
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
// 使用时再确定 T 到底是什么
Box<Integer> intBox = new Box<>();
2
3
4
5
6
7
8
9
# 3.2 注解 (Annotation) —— “代码的便利贴”
你经常看到 @Override, @Autowired, @SpringBootApplication,这些就是注解。
1. 通俗理解
注解本身不会改变代码的逻辑,它只是标签或备注。
- 比喻:就像你在冰箱的布丁上贴个便利贴写着“张三的”。布丁还是布丁,味道没变。但是如果你妈妈(编译器/框架)看到了这个便利贴,她就知道“哦,这个不能扔,是张三要吃的”。
2. 三类常见注解
- 给编译器看的:
@Override:告诉编译器“我在重写父类方法,帮我检查一下名字写错没”。@Deprecated:告诉编译器“这个方法过时了,画个删除线提醒调用者”。
- 给框架看的 (运行时):
- Spring 的
@Controller,@Service。Spring 启动时扫描代码,看到这些标签,就知道要把这些类管理起来。
- Spring 的
- 自定义注解:你自己造的标签。
3. 核心:元注解
定义注解的注解,叫元注解(Matryoshka doll 套娃)。
@Target:贴在哪里?(类上?方法上?字段上?)@Retention:保留多久?(源码里?编译后?运行时?—— 绝大多数框架注解都是 Runtime,为了反射读取)。
# 3.3 异常 (Exception) —— “驾驭意外”
程序运行就像开车,总会遇到突发状况(路断了、油没了)。Java 也就是通过异常机制来处理这些状况。
1. 异常体系结构 (Throwable)
- Error (错误):绝症。比如内存溢出 (OOM),JVM 崩溃。代码救不回来,直接挂。
- Exception (异常):生病。可以被治愈(捕获处理)。
2. 关键分类 (面试必问)
- 受检异常 (Checked Exception):红灯。
- 编译器强制你处理。比如
IOException(文件可能不存在)。你必须写try-catch或者throws,否则代码编译不过。 - 含义:这是你可以预见并应该处理的。
- 编译器强制你处理。比如
- 非受检异常 (Runtime Exception):开车走神。
- 编译器不管。比如
NullPointerException(空指针),ArithmeticException(除以0)。 - 含义:这是你的代码逻辑 bug,应该去修代码,而不是靠 try-catch 掩盖。
- 编译器不管。比如
3. 最佳实践
try {
// 可能出事的代码
readFile("data.txt");
} catch (FileNotFoundException e) {
// 补救措施:文件找不到,我就创建一个新的,或者提示用户
System.out.println("文件不存在,请检查路径");
e.printStackTrace(); // 打印错误日志,方便排查
} finally {
// 善后工作:无论是否报错,这里都会执行
// 通常用来关闭资源 (如关闭数据库连接)
closeFile();
}
2
3
4
5
6
7
8
9
10
11
12
# 3.4 反射 (Reflection) —— “上帝视角/解剖刀”
这是 Java 最强大的特性,也是所有框架(Spring/MyBatis)的基石。
1. 通俗理解
正常情况下,我们是 new Object()。
反射允许你在程序运行的时候,通过一个字符串(类名),把这个类“解剖”开。你可以看到它有哪些私有属性、私有方法,甚至强行修改它们。
- 比喻:
- 正常调用:你拿着菜单点菜(你知道有什么菜)。
- 反射:你是医生,拿着X光和手术刀。你不看菜单,直接切开这个人的肚子,看他胃里有什么,甚至能动手术改掉。
2. 核心功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 暴力访问:在运行时调用任意一个对象的属性和方法(哪怕是
private的)。
3. 代码演示 (感受上帝视角)
// 假设有一个类 User,里面有个 private 方法 saySecret()
// 1. 获取类的“图纸” (Class对象)
Class<?> clazz = Class.forName("com.example.User");
// 2. 动态创建对象 (相当于 new User())
Object user = clazz.newInstance();
// 3. 获取那个私有的方法
Method method = clazz.getDeclaredMethod("saySecret");
// 4. 暴力破解:允许访问私有内容!
method.setAccessible(true);
// 5. 执行方法
method.invoke(user); // 输出了私有秘密!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4. 为什么需要它?
Spring 框架为什么只需你在 XML 或注解里写个类名,它就能帮你创建对象?因为它用了反射!它读取类名字符串,利用反射创建实例并注入属性。
# 3.5 SPI (Service Provider Interface) —— “插件机制”
这是架构师级别的知识点,用于模块解耦。
1. 通俗理解
SPI 是 Java 提供的一套用来被第三方实现或者扩展的 API。
- 比喻:USB 接口。
- 电脑厂商(Java/官方)定义了 USB 插口的形状和标准(接口)。
- 鼠标厂商(罗技)、键盘厂商(雷蛇)去制造具体的硬件(实现类)。
- 作为用户,你只需要插入设备,电脑会自动发现并加载驱动。
2. 经典案例:JDBC
Java 定义了 java.sql.Driver (接口)。
- MySQL 提供了
mysql-connector.jar(实现)。 - Oracle 提供了
ojdbc.jar(实现)。
你写 Java 代码时,只面向 Driver 接口编程。当你把 MySQL 的 jar 包扔进项目时,Java 的 SPI 机制 会自动扫描 jar 包里的 META-INF/services 文件夹,发现并加载 MySQL 的驱动。
3. 核心类
ServiceLoader。它负责去寻找服务提供者。
// 加载所有实现了 SearchService 接口的类 (比如 GoogleSearch, BingSearch)
ServiceLoader<SearchService> loaders = ServiceLoader.load(SearchService.class);
for (SearchService s : loaders) {
s.search("Hello World");
}
2
3
4
5
6
# 3.6 总结:从入门到精通的路线
- OOP 基础:熟练掌握 类、继承、多态。
- 泛型与集合:学会用
List<T>,Map<K,V>,理解为什么这么设计。 - 异常处理:学会写健壮的代码,不让程序随意崩溃。
- 注解与反射:这是分水岭。想读懂 Spring 源码,必须深耕这一块。尝试用反射写一个简单的“自动根据注解赋值”的小工具。
- SPI:当你开始设计可扩展的系统,或者写中间件时,会用到它。
这部分内容比较抽象,建议你打开 IDEA,针对“反射”和“泛型”多敲几个 Demo,理解会深刻很多!
友情链接:
Adoptium的OpenJDK:https://adoptium.net/zh-CN/
Oracle官网下载地址:https://www.oracle.com/java/technologies/downloads/
Core Java第十三版第一卷(英文版)下载地址:https://library.lol/main/1863767A9A332F5A2317AAFF24007AFD
Core Java第十三版第二卷(英文版)下载地址:https://library.lol/main/91F8F511C4441F0B2D55E0C04025F355
Java17API文档:https://docs.oracle.com/en/java/javase/17/docs/api/
Java 全栈知识体系:https://www.pdai.tech/md/java/basic/java-basic-oop.html