获取某个类或某个对象所对应的 Class 对象的常用的 3 种方式:
a) 使用 Class 类的静态方法 forName:Class.forName(“java.lang.String”);
b) 使用类的.class 语法:String.class;
c) 使用对象的 getClass()方法:String s = “aa”; Class<?> clazz = s.getClass();
d) ClassLoader.getSystemClassLoader().loadClass(“month4.反射.Object0505”);
Class.forName与loadClass的区别
Java中Class.forName和classloader都可以用来对类进行加载。
Class.forName(“className”);
其实这种方法调运的是:Class.forName(className, true, ClassLoader.getCallerClassLoader())方法
参数一:className,需要加载的类的名称。
参数二:true,是否对class进行初始化(需要initialize)
参数三:classLoader,对应的类加载器
ClassLoader.laodClass(“className”);
其实这种方法调运的是:ClassLoader.loadClass(name, false)方法
参数一:name,需要加载的类的名称
参数二:false,这个类加载以后是否需要去连接(不需要linking)
可见Class.forName除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
若想通过类的不带参数的构造方法来生成对象,我们有两种方式:
a) 先获得 Class 对象,然后通过该 Class 对象的 newInstance()方法直接生成即可:
Class<?> classType = String.class; Object obj = classType.newInstance();
b) 先获得 Class 对象,然后通过该对象获得对应的 Constructor 对象,再通过该 Constructor 对的 newInstance()方法生成:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});
若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[{String.class, int.class});
Object obj = cons.newInstance(new Object[]{“hello”, 3});
代理模式
静态代理
https://blog.csdn.net/qq_33321609/article/details/87859654
动态代理模式
具体步骤是:
实现InvocationHandler接口创建自己的调用处理器
给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
核心:使用Proxy
package month4.反射.动态代理;
/*
* 抽象角色
* 上一个例子我们可以用接口也可以用抽象类
* 但是在动态代理中我们必须用接口来实现,因为在方法 newProxyInstance()参数传递的接口的数组
*/
public interface Subject {
public void request();
public void requestB();
}
/*
* 真实角色
*/
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("From the RealSupject.");
}
@Override
public void requestB() {
System.out.println("From the RealSupjectB");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
* 动态代理角色
* 使用动态代理必须先实现 InvocationHandler 这个接口
*/
public class DynamicSubject implements InvocationHandler {
//代理角色存在一个对真实对象的引用,动态代理可以代理不同的真实对象,所以这边用Object类型以接受所有对象
private Object sub;
public DynamicSubject(Object obj){
this.sub = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before calling " + method);
//使用反射调,参数sub表示用构造方法传过来的 Object sub 这个对象.
method.invoke(sub, args);
System.out.println(args == null);
System.out.println("after calling " + method);
return null;
}
}
测试
package month4.反射.动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject(); // 代理谁就生成谁
InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();
// 下面的代码一次性生成代理
Subject subject = (Subject)Proxy.newProxyInstance(classType.getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
subject.request();
System.out.println();
subject.requestB();
System.out.println(subject.getClass());
}
}
通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理
注解
@Retention(RetentionPolicy.RUNTIME)
注解在哪些时候相应
RetentionPolicy枚举有三个
SOURCE//注释将被编译器丢弃(有道翻译) Override
CLASS//默认:注释由编译器记录在类文件中,但不需要由VM在运行时保留(有道翻译);使用者:NotNull
RUNTIME//注释由编译器记录在类文件中,并在运行时由VM保存,因此可以反射性地读取它们(有道翻译);使用者:Autowired
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
注解可以注解什么
ElementType枚举
ANNOTATION_TYPE: 注解只能修饰注解,不能修饰其他的东西
CONSTRUCTOR: 注解只能修饰构造方法
FIELD: 注解只能修饰属性(成员变量)
LOCAL_VARIABLE: 注解只能修饰局部变量
METHOD: 注解只能修饰方法
PACKAGE: 注解只能修饰包
PARAMETER: 注解只能修饰方法的参数
TYPE: 注解只能修饰类、接口、枚举
@Documented
如果注解使用该注解
生成完成打开生成API文档,查看我们定义的method()方法如下图所示,已经被标注了注解信息了。
读取注解的例子
注解
package month4.反射.注解;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String hello() default "ahuier";
String world();
}
使用注解的类
package month4.反射.注解;
@MyAnnotation(hello = "beijing", world = "shanghai")
public class MyTest {
@MyAnnotation(hello = "tianjin", world = "shangdi")
@Deprecated
@SuppressWarnings("unchecked") //一个方法可以有多个注解所修饰
public void output() {
System.out.println("output something!");
}
}
测试
package month4.反射.注解;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class MyReflection {
public static void main(String[] args) throws Exception {
MyTest myTest = new MyTest();
Class<MyTest> c = MyTest.class;
/*
* 通过反射获取method对象,由于Method实现了AnnotatedElement接口,所以它可以调用获取注解信息的API来获得注解信息。
*/
Method method = c.getMethod("output", new Class[]{});
/*
* 查看JDk Doc 文档,在Method类的父类AccessibleObject类中isAnnotationPresent()
* 判断某一个方法上面是否存在某一个注解
*/
if (method.isAnnotationPresent(MyAnnotation.class)) {
method.invoke(myTest, new Object[]{}); //如果存在这个注解,则去调用MyTest类中output()方法
/*
* 查看JDK Doc中Method类的public <T extends Annotation> T getAnnotation(Class<T> annotationClass)方法。
* 如果注解存在,则放回这个注解,否则返回为空
*/
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
String hello = myAnnotation.hello(); //获取 MyAnnotation中的 hello()注解信息
String world = myAnnotation.world(); //获取 MyAnnotation中的 world()注解信息
System.out.println(hello + ", " + world);
}
/*
* 查看JDK Doc文档中 public Annotation[] getAnnotations()
* 放回所有在特定元素上的注解,如果这个元素上没有注解,则返回一个数组长度为0的数组,而非返回空的数组,所以不需要判断数组是否为空,直接遍历数组
*/
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
/*
* 查看JDK文档Annotation接口的 Class<? extends Annotation> annotationType()
* 返回它所对应的Class对象
* 以下输出:
* com.ahuier.annotation.MyAnnotation
* java.lang.Deprecated
* 原因是MyTest中的output()上面的定义的三个注解中有两个注解RetentionPolicy为“RUNTIME”,如果是“CLASS” 和 "SOURCE",则得不到的
*/
System.out.println(annotation.annotationType().getName());
}
}
}
很明显,我们只能读到加了@Retention(RetentionPolicy.RUNTIME)的注解