1.1 反射机制
反射机制的作用和功能以及获取Class实例的方法
1.说白了就是可以对任意类的方法,对象,属性,构造器,变量进行操作(私有属性也可以)
2.反射相关的API:
java.lang.Class:代表一个类 java.lang.reflect.Method:代表类的方法 java.lang.reflect.Field:代表类的成员变量 java.lang.reflect.Constructor:代表类的构造器
3.获取Class的实例的理解和方式(四种方法)
理解: 哪些类型可以有Class对象?
-
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
-
接口
-
数组
-
枚举
-
注解
-
基本数据类型
-
void
方法一:调用运行时类的属性:.class Class clazz1 = Person.class; 方法二:通过运行时类的对象,调用getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); 方法三:调用Class的静态方法:forName(String classPath) Class clazz3= Class.forName ("路径地址" );//使用频率多 方法四:使用类的加载器:ClassLoader ClassLoader classLoader =类.class.getClassLoader(); Class clazz4 = classLoader.loadClass("目标类的地址")
反射机制的优缺点
优点:
- 灵活性:可以在运行时动态地创建和操作对象,适用于需要灵活性和扩展性的场景。
- 调试和测试:可以方便地获取和修改类的内部状态,便于调试和测试。
缺点:
- 性能开销:反射操作比直接调用方法或访问字段要慢,因为涉及到动态解析和安全检查。
- 安全性:反射可以绕过访问修饰符的限制,可能会破坏类的封装性。
- 可读性:反射代码通常比直接调用方法或访问字段的代码更复杂,可读性较差。
1.2 了解:类的加载过程
1.3ClassLoader具体用法
使用ClassLoader加载配置文件Properties
ClassLoader与反射机制的应用
1.4通过反射创建运行时类的对象
举例体会反射的动态性
1.5获取运行时类的指定结构
提供具有丰富内容的Person
类
//接口
public interface MyInterface {
void info();
}
//注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
//父类
public class Creature<T> implements Serializable {
private char gender;
public double weight;
private void breath(){
System.out.println("生物呼吸");
}
public void eat(){
System.out.println("生物吃东西");
}
}
//Person类
@MyAnnotation(value="hi")
public class Person extends Creature<String> implements Comparable<String>,MyInterface{
private String name;
int age;
public int id;
public Person(){}
@MyAnnotation(value="abc")
private Person(String name){
this.name = name;
}
Person(String name,int age){
this.name = name;
this.age = age;
}
@MyAnnotation
private String show(String nation){
System.out.println("我的国籍是:" + nation);
return nation;
}
public String display(String interests,int age) throws NullPointerException,ClassCastException{
return interests + age;
}
@Override
public void info() {
System.out.println("我是一个人");
}
@Override
public int compareTo(String o) {
return 0;
}
private static void showDesc(){
System.out.println("我是一个可爱的人");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
1. 获取当前运行时类的属性结构
方法 | 作用 |
---|---|
public Constructor<T>[] getConstructors() | 返回此 Class 对象所表示的类的所有public构造方法。 |
public Constructor<T>[] getDeclaredConstructors() | 返回此 Class 对象表示的类声明的所有构造方法。 |
-
Field方法中:
方法 | 作用 |
---|---|
public int getModifiers() | 以整数形式返回此Field 的修饰符 |
public Class<?> getType() | 得到Field 的属性类型 |
public String getName() | 返回Field 的名称 |
2. 获取当前运行时类的方法结构
方法 | 作用 |
---|---|
public Method[] getMethods() | 返回此Class 对象所表示的类或接口的public 的方法 |
public Method[] getDeclaredMethods() | 返回此Class 对象所表示的类或接口的全部方法 |
-
Method类中:
方法 | **作用 |
---|---|
public Class<?> getReturnType() | 取得全部的返回值 |
public Class<?>[] getParameterTypes() | 取得全部的参数 |
public int getModifiers() | 取得修饰符 |
public Class<?>[] getExceptionTypes() | 取得异常信息 |
3. 获取当前运行时类的构造器结构
方法 | 作用 |
---|---|
public Constructor<T>[] getConstructors() | 返回此 Class 对象所表示的类的所有public 构造方法 |
public Constructor<T>[] getDeclaredConstructors() | 返回此 Class 对象表示的类声明的所有构造方法。 |
-
Constructor类中:
方法 | 作用 |
---|---|
public int getModifiers() | 取得修饰符 |
public String getName() | 取得方法名称 |
public Class<?>[] getParameterTypes() | 取得参数的类型 |
1.6调用运行时类的指定结构(了解)
1.关于setAccessible方法的使用
-
Method和Field、Constructor对象都有setAccessible()方法。
-
setAccessible启动和禁用访问安全检查的开关。
-
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
-
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被 调用,那么请设置为true,使得原本无法访问的私有成员也可以访问,参数值为false则指示反射的对象应该实施Java语言访问检查。
2. 调用运行时类中指定的属性:
在反射机制中,可以直接通过Field
类操作类中的属性,通过Field
类提供的set()
和 get()
方法就可以完成设置和取得属性内容的操作。
方法 | 作用 |
---|---|
public Field getField(String name) | 返回此Class 对象表示的类或接口的指定的public 的Field |
public Field getDeclaredField(String name) | 返回此Class 对象表示的类或接口的指定的Field |
在Field中:
方法 | 作用 |
---|---|
public Object get(Object obj) | 取得指定对象obj 上此Field 的属性内容 |
public void set(Object obj,Object value) | 设置指定对象obj 上此Field 的属性内容 |
3. 调用运行时类中的指定的方法
通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得 一个Method对象,并设置此方法操作时所需要的参数类型。 2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中 传递要设置的obj对象的参数信息。
bject invoke(Object obj, Object … args) 说明: ⭕ Object 对应原方法的返回值,若原方法无返回值,此时返回null
⭕ 若原方法若为静态方法,此时形参Object obj可为null
⭕ 若原方法形参列表为空,则Object[] args为null
⭕ 若原方法声明为private,则需要在调用此invoke()方法前,显式调用 方法对象的setAccessible(true)方法,将可访问private的方法。
4. 调用运行时类中的指定的构造器
*
如何调用运行时类中的指定的构造器
*/
@Test
public void testConstructor() throws Exception {
Class clazz = Person.class;
//private Person(String name)
/*
1.获取指定的构造器
getDeclaredConstructor():参数:指明构造器的参数列表
*/
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保证此构造器是可访问的
constructor.setAccessible(true);
//3.调用此构造器创建运行时类的对象
Person per = (Person) constructor.newInstance("Tom");
System.out.println(per);
}
}
1.7代理
1.静态代理的理解:
在Java中,静态代理是一种设计模式,用于在不修改目标对象的情况下,通过代理对象来控制对目标对象的访问。静态代理的特点是在编译时就已经确定了代理类和目标类的关系,因此称为静态代理。
2.静态代理的基本思想是:
-
定义接口:代理类和目标类都实现同一个接口。
-
创建目标对象:目标对象是实际执行任务的对象。
-
创建代理对象:代理对象包含一个目标对象的引用,并在调用目标对象的方法前后执行一些额外的逻辑
1. 定义接口
public interface Subject {
void request();
}
CopyInsert
接口 Subject:定义了一个方法 request(),这个方法将在目标对象和代理对象中实现。接口是代理模式的核心,因为它确保了代理对象和目标对象具有相同的行为。
2. 创建目标对象
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
CopyInsert
目标对象 RealSubject:实现了 Subject 接口,并提供了 request() 方法的具体实现。在这个例子中,RealSubject 只是简单地打印一条消息,表示它正在处理请求。
3. 创建代理对象
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
// 在调用目标对象的方法前执行一些逻辑
System.out.println("ProxySubject: Preparing to handle request.");
// 调用目标对象的方法
realSubject.request();
// 在调用目标对象的方法后执行一些逻辑
System.out.println("ProxySubject: Finished handling request.");
}
}
CopyInsert
代理对象 ProxySubject:也实现了 Subject 接口,并包含一个 RealSubject 对象的引用。
构造方法:ProxySubject(RealSubject realSubject) 用于初始化代理对象,将目标对象传递给代理对象。
request() 方法:在调用目标对象的 request() 方法前后,代理对象可以执行一些额外的逻辑。在这个例子中,代理对象在调用目标对象的方法前打印一条准备消息,在调用目标对象的方法后打印一条完成消息。
4. 使用代理对象
public class Main {
public static void main(String[] args) {
// 创建目标对象
RealSubject realSubject = new RealSubject();
// 创建代理对象,并将目标对象传递给代理对象
ProxySubject proxySubject = new ProxySubject(realSubject);
// 通过代理对象调用目标对象的方法
proxySubject.request();
}
}
主类 Main:包含 main 方法,用于演示如何使用代理对象。
创建目标对象:RealSubject realSubject = new RealSubject(); 创建一个 RealSubject 对象。
创建代理对象:ProxySubject proxySubject = new ProxySubject(realSubject); 创建一个 ProxySubject 对象,并将目标对象传递给代理对象。
通过代理对象调用目标对象的方法:proxySubject.request(); 通过代理对象调用 request() 方法。在这个过程中,代理对象会在调用目标对象的方法前后执行一些额外的逻辑。
3.动态代理
动态代理是Java中一种高级编程技术,它允许你在运行时创建一个代理对象,这个代理对象可以用来代替另一个对象。动态代理主要用于在不修改原有代码的情况下,增加或修改对象的行为。
1.基本概念
-
代理对象:一个代理对象是客户端和真实对象之间的中介。客户端通过代理对象来访问真实对象。
-
真实对象:这是你实际想要操作的对象。
-
InvocationHandler:这是一个接口,你需要实现它来定义代理对象在调用方法时应该执行的操作。
2.实现步骤
-
定义接口:首先,你需要定义一个接口,这个接口包含了你希望代理对象能够调用的方法。
-
实现接口:创建一个类来实现这个接口,这个类就是你的真实对象。
-
创建InvocationHandler:实现
InvocationHandler
接口,重写invoke
方法,在这个方法中定义代理对象在调用方法时应该执行的操作。 -
创建代理对象
使用Proxy.newProxyInstance方法来创建代理对象。这个方法需要三个参数:
-
类加载器(ClassLoader)
-
代理对象实现的接口数组
-
InvocationHandler实例
-
以下是一个简单的动态代理示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface Hello {
void sayHello();
}
// 实现接口的目标类
class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello, world!");
}
}
// 实现InvocationHandler接口
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method invocation");
Object result = method.invoke(target, args);
System.out.println("After method invocation");
return result;
}
}
public class DynamicProxyDemo {
public static void main(String[] args) {
// 创建目标对象
HelloImpl target = new HelloImpl();
// 创建InvocationHandler实例
MyInvocationHandler handler = new MyInvocationHandler(target);
// 创建代理对象
Hello proxy = (Hello) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
// 调用代理对象的方法
proxy.sayHello();
}
}
CopyInsert
运行结果
Before method invocation
Hello, world!
After method invocation
动态代理是Java中一种强大的机制,可以在运行时动态地创建代理对象,并在不修改原有代码的情况下增强或修改对象的行为。通过结合反射机制和InvocationHandler接口,可以实现灵活的方法调用处理逻辑。
1.8 简洁性总结全文
反射机制(Reflection)是Java语言的一项强大特性,它允许程序在运行时检查和操作类的结构、方法、字段等信息。通过反射,程序可以动态地创建对象、调用方法、访问字段,甚至可以修改类的行为。反射机制主要通过java.lang.reflect
包中的类来实现。
反射机制的主要用途
-
动态加载类:在运行时加载类,而不是在编译时。
-
获取类的信息:获取类的构造方法、方法、字段等信息。
-
创建对象:通过类的构造方法动态创建对象。
-
调用方法:通过方法名动态调用对象的方法。
-
访问字段:通过字段名动态访问和修改对象的字段。
-
实现动态代理:结合
InvocationHandler
接口,实现动态代理模式。
反射机制的主要类和接口
-
Class
:表示类的实体,在运行的Java应用程序中表示类和接口。 -
Field
:表示类的字段(成员变量)。 -
Method
:表示类的方法。 -
Constructor
:表示类的构造方法。 -
Modifier
:表示类和成员访问修饰符。
反射机制的基本操作
1. 获取Class
对象
有三种主要方法可以获取Class
对象:
-
使用
类名.class
:Class<?> clazz = String.class;
-
使用
对象.getClass()
:String str = "Hello"; Class<?> clazz = str.getClass();
-
使用
Class.forName(String className)
:Class<?> clazz = Class.forName("java.lang.String");
2. 获取类的信息
-
获取类的构造方法:
Constructor<?> constructor = clazz.getConstructor(String.class);
-
获取类的方法:
Method method = clazz.getMethod("toString");
-
获取类的字段:
Field field = clazz.getField("value");
3. 创建对象
-
使用无参构造方法创建对象:
Object obj = clazz.newInstance();
-
使用有参构造方法创建对象:
Constructor<?> constructor = clazz.getConstructor(String.class); Object obj = constructor.newInstance("Hello");
4. 调用方法
-
调用无参方法:
Method method = clazz.getMethod("toString"); Object result = method.invoke(obj);
-
调用有参方法:
Method method = clazz.getMethod("concat", String.class); Object result = method.invoke(obj, " World");
5. 访问字段
-
获取字段值:
Field field = clazz.getField("value"); Object value = field.get(obj);
-
设置字段值:
Field field = clazz.getField("value"); field.set(obj, "New Value");