4.Java基础深度回顾:泛型、集合框架、接口与异常处理

引言

在Java开发领域,掌握核心基础知识是构建稳健系统的基石。本教程将系统梳理Java泛型(Generics)、集合框架(Collection Framework)、接口(Comparable/Comparator)及异常处理四大核心模块,结合JDK 8-17特性与工业级实践案例,帮助开发者建立类型安全编程思维、高效数据结构应用能力及健壮的错误处理机制。

第一章 泛型

1.1 泛型本质与演进

Java泛型通过类型参数化实现编译期类型检查,其核心价值在于:

  • 类型安全:消除强制类型转换(如List<String>无法添加Integer)
  • 代码复用:同一类/方法可处理多种数据类型(如Box<T>可存储任意对象)
  • 可读性提升:类型参数显式声明(如Map<K,V>明确键值类型)

历史演进

  • JDK 5(2004)引入基础泛型
  • JDK 7(2011)支持菱形操作符<>
  • JDK 9(2017)增强泛型类型推断

1.2 泛型类型系统

1.2.1 类型参数命名规范
标识符含义典型场景
TType通用类型参数
EElement集合元素类型
KKeyMap键类型
VValueMap值类型
NNumber数值类型
?Wildcard未知类型(通配符)
1.2.2 泛型通配符实战

上界通配符 <? extends Number>

public double sum(List<? extends Number> numbers) {
    return numbers.stream()
                 .mapToDouble(Number::doubleValue)
                 .sum();
}
// 可接受:List<Integer>, List<Double>, List<Number>

下界通配符 <? super Integer>

public void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}
// 可接受:List<Integer>, List<Number>, List<Object>

PECS原则(Producer-Extends, Consumer-Super):

  • 生产者角色使用extends(如read操作)
  • 消费者角色使用super(如write操作)

1.3 类型擦除机制解析

Java泛型采用类型擦除实现跨平台兼容性:

// 编译前
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();

// 编译后(字节码层面)
List strings = new ArrayList(); // 类型参数被擦除为Object
List integers = new ArrayList; // 仅保留原始类型

桥方法生成
当泛型类继承时,编译器会自动生成桥方法保证多态性:

class Node<T> {
    T data;
    void setData(T data) { this.data = data; }
}

class MyNode extends Node<Integer> {
    @Override
    void setData(Integer data) { // 实际重写的是泛型方法
        super.setData(data);
    }
}
// 编译器生成桥方法:
void setData(Object data) { 
    setData((Integer)data); // 强制类型转换
}

1.4 泛型的补充说明

  1. 避免原始类型:优先使用List<String>而非List
  2. 类型参数数量控制:单个类不超过3个类型参数
  3. 递归类型边界(JDK 10+):
static <T extends Comparable<? super T>> T max(List<T> list) {
    // 允许T实现Comparable<T>或其父类
}
  1. Kotlin互操作:使用@JvmSuppressWildcards处理泛型差异

第二章 集合框架

2.1 集合体系架构

集合框架
核心接口对比

接口特性典型实现时间复杂度(平均)
List有序、可重复ArrayList, LinkedListO(1) add/get
Set无序、唯一HashSet, TreeSetO(1) add (HashSet)
QueueFIFO/LIFOLinkedList, ArrayDequeO(1) offer/poll
Map键值对HashMap, TreeMapO(1) put/get

2.2 List实现深度解析

2.2.1 ArrayList扩容机制
// JDK 8 ArrayList.java片段
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍扩容
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

性能优化建议

  • 预估容量时使用Arrays.copyOf(null, initialCapacity)
  • 批量操作优先使用addAll(Collection)而非循环add()
  • 随机访问使用get(index),顺序遍历使用迭代器
2.2.2 LinkedList双端操作
// JDK 8 LinkedList.java片段
public void addFirst(E e) {
    linkFirst(e); // O(1)时间复杂度
}

public void addLast(E e) {
    if (e == null)
        throw new NullPointerException();
    linkLast(e); // O(1)时间复杂度
}

适用场景

  • 频繁插入/删除:LinkedList优于ArrayList
  • 随机访问:ArrayList性能是LinkedList的500-1000倍

2.3 Set实现原理

2.3.1 HashSet哈希策略
// JDK 8 HashSet.java片段
public boolean add(E e) {
    return map.put(e, PRESENT)==null; // PRESENT是静态常量Object
}

// 内部使用HashMap存储元素
private transient HashMap<E,Object> map;
static final Object PRESENT = new Object();

哈希冲突处理

  • 链表法(JDK 7及之前)
  • 红黑树法(JDK 8+当链表长度>8时转为树结构)
2.3.2 TreeSet排序机制
// 自然排序示例
Set<String> treeSet = new TreeSet<>();
treeSet.add("banana");
treeSet.add("apple"); // 自动按字母顺序排序

// 定制排序示例
Set<Integer> customSet = new TreeSet<>((a, b) -> b - a);
customSet.add(3);
customSet.add(1); // 降序排列 [3,1]

2.4 Map高级特性

2.4.1 HashMap并发问题

线程不安全示例

Map<String, Integer> map = new HashMap<>();
// 并发环境下可能导致数据不一致
map.put("key", 1);

解决方案

  1. Collections.synchronizedMap()
  2. ConcurrentHashMap(推荐)
    Map<String, Integer> concurrentMap = new ConcurrentHashMap<>(16, 0.75f);
    concurrentMap.putIfAbsent("key", 1); // 原子操作
    
2.4.2 Java 8 Map增强

computeIfAbsent

Map<String, List<String>> map = new HashMap<>();
map.computeIfAbsent("group1", k -> new ArrayList<>()).add("item1");

merge操作

Map<String, Integer> scores = new HashMap<>();
scores.merge("Alice", 10, Integer::sum); // {"Alice":10}
scores.merge("Alice", 5, Integer::sum);  // {"Alice":15}

第三章 接口

3.1 Comparable自然排序

3.1.1 实现规范
public class Product implements Comparable<Product> {
    private String name;
    private double price;

    @Override
    public int compareTo(Product other) {
        // 价格升序排列
        return Double.compare(this.price, other.price);
        
        // 如需降序:
        // return Double.compare(other.price, this.price);
    }
}

排序性能对比

排序算法时间复杂度空间复杂度稳定性
TimSort(Arrays.sort)O(n log n)O(n)稳定
DualPivotQuickSortO(n log n)O(log n)不稳定

3.2 Comparator定制排序

3.2.1 Lambda表达式简化
List<String> names = Arrays.asList("John", "Alice", "Bob");
// 按字符串长度排序
names.sort((s1, s2) -> s1.length() - s2.length());

// Java 8+ 方法引用
names.sort(Comparator.comparingInt(String::length));
3.2.2 多条件排序
List<Employee> employees = ...;
// 先按部门升序,再按薪资降序
employees.sort(Comparator.comparing(Employee::getDepartment)
                         .thenComparing(Employee::getSalary, Comparator.reverseOrder()));

3.3 接口演进

3.3.1 默认方法(JDK 8+)
public interface Cache {
    void put(String key, Object value);
    
    default Object getOrDefault(String key, Object defaultValue) {
        Object value = get(key); // 假设get是抽象方法
        return value != null ? value : defaultValue;
    }
    
    // 静态工具方法
    static <K,V> Cache<K,V> newInstance() {
        return new HashMapCache<>();
    }
}

默认方法冲突解决

interface A { default void foo() { System.out.println("A"); } }
interface B { default void foo() { System.out.println("B"); } }

class C implements A, B {
    @Override
    public void foo() {
        A.super.foo(); // 显式指定调用A的实现
        // 或 B.super.foo();
    }
}
3.3.2 函数式接口

Java内置函数式接口

接口参数类型返回类型典型用途
PredicateTboolean条件过滤
Function<T,R>TR类型转换
ConsumerTvoid副作用操作
SupplierNoneT延迟初始化

自定义函数式接口

@FunctionalInterface
interface TriFunction<T,U,V,R> {
    R apply(T t, U u, V v);
}

// 使用示例
TriFunction<Integer, Integer, Integer, Integer> sum = (a,b,c) -> a+b+c;

第四章 异常处理

4.1 异常体系架构

图片来源@千锋教育
关键分类

  1. Checked Exception

    • 必须显式处理(try-catch或throws)
    • 示例:IOException, SQLException
  2. Unchecked Exception

    • 包括RuntimeException及其子类
    • 示例:NullPointerException, IllegalArgumentException
  3. Error

    • JVM级严重错误(通常不应捕获)
    • 示例:OutOfMemoryError, StackOverflowError

4.2 异常处理模式

4.2.1 防御性编程
public double divide(double dividend, double divisor) {
    if (divisor == 0) {
        throw new ArithmeticException("Divisor cannot be zero");
    }
    return dividend / divisor;
}
4.2.2 资源管理(try-with-resources)
// JDK 7+ 自动关闭资源
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"));
     PrintWriter pw = new PrintWriter("output.txt")) {
    String line;
    while ((line = br.readLine()) != null) {
        pw.println(line.toUpperCase());
    }
} catch (IOException e) {
    log.error("File processing failed", e);
}

实现AutoCloseable

public class CustomResource implements AutoCloseable {
    private boolean isOpen = true;
    
    public void doWork() {
        if (!isOpen) throw new IllegalStateException("Resource closed");
        System.out.println("Working...");
    }
    
    @Override
    public void close() {
        isOpen = false;
        System.out.println("Resource closed");
    }
}

// 使用示例
try (CustomResource res = new CustomResource()) {
    res.doWork();
}

4.3 自定义异常设计

4.3.1 异常链
public class DataProcessingException extends Exception {
    public DataProcessingException(String message, Throwable cause) {
        super(message, cause);
    }
}

// 使用场景
try {
    parseData(rawInput);
} catch (ParseException e) {
    throw new DataProcessingException("Failed to parse input", e);
}
4.3.2 业务异常
  1. 命名规范

    • Exception结尾(如InvalidOrderException
    • 避免使用Error后缀(保留给JVM错误)
  2. 状态码封装

public class BusinessException extends RuntimeException {
    private final int code;
    private final String message;
    
    public BusinessException(int code, String message) {
        this.code = code;
        this.message = message;
    }
    
    // getters...
}

// 使用示例
throw new BusinessException(400, "Invalid request parameters");
  1. 国际化支持
public class I18nException extends RuntimeException {
    private final String messageKey;
    private final Object[] args;
    
    public I18nException(String messageKey, Object... args) {
        this.messageKey = messageKey;
        this.args = args;
    }
    
    @Override
    public String getMessage() {
        return MessageFormat.format(
            ResourceBundle.getBundle("messages").getString(messageKey),
            args
        );
    }
}

4.4 异常处理性能优化

4.4.1 异常捕获成本

微基准测试结果(JMH测试):

操作吞吐量(ops/ms)
正常流程12,500
捕获异常3,200
抛出异常850

优化建议

  1. 避免在循环中使用异常控制流程
  2. 优先使用条件判断而非异常处理预期情况
  3. 缓存频繁抛出的异常对象(如IllegalArgumentException实例)
4.4.2 日志记录
// 错误级别记录
log.error("Failed to process order {}", orderId, exception);

// 调试级别记录(避免在生产环境记录敏感数据)
if (log.isDebugEnabled()) {
    log.debug("Raw input: {}", sensitiveData);
}

// MDC上下文记录
try (MDC.putCloseable("requestId", requestId)) {
    log.info("Processing request");
    // 业务逻辑
}

第五章 实践案例

5.1 泛型+集合构建缓存系统

public class LRUCache<K, V> implements AutoCloseable {
    private final Map<K, V> cache;
    private final int maxSize;
    private final Deque<K> accessOrder;

    public LRUCache(int maxSize) {
        this.maxSize = maxSize;
        this.cache = new LinkedHashMap<>(maxSize, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
                boolean shouldRemove = size() > maxSize;
                if (shouldRemove) {
                    accessOrder.removeFirst();
                }
                return shouldRemove;
            }
        };
        this.accessOrder = new ArrayDeque<>();
    }

    public synchronized V get(K key) {
        V value = cache.get(key);
        if (value != null) {
            accessOrder.remove(key);
            accessOrder.addLast(key);
        }
        return value;
    }

    public synchronized void put(K key, V value) {
        if (cache.containsKey(key)) {
            accessOrder.remove(key);
        } else if (cache.size() >= maxSize) {
            K eldestKey = accessOrder.removeFirst();
            cache.remove(eldestKey);
        }
        cache.put(key, value);
        accessOrder.addLast(key);
    }

    @Override
    public void close() {
        cache.clear();
        accessOrder.clear();
    }
}

5.2 接口+异常构建验证框架

public interface Validator<T> {
    void validate(T object) throws ValidationException;
    
    default Validator<T> and(Validator<T> other) {
        return t -> {
            validate(t);
            other.validate(t);
        };
    }
}

public class UserValidator implements Validator<User> {
    @Override
    public void validate(User user) throws ValidationException {
        if (user.getName() == null || user.getName().trim().isEmpty()) {
            throw new ValidationException("Username cannot be empty");
        }
        if (user.getAge() < 0 || user.getAge() > 120) {
            throw new ValidationException("Invalid age range");
        }
    }
}

// 使用示例
Validator<User> userValidator = new UserValidator()
    .and(user -> {
        if (!user.getEmail().contains("@")) {
            throw new ValidationException("Invalid email format");
        }
    });

try {
    userValidator.validate(new User("john", 30, "john@example.com"));
} catch (ValidationException e) {
    System.err.println("Validation failed: " + e.getMessage());
}

总结

本教程系统梳理了Java泛型、集合框架、接口及异常处理的核心机制与工业实践:

  1. 泛型:通过类型参数化实现编译期类型安全,需理解类型擦除机制
  2. 集合框架:根据数据特征选择合适实现(ArrayList/LinkedList/HashMap等)
  3. 接口:利用Comparable/Comparator实现灵活排序,掌握默认方法演进
  4. 异常处理:构建分层异常体系,结合日志实现可观测性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值