第十七章 反射与设计模式
一、反射机制
1. 核心概念拆解
• 实例对象:通过new关键字创建的类实体,直接对应类的业务实例,包含成员变量、方法的具体数据。
• Class类对象:JVM在类加载时自动生成的“类元数据容器”,封装了类的全限定名、父类、接口、构造器、方法、字段等所有结构信息,是反射操作的核心入口。
• 反射:一种动态获取类信息、调用类成员的技术,打破了编译期的封装限制,允许程序在运行时操作类的结构,是框架实现灵活性的核心基础。
2. 获取Class对象的三种方式
1. 实例对象调用getClass():适用于已有实例对象的场景
Student stu = new Student();
Class<?> clazz1 = stu.getClass(); // 获取Student类的Class对象
2. 类名直接调用.class属性:编译期即可确定类型,无需实例化
Class<?> clazz2 = Student.class; // 直接通过类名获取Class对象
3. Class类静态方法forName():通过全限定类名字符串动态加载类,框架中最常用
Class<?> clazz3 = Class.forName("com.example.Student");
// 必须传入"包名.类名"格式的全限定名,否则抛出ClassNotFoundException
3. 类加载触发时机
• 首次创建类的实例(new关键字)
• 首次调用类的静态成员(静态变量、静态方法)
• 子类加载时,会优先触发父类的加载
• 调用Class.forName()获取Class对象时,直接触发类加载
4. 反射常用API详解
(1)获取类的基本信息
Class<?> clazz = Class.forName("com.example.Student");
String className = clazz.getName(); // 获取全限定类名:com.example.Student
String simpleName = clazz.getSimpleName(); // 获取简单类名:Student
Class<?> superClass = clazz.getSuperclass(); // 获取父类的Class对象
Class<?>[] interfaces = clazz.getInterfaces(); // 获取类实现的所有接口
(2)获取类的成员方法
• getMethods():获取本类及父类中所有public修饰的方法
• getDeclaredMethods():获取本类中所有方法(包括private、protected、default修饰的方法)
Method[] publicMethods = clazz.getMethods();
Method[] allMethods = clazz.getDeclaredMethods();
(3)通过反射创建实例对象
• 调用无参构造器创建实例(默认方式)
Class<?> clazz = Class.forName("com.example.Student");
Student student = (Student) clazz.newInstance(); // 底层调用无参构造器
注意:如果类没有无参构造器,会抛出InstantiationException。
• 调用有参构造器创建实例
// 1. 获取指定参数类型的构造器
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// 2. 传入参数创建实例
Student student = (Student) constructor.newInstance("张三", 18);
(4)通过反射调用方法
// 1. 获取方法对象(方法名+参数类型)
Method studyMethod = clazz.getDeclaredMethod("study", String.class);
// 2. 解除私有方法的访问权限限制
studyMethod.setAccessible(true);
// 3. 调用方法(实例对象 + 方法参数)
studyMethod.invoke(student, "Java反射");
5. 反射的优缺点
• 优点:实现代码的动态性与通用性,是Spring、MyBatis等框架实现底层逻辑的核心技术,大幅提升程序的灵活性。
• 缺点:破坏类的封装性,可读性差;绕过编译期语法检查,错误只能在运行时暴露;性能低于直接调用,频繁反射会影响程序效率。
二、设计模式基础
1. 设计模式概述
设计模式是软件开发中反复出现问题的通用解决方案,是前人经验的总结,能提升代码的可复用性、可维护性和扩展性。Java中共有23种经典设计模式,本次重点学习单例模式与工厂模式。
2. 单例模式(Singleton Pattern)
单例模式确保一个类在JVM中只有一个实例对象,并提供全局访问入口,常用于配置类、连接池等场景。
(1)饿汉式单例
public class HungrySingleton {
// 类加载时直接创建实例
private static final HungrySingleton instance = new HungrySingleton();
// 私有构造器,禁止外部实例化
private HungrySingleton() {}
// 全局访问入口
public static HungrySingleton getInstance() {
return instance;
}
}
• 优点:实现简单,线程安全
• 缺点:类加载时就创建实例,占用内存,可能造成资源浪费
(2)懒汉式单例(线程安全版)
public class LazySingleton {
// 延迟初始化实例
private static LazySingleton instance;
private LazySingleton() {}
// 加锁保证线程安全
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
• 优点:延迟加载,节省内存
• 缺点:加锁导致多线程环境下效率较低
(3)静态内部类单例(推荐写法)
public class InnerClassSingleton {
private InnerClassSingleton() {}
// 静态内部类,JVM加载时不会初始化
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
• 优点:结合饿汉式与懒汉式的优点,线程安全且延迟加载,无性能损耗
3. 工厂模式(Factory Pattern)
工厂模式专门处理对象的创建逻辑,将对象创建与业务逻辑分离,提升代码的可维护性,结合反射可实现通用工厂。
(1)核心作用
将对象的创建逻辑封装在工厂类中,业务代码无需直接使用new创建对象,降低耦合度。
(2)结合反射的通用工厂实现
public class ObjectFactory {
// 根据全限定类名创建对象
public static Object createObject(String className) throws Exception {
Class<?> clazz = Class.forName(className);
return clazz.newInstance();
}
}
通过传入不同的全限定类名,即可创建不同类的实例,实现工厂的通用性。
4. Properties配置文件结合工厂模式
利用Properties读取配置文件,实现对象创建的解耦,是框架中常见的实现方式:
// 1. 创建Properties对象
Properties props = new Properties();
// 2. 加载配置文件
props.load(new FileInputStream("config.properties"));
// 3. 获取配置文件中的类全限定名
String className = props.getProperty("student.class");
// 4. 通过工厂创建对象
Student student = (Student) ObjectFactory.createObject(className);
• Properties是Hashtable的子类,默认键值对均为String类型
• load()方法可自动解析配置文件,按=或:分割键值对
💡 补充:反射破坏单例模式的问题
反射可以通过getConstructor()调用私有构造器,创建多个单例对象,破坏单例的唯一性。解决方案:在构造器中判断实例是否已存在,若存在则抛出异常:
private InnerClassSingleton() {
if (SingletonHolder.INSTANCE != null) {
throw new RuntimeException("单例模式不允许重复创建实例!");
}
}
