Java高级

集合高级

理论篇

  • 集合体系与学习路径

    • 单列集合:实现 Collection
      • List:有序、有索引、可重复(ArrayList、LinkedList)
      • Set:无序(有规则)、无索引、不可重复(HashSet、LinkedHashSet、TreeSet)
    • 双列集合:实现 Map
      • HashMap(键无序)、LinkedHashMap(键有序)、TreeMap(键排序)
    • 学习三步:创建与特点 → 常用 API → 遍历方式
  • Collection 接口与通用遍历

    • 常用方法:add、remove、contains、clear、isEmpty、size(remove/contains 依赖 equals)
    • 遍历三板斧:
      • 迭代器 Iterator:hasNext/next
      • 增强 for:for (T e : collection)
      • forEach:c.forEach(Consumer) 支持 Lambda 与方法引用(::)
  • 方法引用(JDK8)

    • 作用:对 Lambda 的进一步简化,用“::”根据“可推导可省略”原则省略参数
    • 形式:对象::实例方法、类名::静态方法、类名::实例方法(特殊用法)
  • List 专题

    • List 特点:存取有序,有索引,可重复;提供大量基于索引的方法
    • ListIterator:双向遍历(先正序移动到尾,再倒序)
    • 并发修改异常 CME:遍历过程中用集合自身的 add/remove 会抛出;用迭代器 it.remove()/add() 解决
    • ArrayList 原理:JDK8 空参初始容量0,首次添加扩容到10;满时按1.5倍扩容
    • LinkedList:双向链表,首尾增删快;查找按就近端逐节点查(表面 get(index) 实际链式定位)
  • 数据结构速览

    • 栈:先进后出;队列:先进先出;数组:随机访问快、增删慢;链表:增删快、随机访问慢
  • 泛型(JDK5)

    • 目的:编译期约束与检查,统一类型,消除强转
    • 泛型类/方法/接口:E、T 等类型形参;静态方法需自定义泛型;接口实现可指定或延后到类泛型
    • 通配符:? 任意;? extends E(E 及子类);? super E(E 及父类)
  • Set 专题

    • Set 特点:无序(有规则)、无索引、不可重复
    • TreeSet:红黑树,排序且去重
      • 自然排序:实现 Comparable,compareTo 返回负/零/正(左/不存/右)
      • 比较器排序:构造传 Comparator,compare 规则同上;优先级高于自然排序
      • 同值保留技巧:compareTo 计算为0时返回非0(一般真实项目用唯一字段继续比较,如学号)
    • HashSet:哈希表(JDK7:数组+链表;JDK8+:数组+链表+红黑树),需重写 equals 和 hashCode(基于属性)保证唯一性与性能
    • LinkedHashSet:哈希表 + 双向链表,保序 + 去重
  • 可变参数

    • 形式:类型... 参数名;可接收0个、多个或数组;方法中只能有一个且需放最后
  • Collections 工具类

    • 常用:addAll、shuffle、max/min、sort(list)(自然排序)、sort(list,comp)(指定排序)
  • Map 专题

    • Map 特点:键值对,key 不重复,value 不限
    • 实现类:
      • HashMap:键无序,哈希表
      • LinkedHashMap:键有序(插入顺序),哈希表+链表
      • TreeMap:键排序(红黑树)
    • 常用 API:put/put 覆盖返回旧值、get、remove 返回旧值、containsKey/Value、isEmpty、size、clear
    • 三种遍历:
      • 键找值:keySet() → for key → get(key)
      • entrySet:for (Map.Entry<K,V> e : map.entrySet()) { e.getKey()/e.getValue() }
      • forEach(BiConsumer)
  • Stream 流(JDK8)

    • 目的:配合 Lambda 简化集合/数组操作,流水线串联中间操作,终结操作落地
    • 获取:
      • 集合:collection.stream()
      • 数组:Arrays.stream(arr)
      • 零散:Stream.of(T...)
      • Map:keySet/values/entrySet 各自 stream()
    • 中间操作(返回 Stream):
      • filter、limit、skip、distinct(依赖 equals/hashCode)、concat、map
    • 终结操作(非 Stream):
      • forEach、count、collect(Collectors.toList/toSet/toMap)、toList(JDK16+)
    • 流一经消费不可重用;不修改源

代码篇


Collection 常用方法与遍历

作用/概念简述

  • 增删查判空等基础;三种通用遍历(Iterator、增强 for、forEach)

通用模版

Collection<String> c = new ArrayList<>();
c.add("张三"); c.add("李四"); c.add("王五");

System.out.println(c.contains("李四")); // true
c.remove("李四");
System.out.println(c.isEmpty()); // false
System.out.println(c.size());

// 迭代器
Iterator<String> it = c.iterator();
while (it.hasNext()) {
    String s = it.next();
    System.out.println(s);
}

// 增强 for
for (String s : c) System.out.println(s);

// forEach + 方法引用
c.forEach(System.out::println);

属性知识卡片

方法说明
add/remove/contains依赖 equals
iterator取迭代器
forEach默认方法,接收 Consumer

方法引用(简化 Lambda)

作用/概念简述

  • 以方法名替代 Lambda,减少冗余

通用模版

list.forEach(System.out::println); // 等价于 x -> System.out.println(x)
Arrays.stream(arr).map(String::valueOf).forEach(System.out::println);

属性知识卡片

形式示例
对象::实例方法System.out::println
类名::静态方法Integer::parseInt
类名::实例方法String::toUpperCase(某些场景)

List 遍历与 ListIterator、CME 解决

作用/概念简述

  • List 五种遍历、双向迭代、并发修改异常与规避

通用模版

List<String> list = new ArrayList<>(List.of("张三","李四","王五","赵六"));

// 1. 迭代器删除(安全)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    if ("李四".equals(it.next())) it.remove();
}

// 2. 普通 for
for (int i=0;i<list.size();i++) System.out.println(list.get(i));

// 3. ListIterator 双向
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) System.out.println(lit.next());
while (lit.hasPrevious()) System.out.println(lit.previous());

// 4. 增强 for、5. forEach 同上

属性知识卡片

说明
CME遍历时用集合增删会抛异常
解决用迭代器 it.remove()/add()
倒序ListIterator 需先跑完正序

ArrayList 与 LinkedList

作用/概念简述

  • ArrayList 扩容机制;LinkedList 首尾快操作

通用模版

// ArrayList:新增少、随机访问多
List<Integer> arr = new ArrayList<>();
// LinkedList:首尾操作高效
LinkedList<String> l = new LinkedList<>();
l.addFirst("张三");
l.addLast("赵六");
System.out.println(l.getFirst());
l.removeLast();

属性知识卡片

适用
ArrayList读多写少、随机访问
LinkedList频繁首尾增删

泛型:类/方法/接口/通配符

作用/概念简述

  • 类型参数化;编译期检查

通用模版

// 泛型类
class Box<E>{ private E e; public E get(){return e;} public void set(E e){this.e=e;} }

// 泛型方法(静态需自定义)
public static <T> void printArray(T[] arr){ for(T t:arr) System.out.println(t); }

// 泛型接口实现
interface Inter<E>{ void show(E e); }
class InterAImpl implements Inter<String>{ public void show(String s){} }
class InterCImpl<E> implements Inter<E>{ public void show(E e){} }

// 通配符
public static void showAll(List<? extends Number> list){ for(Number n:list) System.out.println(n); }

属性知识卡片

关键点说明
静态泛型方法必须自声明泛型
通配符extends 上界、super 下界

TreeSet 排序:Comparable 与 Comparator

作用/概念简述

  • 自然排序 vs 比较器排序;红黑树结构

通用模版

// 自然排序
class Student implements Comparable<Student>{
    String name; int age;
    public int compareTo(Student o){
        int r = this.age - o.age;             // 正序
        return r==0 ? this.name.compareTo(o.name) : r;
    }
}
TreeSet<Student> ts = new TreeSet<>();

// 比较器排序(优先于自然)
TreeSet<Integer> set = new TreeSet<>((o1,o2)-> o2 - o1); // 倒序

属性知识卡片

返回值位置
负数
0不存(根节点除外)
正数

HashSet 唯一性:equals 与 hashCode

作用/概念简述

  • 重写 equals/hashCode,基于属性计算,避免碰撞与重复

通用模版

class Person{
    String name; int age;
    @Override public boolean equals(Object o){
        if(this==o) return true;
        if(o==null || getClass()!=o.getClass()) return false;
        Person p=(Person)o;
        return age==p.age && java.util.Objects.equals(name,p.name);
    }
    @Override public int hashCode(){
        return java.util.Objects.hash(name, age);
    }
}
Set<Person> hs = new HashSet<>();

属性知识卡片

说明
hashCode参与属性,分布均匀
equals内容相等为真

可变参数

作用/概念简述

  • 便捷接参;0/多/数组

通用模版

public static int sum(int... nums){
    int s=0; for(int n:nums) s+=n; return s;
}
System.out.println(sum()); System.out.println(sum(1,2,3));

属性知识卡片

限制说明
位置只能一个且放最后

Collections 工具

作用/概念简述

  • 常用集合操作

通用模版

ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5);
Collections.sort(list, (a,b)-> b - a);
Collections.shuffle(list);
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));

属性知识卡片

方法说明
sort自然/比较器排序
shuffle打乱

Map 常用 API 与三种遍历

作用/概念简述

  • put/覆盖、get/remove、contains、key/value/entry 遍历

通用模版

Map<String,String> map = new HashMap<>();
map.put("张三","北京"); map.put("李四","上海");

// 1. 键找值
for (String k : map.keySet()) {
    System.out.println(k + "---" + map.get(k));
}

// 2. entrySet
for (Map.Entry<String,String> e : map.entrySet()) {
    System.out.println(e.getKey() + "---" + e.getValue());
}

// 3. forEach
map.forEach((k,v)-> System.out.println(k + "---" + v));

属性知识卡片

实现特点
HashMap键无序,哈希表
LinkedHashMap键有序
TreeMap键排序,红黑树

练习:统计字符次数(a(5)b(4)...)

作用/概念简述

  • Map 计数模式 + StringBuilder 输出

通用模版

String s = "aababcabcdabcde";
Map<Character,Integer> m = new HashMap<>();
for(char c: s.toCharArray()){
    m.put(c, m.getOrDefault(c,0)+1);
}
StringBuilder sb = new StringBuilder();
m.forEach((k,v)-> sb.append(k).append("(").append(v).append(")"));
System.out.println(sb);

Stream:获取与常用操作

作用/概念简述

  • 从集合/数组/零散数据取流;中间操作链;终结/收集

通用模版

// 获取
list.stream();
Arrays.stream(new int[]{1,2,3});
Stream.of("张三","李四");

// 中间
list.stream()
    .filter(s-> s.startsWith("张"))
    .filter(s-> s.length()==3)
    .map(String::toUpperCase)
    .distinct()
    .limit(5)
    .skip(1)
    .forEach(System.out::println);

// 终结
long cnt = list.stream().count();

// 收集
List<String> l = list.stream().filter(...).collect(Collectors.toList());
Set<String> s = list.stream().filter(...).collect(Collectors.toSet());
Map<String,Integer> map = list.stream().collect(
    Collectors.toMap(
        x -> x.split(",")[0],
        x -> Integer.parseInt(x.split(",")[1])
    )
);

属性知识卡片

操作说明
filter过滤
map映射
distinct依赖 equals/hashCode
concat合并流
forEach/count/collect终结操作
注意流消费一次即失效

异常

理论篇

  • 异常概念与体系

    • 概念:程序在编译或运行期间出现的非正常情况(错误)。
    • 继承体系:所有异常均继承自 Throwable
      • Error:严重错误(如 StackOverflowError、OutOfMemoryError),程序一般无法处理。
      • Exception:
        • 运行时异常 RuntimeException 及其子类(如 NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException、NumberFormatException):编译期不强制处理,通常由代码不严谨引起。
        • 编译时异常(除 RuntimeException 外的 Exception,如 FileNotFoundException、ClassNotFoundException):编译期强制处理(捕获或声明)。
  • Java 默认异常处理流程(不处理时)

    • 异常位置创建异常对象并向上抛出 → 逐层调用者继续向上抛出 → 最终到达 JVM → 打印异常堆栈并终止程序。
  • 异常处理的两种方式

    • try...catch 捕获异常
      • 将“可能异常”的代码放入 try,发生异常进入对应 catch,后续代码可继续执行。
      • 多个 catch 从小到大(子类在前,父类在后)。
    • throws 抛出异常(声明)
      • 用在方法签名上,声明此方法将异常“交给调用方处理”。
      • 覆写规则:子类重写父类方法时,不能抛出比父类更大的(更宽泛的)异常,也不能抛出父类方法声明中不存在的异常。
  • throw 抛出异常对象(真正抛)

    • 用在方法体内,手动 new 异常对象并抛出。
    • 抛出编译时异常:方法上必须 throws 或在内部 try...catch;抛出运行时异常:可不写 throws。
  • try...catch vs throws 使用选择

    • 是否需要暴露问题给上层:
      • 需要暴露:throws(如底层工具方法、框架回调等)
      • 不需要暴露:try...catch(如用户交互输入校验、本地容错)
  • 自定义异常

    • 目的:Java 无法覆盖所有业务错误,自定义异常类表达特定业务问题(如学生年龄异常)。
    • 继承选择:
      • 继承 RuntimeException:非受检异常,调用者可选择性处理,常用于业务校验失败。
      • 继承 Exception:受检异常,调用者必须处理(捕获或声明)。
    • 常规实现:提供无参与带 message 的构造方法;在业务中 throw new 自定义异常(...);通过 e.getMessage() 获取错误信息。

代码篇


默认异常处理流程演示

作用/概念简述

  • 未处理异常的向上抛流程,最终由 JVM 打印异常并终止程序。

通用模版

public class ExceptionDemo1 {
    public static void main(String[] args) {
        System.out.println("main开始");
        method(); // 抛出至 main,最终到 JVM
        System.out.println("main结束"); // 不会执行
    }
    private static void method() {
        System.out.println("method开始");
        int i = 1 / 0; // 这里抛出 ArithmeticException
        System.out.println("method结束");
    }
}

属性知识卡片

说明
抛出链异常点 → 调用者 → JVM
结果打印堆栈,程序终止

try...catch 捕获异常(多 catch)

作用/概念简述

  • 捕获并处理异常,程序可继续向下执行;多 catch 从小到大。

通用模版

try {
    int i = 1 / 1;        // OK
    int[] arr = new int[10];
    System.out.println(arr[10]); // 触发 ArrayIndexOutOfBoundsException
} catch (ArithmeticException e) {
    System.out.println("捕获运算异常");
} catch (NullPointerException e) {
    System.out.println("捕获空指针异常");
} catch (Exception e) {
    System.out.println("捕获其它异常");
}
System.out.println("后续代码继续执行...");

属性知识卡片

说明
顺序子类在前,父类在后
继续执行catch 后可继续运行

输入校验场景:NumberFormatException 捕获

作用/概念简述

  • 键盘输入年龄为字符串,转换为 int,异常则提示重输。

通用模版

Scanner sc = new Scanner(System.in);
System.out.print("请输入学生姓名: ");
String name = sc.next();
Student stu = new Student();
stu.setName(name);

while (true) {
    try {
        System.out.print("请输入学生年龄: ");
        int age = Integer.parseInt(sc.next()); // 可能抛 NumberFormatException
        stu.setAge(age); // 可能抛业务异常(见自定义异常)
        break;
    } catch (NumberFormatException e) {
        System.out.println("您输入的年龄有误, 请检查!");
    } catch (StudentAgeException e) {
        System.out.println(e.getMessage());
    }
}
System.out.println(stu);

属性知识卡片

说明
parseInt"23" → 23,非法数字抛 NumberFormatException
组合校验转换异常 + 业务异常

throws 声明抛出(处理受检异常)

作用/概念简述

  • 方法内存在受检异常(编译时异常),使用 throws 声明交给调用方处理。

通用模版

import java.io.FileNotFoundException;
import java.io.FileReader;

public class ExceptionDemo3 {
    public static void main(String[] args) throws Exception {
        method(); // main 此处继续声明或 try...catch
    }
    public static void method() throws FileNotFoundException, ClassNotFoundException {
        System.out.println("开始");
        FileReader fr = new FileReader("D:\\abc.txt");        // 可能 FileNotFoundException
        Class.forName("com.itheima.pojo.Student");             // 可能 ClassNotFoundException
        System.out.println("结束");
    }
}

属性知识卡片

说明
throws用在方法签名,声明交由调用者
受检异常必须捕获或声明

throw 抛出异常对象(运行时/编译时)

作用/概念简述

  • 在方法体内主动抛异常,运行时异常可不声明;编译时异常需声明。

通用模版

// 运行时异常:可不写 throws
public void setAge(int age) {
    if (age < 0 || age > 100) {
        throw new IllegalArgumentException("年龄必须在0~100之间");
    }
    this.age = age;
}

// 编译时异常:必须 throws 或 try...catch
public void read() throws java.io.IOException {
    throw new java.io.IOException("IO错误");
}

属性知识卡片

关键字作用
throw真正抛出一个异常对象
throws声明此方法可能抛出的异常类型

方法重写与 throws 的约束

作用/概念简述

  • 子类重写方法时,不能抛出比父类更大的受检异常,也不能新增父类没有的受检异常。

通用模版

class Fu {
    public void show() throws Exception { }
}
class Zi extends Fu {
    @Override
    public void show() throws Exception { // OK:不大于父类
        // 具体实现
    }
}

属性知识卡片

说明
受检异常子类抛出的范围 ≤ 父类声明
运行时异常不受此限制

自定义异常类与使用

作用/概念简述

  • 使用自定义异常表达业务错误,抛出并在调用处提示。

通用模版

// 1) 自定义异常(运行时)
public class StudentAgeException extends RuntimeException {
    public StudentAgeException() { }
    public StudentAgeException(String message) { super(message); }
}

// 2) JavaBean 中使用
public class Student {
    private String name;
    private int age;
    public void setName(String name) { this.name = name; }
    public void setAge(int age) {
        if (age >= 0 && age <= 100) {
            this.age = age;
        } else {
            throw new StudentAgeException("年龄有误, 请检查是否是0~100之间的!");
        }
    }
    // toString/getter 省略
}

属性知识卡片

说明
继承RuntimeException(非受检)或 Exception(受检)
构造建议提供无参与 message 构造
获取信息e.getMessage() 获取错误提示

try...catch 与 throws 的选择

作用/概念简述

  • 根据“是否需要暴露问题”选择处理方式。

通用模版

  • 需要暴露给上层:throws(如工具方法、框架回调)
  • 不需要暴露(就地消化):try...catch(如用户输入校验)

属性知识卡片

场景推荐方式
底层通用库throws
交互/容错try...catch

File类

理论篇

  • File 概念与对象创建

    • File 用于封装“路径名”,本身不读写文件内容。封装的路径可以存在也可以不存在。
    • 常用构造
      • new File(String pathname)
      • new File(String parent, String child)
      • new File(File parent, String child)
  • 路径类型

    • 绝对路径:从盘符根开始(如 E:\data\log.txt)
    • 相对路径:相对当前项目工作目录(如 day03\A.txt)
  • 常用判断与获取信息

    • 判断:exists、isFile、isDirectory
    • 获取:length(字节数,仅文件有效)、getAbsolutePath、getPath、getName、lastModified(毫秒值)
    • 注意:length 对文件夹返回的不是总大小;需要递归统计。
  • 创建与删除

    • 文件:createNewFile
    • 目录:mkdir(一级)、mkdirs(多级)
    • 删除:delete 仅能删除文件或“空目录”,且不进回收站
  • 遍历与过滤

    • listFiles 返回当前目录下“一级”File 对象数组
    • 过滤 .java:判断 isFile 且 getName().endsWith(".java")
  • 递归基础

    • 定义:方法直接或间接调用自身
    • 关键:必须有明确终止条件,否则可能栈溢出(StackOverflowError)
    • 思路:将大问题转化为规模更小的同类问题(如阶乘、斐波那契)
  • 递归在文件操作中的应用

    • 遍历子目录查找指定类型文件
    • 递归删除非空目录
    • 递归统计目录大小
  • 常见细节与陷阱

    • listFiles 可能返回 null(权限不足、IO 错误或非目录),使用前判空
    • delete 不可删除非空目录;删除目录需先递归删除其中的文件与子目录
    • 路径分隔符跨平台建议使用 File.separator
    • 相对路径基于当前工作目录(可通过 System.getProperty("user.dir") 查看)

代码篇


File 对象创建与路径

作用/概念简述

  • 演示三种构造与路径存在性;File 仅封装路径。

通用模版

File f1 = new File("E:\\A.txt");
File f2 = new File("E:\\", "Develop");
File f3 = new File(new File("E:\\"), "Develop");

System.out.println(f1.exists()); // 路径是否存在
System.out.println(f2.exists());
System.out.println(f3.exists());

属性知识卡片

构造说明
File(String)直接路径
File(String,String)父路径+子路径
File(File,String)父File+子路径

相对路径与绝对路径

作用/概念简述

  • 正确使用相对/绝对路径,获取真实绝对路径。

通用模版

File rel = new File("day03\\A.txt"); // 相对
File abs = new File("E:\\Develop\\A.txt"); // 绝对

System.out.println(rel.getAbsolutePath());
System.out.println(abs.getPath());

属性知识卡片

类型示例备注
绝对E:\data\log.txt从盘符开始
相对day03\A.txt相对 user.dir

判断方法

作用/概念简述

  • 判断路径存在性与类型(文件/文件夹)。

通用模版

File f = new File("day03\\A.txt");
System.out.println(f.exists());
System.out.println(f.isFile());
System.out.println(f.isDirectory());

属性知识卡片

方法说明
exists路径是否存在
isFile是否为文件
isDirectory是否为目录

获取信息方法

作用/概念简述

  • 文件信息获取与时间戳格式化。

通用模版

File f = new File("E:\\A.txt");
System.out.println(f.length());                  // 文件字节数
System.out.println(f.getAbsolutePath());         // 绝对路径
System.out.println(f.getPath());                 // 定义时的路径
System.out.println(f.getName());                 // 文件名

long ts = f.lastModified();                      // 毫秒值
System.out.println(new java.util.Date(ts));

属性知识卡片

方法说明
length文件大小;目录不可靠
lastModified上次修改时间戳

创建与删除

作用/概念简述

  • 创建文件/目录,删除文件或空目录。

通用模版

File f1 = new File("day03\\B.txt");
boolean created = f1.createNewFile();  // 存在则返回 false
File d1 = new File("day03\\aaa");
boolean mk1 = d1.mkdir();              // 一级目录
File d2 = new File("day03\\a\\b\\c");
boolean mk2 = d2.mkdirs();             // 多级目录

boolean delFile = f1.delete();         // 删除文件
boolean delEmptyDir = d1.delete();     // 删除空目录

属性知识卡片

方法注意
createNewFile父目录需存在
mkdir/mkdirsmkdir 仅一级;mkdirs 递归创建
delete仅文件/空目录;不进回收站

遍历一级文件对象并筛选 .java

作用/概念简述

  • 使用 listFiles 遍历一级文件对象,筛选指定后缀。

通用模版

File dir = new File("E:\\project");
File[] files = dir.listFiles();
if (files != null) {
    for (File file : files) {
        if (file.isFile() && file.getName().endsWith(".java")) {
            System.out.println(file);
        }
    }
}

属性知识卡片

说明
listFiles可能返回 null(判空)
一级仅当前目录,不含子目录

递归基础:阶乘与斐波那契

作用/概念简述

  • 经典递归模板,强调终止条件。

通用模版

// 阶乘 n!
public static int fact(int n){
    if (n <= 1) return 1;
    return n * fact(n - 1);
}

// 斐波那契(第 month 月兔子对数)
public static int fib(int month){
    if (month == 1 || month == 2) return 1;
    return fib(month - 1) + fib(month - 2);
}

属性知识卡片

要点说明
终止条件防止无限递归
复杂度斐波那契朴素递归指数级,可加缓存优化

递归查找所有 .java 文件

作用/概念简述

  • 深度遍历子目录,打印所有 .java 文件。

通用模版

public static void printJavaFiles(File dir){
    File[] files = dir.listFiles();
    if (files == null) return; // 判空
    for (File f : files){
        if (f.isFile() && f.getName().endsWith(".java")){
            System.out.println(f);
        } else if (f.isDirectory()){
            printJavaFiles(f);
        }
    }
}

属性知识卡片

说明
判空无权限/IO 错误/非目录
递归目录进入再查

递归删除非空目录

作用/概念简述

  • 先删子文件与子目录,最后删除当前目录。

通用模版

public static void deleteDir(File dir){
    File[] files = dir.listFiles();
    if (files != null) {
        for (File f : files){
            if (f.isFile()){
                f.delete();
            } else {
                deleteDir(f);
            }
        }
    }
    dir.delete(); // 删除空目录
}

属性知识卡片

说明
顺序先深后浅(后序遍历)
返回值可收集删除失败项做日志

递归统计目录大小(字节数)

作用/概念简述

  • 统计所有文件 length 累加,目录递归进入。

通用模版

public static long dirSize(File dir){
    long sum = 0;
    File[] files = dir.listFiles();
    if (files == null) return 0L;
    for (File f : files){
        if (f.isFile()){
            sum += f.length();
        } else {
            sum += dirSize(f);
        }
    }
    return sum;
}

属性知识卡片

说明
length仅文件有效
溢出极大目录注意 long 溢出风险极低但仍建议用 long

实用:控制台校验目录输入

作用/概念简述

  • 循环输入直到获得“存在且为目录”的 File 对象。

通用模版

public static File readDirFromConsole(){
    java.util.Scanner sc = new java.util.Scanner(System.in);
    while (true){
        System.out.print("请输入一个文件夹路径: ");
        File dir = new File(sc.next());
        if (!dir.exists()){
            System.out.println("路径不存在, 请检查!");
        } else if (dir.isFile()){
            System.out.println("输入的是文件路径, 请重新输入文件夹路径!");
        } else {
            return dir;
        }
    }
}

属性知识卡片

校验说明
exists存在性
isFile排除文件,要求目录

常用 API、IO 流

理论篇

  • Math 类(数值运算)

    • abs 绝对值
    • ceil 向上取整
    • floor 向下取整
    • round 四舍五入(float→int / double→long)
    • max/min 最大/小值
    • pow 幂
    • random [0.0,1.0)
  • System 类(系统功能)

    • exit(int) 终止 JVM(0 正常,非 0 异常)
    • currentTimeMillis() 自 1970-01-01 00:00:00(北京时间为 08:00:00)至今的毫秒值
    • arraycopy(src, srcPos, dest, destPos, length) 数组高效拷贝
  • 包装类(基本类型 ↔ 引用类型)

    • 对应关系:byte/Byte,short/Short,int/Integer,long/Long,char/Character,float/Float,double/Double,boolean/Boolean
    • 常用方法
      • valueOf 装箱(推荐,构造器已过时)
      • xxxValue 拆箱(如 intValue、doubleValue)
      • parseXxx 字符串转基本类型(如 parseInt、parseDouble)
    • 自动装箱/拆箱:JDK5+ 支持 i = 10; int x = i; 表达式中自动转换
    • 注意:parseXxx 解析失败抛 NumberFormatException
  • BigDecimal(高精度小数运算)

    • 适用:解决小数计算不精确(如 0.1 + 0.2)
    • 创建
      • new BigDecimal(String)(推荐)
      • BigDecimal.valueOf(double)(推荐,会做必要转换)
      • new BigDecimal(double)(不推荐)
    • 常用:add、subtract、multiply、divide(若除不尽需指定小数位与舍入模式)
    • 舍入模式:RoundingMode.HALF_UP 四舍五入;UP 进一;DOWN 去尾
  • Arrays(数组工具)

    • toString 数组转字符串
    • equals 判断两个数组内容是否相同(元素、顺序、长度)
    • sort 排序(可自定义 Comparator)
    • binarySearch 二分查找元素索引(要求已排序,未找到返回负值)
  • 传统日期时间(Date、SimpleDateFormat)

    • Date:表示时间点(毫秒值封装)
      • new Date() 当前时间;new Date(ms) 指定毫秒
      • getTime()/setTime(ms)
    • SimpleDateFormat:格式化/解析
      • new SimpleDateFormat("yyyy年MM月dd日")
      • format(Date) → String;parse(String) → Date
    • 注意:格式必须与字符串匹配;parse 抛 ParseException
  • JDK8 日期时间(推荐)

    • LocalDate(日期)、LocalTime(时间)、LocalDateTime(日期+时间)
    • 获取:now();of(…);由 LocalDateTime 拆分 toLocalDate()/toLocalTime()
    • 读取:getYear/getMonthValue/getDayOfMonth/getDayOfWeek/getHour/…
    • 运算:plusXxx/minusXxx;withXxx(设置某字段)
    • 比较:isBefore/isAfter/equals
    • DateTimeFormatter:ofPattern(…),format(temporal),parse(string, formatter)
    • ChronoUnit:between(开始, 结束) 计算两时间的间隔(年/月/日/…)
    • 注意:Local* 不含时区信息;格式化与解析由具体类型的 parse 调用
  • IO 流体系

    • 输入(Input)读取;输出(Output)写出
    • 字节流:FileInputStream / FileOutputStream(万能流,任何数据)
    • 字符流:FileReader / FileWriter(文本文件,避免中文乱码)
    • 异常处理
      • 传统 try-catch-finally 手动 close
      • JDK7 try-with-resources 自动 close(实现 AutoCloseable)
    • 写出细节:FileOutputStream/FileWriter 默认覆盖,传 true 追加
    • 字符输出需 flush 或 close 刷盘;字节输出 write 后一般直接落盘
  • Properties(配置键值对)

    • 特点:双列集合(Hashtable 子类),键值均为字符串
    • 作为集合
      • setProperty、getProperty、stringPropertyNames
    • 与 IO
      • load(InputStream/Reader) 加载
      • store(OutputStream/Writer, comments) 写出(不建议存中文,.properties 会转 Unicode)
  • Hutool(工具库)

    • IoUtil:copy(InputStream, OutputStream, bufferSize),readLines(Reader, Collection),close(…)
    • FileUtil:touch(创建文件并创建父目录)、mkdir(创建多级目录)、copy(文件/目录)、move(移动)
    • 需引入依赖或 jar 包
  • Lombok(代码生成)

    • 常用注解:@Getter @Setter @ToString @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode @Data
    • 需安装 IDE 插件并引入依赖(在 JavaBean 上标注,自动生成构造/get/set/toString)

代码篇


Math 与 System

作用/概念简述

  • 常用数值方法与系统功能(时间、拷贝、退出)

通用模版

// Math
System.out.println(Math.abs(-10));
System.out.println(Math.ceil(12.3));   // 13.0
System.out.println(Math.floor(12.9));  // 12.0
System.out.println(Math.round(12.5));  // 13
System.out.println(Math.max(10, 20));  // 20
System.out.println(Math.pow(2, 3));    // 8.0
double r = Math.random();               // [0,1)

// System
long start = System.currentTimeMillis();
// ... 业务耗时
long end = System.currentTimeMillis();
System.out.println("耗时(ms): " + (end - start));

int[] a = {1,2,3,4,5}, b = new int[3];
System.arraycopy(a, 1, b, 0, 3);       // b -> 2,3,4
// System.exit(0);

包装类与解析

作用/概念简述

  • 基本类型与包装类转换;字符串解析

通用模版

// 装箱
Integer i = Integer.valueOf(10);  // 推荐
// 拆箱
int x = i.intValue();
// 自动装拆箱
Integer j = 20; int y = j;

// 解析
int n = Integer.parseInt("123");        // NumberFormatException 风险
double d = Double.parseDouble("12.3");

练习:字符串数字求最大值

String s = "10,50,30,20,40";
String[] parts = s.split(",");
int max = Integer.MIN_VALUE;
for (String p : parts) {
    int v = Integer.parseInt(p);
    if (v > max) max = v;
}
System.out.println("最大值: " + max);

BigDecimal 精确运算

作用/概念简述

  • 精确小数计算,避免二进制误差

通用模版

import java.math.BigDecimal;
import java.math.RoundingMode;

BigDecimal a = new BigDecimal("0.1");
BigDecimal b = BigDecimal.valueOf(0.2);

BigDecimal sum = a.add(b);
BigDecimal diff = a.subtract(b);
BigDecimal prod = a.multiply(b);

// 除法:除不尽需指定小数位和舍入模式
BigDecimal div = BigDecimal.valueOf(10).divide(BigDecimal.valueOf(3), 2, RoundingMode.HALF_UP);

System.out.println(sum);   // 0.3
System.out.println(div);   // 3.33

注意

  • 不要使用 new BigDecimal(double)
  • divide 遇到除不尽必须指定精度与舍入模式

Arrays 工具类

作用/概念简述

  • 打印、比较、排序、二分查找

通用模版

import java.util.Arrays;
import java.util.Comparator;

int[] x = {11,22,33,44,55};
System.out.println(Arrays.toString(x));
System.out.println(Arrays.equals(x, new int[]{11,22,33,44,55}));

System.out.println(Arrays.binarySearch(x, 33)); // 保证已排序,否则结果不可靠

Integer[] arr = {22,11,66,77,44,55};
Arrays.sort(arr); // 升序
Arrays.sort(arr, (o1,o2) -> o2 - o1); // 降序
System.out.println(Arrays.toString(arr));

Date 与 SimpleDateFormat

作用/概念简述

  • 格式化/解析传统 Date 对象

通用模版

import java.text.SimpleDateFormat;
import java.util.Date;

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
String text = sdf.format(new Date());
System.out.println(text);

try {
    Date birth = sdf.parse("2008年08月08日");
    System.out.println(birth.getTime());
} catch (java.text.ParseException e) {
    e.printStackTrace();
}

计算年龄(传统)

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
Date birth = sdf.parse("2000年01月01日");
long years = (System.currentTimeMillis() - birth.getTime()) / 1000 / 60 / 60 / 24 / 365;
System.out.println(years);

JDK8 时间 API

作用/概念简述

  • 获取/读取/运算/比较/格式化/解析

通用模版

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

// 获取
LocalDateTime now = LocalDateTime.now();
LocalDate date = LocalDate.of(2008,8,8);
LocalTime time = LocalTime.of(8,8,8);

// 读取
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();

// 运算与设置(不可变返回新对象)
LocalDateTime later = now.plusDays(1).withHour(8);

// 比较
boolean before = date.isBefore(LocalDate.now());

// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy年M月d日");
String text = fmt.format(now);

// 解析
LocalDate parsed = LocalDate.parse("2008年12月12日", fmt);

// 计算时间间隔
long age = ChronoUnit.YEARS.between(LocalDate.parse("2000年1月1日", fmt), LocalDate.now());
System.out.println(age);

IO:字节输出 FileOutputStream

作用/概念简述

  • 程序数据写出到文件;支持追加

通用模版

import java.io.FileOutputStream;
import java.io.IOException;

try (FileOutputStream fos = new FileOutputStream("E:\\A.txt", true)) { // true 追加
    fos.write(new byte[]{97,98,99});       // abc
    fos.write("你好".getBytes());           // 默认平台编码
} catch (IOException e) {
    e.printStackTrace();
}

注意

  • 文件不存在会自动创建;存在且未追加时会清空
  • try-with-resources 自动 close

IO:字节输入 FileInputStream

作用/概念简述

  • 从文件读取字节到程序(通用数据)

通用模版

import java.io.FileInputStream;
import java.io.IOException;

try (FileInputStream fis = new FileInputStream("E:\\A.txt")) {
    byte[] buf = new byte[1024];
    int len;
    while ((len = fis.read(buf)) != -1) {
        String s = new String(buf, 0, len);
        System.out.print(s);
    }
} catch (IOException e) {
    e.printStackTrace();
}

文件拷贝

try (FileInputStream in = new FileInputStream("D:\\pic.jpg");
     FileOutputStream out = new FileOutputStream("E:\\copy.jpg")) {
    byte[] buf = new byte[8192];
    int len;
    while ((len = in.read(buf)) != -1) {
        out.write(buf, 0, len);
    }
}

IO:字符流 FileReader / FileWriter

作用/概念简述

  • 文本文件读写(中文不乱码),注意 flush/close

通用模版

// 读取
try (java.io.FileReader fr = new java.io.FileReader("E:\\A.txt")) {
    char[] chs = new char[1024];
    int len;
    while ((len = fr.read(chs)) != -1) {
        System.out.print(new String(chs, 0, len));
    }
}

// 写出
try (java.io.FileWriter fw = new java.io.FileWriter("E:\\A.txt")) {
    fw.write('你');
    fw.write(new char[]{'a','b','c'});
    fw.write("黑马程序员", 0, 2);
    // fw.flush(); // try-with-resources 会在 close 前自动 flush
}

提示

  • FileReader/FileWriter 使用平台默认编码。如需指定编码使用 InputStreamReader/OutputStreamWriter。

Properties:集合与 IO

作用/概念简述

  • 字符串键值对配置;加载/保存

通用模版

import java.util.Properties;

// 作为集合
Properties prop = new Properties();
prop.setProperty("username", "admin");
prop.setProperty("password", "123456");
System.out.println(prop.getProperty("username"));

for (String key : prop.stringPropertyNames()) {
    System.out.println(key + "=" + prop.getProperty(key));
}

// 与 IO
try (java.io.FileWriter fw = new java.io.FileWriter("config.properties")) {
    prop.store(fw, "app config"); // 中文将被转为Unicode,影响可读性
}

Properties loaded = new Properties();
try (java.io.FileReader fr = new java.io.FileReader("config.properties")) {
    loaded.load(fr);
}
System.out.println(loaded);

Hutool 快速 IO

作用/概念简述

  • 复制/移动/创建 文件与目录;流拷贝/读取行;安全关闭

通用模版

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;

// 文件/目录
FileUtil.touch("E:\\aaa\\bbb\\ccc\\A.txt");               // 自动创建父目录
FileUtil.mkdir("E:\\abc\\ddd");
FileUtil.copy("E:\\test", "D:\\", true);                   // 覆盖
FileUtil.move(new java.io.File("E:\\test"), new java.io.File("D:\\"), true);

// 流拷贝
try (java.io.FileInputStream in = new java.io.FileInputStream("D:\\1.jpg");
     java.io.FileOutputStream out = new java.io.FileOutputStream("E:\\copy.jpg")) {
    IoUtil.copy(in, out, 2048);
}

// 按行读取
java.util.ArrayList<String> lines = new java.util.ArrayList<>();
try (java.io.FileReader r = new java.io.FileReader("day04\\出师表.txt")) {
    IoUtil.readLines(r, lines);
}
lines.forEach(System.out::println);

依赖(Maven 示例)

<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.8.26</version>
</dependency>

Lombok 快速 JavaBean

作用/概念简述

  • 通过注解自动生成 getter/setter/toString/构造器

通用模版

// 需安装 IDE 插件,并添加依赖
// <dependency>
//   <groupId>org.projectlombok</groupId>
//   <artifactId>lombok</artifactId>
//   <version>1.18.32</version>
//   <scope>provided</scope>
// </dependency>

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private int age;
}

多线程

理论篇

  • 进程与线程

    • 进程:程序一次运行活动,是系统资源分配的基本单位。一个应用可包含多个进程。
    • 线程:进程内的最小执行单位,一个进程可并发多个线程执行不同任务。
    • 并行 vs 并发
      • 并行:多个核上真正同时执行(多核多线程 CPU)。
      • 并发:单核上在多个任务间快速切换,宏观“同时”,微观看是交替执行。
    • Java 程序默认是多线程的:至少包含 main 线程与垃圾回收线程。
  • 线程创建的三种方式

    • 继承 Thread
      • 步骤:继承 Thread → 重写 run → new 子类 → 调用 start 开启线程(注意:start 才能异步启动,直接调用 run 只是普通方法调用)。
    • 实现 Runnable
      • 步骤:实现 Runnable → 重写 run → new Thread(Runnable) → start。
      • 特点:更灵活,便于资源共享(同一个 Runnable 实例可被多个线程共享)。
    • 实现 Callable 配合 FutureTask
      • 步骤:实现 Callable → 重写 call 返回结果 → 用 FutureTask 包装 → new Thread(FutureTask) → start → 通过 FutureTask.get() 获取结果(阻塞等待)。
      • 特点:能有返回值、能抛出受检异常。
  • Thread 常见方法

    • 线程名:getName、setName;当前线程对象:Thread.currentThread()
    • 休眠:Thread.sleep(ms)(让出 CPU 一段时间,可能抛 InterruptedException)
    • 优先级:setPriority(1~10),优先级越高仅代表“更有可能”被调度,非绝对先后
    • 守护线程:setDaemon(true) 将线程设置为守护线程(所有非守护线程结束后守护线程会随 JVM一并结束)
  • 线程安全问题与出现条件

    • 条件
      • 存在多线程环境
      • 存在共享数据
      • 多条语句对共享数据进行读改写
    • 现象:数据错乱(重复票、负数票等)
    • 根因:线程切换发生在关键操作中间,导致“读-改-写”序列被打断
  • 同步与锁

    • synchronized 同步代码块/同步方法
      • 同步块:synchronized(锁对象){ 临界区 }
      • 同步方法:在方法签名前加 synchronized
      • 锁对象选择
        • 实例方法:锁对象是 this
        • 静态方法:锁对象是 当前类的字节码对象(Class对象)
        • 多线程共享同一把锁(同一个对象/同一个 Class)才能互斥
    • Lock 显式锁(以 ReentrantLock 为例)
      • 优点:加锁/解锁范围可控、可尝试锁、可公平锁
      • 使用:lock.lock() → try { 临界区 } finally { lock.unlock() }
    • 注意
      • 同步可以保证正确性,但会降低并发性能
      • 锁的粒度越小越好;避免在锁内做耗时操作
      • 小心死锁(多个锁交叉持有)
  • 线程池

    • 目的:复用线程,避免频繁创建/销毁线程的成本,提升吞吐与稳定性
    • Executors 工厂创建(了解)
      • newCachedThreadPool、newFixedThreadPool 等,不便于精细化配置,实际生产不推荐
    • ThreadPoolExecutor(推荐,7 个核心参数)
      • corePoolSize:核心线程数(常驻)
      • maximumPoolSize:最大线程数
      • keepAliveTime + unit:非核心线程空闲存活时间
      • workQueue:任务队列(如 ArrayBlockingQueue、LinkedBlockingQueue)
      • threadFactory:线程工厂(可自定义命名)
      • handler:拒绝策略(AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy)
    • 生命周期与关闭
      • 提交任务:submit/execute
      • 关闭:shutdown(平滑)、shutdownNow(尝试中断),可配合 awaitTermination
  • 建议与最佳实践

    • 线程创建优先使用 Runnable/Callable,少用继承 Thread
    • 共享资源访问加锁(synchronized/Lock),锁对象保持一致
    • 有返回值、异常需传递使用 Callable + FutureTask 或线程池 submit 返回 Future
    • 使用线程池统一管理线程,合理配置参数与拒绝策略,及时关闭
    • 关键路径减少锁范围与竞争,避免在锁内进行 IO 或复杂计算
    • 守护线程仅用于辅助服务(如监控),不要依靠守护线程处理必须完成的业务

代码篇


线程创建:Thread / Runnable / Callable

作用/概念简述

  • 三种方式模板化使用;注意 start 与 run 的区别;Callable 可返回结果。

通用模版

// 1) 继承 Thread
class MyThread extends Thread {
    public MyThread(String name){ super(name); }
    @Override public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + " 执行 " + i);
        }
    }
}
new MyThread("线程A").start();

// 2) 实现 Runnable
class MyRunnable implements Runnable {
    @Override public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 执行 " + i);
        }
    }
}
Runnable task = new MyRunnable();
new Thread(task, "线程B").start();
new Thread(task, "线程C").start(); // 共享同一任务资源

// 3) 实现 Callable + FutureTask
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {
    @Override public Integer call() {
        int sum = 0;
        for (int i = 1; i <= 100; i++) sum += i;
        return sum;
    }
}
FutureTask<Integer> ft = new FutureTask<>(new MyCallable());
Thread t = new Thread(ft, "线程D");
t.start();
Integer result = ft.get(); // 阻塞等待结果
System.out.println("返回结果: " + result);

属性/知识点卡片

  • start() 才会真正开启新线程并异步调用 run()
  • Callable 适合有结果、有异常传递的任务
  • FutureTask.get() 会阻塞,建议在任务启动后再获取

线程名、休眠、优先级、守护线程

作用/概念简述

  • 线程调试与控制常用方法。

通用模版

// 名字
Thread t1 = new Thread(() -> {
    System.out.println(Thread.currentThread().getName());
}, "业务线程-1");
t1.start();

// 休眠
for (int i = 5; i >= 1; i--) {
    System.out.println("倒计时:" + i + " 秒");
    Thread.sleep(1000);
}

// 优先级(1~10)
Thread t2 = new Thread(() -> {/*...*/}, "高优先级");
t2.setPriority(10);
t2.start();

// 守护线程:主线程结束后,守护线程随 JVM 一并结束
Thread daemon = new Thread(() -> {
    while (true) { /* 心跳/日志... */ }
}, "守护线程");
daemon.setDaemon(true);
daemon.start();

属性/知识点卡片

  • 优先级仅影响调度概率,结果顺序不保证
  • 守护线程不要承载必须完成的业务逻辑

线程安全:卖票示例与 synchronized

作用/概念简述

  • 多线程共享资源,使用同步块/方法保护临界区。

通用模版

class TicketTask implements Runnable {
    private int tickets = 100;
    // 对于静态共享,考虑 static 成员并锁 Class 对象

    @Override public void run() {
        while (true) {
            synchronized (this) { // 锁对象要一致(同一实例被多个线程共享)
                if (tickets <= 0) break;
                System.out.println(Thread.currentThread().getName() + " 卖出第 " + tickets + " 张票");
                tickets--;
            }
        }
    }
}
TicketTask task = new TicketTask();
new Thread(task, "窗口A").start();
new Thread(task, "窗口B").start();
new Thread(task, "窗口C").start();

// 同步方法(实例方法:锁 this;静态方法:锁 类对象)
class SafeCounter {
    private int n = 0;
    public synchronized void inc() { n++; }
    public synchronized int get() { return n; }
}

属性/知识点卡片

  • 共享资源 + 多语句更新是安全隐患,要在同一把锁保护
  • 锁对象应为同一实例或同一 Class

Lock 显式锁

作用/概念简述

  • ReentrantLock 提供更灵活的锁控制(加锁/解锁范围、可重入)。

通用模版

import java.util.concurrent.locks.ReentrantLock;

class TicketTask3 implements Runnable {
    private int tickets = 100;
    private final ReentrantLock lock = new ReentrantLock();

    @Override public void run() {
        while (true) {
            lock.lock();
            try {
                if (tickets <= 0) break;
                System.out.println(Thread.currentThread().getName() + " 卖出第 " + tickets + " 张票");
                tickets--;
            } finally {
                lock.unlock();
            }
        }
    }
}
TicketTask3 t = new TicketTask3();
new Thread(t, "窗口A").start();
new Thread(t, "窗口B").start();

属性/知识点卡片

  • 必须在 finally 中 unlock,避免异常导致死锁
  • 高级用法:tryLock、lockInterruptibly、公平锁等

线程池:JDK 线程池与自定义 ThreadPoolExecutor

作用/概念简述

  • 统一管理线程,提高复用与吞吐

通用模版

// 不推荐:Executors 简单工厂(了解)
// ExecutorService pool = Executors.newFixedThreadPool(10);

// 推荐:自定义 ThreadPoolExecutor
import java.util.concurrent.*;

ThreadPoolExecutor pool = new ThreadPoolExecutor(
        2,                      // corePoolSize 核心线程数
        5,                      // maximumPoolSize 最大线程数
        60L, TimeUnit.SECONDS,  // 非核心线程空闲存活时间
        new ArrayBlockingQueue<>(10),           // 有界队列
        Executors.defaultThreadFactory(),       // 线程工厂(可自定义命名)
        new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

// 提交任务
for (int i = 1; i <= 16; i++) {
    pool.submit(() -> {
        System.out.println(Thread.currentThread().getName() + " 执行任务");
    });
}

// 关闭线程池
pool.shutdown(); // 平滑关闭
// 可选:pool.awaitTermination(30, TimeUnit.SECONDS);

属性/知识点卡片

  • 任务>队列+最大线程时触发拒绝策略
  • 常见拒绝策略
    • AbortPolicy:抛出 RejectedExecutionException
    • CallerRunsPolicy:由提交任务的线程执行
    • DiscardPolicy:丢弃任务
    • DiscardOldestPolicy:丢弃队列中最旧任务,再尝试提交
  • 队列选择
    • ArrayBlockingQueue(有界):可控内存,适合并发受限场景
    • LinkedBlockingQueue(近似无界):容易 OOM,不建议无限制使用

综合:线程命名与自定义 ThreadFactory

作用/概念简述

  • 方便定位线程来源与问题排查。

通用模版

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

class NamedThreadFactory implements ThreadFactory {
    private final String prefix;
    private final AtomicInteger idx = new AtomicInteger(1);
    NamedThreadFactory(String prefix){ this.prefix = prefix; }
    @Override public Thread newThread(Runnable r) {
        Thread t = new Thread(r, prefix + "-" + idx.getAndIncrement());
        t.setDaemon(false);
        t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

ThreadPoolExecutor pool = new ThreadPoolExecutor(
        2, 4, 60, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(50),
        new NamedThreadFactory("biz-worker"),
        new ThreadPoolExecutor.AbortPolicy()
);

属性/知识点卡片

  • 线程名应包含业务前缀 + 自增编号,便于日志排查
  • 关键线程不要设为守护线程

网络编程

理论篇

  • 网络编程概念与架构

    • 定义:让设备中的程序与网络上其他设备中的程序进行数据交互的技术(java.net 包提供支持)。
    • 架构:
      • CS(Client/Server):客户端与服务端直连,强交互、可定制。
      • BS(Browser/Server):浏览器 + 服务端,通过 HTTP 通信,部署更新方便。
  • 网络通信三要素

    • IP
      • 设备在网络中的唯一地址。IPv4(32 位,已枯竭但配合路由/NAT仍主流),IPv6(128 位,地址充足)。
      • 127.0.0.1/localhost:本地回环地址,只指向本机,不经路由。
      • 常用命令:ipconfig(看本机 IP)、ping(测试连通性)。
      • DNS:域名解析服务,负责将域名解析为 IP。
      • Java:InetAddress 类用于 IP/主机名解析和查询。
    • 端口
      • 应用在设备中的唯一标识,范围 0~65535。
      • 0~1023 为知名端口,建议使用 >1024。
      • 同一台机器上同一时间端口不可重复被占用。
    • 协议
      • UDP:面向无连接,发送快、最多单包 64KB、不可靠(可能丢包、乱序),适合实时性场景(语音、直播等)。
      • TCP:面向连接(三次握手、四次挥手),可靠传输、无数据大小限制(流式),适合文件传输、聊天、邮件、下载等。
  • UDP 与 TCP 对比

    • UDP:快、简单、无连接、不可靠,适合“速度优先、可容错”的场景。
    • TCP:慢一些、可靠、有连接、流式传输,适合“准确性优先”的场景。
  • TCP 连接管理

    • 三次握手:建立连接(SYN→SYN-ACK→ACK)。
    • 四次挥手:断开连接(FIN/ACK 交互)。
  • 常见注意事项与实践建议

    • IP 与端口要匹配,接收方必须先启动并占用端口。
    • UDP 收包缓冲区要足够大(否则截断);TCP 为流式没有报文边界,需要自定义“协议”(长度前缀、分隔符等)。
    • 本地测试可用 127.0.0.1 或 localhost;跨机通信要确保在同一网络、端口未被防火墙拦截。
    • 资源释放:Socket/ServerSocket/DatagramSocket/流对象使用完必须关闭(推荐 try-with-resources) 。
    • TCP 文件传输:客户端写完要调用 socket.shutdownOutput() 告知服务端写入结束。

代码篇


IP 与主机名解析(InetAddress)

作用/概念简述

  • 将主机名解析为 IP,查询主机名与文本 IP。

通用模版

import java.net.InetAddress;

InetAddress addr = InetAddress.getByName("www.baidu.com"); // 或主机名 / 文本IP
System.out.println(addr.getHostName());     // 主机名(可能为域名/计算机名)
System.out.println(addr.getHostAddress());  // 文本IP

属性知识卡片

  • getByName 支持域名、主机名、字符串 IP
  • getHostName 可能触发反向解析,受网络/DNS 影响

UDP 发送方

作用/概念简述

  • 使用 DatagramSocket 发送数据报到指定 IP:端口。

通用模版

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

try (DatagramSocket socket = new DatagramSocket(8888)) { // 也可无参随机端口
    byte[] data = "你好".getBytes();
    DatagramPacket packet = new DatagramPacket(
            data, data.length,
            InetAddress.getByName("127.0.0.1"), 9999);
    socket.send(packet);
} // 自动关闭

属性知识卡片

  • 端口范围 1024+,避免与系统端口冲突
  • 发送端可随机端口或指定固定端口

UDP 接收方

作用/概念简述

  • 绑定端口,接收单个数据报并解析内容与来源 IP。

通用模版

import java.net.DatagramPacket;
import java.net.DatagramSocket;

byte[] buf = new byte[1024]; // 注意容量足够
try (DatagramSocket socket = new DatagramSocket(9999)) {
    DatagramPacket packet = new DatagramPacket(buf, buf.length);
    socket.receive(packet); // 阻塞等待
    String msg = new String(packet.getData(), 0, packet.getLength());
    String ip = packet.getAddress().getHostAddress();
    System.out.println("来自 " + ip + " 的消息: " + msg);
}

属性知识卡片

  • receive 阻塞直到有包到达
  • getLength 为本次有效字节数

TCP 文件上传(客户端)

作用/概念简述

  • 与服务端建立连接,流式发送文件,结束后读取服务端响应。

通用模版

import java.io.*;
import java.net.Socket;

File src = new File("D:\\2.jpg");
try (Socket socket = new Socket("localhost", 8899);
     InputStream fileIn = new FileInputStream(src);
     OutputStream netOut = socket.getOutputStream();
     InputStream netIn = socket.getInputStream()) {

    byte[] buf = new byte[8192];
    int len;
    while ((len = fileIn.read(buf)) != -1) {
        netOut.write(buf, 0, len);
    }
    socket.shutdownOutput(); // 告知服务端:我写完了

    byte[] resp = new byte[1024];
    int n = netIn.read(resp); // 阻塞直到服务端返回
    if (n != -1) {
        System.out.println("服务端应答: " + new String(resp, 0, n));
    }
}

属性知识卡片

  • shutdownOutput 通知“写完”,便于服务端 read() 返回 -1
  • 读写用缓冲(8KB+)更高效

TCP 文件接收(服务端)

作用/概念简述

  • 监听端口,接受连接,读取客户端字节写入文件,回写应答。

通用模版

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

try (ServerSocket server = new ServerSocket(8899)) {
    System.out.println("服务端启动,等待连接...");
    try (Socket socket = server.accept();
         InputStream netIn = socket.getInputStream();
         OutputStream netOut = socket.getOutputStream();
         OutputStream fileOut = new FileOutputStream("E:\\result.jpg")) {

        byte[] buf = new byte[8192];
        int len;
        while ((len = netIn.read(buf)) != -1) {
            fileOut.write(buf, 0, len);
        }
        netOut.write("上传成功".getBytes());
    }
}

属性知识卡片

  • 单线程服务端一次只服务一个客户端;多客户端需每个 accept() 交给新线程/线程池处理
  • 文件路径需确保父目录存在并且有写权限

TCP 多客户端并发(服务端模板)

作用/概念简述

  • 通过线程池处理每个客户端,提升吞吐。

通用模版

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class TcpServerPool {
    public static void main(String[] args) throws IOException {
        ExecutorService pool = new ThreadPoolExecutor(
                2, 8, 60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        try (ServerSocket server = new ServerSocket(8899)) {
            while (true) {
                Socket socket = server.accept();
                pool.submit(() -> handle(socket));
            }
        } finally {
            pool.shutdown();
        }
    }

    private static void handle(Socket socket) {
        try (Socket s = socket;
             InputStream in = s.getInputStream();
             OutputStream out = s.getOutputStream()) {
            // TODO: 读取请求、处理业务、写回响应(示例:回显)
            byte[] buf = new byte[1024];
            int n = in.read(buf);
            if (n > 0) out.write(("已收到: " + new String(buf, 0, n)).getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

属性知识卡片

  • 线程池参数按实际负载调整
  • 业务协议要明确消息边界(建议自定义协议:固定头、长度前缀、分隔符等)

自定义简单协议建议(TCP 流式边界)

作用/概念简述

  • 解决 TCP 无边界:发送前写入长度,接收方先读长度再读内容。

通用模版

// 发送方
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
byte[] payload = "Hello".getBytes("UTF-8");
dos.writeInt(payload.length);
dos.write(payload);

// 接收方
DataInputStream dis = new DataInputStream(socket.getInputStream());
int len = dis.readInt();
byte[] body = new byte[len];
dis.readFully(body);
String msg = new String(body, "UTF-8");

属性知识卡片

  • DataInputStream/DataOutputStream 便于读写基本类型(支持大端序)
  • readFully 确保按指定长度完整读取

常见排障与技巧

作用/概念简述

  • 网络编程常见问题定位与解决。

要点卡片

  • 连不上:确认 IP、端口、进程是否监听、客户端/服务端是否同网段/公网可达、防火墙是否放行。
  • 端口占用:Windows 用 netstat -ano | findstr 端口;Linux 用 lsof -i:端口。
  • UDP 丢包:适当增大接收缓冲区、控制发送速率;必要时切 TCP 或做重传机制。
  • 文件损坏:确保完整读取写入、及时 flush/close;TCP 端需要明确结束标识(如 shutdownOutput 或长度协议)。
  • 本地/远端:127.0.0.1 仅本机可用,跨机需用真实局域网/公网 IP。

Java 高级技术

理论篇

  • 枚举(enum)

    • 定义:Java 的一种特殊类型,用于信息的标记与分类,比常量更严谨(类型安全、IDE 友好提示)。
    • 特点:
      • 每个枚举项本质上是枚举类的一个实例。
      • 所有枚举类隐式继承自 java.lang.Enum。
      • 可定义字段、构造器(默认 private)、方法,甚至抽象方法(各枚举项必须实现)。
      • 第一行必须是枚举项;若后面还有其他成员,枚举项行末必须加分号。
      • 常用方法:name、ordinal、values、valueOf。
    • 适用:状态、类型、错误码、开关等“有限集合”。
  • 类加载器与类加载过程

    • 作用:将类的字节码加载到方法区(JDK8 及以下)/元空间(JDK8+)。
    • 加载时机:用到时加载(首次主动使用)。
    • 三个阶段:
      • 加载:根据全限定名查找字节码,生成 Class 对象。
      • 链接:
        • 验证:字节码格式校验。
        • 准备:为静态变量分配内存并赋默认值。
        • 解析:常量池中的符号引用转直接引用(将“名字”换成“地址”)。
      • 初始化:执行静态变量显式赋值与静态代码块。
    • 类加载器类型:
      • Bootstrap(引导/启动):加载 JDK 核心类(null 表示)。
      • Platform(平台,JDK9+)/Extension(扩展,JDK8-):加载一些扩展模块。
      • Application(应用):加载应用 classpath 下的类。
      • 自定义类加载器:可定制加载来源和策略(不常用)。
    • 双亲委派:类加载请求自下而上委托父加载器,父加载器无法完成时子加载器才尝试,避免重复加载与安全问题。
  • 反射(Reflection)

    • 定义:运行时动态获取类的结构信息并操作(字段、方法、构造等),框架的基础。
    • 获取 Class 对象方式:
      • Class.forName("全类名")
      • 类名.class
      • 对象.getClass()
    • 常用反射对象:
      • Constructor:获取/实例化对象(可暴力反射 setAccessible(true))。
      • Field:读写成员变量(需指定目标对象)。
      • Method:调用成员方法(invoke 指定目标对象与参数)。
    • 注意:暴力反射破坏封装,生产慎用;反射有性能损耗。
  • 注解(Annotation)

    • 作用:为代码元素添加元数据(给编译器/JVM/框架看);如 @Override、@Deprecated、@SuppressWarnings。
    • 自定义注解:
      • 使用 @interface 声明;属性可指定默认值;单属性名为 value 时可省略 key。
      • 元注解:
        • @Target:限定使用位置(TYPE/METHOD/FIELD/…)
        • @Retention:生命周期(SOURCE/CLASS/RUNTIME)
        • @Documented、@Inherited(可选)
      • 运行期配合反射使用(注解需标注 @Retention(RUNTIME))。
  • 动态代理(Dynamic Proxy)

    • 定义:在运行时生成代理对象,在不修改目标类源码的前提下增强方法(AOP 基础)。
    • JDK 动态代理要求:目标对象必须实现接口(代理类实现同样的接口)。
    • 关键 API:Proxy.newProxyInstance(ClassLoader, Class<?>[] interfaces, InvocationHandler)
      • InvocationHandler.invoke(proxy, method, args) 中编写增强逻辑(如耗时统计、权限校验、事务等)。
    • 备注:无接口的类可选 CGLIB(字节码增强)代理(框架中常见)。

代码篇


枚举:定义与使用

作用/概念简述

  • 用类型安全的枚举替代常量,增强可读性与严谨性。

通用模板

enum Season {
    SPRING, SUMMER, AUTUMN, WINTER;
}

public class EnumTest {
    public static void main(String[] args) {
        useSeason(Season.WINTER);
        for (Season s : Season.values()) {
            System.out.println(s.name() + " -> " + s.ordinal());
        }
    }
    static void useSeason(Season season) {
        switch (season) {
            case SPRING -> System.out.println("春季");
            case SUMMER -> System.out.println("夏季");
            case AUTUMN -> System.out.println("秋季");
            case WINTER -> System.out.println("冬季");
        }
    }
}

扩展:带字段/构造/抽象方法

enum OrderStatus {
    NEW(0, "新建") { public boolean canCancel(){ return true; } },
    PAID(1, "已支付") { public boolean canCancel(){ return false; } };
    public final int code; public final String desc;
    OrderStatus(int code, String desc){ this.code = code; this.desc = desc; }
    public abstract boolean canCancel();
}

属性知识卡片

  • 枚举项本质是实例;可定义字段/方法;可重写抽象方法。

类加载器:获取与层级

作用/概念简述

  • 获取不同类由谁加载、查看父子关系与初始化时机。

通用模板

public class ClassLoaderDemo {
    public static void main(String[] args) {
        System.out.println(String.class.getClassLoader()); // null (Bootstrap)
        System.out.println(ClassLoaderDemo.class.getClassLoader()); // AppClassLoader
        System.out.println(ClassLoaderDemo.class.getClassLoader().getParent()); // Platform
        System.out.println(ClassLoaderDemo.class.getClassLoader().getParent().getParent()); // null
    }
}

初始化演示(静态块)

class X {
    static { System.out.println("X 初始化"); }
    static int v = 42;
}
public class InitDemo {
    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("X"); // 触发初始化(主动使用)
        System.out.println(X.v);
    }
}

属性知识卡片

  • 双亲委派:先委托父加载器,父无法加载才自己加载。

反射:获取 Class 与构造/字段/方法

作用/概念简述

  • 运行期动态操作结构。

通用模板

// 1) 获取 Class
Class<?> c1 = Class.forName("com.itheima.pojo.Student");
Class<?> c2 = com.itheima.pojo.Student.class;
Class<?> c3 = new com.itheima.pojo.Student().getClass();

// 2) 构造:创建对象
var cons = c1.getDeclaredConstructor(String.class, int.class);
cons.setAccessible(true);
Object stu = cons.newInstance("张三", 23);

// 3) 字段:读写
var nameF = c1.getDeclaredField("name");
nameF.setAccessible(true);
nameF.set(stu, "李四");
System.out.println(nameF.get(stu));

// 4) 方法:调用
var m1 = c1.getMethod("eat");
var m2 = c1.getMethod("eat", int.class);
m1.invoke(stu);
m2.invoke(stu, 3);

练习:绕过泛型限制(仅演示)

var list = new java.util.ArrayList<Integer>();
list.add(1);
var add = list.getClass().getMethod("add", Object.class);
add.invoke(list, "张三");
System.out.println(list); // [1, 张三]

属性知识卡片

  • 暴力反射需 setAccessible(true),生产慎用。

注解:定义、元注解与扫描执行

作用/概念简述

  • 自定义 @Test 注解,运行期扫描并执行标注的方法。

通用模板

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test { }

public class MethodDemo {
    @Test public void print(){ System.out.println("print"); }
    public void show(){ System.out.println("show"); }
    @Test public void method(){ System.out.println("method"); }
}

import java.lang.reflect.Method;
public class ScanTest {
    public static void main(String[] args) throws Exception {
        Class<MethodDemo> clz = MethodDemo.class;
        MethodDemo obj = clz.getConstructor().newInstance();
        for (Method m : clz.getMethods()) {
            if (m.isAnnotationPresent(Test.class)) {
                m.invoke(obj);
            }
        }
    }
}

属性知识卡片

  • @Retention(RUNTIME) 才能在运行期通过反射读取注解。
  • 单一属性名为 value 时,使用时可省略键名。

动态代理:JDK Proxy 增强方法

作用/概念简述

  • 运行期生成代理对象,在方法前后增强(如耗时统计)。

通用模板

import java.lang.reflect.*;

@SuppressWarnings("unchecked")
public class ProxyDemo {
    public static void main(String[] args) {
        java.util.ArrayList<String> target = new java.util.ArrayList<>();
        java.util.List<String> proxy = (java.util.List<String>) Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(),
                new Class[]{java.util.List.class},
                new TimeCostHandler(target)
        );

        proxy.add("张三"); // 增强 add 方法
        proxy.add("李四");
        System.out.println(target);
    }

    static class TimeCostHandler implements InvocationHandler {
        private final Object target;
        TimeCostHandler(Object target){ this.target = target; }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("add".equals(method.getName())) {
                long start = System.currentTimeMillis();
                Object res = method.invoke(target, args);
                long end = System.currentTimeMillis();
                System.out.println("add耗时: " + (end - start) + "ms");
                return res;
            }
            return method.invoke(target, args);
        }
    }
}

通用代理工厂(任意接口对象的通用增强)

@SuppressWarnings("unchecked")
public static <T> T wrap(T target, java.util.function.BiFunction<Method, Object[], Object> around, Class<?>... ifaces) {
    return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            ifaces.length == 0 ? target.getClass().getInterfaces() : ifaces,
            (proxy, method, args) -> around.apply(method, args) != null
                    ? around.apply(method, args)
                    : method.invoke(target, args));
}

属性知识卡片

  • JDK 动态代理要求目标有接口;无接口可用 CGLIB(框架层常见,如 Spring AOP)。
  • InvocationHandler 中可实现统一日志、鉴权、事务、重试、熔断等横切逻辑。

评论