文章目录:
  1. 面向对象基本知识
    1. 方法
    2. 构造方法
    3. 方法重载
    4. 继承、多态
    5. 抽象
    6. 接口
    7. 静态字段方法
    8. 作用域
    9. 内部类
  2. Java知识
    1. 变量类型
    2. 内存管理
    3. 垃圾回收
  3. 选择题的其他知识
    1. 异常处理
    2. IO流
    3. 多线程
    4. 网络编程
    5. 单例模式
  4. For 设计题
    1. UML图
    2. 设计原则
    3. 设计模式
      1. 创建型
        1. 单例模式
        2. 工厂模式
        3. 原型模式
      2. 结构型
        1. 适配器模式
        2. 装饰者模式
        3. 外观模式/门面模式
      3. 行为型
        1. 策略模式
        2. 访问者模式
        3. 责任链模式
        4. 观察者模式
  5. 编程题
    1. 数组
    2. 集合框架
        1. 1. List 接口(有序列表)
        2. 2. Set 接口(唯一集合)
        3. 3. Map
        4. Interator
        5. Collections工具类
    3. String字符串
    4. BigInteger/BigDemical

极速版。

Recommend:https://liaoxuefeng.com/books/java/

面向对象基本知识

三要素:封装、继承、多态。

方法

this关键字(避免与方法参数重复)、super关键字

可变参数,相当于数组:

public void setNames(String... names){
    this.names = names;
}

参数绑定:基本类型参数是复制,其他引用类型都传递的是同一个引用。

构造方法

默认构造方法,没写其他的才会自动添加。

子类构造方法会默认调用super(),可以自己调别的,但第一行语句必须是调用父类的构造方法。

构造方法不可以继承

方法重载

Overload。方法名相同,参数不同,返回类型都随便。

❌报错:参数相同,返回类型相同。

继承、多态

父类、超类、基类;子类、派生类。

所有类都继承自Object

super关键字:访问父类。只能在类里面用,且受权限控制(不能访问父类private)。

隐藏: 成员变量、静态方法。

重写、覆盖:Override。

向上转型:安全。-> 多态

向下转型:必须得确实是子类,才能转为子类。检查使用obj instanceof Son。(抛出的是运行时异常)

多态: 一个方法可以被调用出多种效果。

final关键字:变量不可修改,方法不可重写,类不可继承。

区分继承与组合:多用组合,高内聚低耦合。

重写Object方法:toString、equals(默认是==比较地址,常用类比较值)

抽象

abstract 关键字。

修饰方法:抽象方法,本身没有实现任何方法语句,子类重写(所以不能是不能重写的关键字)。

修饰类:包含抽象方法的。只能继承,无法实例化。

接口

interface关键字。接口与继承不同,更多体现一种“能力”(has a)。

一个类可以实现多个接口,一个接口可以继承另一个接口。

接口所有方法都是默认public abstract的(继承权限不能更低)。

接口可以写变量,但不管怎么修饰,都会是public static final。

(定义非抽象方法:default方法)

接口回调:类似于动多态,向上转型为接口了,只要实现了接口的类都能调用。

静态字段方法

static关键字。

静态变量属于类,不属于实例,只有一个。可以用类名/实例进行访问。

静态方法一样。同时静态方法内部无法访问this、实例字段,可以访问静态字段。

静态代码块:仅能定义在类中 ,一般用于初始化类的静态变量。

类:只有内部类才能static,别的类不行。

语句执行顺序

(3)初始化有显式初始化的非静态成员变量;(4)顺序执行非静态代码块;(5)调用构造。

块;(3)调用构造。

package。

包可以是多层结构,用.隔开。

默认的作用域就是包作用域。包没有父子关系,不同的包代表作用域完全不一样。

访问外部包:可以写出完整包名.类名(不用import),或者import把类或者整个包(使用*)导入进来。

作用域

修饰符同一个类同一个包子类 (不同包)全局 (不同包非子类)
private
(default)
protected✅ (有条件)
public

protected:对子类可见(子类和父类在同一个包:通过自己访问、通过父类访问。在不同包:子类只能通过“自己或者自己子类”的引用,来访问父类的 protected 成员。不可以访问别人父类。)、protected的static 成员对所有子类可见。

还有局部变量:只能在作用域(对应的块)有效。

一个java文件最多一个public类,类本身权限只能是public或default。

内部类

有点恶心

Java知识

Java是编译与解释相结合: 源代码(.java) -> 编译 -> 字节码(.class) -> JVM解释执行。

image-20260112173645619

变量类型

分为基本类型(下图)和引用类型。

image-20251229173216861

初始值: 0 / false / null

内存管理

new 的作用: 分配空间; 调用构造; 返回引用

垃圾回收

快把我回收了吧,真的不行了

垃圾回收机制 (GC)

匿名对象

选择题的其他知识

这几节学的太烂,所以不是很精简了

集合和设计模式也可能包括在选择题,在下面

异常处理

异常分类、常见异常、简单的异常处理、(自定义异常)

分为编译错误、运行时异常。

image-20260112174504985

处理方式:

自定义异常:创建一个类,继承 Exception(如果你想让它是受检的)或者 RuntimeException(如果你想让它是非受检的)。

IO流

学不会...

IO两大类、File文件、IO基本操作、(装饰者模式IO)

IO是指Input/Output,即输入和输出。以内存为中心:

关系:

处理字节 (Byte)
(图片、视频、exe文件)
处理字符 (Char)
(txt文本、汉字)
输入 (读进来)InputStreamReader
输出 (写出去)OutputStreamWriter
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文件类: 先学会怎么拿到文件,才能转换为流。

字节流

字符流:

多线程

牛魔这是人学的吗

定义、生命周期过程、几种方法与效果、锁、死锁

进程间资源独立,(同一进程下)线程间资源共享。

Thread类、Runnable接口(支持多继承)

生命周期:

  1. 新建 (New)new Thread(),此时它只是个Java对象,还没进操作系统队列。
  2. 就绪 (Runnable):调用了 start()。做好了所有准备,正在排队。只要 CPU 一喊“下一个”,立马就能上。
  3. 运行 (Running):CPU真的分配时间片给它了,开始执行 run() 方法。
  4. 阻塞 (Blocked):因为某些原因(等I/O、睡着了sleep、等锁synchronized、等别人join)暂停了。阻塞结束后,不能直接回到Running,必须先回Runnable排队
  5. 终止 (Terminated)run() 执行完了,或者抛异常挂了

锁的是对象,不是代码!
synchronized 就像是给某个对象加了一把锁,想要执行这段代码的线程,必须先拿到这个对象的钥匙。

三种:

方法:

方法来源作用是否释放锁状态转换备注
start()Thread启动线程N/ANew -> Runnable只有调它才是多线程
run()Thread逻辑代码直接调就是普通方法
sleep()Thread暂停一会不释放Running -> Waiting抱着锁睡,时间到自动醒
yield()Thread礼让一下不释放Running -> Runnable只是重新排队,可能立刻又被选中
join()Thread等别人完-Running -> Blocked比如“等儿子买烟回来再做饭”
wait()Object等待被叫释放锁Running -> Waiting必须在 synchronized 块里用(必须持有锁)
notify()Object唤醒别人-必须在 synchronized 块里用

Thread类方法:

死锁:资源互斥、请求与保持(已经持有资源,但不释放、还想要新的)、不剥夺(不能直接抢资源)、循环等待(A等B,B等A)

AI的例子:

假设你是线程 A,你要执行一段被 synchronized 保护的代码(上厕所)。

  1. 拿到锁:你抢到了厕所钥匙,进去了,把门锁死。(此时:你有钥匙 + 你在动)。
  2. 执行 sleep:你在厕所里突然决定睡 10 秒钟。

    • 关键点 1(不放锁):你睡着了,但你手里依然死死攥着钥匙!门依然锁着!
    • 关键点 2(失去 CPU):因为你睡着了,你不再占用 CPU 资源。CPU 觉得你占着茅坑不拉屎,于是 CPU 跑出去服务外面排队的人(线程 B、C)。
  3. 外面的线程 B

    • CPU 跑来问 B:“你想动吗?” B 说:“想!”
    • B 冲到厕所门口,发现门锁着(因为钥匙还在睡着的 A 手里)。
    • B 只能灰溜溜地去等待锁释放的队列里蹲着(Blocked)。
  4. A 睡醒了

    • 10 秒钟到了,A 醒了。
    • 为什么还要重新排队? 因为 CPU 现在可能正在外面跟线程 C 聊天呢!
    • A 虽然手里拿着钥匙(还在厕所里),但 A 必须等 CPU 回来临幸它,它才能继续动作(比如冲水、开门)。
    • 这就叫 “重新排队获取 CPU 执行权”

网络编程

记下概念得了,真看不下去了

1. 网络架构模式

2. TCP/UDP

3. URI/URL

4. Socket套接字Socket = IP地址 + 端口号。它是通信的端点,是驱动层提供给我们的编程接口。

单例模式

image-20260112174849077

For 设计题

UML图

泛化:继承,直线三角箭头

实现:接口实现,虚线三角箭头

关联:拥有作为成员变量,实线、箭头(双向关联可能没有)

依赖:使用、作为方法参数传递,虚线、箭头

方框

方框里面三栏,中间变量,下面方法。名称:类型/返回类型。

权限表示:

image-20260112192640645

image-20260112192813187

设计原则

image-20260101232524376

迪米特反例:objectA.getObjectB().getObjectC().doSomething()

里氏:所有引用基类 (父类) 的地方必须能透明地使用其子类的对象。

设计模式

推荐:https://refactoringguru.cn/design-patterns/

真看不下去了

image-20260101233547747

image-20260112193632211

创建型

单例模式

饿汉式/懒汉式。

私有化构造方法,然后创建类时实例化/第一次调用实例化。

有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,所以需要向下转型一下。

时机:当你发现初始化一个类需要查数据库、读取文件,或者构造函数里计算量巨大,而你又需要很多个这种对象时。

image-20260113212134753

结构型

适配器模式

两种接口不兼容,一个对象想用其中一种接口“目标”A,但你只有另外一个被适配者B,需要适配。

  1. 识别时机:当你需要使用一个现有的类,但它的接口(方法名、参数)跟你的系统要求的不一样,且你不能修改那个现有类的代码时。
  2. 口诀:“实现目标接口,持有旧类引用”。

image-20260113212846939

image-20260113212918502

image-20260113212741906

image-20260113213111514

装饰者模式

套娃。动态地给对象“贴”上新功能,而不是通过继承生成子类。

  1. 识别时机:当你想给一个类增加功能,但不想创建 SuperBird, SuperSuperBird, MegaBird 这种无限子类时。
  2. 设计原则组合优于继承
  3. 写法

    • 创建一个装饰类,实现和原对象一样的接口。
    • 构造函数里传入原对象。
    • 在方法里调用原对象的方法,并在前后加上你的“加料”代码。

image-20260113213549053

外观模式/门面模式

本来有好几个不同的类一起干活,太麻烦!这时候用一个外观类Leader来调用这些类与方法,集合成一个方法,使用的时候直接调这一个类就能干完了。

用于由于系统内部类太多、依赖关系太复杂,导致客户端调用起来很痛苦时。

image-20260113213756516

行为型

策略模式

当一件事情有很多种算法、策略时,他们都继承自同一个策略接口,然后上下文里面存有一个策略接口的引用。使用的时候可以通过修改上下文中的策略,然后调用上下文方法时就会使用不同策略中的方法,便捷切换。

image-20260113214409961

访问者模式

数据结构不变,但操作逻辑经常变。让“操作”去访问“数据”。

你的对象结构非常稳定(比如员工类只有工程师和经理),但你需要经常给它们添加新的报表、统计功能,又不想改动员工类的代码。

抽象元素定义有一个电表,然后定义具体元素,里面有电表的数值。不同的访问者采取不同的策略进行计价。

image-20260113215626478

image-20260114105349192

责任链模式

踢皮球。事情我处理不了,就传给我的上级。

每个都设置一个上级,然后定义一个处理方法,如果处理不了就传到上级方法里。

image-20260113221006251

观察者模式

当一个对象的状态改变,需要同时触发其他一堆对象的行为,而且你不想把这些对象的代码硬编码在一起(解耦)。

eg.

image-20260113221800359

编程题

数组

数组本身是引用类型(对象):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集合框架主要由两个根接口派生出来:

  1. Collection 接口:处理单列数据(一组对象)。

    • 它有两个主要的亲儿子:List(有序、可重复)和 Set(无序、不可重复)。
  2. Map 接口:处理键值对(Key-Value)数据(双列数据)。

    • 它和Collection没有继承关系!这是常考的坑点。

接口和实现类相分离,例如,有序表的接口是List,具体的实现类有ArrayListLinkedList

1. List 接口(有序列表)

List最典型的特征是:有序,指元素的存入顺序和取出顺序一致。

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注重独一无二

因为 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
  1. hasNext(): 还有没有下一个元素?
  2. next(): 把下一个元素拿出来,指针下移。
  3. remove(): 删除当前元素。
Collections工具类

常用:(部分用对象就能访问)

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); // 能直接输出