Java 进阶从入门到精通:核心特性与实战(附详细代码)
前言
对于零基础学习者,掌握Java基础语法后,进阶内容是突破瓶颈的关键。本文涵盖面向对象高级特性、集合框架、常用API、异常处理、日志框架等核心知识点,从概念到实战,通过大量带注释的代码示例,帮你从“会用”到“精通”,逐步构建完整的Java知识体系。
一、static 关键字:静态成员的使用
static 用于修饰类的成员(变量、方法、代码块),表示“静态”,属于类本身而非实例,可直接通过类名访问。
1. 静态变量(类变量)
- 被所有实例共享,内存中只存一份。
- 适合存储全局共享数据(如计数器、常量)。
代码示例:
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或实例变量(因可能无实例)。
代码示例:
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. 静态代码块
- 类加载时执行(仅一次),用于初始化静态资源(如加载配置、初始化常量)。
代码示例:
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. 饿汉式(线程安全)
- 类加载时创建实例,简单但可能浪费内存(无论是否使用都初始化)。
代码示例:
public class SingletonHungry {
// 1. 私有构造器:防止外部创建实例
private SingletonHungry() {}
// 2. 私有静态实例(类加载时初始化)
private static SingletonHungry instance = new SingletonHungry();
// 3. 公开静态方法:返回唯一实例
public static SingletonHungry getInstance() {
return instance;
}
}2. 懒汉式(延迟初始化,需处理线程安全)
- 首次使用时创建实例,节省内存,但多线程下需加锁保证唯一。
线程安全版代码:
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. 实例代码块
- 每次创建对象时执行(在构造器前执行),用于初始化实例变量。
代码示例:
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)
- 子类继承父类的非私有成员(属性、方法),实现代码复用,子类可扩展新功能。
代码示例:
// 父类:动物
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 | √ | × | × | × |
代码示例:
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)
- 含抽象方法(无实现的方法)的类,不能实例化,需子类继承并实现抽象方法。
- 适合定义“模板”(如形状类,子类为圆形、矩形)。
代码示例:
// 抽象类:形状
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单继承的限制)。
代码示例:
// 接口:飞行
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);
- 方法重写;
- 父类引用指向子类对象。
代码示例:
// 父类:交通工具
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. 成员内部类
- 定义在外部类的成员位置,可访问外部类所有成员。
代码示例:
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修饰,属于外部类本身,不能访问外部类非静态成员。
代码示例:
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. 局部内部类
- 定义在方法内部,作用域仅限当前方法。
代码示例:
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. 匿名内部类(常用)
- 无类名的局部内部类,常用于简化接口/抽象类的实现(仅用一次的场景)。
代码示例:
// 接口
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():返回对象字符串表示(默认类名@哈希值,需重写)。
代码示例:
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)等。 - 作用:泛型只能用引用类型,集合中需用包装类。
代码示例:
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(日期时间)。
代码示例:
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匹配aab、acb |
* | 前一个字符出现0~多次 | ab*匹配a、ab、abb |
+ | 前一个字符出现1~多次 | ab+匹配ab、abb |
? | 前一个字符出现0~1次 | ab?匹配a、ab |
[] | 匹配括号内任意字符 | [0-9]匹配任意数字 |
() | 分组 | (ab)+匹配ab、abab |
\d | 匹配数字(等价[0-9]) | |
\w | 匹配字母、数字、下划线 | |
^ | 开头 | ^a匹配以a开头的字符串 |
$ | 结尾 | a$匹配以a结尾的字符串 |
2. 正则表达式在Java中的使用
通过 java.util.regex.Pattern 和 Matcher 类,或 String 的 matches()、replaceAll() 方法。
代码示例:
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(线程)
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)
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>(键为字符串,值为整数)。
代码示例:
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):
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):
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):
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
提供操作集合的静态方法(排序、查找、同步化等)。
代码示例:
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>>)。
代码示例:
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+)
创建后不能修改(增删元素),保证线程安全。
代码示例:
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操作步骤
- 获取流:从集合/数组获取(
list.stream()); - 中间操作:过滤(
filter)、映射(map)、排序(sorted)等(返回新流); - 终止操作:收集(
collect)、计数(count)、遍历(forEach)等(触发执行)。
2. 代码示例
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 无论是否异常都会执行(适合释放资源)。
代码示例:
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
声明方法可能抛出的异常,由调用者处理。
代码示例:
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. 自定义异常
当系统异常不能满足需求时,可自定义异常(继承 Exception 或 RuntimeException)。
代码示例:
// 自定义异常(继承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. 基本使用步骤
- 导入依赖(Maven):
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>- 配置日志(
src/main/resources/logback.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>- 代码中使用:
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(模型类)
public class Book {
private String id; // 图书ID
private String name; // 书名
private String author; // 作者
private boolean isBorrowed; // 是否借出
// 构造器、getter、setter、toString()
}(2)BookService.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(主程序)
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流、异常处理、日志框架,最后通过图书管理系统综合应用。
学习建议:
- 每个知识点配合代码练习,理解“为什么这么用”而非死记;
- 集合和Stream流是开发高频使用内容,需重点掌握其原理和API;
- 项目实战是巩固知识的关键,尝试扩展图书管理系统(如添加用户管理、持久化存储)。
掌握这些内容后,你将具备Java开发的核心能力,可进一步学习Spring、MyBatis等框架,迈向企业级开发。