Skip to content

Java 进阶从入门到精通:核心特性与实战(附详细代码)

前言

对于零基础学习者,掌握Java基础语法后,进阶内容是突破瓶颈的关键。本文涵盖面向对象高级特性、集合框架、常用API、异常处理、日志框架等核心知识点,从概念到实战,通过大量带注释的代码示例,帮你从“会用”到“精通”,逐步构建完整的Java知识体系。

一、static 关键字:静态成员的使用

static 用于修饰类的成员(变量、方法、代码块),表示“静态”,属于类本身而非实例,可直接通过类名访问。

1. 静态变量(类变量)

  • 被所有实例共享,内存中只存一份。
  • 适合存储全局共享数据(如计数器、常量)。

代码示例

java
public class Student {
    // 实例变量:每个对象独有
    private String name;
    // 静态变量:所有学生共享(班级名称)
    public static String className = "Java班";

    public Student(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println(name + "属于" + className);
    }

    public static void main(String[] args) {
        // 直接通过类名访问静态变量
        System.out.println(Student.className); // Java班

        Student s1 = new Student("张三");
        Student s2 = new Student("李四");
        s1.show(); // 张三属于Java班
        s2.show(); // 李四属于Java班

        // 修改静态变量(所有实例都会受影响)
        Student.className = "高级Java班";
        s1.show(); // 张三属于高级Java班
    }
}

2. 静态方法(类方法)

  • 属于类,不依赖实例,可直接通过类名调用。
  • 内部不能使用 this 或实例变量(因可能无实例)。

代码示例

java
public class MathUtil {
    // 静态方法:计算两数之和(工具类常用)
    public static int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        // 直接通过类名调用,无需创建对象
        int sum = MathUtil.add(3, 5);
        System.out.println(sum); // 8
    }
}

常见应用:工具类(如 java.util.Math)、单例模式。

3. 静态代码块

  • 类加载时执行(仅一次),用于初始化静态资源(如加载配置、初始化常量)。

代码示例

java
public class StaticBlockDemo {
    // 静态变量
    public static String config;

    // 静态代码块:初始化静态变量
    static {
        System.out.println("静态代码块执行");
        config = "加载默认配置..."; // 模拟加载配置
    }

    public static void main(String[] args) {
        System.out.println(config); // 加载默认配置...
        // 再次创建对象,静态代码块不会重复执行
        StaticBlockDemo obj = new StaticBlockDemo();
    }
}
// 输出:
// 静态代码块执行
// 加载默认配置...

二、单例模式:确保对象唯一

单例模式是设计模式的一种,保证一个类仅有一个实例,并提供全局访问点(如工具类、配置管理器)。

1. 饿汉式(线程安全)

  • 类加载时创建实例,简单但可能浪费内存(无论是否使用都初始化)。

代码示例

java
public class SingletonHungry {
    // 1. 私有构造器:防止外部创建实例
    private SingletonHungry() {}

    // 2. 私有静态实例(类加载时初始化)
    private static SingletonHungry instance = new SingletonHungry();

    // 3. 公开静态方法:返回唯一实例
    public static SingletonHungry getInstance() {
        return instance;
    }
}

2. 懒汉式(延迟初始化,需处理线程安全)

  • 首次使用时创建实例,节省内存,但多线程下需加锁保证唯一。

线程安全版代码

java
public class SingletonLazy {
    // 1. 私有构造器
    private SingletonLazy() {}

    // 2. 私有静态实例(未初始化)
    private static volatile SingletonLazy instance; // volatile防止指令重排序

    // 3. 公开静态方法:双重检查锁(DCL)保证线程安全
    public static SingletonLazy getInstance() {
        if (instance == null) { // 第一次检查:减少锁竞争
            synchronized (SingletonLazy.class) { // 加锁
                if (instance == null) { // 第二次检查:防止多线程同时通过第一次检查
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;
    }
}

应用场景:日志工具、数据库连接池(避免频繁创建销毁资源)。

三、代码块:初始化对象的灵活方式

代码块是类中独立的代码片段,用于初始化对象或静态资源,分实例代码块静态代码块

1. 实例代码块

  • 每次创建对象时执行(在构造器前执行),用于初始化实例变量。

代码示例

java
public class InstanceBlockDemo {
    private String name;

    // 实例代码块
    {
        System.out.println("实例代码块执行");
        name = "默认名称"; // 初始化实例变量
    }

    public InstanceBlockDemo() {
        System.out.println("构造器执行");
    }

    public static void main(String[] args) {
        InstanceBlockDemo obj1 = new InstanceBlockDemo();
        // 输出:
        // 实例代码块执行
        // 构造器执行

        InstanceBlockDemo obj2 = new InstanceBlockDemo(); // 再次执行实例代码块
    }
}

2. 静态代码块(见上文“static关键字”)

  • 类加载时执行一次,优先级:静态代码块 > 实例代码块 > 构造器。

四、继承与权限修饰符:类的扩展与访问控制

1. 继承(extends

  • 子类继承父类的非私有成员(属性、方法),实现代码复用,子类可扩展新功能。

代码示例

java
// 父类:动物
class Animal {
    public void eat() {
        System.out.println("动物会吃");
    }
}

// 子类:狗(继承动物)
class Dog extends Animal {
    // 扩展父类:新增方法
    public void bark() {
        System.out.println("狗会叫");
    }

    // 重写父类方法(@Override注解标识)
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}

public class InheritDemo {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat(); // 调用重写的方法:狗吃骨头
        dog.bark(); // 调用子类新增方法:狗会叫
    }
}

注意:Java单继承(一个类只能有一个父类),但可多层继承(如 A→B→C)。

2. 权限修饰符

控制类、成员的访问范围,从宽到严:public > protected > default(缺省) > private

修饰符本类同包子类其他包
public
protected×
default××
private×××

代码示例

java
public class AccessDemo {
    public int pub;      // 所有地方可访问
    protected int pro;   // 本类、同包、子类可访问
    int def;             // 本类、同包可访问(default)
    private int pri;     // 仅本类可访问

    public void method() {
        pub = 1; // 可访问
        pro = 2; // 可访问
        def = 3; // 可访问
        pri = 4; // 可访问
    }
}

五、抽象类与接口:抽象层设计

1. 抽象类(abstract class

  • 含抽象方法(无实现的方法)的类,不能实例化,需子类继承并实现抽象方法。
  • 适合定义“模板”(如形状类,子类为圆形、矩形)。

代码示例

java
// 抽象类:形状
abstract class Shape {
    // 抽象方法:计算面积(无实现)
    public abstract double getArea();

    // 普通方法:共有的实现
    public void show() {
        System.out.println("这是一个形状");
    }
}

// 子类:圆形(实现抽象方法)
class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // 必须实现抽象方法
    @Override
    public double getArea() {
        return Math.PI * radius * radius; // 圆面积公式
    }
}

public class AbstractDemo {
    public static void main(String[] args) {
        Shape circle = new Circle(2); // 多态:父类引用指向子类对象
        System.out.println("圆面积:" + circle.getArea()); // 约12.566
        circle.show(); // 这是一个形状
    }
}

2. 接口(interface

  • 全是抽象方法(Java 8+ 可含默认方法 default 和静态方法),是“规范”而非“实现”。
  • 类通过 implements 实现接口,可多实现(弥补Java单继承的限制)。

代码示例

java
// 接口:飞行
interface Flyable {
    // 抽象方法(默认public abstract)
    void fly();

    // 默认方法(有实现,子类可重写)
    default void stop() {
        System.out.println("停止飞行");
    }
}

// 接口:游泳
interface Swimmable {
    void swim();
}

// 类:鸭子(实现两个接口)
class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("鸭子飞");
    }

    @Override
    public void swim() {
        System.out.println("鸭子游");
    }
}

public class InterfaceDemo {
    public static void main(String[] args) {
        Duck duck = new Duck();
        duck.fly(); // 鸭子飞
        duck.swim(); // 鸭子游
        duck.stop(); // 停止飞行
    }
}

抽象类 vs 接口

  • 抽象类:含具体实现,体现“is-a”关系(如“狗是动物”)。
  • 接口:纯规范,体现“can-do”关系(如“鸭子会飞”)。

六、多态:同一行为的不同表现

多态是面向对象三大特性之一(封装、继承、多态),指“父类引用指向子类对象,调用方法时实际执行子类实现”。

1. 多态的条件

  • 继承或实现(extends/implements);
  • 方法重写;
  • 父类引用指向子类对象。

代码示例

java
// 父类:交通工具
class Vehicle {
    public void run() {
        System.out.println("交通工具运行");
    }
}

// 子类:汽车
class Car extends Vehicle {
    @Override
    public void run() {
        System.out.println("汽车跑");
    }
}

// 子类:自行车
class Bike extends Vehicle {
    @Override
    public void run() {
        System.out.println("自行车骑");
    }
}

public class PolymorphismDemo {
    // 多态参数:接收父类类型,可传入任意子类对象
    public static void start(Vehicle v) {
        v.run(); // 实际执行子类重写的方法
    }

    public static void main(String[] args) {
        Vehicle car = new Car(); // 多态:父类引用指向子类
        Vehicle bike = new Bike();

        start(car); // 汽车跑
        start(bike); // 自行车骑
    }
}

2. 多态的好处

  • 代码灵活:方法参数用父类/接口,可兼容所有子类对象;
  • 便于扩展:新增子类无需修改原有代码(符合“开闭原则”)。

七、内部类:类中的类

内部类是定义在类内部的类,可访问外部类的私有成员,常用于“仅当前类需要使用的辅助类”。

1. 成员内部类

  • 定义在外部类的成员位置,可访问外部类所有成员。

代码示例

java
public class Outer {
    private int num = 10;

    // 成员内部类
    class Inner {
        public void show() {
            // 访问外部类私有成员
            System.out.println("外部类num:" + num);
        }
    }

    public static void main(String[] args) {
        // 创建内部类对象:外部类对象.new 内部类()
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.show(); // 外部类num:10
    }
}

2. 静态内部类

  • static 修饰,属于外部类本身,不能访问外部类非静态成员。

代码示例

java
public class OuterStatic {
    private static int staticNum = 20;
    private int num = 10;

    // 静态内部类
    static class StaticInner {
        public void show() {
            System.out.println("外部类静态变量:" + staticNum);
            // System.out.println(num); // 错误:不能访问非静态成员
        }
    }

    public static void main(String[] args) {
        // 创建静态内部类对象:外部类.内部类()
        OuterStatic.StaticInner inner = new OuterStatic.StaticInner();
        inner.show(); // 外部类静态变量:20
    }
}

3. 局部内部类

  • 定义在方法内部,作用域仅限当前方法。

代码示例

java
public class OuterLocal {
    public void method() {
        int localNum = 30; // 局部变量(JDK 8+ 隐式final)

        // 局部内部类
        class LocalInner {
            public void show() {
                System.out.println("局部变量:" + localNum);
            }
        }

        // 方法内创建并使用
        LocalInner inner = new LocalInner();
        inner.show(); // 局部变量:30
    }

    public static void main(String[] args) {
        new OuterLocal().method();
    }
}

4. 匿名内部类(常用)

  • 无类名的局部内部类,常用于简化接口/抽象类的实现(仅用一次的场景)。

代码示例

java
// 接口
interface Greet {
    void sayHello();
}

public class AnonymousInner {
    public static void main(String[] args) {
        // 匿名内部类:直接实现接口
        Greet greet = new Greet() {
            @Override
            public void sayHello() {
                System.out.println("Hello 匿名内部类");
            }
        };
        greet.sayHello(); // Hello 匿名内部类
    }
}

八、常用API(核心类库)

Java提供大量预定义类(API),以下是开发中最常用的类。

1. Object类(所有类的父类)

  • 常用方法:
    • equals(Object obj):比较对象内容(默认比较地址,需重写);
    • hashCode():返回哈希值(配合equals使用);
    • toString():返回对象字符串表示(默认类名@哈希值,需重写)。

代码示例

java
public class Person {
    private String name;
    private int age;

    // 重写toString():方便打印对象信息
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }

    // 重写equals():比较内容
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && name.equals(person.name);
    }

    public static void main(String[] args) {
        Person p1 = new Person("张三", 18);
        Person p2 = new Person("张三", 18);
        System.out.println(p1); // 调用toString():Person{name='张三', age=18}
        System.out.println(p1.equals(p2)); // true(内容相同)
    }
}

2. String类(字符串)

  • 不可变字符序列,常用方法:length()charAt()equals()substring()split()等(见基础篇)。

3. 包装类(基本类型→引用类型)

  • 8种基本类型对应包装类:Integer(int)、Double(double)、Boolean(boolean)等。
  • 作用:泛型只能用引用类型,集合中需用包装类。

代码示例

java
public class WrapperDemo {
    public static void main(String[] args) {
        // 装箱:基本类型→包装类
        Integer i1 = Integer.valueOf(10); // 手动装箱
        Integer i2 = 10; // 自动装箱(JDK 5+)

        // 拆箱:包装类→基本类型
        int num = i1.intValue(); // 手动拆箱
        int num2 = i2; // 自动拆箱

        // 字符串转基本类型
        int num3 = Integer.parseInt("123"); // "123"→123
        double num4 = Double.parseDouble("3.14");
    }
}

4. 日期时间类(JDK 8+)

  • LocalDate(日期)、LocalTime(时间)、LocalDateTime(日期时间)。

代码示例

java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeDemo {
    public static void main(String[] args) {
        // 获取当前日期时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now); // 2023-10-01T15:30:45.123

        // 格式化
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formatted = now.format(formatter);
        System.out.println(formatted); // 2023-10-01 15:30:45

        // 增减时间
        LocalDateTime tomorrow = now.plusDays(1); // 加1天
        System.out.println(tomorrow.format(formatter));
    }
}

九、正则表达式:字符串匹配神器

正则表达式是用于匹配字符串的模式,常用于验证(如手机号、邮箱)、提取、替换字符串。

1. 常用元字符

元字符含义示例
.匹配任意字符(除换行)a.b匹配aabacb
*前一个字符出现0~多次ab*匹配aababb
+前一个字符出现1~多次ab+匹配ababb
?前一个字符出现0~1次ab?匹配aab
[]匹配括号内任意字符[0-9]匹配任意数字
()分组(ab)+匹配ababab
\d匹配数字(等价[0-9]
\w匹配字母、数字、下划线
^开头^a匹配以a开头的字符串
$结尾a$匹配以a结尾的字符串

2. 正则表达式在Java中的使用

通过 java.util.regex.PatternMatcher 类,或 Stringmatches()replaceAll() 方法。

代码示例

java
public class RegexDemo {
    public static void main(String[] args) {
        // 验证手机号(11位数字,以1开头)
        String phone = "13812345678";
        String regex = "^1[3-9]\\d{9}$"; // 正则表达式
        boolean isPhone = phone.matches(regex);
        System.out.println("是否为手机号:" + isPhone); // true

        // 提取字符串中的数字
        String str = "abc123def456";
        String nums = str.replaceAll("[^0-9]", ""); // 替换非数字为空
        System.out.println("提取的数字:" + nums); // 123456

        // 验证邮箱(简单版)
        String email = "test@example.com";
        String emailRegex = "^\\w+@\\w+\\.\\w+$";
        System.out.println("是否为邮箱:" + email.matches(emailRegex)); // true
    }
}

十、Lambda表达式:简化代码的利器

Lambda是Java 8+ 新增特性,用于简化函数式接口(只有一个抽象方法的接口)的实现,语法:(参数) -> { 代码 }

1. 函数式接口

常用内置函数式接口:

  • Runnable(无参无返回);
  • Consumer<T>(消费型:有参无返回);
  • Supplier<T>(供给型:无参有返回);
  • Function<T, R>(函数型:有参有返回);
  • Predicate<T>(断言型:有参返回boolean)。

2. Lambda示例

示例1:简化Runnable(线程)

java
public class LambdaRunnable {
    public static void main(String[] args) {
        // 传统方式:匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("传统线程");
            }
        }).start();

        // Lambda方式
        new Thread(() -> {
            System.out.println("Lambda线程");
        }).start();
    }
}

示例2:简化集合遍历(配合forEach)

java
import java.util.Arrays;
import java.util.List;

public class LambdaForEach {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c");
        
        // 传统for循环
        for (String s : list) {
            System.out.println(s);
        }
        
        // Lambda + forEach
        list.forEach(s -> System.out.println(s));
        
        // 方法引用(进一步简化,::表示引用方法)
        list.forEach(System.out::println);
    }
}

十一、集合框架:数据存储的核心

集合用于存储多个数据,相比数组更灵活(动态扩容、支持泛型、提供丰富方法),核心接口:Collection(单列)和 Map(双列)。

1. 泛型(集合的基础)

  • 作用:约束集合中元素的类型,避免类型转换错误(编译时检查)。
  • 语法:List<String>(只能存字符串)、Map<String, Integer>(键为字符串,值为整数)。

代码示例

java
import java.util.ArrayList;
import java.util.List;

public class GenericDemo {
    public static void main(String[] args) {
        // 泛型:指定只能存String
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        // list.add(1); // 编译错误:不能存整数

        // 遍历:无需强转
        for (String s : list) {
            System.out.println(s);
        }
    }
}

2. Collection接口(单列集合)

(1)List接口(有序、可重复)

  • ArrayList:基于动态数组,查询快、增删慢(适合查询多的场景);
  • LinkedList:基于双向链表,增删快、查询慢(适合增删多的场景);
  • Vector:线程安全(已被ArrayList替代)。

代码示例(ArrayList)

java
import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建List(泛型:存Integer)
        List<Integer> list = new ArrayList<>();
        
        // 添加元素
        list.add(1);
        list.add(2);
        list.add(3);
        
        // 遍历(for循环)
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        
        // 遍历(增强for)
        for (Integer num : list) {
            System.out.println(num);
        }
        
        // 删除元素(索引)
        list.remove(0);
        System.out.println(list); // [2, 3]
    }
}

(2)Set接口(无序、不可重复)

  • HashSet:基于哈希表,无序,查询快(默认选择);
  • LinkedHashSet:基于哈希表+链表,有序(保留插入顺序);
  • TreeSet:基于红黑树,可排序(需实现Comparable接口)。

代码示例(HashSet)

java
import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        
        // 添加元素(重复元素会被忽略)
        set.add("a");
        set.add("b");
        set.add("a"); // 重复,不添加
        
        // 遍历(无序)
        for (String s : set) {
            System.out.println(s); // 输出a、b(顺序不确定)
        }
        
        // 判断是否包含元素
        System.out.println(set.contains("a")); // true
    }
}

3. Map接口(双列集合:键值对)

  • HashMap:基于哈希表,无序,查询快(默认选择);
  • LinkedHashMap:有序(保留插入顺序);
  • TreeMap:可排序(按键排序);
  • Hashtable:线程安全(已被HashMap替代)。

代码示例(HashMap)

java
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建Map(键:String,值:Integer)
        Map<String, Integer> map = new HashMap<>();
        
        // 添加键值对
        map.put("张三", 18);
        map.put("李四", 20);
        map.put("张三", 19); // 键重复,覆盖值
        
        // 获取值
        int age = map.get("张三");
        System.out.println("张三年龄:" + age); // 19
        
        // 遍历(键集)
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.println(key + ":" + map.get(key));
        }
        
        // 遍历(键值对)
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }
}

4. 集合工具类:Collections

提供操作集合的静态方法(排序、查找、同步化等)。

代码示例

java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CollectionsDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(1);
        list.add(2);
        
        // 排序(升序)
        Collections.sort(list);
        System.out.println(list); // [1, 2, 3]
        
        // 反转
        Collections.reverse(list);
        System.out.println(list); // [3, 2, 1]
        
        // 查找最大值
        int max = Collections.max(list);
        System.out.println("最大值:" + max); // 3
    }
}

5. 集合嵌套与不可变集合

(1)集合嵌套

集合中存储另一个集合(如List<List<Integer>>Map<String, List<String>>)。

代码示例

java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class NestedCollection {
    public static void main(String[] args) {
        // 班级-学生(Map嵌套List)
        Map<String, List<String>> classMap = new HashMap<>();
        
        // 一班学生
        List<String> class1 = new ArrayList<>();
        class1.add("张三");
        class1.add("李四");
        
        // 二班学生
        List<String> class2 = new ArrayList<>();
        class2.add("王五");
        class2.add("赵六");
        
        // 放入Map
        classMap.put("一班", class1);
        classMap.put("二班", class2);
        
        // 遍历
        for (Map.Entry<String, List<String>> entry : classMap.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
    }
}

(2)不可变集合(Java 9+)

创建后不能修改(增删元素),保证线程安全。

代码示例

java
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ImmutableCollection {
    public static void main(String[] args) {
        // 不可变List
        List<String> list = List.of("a", "b", "c");
        // list.add("d"); // 运行时错误:UnsupportedOperationException
        
        // 不可变Set
        Set<Integer> set = Set.of(1, 2, 3);
        
        // 不可变Map
        Map<String, Integer> map = Map.of("a", 1, "b", 2);
    }
}

十二、Stream流:集合的高效处理

Stream流是Java 8+ 用于处理集合的高级API,通过“流水线”操作(过滤、映射、排序等)高效处理数据,类似SQL查询。

1. Stream操作步骤

  1. 获取流:从集合/数组获取(list.stream());
  2. 中间操作:过滤(filter)、映射(map)、排序(sorted)等(返回新流);
  3. 终止操作:收集(collect)、计数(count)、遍历(forEach)等(触发执行)。

2. 代码示例

java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(3, 1, 2, 4, 5, 3);
        
        // 需求:过滤出大于2的数字 → 去重 → 排序 → 收集为List
        List<Integer> result = numbers.stream()
                .filter(n -> n > 2) // 过滤:保留>2的数字
                .distinct() // 去重
                .sorted() // 排序
                .collect(Collectors.toList()); // 收集为List
        
        System.out.println(result); // [3, 4, 5]

        // 需求:计算所有偶数的和
        int sum = numbers.stream()
                .filter(n -> n % 2 == 0) // 偶数
                .mapToInt(Integer::intValue) // 转为int流
                .sum(); // 求和
        System.out.println("偶数和:" + sum); // 6(2+4)
    }
}

十三、异常:程序错误的处理机制

异常是程序运行时的错误(如空指针、数组越界),通过异常处理机制可捕获错误并优雅处理,避免程序崩溃。

1. 异常体系

  • 顶层类:Throwable
  • 子类:Error(严重错误,如内存溢出,无需处理)和 Exception(可处理的异常);
  • Exception 分:
    • 编译时异常(检查型):必须处理(如 IOException);
    • 运行时异常(非检查型):可处理可不处理(如 NullPointerException)。

2. 异常处理方式

(1)try-catch-finally

捕获并处理异常,finally 无论是否异常都会执行(适合释放资源)。

代码示例

java
public class TryCatchDemo {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        try {
            // 可能出错的代码(数组索引越界)
            System.out.println(arr[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
            // 处理异常
            System.out.println("错误:索引越界");
            e.printStackTrace(); // 打印异常堆栈信息
        } finally {
            // 无论是否异常,都会执行
            System.out.println("程序执行完毕");
        }
    }
}

(2)throws

声明方法可能抛出的异常,由调用者处理。

代码示例

java
import java.io.FileNotFoundException;

// 编译时异常:必须处理(throws或try-catch)
public class ThrowsDemo {
    // 声明方法可能抛出FileNotFoundException
    public static void readFile() throws FileNotFoundException {
        // 模拟文件不存在(编译时异常)
        throw new FileNotFoundException("文件未找到");
    }

    public static void main(String[] args) {
        try {
            readFile(); // 调用者处理异常
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

3. 自定义异常

当系统异常不能满足需求时,可自定义异常(继承 ExceptionRuntimeException)。

代码示例

java
// 自定义异常(继承RuntimeException,运行时异常)
class AgeException extends RuntimeException {
    public AgeException(String message) {
        super(message); // 调用父类构造器
    }
}

public class CustomException {
    public static void checkAge(int age) {
        if (age < 0 || age > 150) {
            // 抛出自定义异常
            throw new AgeException("年龄无效:" + age);
        }
        System.out.println("年龄有效:" + age);
    }

    public static void main(String[] args) {
        checkAge(200); // 抛出AgeException:年龄无效:200
    }
}

十四、Logback日志框架:程序行为记录

日志用于记录程序运行过程(调试、错误跟踪),Logback是常用日志框架(性能优于Log4j)。

1. 基本使用步骤

  1. 导入依赖(Maven):
xml
<dependencies>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
</dependencies>
  1. 配置日志src/main/resources/logback.xml):
xml
<configuration>
    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 日志级别:DEBUG < INFO < WARN < ERROR -->
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>
  1. 代码中使用
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogbackDemo {
    // 获取日志对象(传入当前类)
    private static final Logger logger = LoggerFactory.getLogger(LogbackDemo.class);

    public static void main(String[] args) {
        // 不同级别日志
        logger.debug("调试信息(级别低,默认不输出)");
        logger.info("普通信息");
        logger.warn("警告信息");
        logger.error("错误信息");
    }
}

输出

16:30:45.123 [main] INFO  com.example.LogbackDemo - 普通信息
16:30:45.125 [main] WARN  com.example.LogbackDemo - 警告信息
16:30:45.125 [main] ERROR com.example.LogbackDemo - 错误信息

十五、阶段项目:图书管理系统

综合应用上述知识点,实现一个简单的图书管理系统,功能包括:添加图书、查询图书、借阅/归还图书、记录日志。

1. 项目结构

bookmanager/
├── model/        // 模型类
│   ├── Book.java  // 图书类
│   └── BorrowRecord.java  // 借阅记录
├── service/      // 业务逻辑
│   └── BookService.java  // 图书服务
├── util/         // 工具类
│   └── LoggerUtil.java  // 日志工具
└── Main.java     // 主程序

2. 核心代码

(1)Book.java(模型类)

java
public class Book {
    private String id; // 图书ID
    private String name; // 书名
    private String author; // 作者
    private boolean isBorrowed; // 是否借出

    // 构造器、getter、setter、toString()
}

(2)BookService.java(业务逻辑)

java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class BookService {
    private static final Logger logger = LoggerFactory.getLogger(BookService.class);
    private List<Book> books = new ArrayList<>(); // 存储图书

    // 添加图书
    public void addBook(Book book) {
        books.add(book);
        logger.info("添加图书:" + book);
    }

    // 查询所有图书
    public List<Book> findAllBooks() {
        return books;
    }

    // 借阅图书
    public boolean borrowBook(String bookId) {
        // 使用Stream流查找图书
        Book book = books.stream()
                .filter(b -> b.getId().equals(bookId) && !b.isBorrowed())
                .findFirst()
                .orElse(null);

        if (book != null) {
            book.setBorrowed(true);
            logger.info("借阅成功:" + book.getName());
            return true;
        }
        logger.warn("借阅失败,图书不存在或已借出:" + bookId);
        return false;
    }

    // 归还图书(类似借阅逻辑)
}

(3)Main.java(主程序)

java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        BookService service = new BookService();
        Scanner sc = new Scanner(System.in);

        while (true) {
            System.out.println("===图书管理系统===");
            System.out.println("1. 添加图书");
            System.out.println("2. 查看图书");
            System.out.println("3. 借阅图书");
            System.out.println("4. 归还图书");
            System.out.println("5. 退出");
            System.out.print("请选择:");
            int choice = sc.nextInt();
            sc.nextLine(); // 消耗回车

            switch (choice) {
                case 1:
                    // 添加图书逻辑
                    break;
                case 2:
                    // 查看图书逻辑
                    break;
                // 其他选项...
                case 5:
                    System.out.println("退出系统");
                    sc.close();
                    return;
                default:
                    System.out.println("输入错误");
            }
        }
    }
}

总结

本文涵盖Java进阶的核心知识点,从面向对象高级特性(static、单例、继承、多态)到集合框架、Stream流、异常处理、日志框架,最后通过图书管理系统综合应用。

学习建议:

  1. 每个知识点配合代码练习,理解“为什么这么用”而非死记;
  2. 集合和Stream流是开发高频使用内容,需重点掌握其原理和API;
  3. 项目实战是巩固知识的关键,尝试扩展图书管理系统(如添加用户管理、持久化存储)。
    掌握这些内容后,你将具备Java开发的核心能力,可进一步学习Spring、MyBatis等框架,迈向企业级开发。

Released under the MIT License.