极速版。
Recommend:https://liaoxuefeng.com/books/java/
面向对象基本知识
三要素:封装、继承、多态。
- 高内聚: 类的内部功能紧密相关,自己能干的事自己干完。
- 低耦合: 外部只通过最少的接口与你交互。
方法
this关键字(避免与方法参数重复)、super关键字
可变参数,相当于数组:
public void setNames(String... names){
this.names = names;
}参数绑定:基本类型参数是复制,其他引用类型都传递的是同一个引用。
构造方法
默认构造方法,没写其他的才会自动添加。
子类构造方法会默认调用super(),可以自己调别的,但第一行语句必须是调用父类的构造方法。
构造方法不可以继承。
方法重载
Overload。方法名相同,参数不同,返回类型都随便。
❌报错:参数相同,返回类型相同。
继承、多态
父类、超类、基类;子类、派生类。
所有类都继承自Object。
super关键字:访问父类。只能在类里面用,且受权限控制(不能访问父类private)。
隐藏: 成员变量、静态方法。
重写、覆盖:Override。
- 方法名、参数列表必须相同。
- 返回值类型可以是原来的子类,别的不行。
- 访问权限更高(>=)。
- 异常更小(<=),甚至没有。
- 不可以:private(隐藏)、final(编译错误)、static(隐藏)、构造方法(不能继承)
- @Override编译器重写检查,不重写报错
向上转型:安全。-> 多态
向下转型:必须得确实是子类,才能转为子类。检查使用obj instanceof Son。(抛出的是运行时异常)
多态: 一个方法可以被调用出多种效果。
- 静多态:重载,编译时绑定对应方法
动多态:重写,运行时才能知道。只有非静态方法有。
- 条件:继承、重写、向上转型
- 向上转型:编译看左边,非静态方法运行看右边,其他还看左边(父类)
final关键字:变量不可修改,方法不可重写,类不可继承。
区分继承与组合:多用组合,高内聚低耦合。
重写Object方法:toString、equals(默认是==比较地址,常用类比较值)
抽象
abstract 关键字。
修饰方法:抽象方法,本身没有实现任何方法语句,子类重写(所以不能是不能重写的关键字)。
修饰类:包含抽象方法的。只能继承,无法实例化。
接口
interface关键字。接口与继承不同,更多体现一种“能力”(has a)。
一个类可以实现多个接口,一个接口可以继承另一个接口。
接口所有方法都是默认public abstract的(继承权限不能更低)。
接口可以写变量,但不管怎么修饰,都会是public static final。
(定义非抽象方法:default方法)
接口回调:类似于动多态,向上转型为接口了,只要实现了接口的类都能调用。
静态字段方法
static关键字。
静态变量属于类,不属于实例,只有一个。可以用类名/实例进行访问。
静态方法一样。同时静态方法内部无法访问this、实例字段,可以访问静态字段。
静态代码块:仅能定义在类中 ,一般用于初始化类的静态变量。
类:只有内部类才能static,别的类不行。
语句执行顺序:
- 第一次 new 时:(1)初始化有显式初始化的静态成员变量;(2)顺序执行静态代码块;
(3)初始化有显式初始化的非静态成员变量;(4)顺序执行非静态代码块;(5)调用构造。
- 之后再 new 时:(1)初始化有显式初始化的非静态成员变量;(2)顺序执行非静态代码
块;(3)调用构造。
包
package。
包可以是多层结构,用.隔开。
默认的作用域就是包作用域。包没有父子关系,不同的包代表作用域完全不一样。
访问外部包:可以写出完整包名.类名(不用import),或者import把类或者整个包(使用*)导入进来。
作用域
| 修饰符 | 同一个类 | 同一个包 | 子类 (不同包) | 全局 (不同包非子类) |
|---|---|---|---|---|
| private | ✅ | ❌ | ❌ | ❌ |
| (default) | ✅ | ✅ | ❌ | ❌ |
| protected | ✅ | ✅ | ✅ (有条件) | ❌ |
| public | ✅ | ✅ | ✅ | ✅ |
protected:对子类可见(子类和父类在同一个包:通过自己访问、通过父类访问。在不同包:子类只能通过“自己或者自己子类”的引用,来访问父类的 protected 成员。不可以访问别人父类。)、protected的static 成员对所有子类可见。
还有局部变量:只能在作用域(对应的块)有效。
一个java文件最多一个public类,类本身权限只能是public或default。
内部类
有点恶心
- 成员内部类:直接定义在类里面,初始化
Outer.Inner in = out.new Inner();(所以必须得有有依赖的类实例才能初始化内部类,或者Outer.Inner in2 = new Outer().new Inner();)。每个内部类有一个指针指向外部类Outer.this,可以访问包括private的全部外部类变量。可以实例化多个。 - 静态内部类:比上面多一个static。其实就是个普通的类,且不依赖外部类对象,创建
Outer.Inner in = new Outer.Inner();,但可以访问Outer的private静态字段和静态方法。 - 局部内部类:方法里面的内部类,只能方法里面用。还可以访问方法中的final参数或最终变量。
匿名内部类: 没有名字,写在方法内部,定义的同时立刻实例化。通常用于继承一个父类或实现一个接口。
Runnable r = new Runnable() { // 实现必要的抽象方法... }; // or 继承 HashMap<String, String> map2 = new HashMap<>() {};
Java知识
Java是编译与解释相结合: 源代码(.java) -> 编译 -> 字节码(.class) -> JVM解释执行。
变量类型
分为基本类型(下图)和引用类型。
初始值: 0 / false / null
内存管理
new 的作用: 分配空间; 调用构造; 返回引用
栈 (Stack):
- 存局部变量。
- 存基本数据类型的具体数值 (
int a = 12)。 - 存引用类型的地址 (
Student s,这个s只是一个遥控器/地址)。
堆 (Heap):
- 存new出来的对象实体。
- 所有的对象属性(成员变量)都存在堆里。
- 数据区/方法区 :存类的元数据、静态变量、代码本身。
垃圾回收
快把我回收了吧,真的不行了
垃圾回收机制 (GC)
- 当一个对象没有任何引用指向它时,这个对象几乎再也找不回来了。
- 判断存储单元是否为垃圾的依据: 引用计数为 0
- 强制回收:
System.gc()或Runtime.gc()。JVM不一定马上执行。 finalize():对象被回收前的一句遗言。
匿名对象
- 写法:
new Student().show();(没有左边的Student s =)。 - 特点:只能用一次。用完那一行代码,它就变成了垃圾,等着被回收。通常用在只调用一次方法,或者作为参数传递时。
选择题的其他知识
这几节学的太烂,所以不是很精简了
集合和设计模式也可能包括在选择题,在下面
异常处理
异常分类、常见异常、简单的异常处理、(自定义异常)
分为编译错误、运行时异常。
- Throwable:所有的异常和错误,祖宗
- Error:辣鸡java(JVM)出错了,
OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出) Exception:程序可处理。又分为两类:
- 受检异常(非运行时异常):程序很大概率会有异常的地方,要求必须处理才能编译(trycatch或者throws出去)。例子:
IOException(文件IO错误)(这个下面又有子类FileNotFoundException)、SQLException、ClassNotFoundException。 非受检异常(运行时异常)
RuntimeException:通常是编程错误导致的,编译器不好检查(觉得这么你厉害怎么会犯这样低级的错误!!!所以不要求强制处理)NullPointerException(空指针,NPE)ArithmeticException(算术异常,比如除以0)ArrayIndexOutOfBoundsException(数组越界)ClassCastException(类型转换异常)NumberFormatException(数字格式异常,比如把"abc"转成int)
- 受检异常(非运行时异常):程序很大概率会有异常的地方,要求必须处理才能编译(trycatch或者throws出去)。例子:

处理方式:
try - catch - finally:捕获异常
- try是要执行的;
- 遇到异常停止、并去catch里面按顺序找对应异常(子类要放父类异常前面);
- finally是不管从try、catch哪里结束,都会在最后执行,一般用于释放资源。
throws:
- 推卸责任到上级处理,eg.
public void readFile() throws IOException。
- 推卸责任到上级处理,eg.
- 抛出异常:throw关键字,用于代码执行过程中
自定义异常:创建一个类,继承 Exception(如果你想让它是受检的)或者 RuntimeException(如果你想让它是非受检的)。
- 构造器:通常写两个,一个无参,一个带
String message(报错信息)并调用super(message)。
IO流
学不会...
IO两大类、File文件、IO基本操作、(装饰者模式IO)
IO是指Input/Output,即输入和输出。以内存为中心:
- Input指从外部读入数据到内存,例如,把文件从磁盘读取到内存,从网络读取数据到内存等等。
- Output指把数据从内存输出到外部,例如,把数据从内存写入到文件,把数据从内存输出到网络等等。
关系:
| 处理字节 (Byte) (图片、视频、exe文件) | 处理字符 (Char) (txt文本、汉字) | |
|---|---|---|
| 输入 (读进来) | InputStream | Reader |
| 输出 (写出去) | OutputStream | Writer |
java.lang.Object
|
+-- InputStream (抽象父类:所有字节输入的统称)
| |
| +-- FileInputStream (节点流:直接连在硬盘文件上,干苦力的)
| |
| +-- FilterInputStream (装饰者基类:不起眼,但很重要)
| |
| +-- BufferedInputStream (处理流:带缓冲区的,速度快)
| +-- DataInputStream (处理流:能读 int, double 等)
|
+-- OutputStream (抽象父类)
|
+-- FileOutputStream (直接写文件)
+-- FilterOutputStream
|
+-- BufferedOutputStream (带缓冲区的输出)
java.lang.Object
|
+-- Reader (抽象父类:所有字符输入的统称)
| |
| +-- InputStreamReader (桥梁:把字节变成字符)
| | |
| | +-- FileReader (节点流:直接读文本文件)
| |
| +-- BufferedReader (处理流:带缓冲,能按行读取)
|
+-- Writer (抽象父类)
|
+-- OutputStreamWriter
| |
| +-- FileWriter (直接写文本文件)
|
+-- BufferedWriter (处理流:带缓冲)File文件类: 先学会怎么拿到文件,才能转换为流。
- 创建:eg.
File f = new File("C:\\Windows\\notepad.exe"); - 用于判断的方法:exists、isDirectory、isFile
- 获取:
getName(),getAbsolutePath()(绝对路径),length()(文件大小,字节数),lastModified()。 - 文件操作:createNewFile()、
mkdir()vsmkdirs()、delete()、list()
字节流:
- 创建:
FileInputStream/FileOutputStream:eg.InputStream input = new FileInputStream("src/readme.txt"); - 基础的字节流只能读取字节byte👇
读 (
read):int read(): 一次读一个字节。重点:读到末尾返回 -1。int read(byte[] b): 一次读一堆到数组里(效率更高)。返回的是实际读取的字节数。
写 (
write):void write(int b): 写一个字节。void write(byte[] b): 写一个数组。
高级的流:装饰者模式:eg.
BufferedInputStream bufIn = new BufferedInputStream(fileIn);(fileIn是一个FileInputStream)- 缓冲流 (BufferedInputStream / BufferedOutputStream):读的快
- 数据流 (DataInputStream / DataOutputStream):可以读Java基本数据类型(int, double, boolean等)
- 打印流 (PrintStream):System.in
- 管道流 (PipedInputStream / PipedOutputStream):线程间通信
字符流:
- 核心区别:字节流一次读 1 个 byte(8位),字符流一次读 1 个 char(16位,即一个汉字或字符),自动处理了编码问题。
- 创建:
FileReader/FileWriter:eg.Reader reader = new FileReader("src/readme.txt"); 基础的 (
read):int read(): 一次读一个字符。重点:虽然返回是 int,但代表的是 char 的 Unicode 编码。读到末尾返回 -1。int read(char[] cbuf): 一次读一堆到字符数组里。返回的是实际读取的字符数。(注意:这里用的是char[]不是byte[])。
基础的 (
write):void write(int c): 写一个字符。void write(char[] cbuf): 写一个字符数组。void write(String str): 直接把一个 String 字符串写进去!不用自己转数组了。
高级: 装饰者模式:eg.
BufferedReader br = new BufferedReader(fileReader);缓冲流 (BufferedReader / BufferedWriter):**
BufferedReader: 提供了String readLine()方法,可以一次读一行文字(读到回车换行符为止)。如果读到末尾返回null。BufferedWriter: 提供了newLine()方法,自动根据系统写入换行符。
转换流 (InputStreamReader / OutputStreamWriter):
- 它是字节和字符之间的桥梁。
- 你可以指定编码格式(UTF-8, GBK)。例如:把一个 UTF-8 的
FileInputStream转换成字符流来读。 - eg.
new InputStreamReader(new FileInputStream("a.txt"), "UTF-8")
打印流 (PrintWriter):
- 对应字节流的 PrintStream。提供了
println()方法,写文本文件神器。
- 对应字节流的 PrintStream。提供了
- 管道流 (PipedReader / PipedWriter):线程间通信(传文本)。
多线程
牛魔这是人学的吗
定义、生命周期过程、几种方法与效果、锁、死锁
- 程序 (Program):静态的。就是你写好的代码文件,躺在硬盘里。
- 进程 (Process):动态的。程序运行起来就是进程。它是系统资源分配的基本单位(拥有独立的内存空间、I/O端口等)。
- 线程 (Thread):比进程更小。它是CPU调度和执行的基本单位。
进程间资源独立,(同一进程下)线程间资源共享。
Thread类、Runnable接口(支持多继承)
生命周期:
- 新建 (New):
new Thread(),此时它只是个Java对象,还没进操作系统队列。 - 就绪 (Runnable):调用了
start()。做好了所有准备,正在排队。只要 CPU 一喊“下一个”,立马就能上。 - 运行 (Running):CPU真的分配时间片给它了,开始执行
run()方法。 - 阻塞 (Blocked):因为某些原因(等I/O、睡着了
sleep、等锁synchronized、等别人join)暂停了。阻塞结束后,不能直接回到Running,必须先回Runnable排队。 - 终止 (Terminated):
run()执行完了,或者抛异常挂了
锁:
锁的是对象,不是代码!synchronized 就像是给某个对象加了一把锁,想要执行这段代码的线程,必须先拿到这个对象的钥匙。
三种:
- 实例方法:锁的是对象实例
- 静态方法:锁的是整个类
- 同步代码块:
synchronized(obj) { ... }。锁的是谁? 括号里的obj。
方法:
| 方法 | 来源 | 作用 | 是否释放锁 | 状态转换 | 备注 |
|---|---|---|---|---|---|
| start() | Thread | 启动线程 | N/A | New -> Runnable | 只有调它才是多线程 |
| run() | Thread | 逻辑代码 | 无 | 无 | 直接调就是普通方法 |
| sleep() | Thread | 暂停一会 | 不释放 | Running -> Waiting | 抱着锁睡,时间到自动醒 |
| yield() | Thread | 礼让一下 | 不释放 | Running -> Runnable | 只是重新排队,可能立刻又被选中 |
| join() | Thread | 等别人完 | - | Running -> Blocked | 比如“等儿子买烟回来再做饭” |
| wait() | Object | 等待被叫 | 释放锁 | Running -> Waiting | 必须在 synchronized 块里用(必须持有锁) |
| notify() | Object | 唤醒别人 | 无 | - | 必须在 synchronized 块里用 |
Thread类方法:
start():启动线程,JVM会由本地方法创建系统线程,然后自动调用run()。这是真正的多线程。run():如果你直接调用thread.run(),那它就是个普通的方法调用,还在主线程里跑,根本没有启动新线程。sleep:让当前正在干活的线程暂停执行一段时间。- 特点:抱着锁睡觉。虽然我休息了,不干活了,但我手里的资源(锁)不放开,别人也别想用。
- 转换:
Running->Timed Waiting(计时等待)。时间到了自动回到Runnable排队。
yield:与sleep相比不暂停,直接变为Runnable状态,不释放锁,相当于sleep(0)join():在线程 A 中调用线程B.join(),意思是A 说:“B 搞完之前,我不动”。- 线程 A 从
Running->Blocked,直到 B 执行完(死掉),A 才会回到Runnable。
- 线程 A 从
wait():交出锁,并且转为waiting状态,等到别人唤醒它才会继续。notify:从waiting区随机唤醒一个线程,转为runnablenotifyAll:唤醒全部。
死锁:资源互斥、请求与保持(已经持有资源,但不释放、还想要新的)、不剥夺(不能直接抢资源)、循环等待(A等B,B等A)
AI的例子:
假设你是线程 A,你要执行一段被
synchronized保护的代码(上厕所)。
- 拿到锁:你抢到了厕所钥匙,进去了,把门锁死。(此时:你有钥匙 + 你在动)。
执行 sleep:你在厕所里突然决定睡 10 秒钟。
- 关键点 1(不放锁):你睡着了,但你手里依然死死攥着钥匙!门依然锁着!
- 关键点 2(失去 CPU):因为你睡着了,你不再占用 CPU 资源。CPU 觉得你占着茅坑不拉屎,于是 CPU 跑出去服务外面排队的人(线程 B、C)。
外面的线程 B:
- CPU 跑来问 B:“你想动吗?” B 说:“想!”
- B 冲到厕所门口,发现门锁着(因为钥匙还在睡着的 A 手里)。
- B 只能灰溜溜地去等待锁释放的队列里蹲着(Blocked)。
A 睡醒了:
- 10 秒钟到了,A 醒了。
- 为什么还要重新排队? 因为 CPU 现在可能正在外面跟线程 C 聊天呢!
- A 虽然手里拿着钥匙(还在厕所里),但 A 必须等 CPU 回来临幸它,它才能继续动作(比如冲水、开门)。
- 这就叫 “重新排队获取 CPU 执行权”。
网络编程
记下概念得了,真看不下去了
1. 网络架构模式
- C/S 模式 (Client/Server):客户端/服务器。必须两边都有专门的软件(比如QQ)。服务器负责管理资源,客户端负责交互。
- B/S 模式 (Browser/Server):浏览器/服务器。客户端不需要专门软件,有浏览器就行(比如网页版淘宝)。
- P2P (Peer to Peer):对等网络。没有固定的服务器,每台电脑既是客户也是服务者(比如迅雷下载)。
2. TCP/UDP
TCP (传输控制协议):
- 面向连接(打电话,必须先接通)。
- 可靠(保证数据不丢、顺序不错)。
- 流模式(数据像水流一样源源不断)。
- 用途:下载文件、浏览网页、聊天。
UDP (用户数据报协议):
- 无连接(写信/发短信,不管你在不在直接发)。
- 不可靠(可能丢包,不保证顺序)。
- 数据报模式(数据是一个个独立的包,有大小限制)。
- 用途:视频会议、即时语音(丢一帧画面无所谓,要的是快)。
3. URI/URL
URI (Uniform Resource Identifier) —— 统一资源【标识】符
- 核心作用:唯一地标识一个资源。
- 就像:身份证号。
- 只要能把这个资源和别的资源区分开,它就是 URI。它不管你能不能找到这个资源,只管“它是谁”。
URL (Uniform Resource Locator) —— 统一资源【定位】符
- 核心作用:不仅标识资源,还告诉你去哪里找它(Location)。
- 就像:家庭住址(比如“地球/中国/北京市/故宫”)。
- URL 是 URI 的一种具体实现(子集)。它通过位置来标识资源。
4. Socket:套接字,Socket = IP地址 + 端口号。它是通信的端点,是驱动层提供给我们的编程接口。
单例模式
For 设计题
UML图
泛化:继承,直线三角箭头
实现:接口实现,虚线三角箭头
关联:拥有作为成员变量,实线、箭头(双向关联可能没有)
依赖:使用、作为方法参数传递,虚线、箭头
方框:
方框里面三栏,中间变量,下面方法。名称:类型/返回类型。
权限表示:
+(Plus):代表 public(公有的)。-(Minus):代表 private(私有的)。#(Hash):代表 protected(受保护的)。~(Tilde):代表 default(包级私有,默认)。- 通常省略不写,或者用波浪号。
设计原则
迪米特反例:objectA.getObjectB().getObjectC().doSomething()。
里氏:所有引用基类 (父类) 的地方必须能透明地使用其子类的对象。
设计模式
推荐:https://refactoringguru.cn/design-patterns/
真看不下去了
创建型
单例模式
饿汉式/懒汉式。
私有化构造方法,然后创建类时实例化/第一次调用实例化。
有static方法getInstance。
工厂模式
有一个工厂专门用于制造各种东西。
简单工厂:输入要的类型,用if-else判断返回哪个新的类型。
class NvwaFactory {
public static Person makePerson(String type) {
if (type.equals("M")) return new Man();
else if (type.equals("W")) return new Woman();
// 缺点:想造 Robot?必须在这里改代码加 else if
else return null;
}
}工厂方法: 把原来的if-else改为:工厂作为接口,然后有不同的实现(分别用于制造不同的东西),然后调用的时候用不同的工厂就可以产生不同的产品。符合开闭原则。
// 1. 工厂接口
interface PenCoreFactory {
PenCore createCore(); // 不传参,由子类决定造啥
}
// 2. 具体工厂:红笔工厂
class RedCoreFactory implements PenCoreFactory {
public PenCore createCore() { return new RedPenCore(); }
}
// 3. 具体工厂:蓝笔工厂
class BlueCoreFactory implements PenCoreFactory {
public PenCore createCore() { return new BluePenCore(); }
}抽象工厂:抽象工厂能生产一整套配套的东西(比如海尔工厂生产海尔电视+海尔空调)。也就是可以生产多个产品。
// 1. 抽象工厂:能生产一套东西(电视+空调)
interface AppFactory {
TV createTV(); // 生产电视
AC createAC(); // 生产空调
}
// 2. 具体工厂:海尔工厂 (Haier Family)
// 保证了造出来的电视和空调是配套的
class HaierFactory implements AppFactory {
public TV createTV() { return new HaierTV(); }
public AC createAC() { return new HaierAC(); }
}
// 3. 具体工厂:TCL工厂 (TCL Family)
class TCLFactory implements AppFactory {
public TV createTV() { return new TCLTV(); }
public AC createAC() { return new TCLAC(); }
}原型模式
通过复制已有的对象,产生新的对象。
类实现Cloneable接口,然后重写一个clone方法,由于clone方法返回的是Object,所以需要向下转型一下。
时机:当你发现初始化一个类需要查数据库、读取文件,或者构造函数里计算量巨大,而你又需要很多个这种对象时。
结构型
适配器模式
两种接口不兼容,一个对象想用其中一种接口“目标”A,但你只有另外一个被适配者B,需要适配。
- 识别时机:当你需要使用一个现有的类,但它的接口(方法名、参数)跟你的系统要求的不一样,且你不能修改那个现有类的代码时。
- 口诀:“实现目标接口,持有旧类引用”。
- 场景:洗衣机用交流电 (AlternateCurrent),录音机只能用直流电 (DirectCurrent)。你想用家里的交流电插座给录音机供电。
角色:
Target(目标): 直流电接口DirectCurrent(录音机想要的)。Adaptee(被适配者): 交流电类PowerCompany(现有的)。Adapter(适配器):ElectricAdapter。




装饰者模式
套娃。动态地给对象“贴”上新功能,而不是通过继承生成子类。
- 识别时机:当你想给一个类增加功能,但不想创建
SuperBird,SuperSuperBird,MegaBird这种无限子类时。 - 设计原则:组合优于继承。
写法:
- 创建一个装饰类,实现和原对象一样的接口。
- 构造函数里传入原对象。
- 在方法里调用原对象的方法,并在前后加上你的“加料”代码。

外观模式/门面模式
本来有好几个不同的类一起干活,太麻烦!这时候用一个外观类Leader来调用这些类与方法,集合成一个方法,使用的时候直接调这一个类就能干完了。
用于由于系统内部类太多、依赖关系太复杂,导致客户端调用起来很痛苦时。

行为型
策略模式
当一件事情有很多种算法、策略时,他们都继承自同一个策略接口,然后上下文里面存有一个策略接口的引用。使用的时候可以通过修改上下文中的策略,然后调用上下文方法时就会使用不同策略中的方法,便捷切换。
- 场景:裁判打分,有不同的去分规则(算法A、算法B)。程序运行时可能要切换规则。
代码分析:
Strategy(接口):定义了一个computerAverage()方法。StrategyA/StrategyB(具体策略):分别实现了具体的计算公式(比如A是直接平均,B是去掉最高最低分)。AverageScore(上下文 Context):这是核心。它有一个成员变量Strategy strategy。- 关键点:
setStrategy()方法。允许你在程序运行时,把“算法A”替换成“算法B”。

访问者模式
数据结构不变,但操作逻辑经常变。让“操作”去访问“数据”。
你的对象结构非常稳定(比如员工类只有工程师和经理),但你需要经常给它们添加新的报表、统计功能,又不想改动员工类的代码。
抽象元素定义有一个电表,然后定义具体元素,里面有电表的数值。不同的访问者采取不同的策略进行计价。


责任链模式
踢皮球。事情我处理不了,就传给我的上级。
每个都设置一个上级,然后定义一个处理方法,如果处理不了就传到上级方法里。

观察者模式
当一个对象的状态改变,需要同时触发其他一堆对象的行为,而且你不想把这些对象的代码硬编码在一起(解耦)。
- 被观察者:写一个
List存人,写一个add方法加人,写一个notify方法循环通知人。 - 观察者:统一实现一个接口(如
Observer),确保被观察者能统一调用。
eg.
- 场景:微信公众号更新,推送给所有关注的用户。
角色:
Subject(主题/公众号):维护一个列表List<Observer>。- 关键方法:
notify()。遍历列表,调用每个观察者的update()方法。 Observer(观察者/用户):定义了update()方法,用来接收消息。

编程题
数组
数组本身是引用类型(对象):Array。
定义:
int[] scores = new int[5]; 读取与使用:
// 获取元素
System.out.println(arr[0]); // 输出 10
System.out.println(arr[2]); // 输出 30
// 修改元素
arr[1] = 99; // 将索引为 1 的位置(原20)修改为 99数组长度:
int size = arr.length; // 返回 3遍历:
for (int num : numbers) {
System.out.println(num);
}排序(Array工具类):
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); // 输出 [1, 2, 5, 9]集合框架
在Java中,如果一个Java对象可以在内部持有若干其他Java对象,并对外提供访问接口,我们把这种Java对象称为集合。很显然,Java的数组可以看作是一种集合。
Java集合框架主要由两个根接口派生出来:
Collection接口:处理单列数据(一组对象)。- 它有两个主要的亲儿子:
List(有序、可重复)和Set(无序、不可重复)。
- 它有两个主要的亲儿子:
Map接口:处理键值对(Key-Value)数据(双列数据)。- 它和Collection没有继承关系!这是常考的坑点。
接口和实现类相分离,例如,有序表的接口是List,具体的实现类有ArrayList,LinkedList等
1. List 接口(有序列表)
List最典型的特征是:有序,指元素的存入顺序和取出顺序一致。
ArrayList:
- 底层实现:动态数组(可变长度数组)。
- 内存特性:连续空间。
- 优点:随机访问(get/set)极快(时间复杂度O(1)),因为可以通过索引直接计算内存地址。
- 缺点:插入和删除(add/remove)慢,尤其是从中间删,因为需要移动后面所有的元素。
- 扩容机制:当空间不够时,默认增长50%。
LinkedList:
- 底层实现:双向链表。
- 内存特性:非连续空间,靠指针连接。
- 优点:插入和删除极快(时间复杂度O(1)),只需要改变前后节点的指针指向。
- 缺点:随机访问慢(时间复杂度O(n)),想拿第100个元素,必须从头一个一个数过去。
- 特有方法:
addFirst,addLast,removeFirst等,这是因为它是链表,操作头尾特别方便。
Vector(老古董):
- 和ArrayList几乎一样,区别在于Vector是线程安全(Thread-Safe)的,效率低。ArrayList线程不安全,效率高。
- Vector扩容默认增长1倍。
List<String> fruits = new ArrayList<>();
LinkedList<String> ss = new LinkedList<>();
// 2. 添加 (Create)
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
fruits.add(1, "Mango"); // 在索引1的位置插入
System.out.println(fruits);
Iterator<String> it = fruits.iterator();
while(it.hasNext()){
String w = it.next();
System.out.println(w);
}
fruits.sort(null); // 传入 null 表示使用默认规则 (自然顺序)
for(String a: fruits){
System.out.println(a);
}
// 3. 读取 (Read)
String first = fruits.get(0); // 获取第一个元素 "Apple"
System.out.println("First: " + first);
// 4. 修改 (Update)
fruits.set(2, "Blueberry"); // 把索引2的 Banana 换成 Blueberry
// 5. 删除 (Delete)
fruits.remove(0); // 按索引删除:移除 "Apple"
fruits.remove("Mango"); // 按对象删除:移除 "Mango"
// 6. 其他常用方法
int size = fruits.size(); // 获取长度
boolean hasApple = fruits.contains("Apple"); // 是否包含
boolean isEmpty = fruits.isEmpty(); // 是否为空
System.out.println(fruits); // 输出: [Blueberry, Cherry]2. Set 接口(唯一集合)
Set注重独一无二。
HashSet:最常用。它是如何保证数据不重复的?
- 考点:放入元素时,先判断
hashCode(),如果哈希冲突了,再调用equals()判断对象是否真的相同。如果你们自定义类(比如Student)要放进Set,必须重写hashCode()和equals()方法。
- 考点:放入元素时,先判断
- TreeSet:元素是排序的(SortedSet),底层是红黑树。
因为 Set 没有索引,所以不能用 for (int i=0; i<size; i++)
Set<String> ss = new HashSet<>();
Set<String> ss2 = new TreeSet<>();
// 1. 创建 (推荐使用多态写法)
Set<String> set = new HashSet<>();
// 2. 添加元素
boolean r1 = set.add("Apple"); // true (添加成功)
set.add("Banana");
set.add("Orange");
// 3. 尝试添加重复元素
boolean r2 = set.add("Apple"); // false (添加失败,因为已存在)
System.out.println("添加Apple结果: " + r1); // true
System.out.println("再次添加Apple: " + r2); // false
System.out.println(set); // 输出顺序可能与添加顺序不同!例如: [Banana, Apple, Orange]
// 4. 判断是否存在
if (set.contains("Banana")) {
System.out.println("有香蕉");
}
// 5. 删除
set.remove("Banana");
// 6. 获取大小
System.out.println(set.size());3. Map
| 类名 | 特点 | 排序规则 | 适用场景 |
|---|---|---|---|
HashMap | 最常用 | 无序 | 90% 的场景,增删查改最快 (O(1)) |
LinkedHashMap | 有序 | 按插入顺序 | 需要记住“谁先放进去的” |
TreeMap | 自动排序 | 按 Key 的大小 | 需要按 Key 排序 (如按日期、字母表) |
Hashtable | 线程安全 | 无序 | 不推荐 (性能差,用 ConcurrentHashMap 代替) |
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
// 1. 创建
Map<String, Integer> scores = new HashMap<>();
// 2. 添加/修改 (Put)
scores.put("Alice", 85);
scores.put("Bob", 92);
scores.put("Bob", 99); // Key 重复,会【覆盖】旧值,现在 Bob 是 99
// 3. 获取 (Get)
Integer score = scores.get("Alice"); // 85
Integer missing = scores.get("Charlie"); // null (不存在返回 null)
// 4. 判断 (Check)
boolean hasAlice = scores.containsKey("Alice"); // true
boolean has100 = scores.containsValue(100); // false
// 5. 删除 (Remove)
scores.remove("Alice");
// 遍历
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
// 6. 获取长度
System.out.println(scores.size());
}
}
Interator
hasNext(): 还有没有下一个元素?next(): 把下一个元素拿出来,指针下移。remove(): 删除当前元素。
Collections工具类
- Collection:是接口(Interface),是List和Set的父接口。
- Collections:是工具类(Utility Class),里面全是
static方法,用来服务集合的。
常用:(部分用对象就能访问)
Collections.sort(list):排序。Collections.shuffle(list):打乱(洗牌)。Collections.reverse(list):反转。Collections.binarySearch(list, key):二分查找(前提是必须有序)。Collections.synchronizedList(list):把不安全的List变成线程安全的List。
String字符串
Scanner sc = new Scanner(System.in);
String s;
s = sc.nextLine();
String s1 = "hello";
String s2 = "HELLO".toLowerCase();
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // 得用这个
// 是否包含子串:
"Hello".contains("ll"); // true
"Hello".indexOf("l"); // 2 查找子串位置
"Hello".indexOf("l",1); // 2 查找子串位置
// 切割子串
"Hello".substring(2); // "llo"
"Hello".substring(2, 4); // "ll"
s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
s.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"BigInteger/BigDemical
大整数/浮点数。内部用数组存的
Scanner s = new Scanner(System.in);
long n = s.nextLong();
BigInteger i = BigInteger.valueOf(n); // 用long/int赋值
BigDecimal ii = BigDecimal.valueOf(20.5);
for(int p = 1; p <= n-1; p++){
i = i.multiply(BigInteger.valueOf(p)); // 相乘
}
System.out.println(i); // 能直接输出








