一、反射?
Java反射(Reflection)是Java语言的一个强大特性,允许在运行时动态地获取类的信息、调用类的方法、操作类的属性等。反射机制使得程序可以检查和修改运行时行为,这对于某些框架、库和工具非常有用,例如JUnit、Spring等。
反射和正常实例化
反射(Reflection)和正常实例化对象在Java中是两种不同的对象创建和操作方式,它们有以下几个主要区别:
实例化方式:
- 正常实例化:通常使用关键字
new
直接实例化一个类的对象,例如ClassName obj = new ClassName();
。 - 反射实例化:通过反射机制可以在运行时动态地实例化对象,即在没有预先知道类名的情况下,通过类的完全限定名(字符串形式)来创建对象。
获取类信息:
- 正常实例化:在编译时已经知道类的类型,直接通过类名访问类的成员变量和方法。
- 反射实例化:可以在运行时动态获取类的信息,包括类的构造方法、成员变量、方法等,并可以通过反射调用这些方法。
灵活性:
- 正常实例化:编译时确定类和方法的调用,通常适用于静态环境和稳定的业务逻辑。
- 反射实例化:适用于需要在运行时根据条件决定具体调用哪个类或方法,或者需要动态加载类的情况。
性能:
- 正常实例化:直接通过关键字
new
创建对象通常比较高效,因为编译器可以进行优化。 - 反射实例化:反射涉及到动态查找类、构造方法和方法,并且涉及到类型转换,因此相比正常实例化效率较低。
访问权限:
- 正常实例化:可以根据类的访问修饰符直接访问类的成员(公共、私有、受保护等)。
- 反射实例化:可以突破类的访问权限限制,例如访问私有构造方法、私有变量等,但这种访问是通过反射提供的方法实现的,并不是直接的访问。
1. 获取Class对象
如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。Java 提供了四种方式获取 Class 对象:
1. 知道具体类的情况下可以使用:类名.class
Class alunbarClass = TargetObject.class;
但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化
2. 通过 Class.forName()
传入类的全路径获取:Class.forName("全限定类名")
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
3. 通过对象实例instance.getClass()
获取:对象.getClass()
TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();
4. 通过类加载器xxxClassLoader.loadClass()
传入类路径获取:
ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行
2. 创建对象
可以使用反射动态创建对象:
Class<?> clazz = Class.forName("java.lang.String"); Object obj = clazz.getDeclaredConstructor().newInstance();
3. 访问字段
可以通过反射访问和修改类的字段:
Class<?> clazz = Person.class; Field field = clazz.getDeclaredField("name"); field.setAccessible(true); // 如果字段是私有的,需要设置为可访问 Object value = field.get(personInstance); // 获取字段值 field.set(personInstance, "New Name"); // 设置字段值
4. 调用方法
可以通过反射调用类的方法:
Class<?> clazz = Person.class; Method method = clazz.getMethod("sayHello"); method.invoke(personInstance); Method methodWithArgs = clazz.getMethod("greet", String.class); methodWithArgs.invoke(personInstance, "World");
5. 获取构造函数
可以使用反射获取和调用构造函数:
Class<?> clazz = Person.class; Constructor<?> constructor = clazz.getConstructor(String.class, int.class); Object obj = constructor.newInstance("John", 30);
6. 获取接口和父类
可以使用反射获取类实现的接口和父类:
Class<?> clazz = Person.class; // 获取所有接口 Class<?>[] interfaces = clazz.getInterfaces(); for (Class<?> i : interfaces) { System.out.println("Interface: " + i.getName()); } // 获取父类 Class<?> superClass = clazz.getSuperclass(); System.out.println("Superclass: " + superClass.getName());
二、设计模式
设计模式是软件工程中常用的解决问题的模板,它们提供了经过验证的解决方案,可以帮助开发人员解决常见的设计问题并提高代码的可重用性、可维护性和可扩展性。下面介绍几种常见的Java设计模式:
1. 创建型模式(Creational Patterns)
这些模式关注对象的创建过程,帮助确保系统在实例化对象时更加灵活和可控。
-
工厂模式(Factory Method Pattern): 定义一个创建对象的接口,但让子类决定实例化哪个类。工厂方法模式使一个类的实例化延迟到其子类。
-
抽象工厂模式(Abstract Factory Pattern): 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
-
单例模式(Singleton Pattern): 确保一个类只有一个实例,并提供一个全局访问点。
-
建造者模式(Builder Pattern): 将一个复杂对象的构建过程和它的表示分离,使得同样的构建过程可以创建不同的表示。
-
原型模式(Prototype Pattern): 用于创建重复的对象,通过复制原型对象来创建新对象。
2. 结构型模式(Structural Patterns)
这些模式处理类和对象的组合,帮助构建更大的结构。
-
适配器模式(Adapter Pattern): 将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能在一起工作的类可以一起工作。
-
桥接模式(Bridge Pattern): 将抽象部分与它的实现部分分离,使它们可以独立地变化。
- 组合模式(Composite Pattern):将对象组合成树形结构以表示部分整体层次结构。
-
装饰者模式(Decorator Pattern): 动态地给一个对象添加一些额外的职责,就扩展功能而言,装饰模式比生成子类方式更为灵活。
-
外观模式(Facade Pattern): 为子系统中的一组接口提供一个统一的接口,外观模式定义了一个更高层的接口,使子系统更易于使用。
-
享元模式(Flyweight Pattern): 通过共享技术有效地支持大量细粒度的对象。
- 代理模式(Proxy Pattern):在客户端和目标对象之间插入一个代理对象,以控制对目标对象的访问。
3. 行为型模式(Behavioral Patterns)
这些模式关注对象之间的通信、算法的分配等行为。
-
模板模式(Template Method Pattern): 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
-
策略模式(Strategy Pattern): 定义一系列算法,将每一个算法封装起来,并且使它们可以互换。
-
观察者模式(Observer Pattern): 定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
-
迭代器模式(Iterator Pattern): 提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
-
责任链模式(Chain of Responsibility Pattern): 为解除请求的发送者和接收者之间的耦合,而使多个对象都有机会处理这个请求。
-
命令模式(Command Pattern): 将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化。
-
访问者模式(Visitor Pattern): 将作用于对象结构中的各元素的操作分离出来,使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
-
备忘录模式(Memento Pattern): 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
- 状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为。
- 中介者模式(Mediator Pattern):用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- 解释器模式(Interpreter Pattern):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。