Java9-反射(机制、ClassLoader、Annotation、代理设计)

1、认识反射机制

(1)概念

  • 在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

(2)功能

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。
  • 生成动态代理。

(3)Class类对象的三种实例化模式

  • 通过类名获取: 类名.class
      Class classa = null;
      //1.通过类名
      classa = Person.class;
  • 通过对象获取:对象名.getClass()
       Class classa = null;
       //2.通过对象名
       //不知道对象类型的时候使用
       Person person = new Person();
       classa = person.getClass();
       //如果传进来是一个Object类,这种做法就是应该的
       Object obj = new Person();
       classa = obj.getClass();
  • 通过全类名获取:Class.forName(全类名)
       Class classa = null;
       //3.通过全类名(会抛出异常)
       String className=" com.atguigu.java.fanshe.Person";
       classa = Class.forName(className);     

2、Class类的常用方法

  • getName():获得类的完整名字。 getFields():获得类的public类型的属性。

  • getDeclaredFields():获得类的所有属性。

  • getMethods():获得类的public类型的方法。

  • getDeclaredMethods():获得类的所有方法。

  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。

  • getConstrutors():获得类的public类型的构造方法。

  • getConstrutor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。

  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

示例1:方法-Method

public class ReflectionTest {
    @Test
    public void testMethod() throws Exception{
        Class clazz = Class.forName("com.atguigu.java.fanshe.Person");
        
        //        //1.获取方法      //  1.1 获取取clazz对应类中的所有方法--方法数组(一)
        //     不能获取private方法,且获取从父类继承来的所有方法
        Method[] methods = clazz.getMethods();
        for(Method method:methods){
            System.out.print(" "+method.getName());
        }
        System.out.println();
        
        //
        //  1.2.获取所有方法,包括私有方法 --方法数组(二)
        //  所有声明的方法,都可以获取到,且只获取当前类的方法
        methods = clazz.getDeclaredMethods();
        for(Method method:methods){
            System.out.print(" "+method.getName());
        }
        System.out.println();
        
        //
        //  1.3.获取指定的方法
        //  需要参数名称和参数列表,无参则不需要写
        //  对于方法public void setName(String name) {  }
        Method method = clazz.getDeclaredMethod("setName", String.class);
        System.out.println(method);
        //  而对于方法public void setAge(int age) {  }
        method = clazz.getDeclaredMethod("setAge", Integer.class);
        System.out.println(method);
        //  这样写是获取不到的,如果方法的参数类型是int型
        //  如果方法用于反射,那么要么int类型写成Integer: public void setAge(Integer age) {  }     //  要么获取方法的参数写成int.class
        
        //
        //2.执行方法
        //  invoke第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数
        Object obje = clazz.newInstance();
        method.invoke(obje,2);
    //如果一个方法是私有方法,第三步是可以获取到的,但是这一步却不能执行    //私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true);    }
}

示例2:字段-Field

@Test
    public void testField() throws Exception{
        String className = "com.atguigu.java.fanshe.Person";        
        Class clazz = Class.forName(className); 
        
        //1.获取字段
      //  1.1 获取所有字段 -- 字段数组
        //     可以获取公用和私有的所有字段,但不能获取父类字段
        Field[] fields = clazz.getDeclaredFields();
        for(Field field: fields){
            System.out.print(" "+ field.getName());
        }
        System.out.println();
        
        //  1.2获取指定字段
        Field field = clazz.getDeclaredField("name");
        System.out.println(field.getName());
        
        Person person = new Person("ABC",12);
        
        //2.使用字段
      //  2.1获取指定对象的指定字段的值
        Object val = field.get(person);
        System.out.println(val);
        
        //  2.2设置指定对象的指定对象Field值
        field.set(person, "DEF");
        System.out.println(person.getName());
        
        //  2.3如果字段是私有的,不管是读值还是写值,都必须先调用setAccessible(true)方法
        //     比如Person类中,字段name字段是公用的,age是私有的
        field = clazz.getDeclaredField("age");
        field.setAccessible(true);
        System.out.println(field.get(person));        
    }

示例3:构造器-Constructor

@Test
    public void testConstructor() throws Exception{
        String className = "com.atguigu.java.fanshe.Person";
        Class<Person> clazz = (Class<Person>) Class.forName(className);
        
        //1. 获取 Constructor 对象
        //   1.1 获取全部
        Constructor<Person> [] constructors = 
                (Constructor<Person>[]) Class.forName(className).getConstructors();
        
        for(Constructor<Person> constructor: constructors){
            System.out.println(constructor); 
        }
        
        //  1.2获取某一个,需要参数列表
        Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
        System.out.println(constructor); 
        
        //2. 调用构造器的 newInstance() 方法创建对象
        Object obj = constructor.newInstance("zhagn", 1);                
    }

3、ClassLoader类加载器

(1)加载器简介

  • Class文件由类装载器装载后,在JVM中将形成描述Class结构信息对象,通过该对象可以获知Class的结构信息:如构造函数,属性和方法等。
  • 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。

(2)class.forName()和classLoader区别。

  • class.forName()将类的.class文件加载到jvm中,还会对类进行解释,执行类中static块,还会执行给静态变量赋值的静态方法。Class.forName(name, initialize, loader)带参函数也可控制是否加载static块

  • classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

4、 反射与Annotation

(1)反射取得Annotation信息

  • 获取全部定义的Annotation方法:public Annotation[] getAnnotations()
  • 获取指定的Annotation对象:public T getDeclaredAnnotation(Class annotationClass)
import java.lang.annotation.Annotation;

@FunctionalInterface
@Deprecated
interface IMessage {            //存在两个Annotation注解
    public void send();
}

@SuppressWarnings("serial")
class MessageImpl implements IMessage {
    @Override
    public void send() {
        System.out.println("消息发送...");
    }
}

public class AnnotationDemo {
    public static void main(String[] args) {
        //获取接口上的Annotation信息
        Annotation[] annotations = IMessage.class.getAnnotations();     //获取接口上的所有的Annotation
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }
}

运行结果:

@java.lang.FunctionalInterface()
@java.lang.Deprecated()

并没有获取到注解@SuppressWarnings(“serial”),其原因是该注解无法在程序执行的时候获取。不同的Annotation有他的存在的范围:

  • @Retention(RetentionPolicy.RUNTIME)  //描述的是运行是生效
  • @Retention(RetentionPolicy.SOURCE)   //描述的是在源代码时生效
  • @Retention(RetentionPolicy.CLASS)  //指的是在类定义的时候生效

可以发现"@FunctionalInterface"是在程序运行时生效的Annotation,所以当程序执行的时候可以获取此Annotation。

(2)自定义Annotation

在java中提供有新的语法使用@interface来定义Annotation。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@Retention(RetentionPolicy.RUNTIME)
        //定义Annotation运行时的策略
@interface DefaultAnnotation {       //自定义的Annotation,
    public String title();      //获取数据

    public String url() default "获取数据的默认值";     //获取数据,默认值
}

class Message {
    @DefaultAnnotation(title = "sendMessage")   //title不具备默认值,因此必须去显示的定义
    public void send(String msg) {
        System.out.println("[消息发送]" + msg); //
    }
}

public class MyAnnotationDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Method method = Message.class.getDeclaredMethod("send", String.class);  //获取指定的方法
        DefaultAnnotation annotation = method.getAnnotation(DefaultAnnotation.class);//获取指定的Annotation1
        String title = annotation.title();      //直接调用Annotation中的方法
        String url = annotation.url();
        System.out.println(title + " " + url); //sendMessage 获取数据的默认值
        String msg = annotation.title() + " " + url;
        method.invoke(Message.class.getDeclaredConstructor().newInstance(),msg);        //利用反射实现消息的发送
    }
}

(3)工厂设计模式与Annotation整合

工厂设计模式

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface IMessage {
    public void send(String msg);
}

class MessageImpl implements IMessage {
    @Override
    public void send(String msg) {
        System.out.println("[消息发送]" + msg);
    }
}

class MessageProxy implements InvocationHandler {
    private Object target;


    public Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    public boolean connect() {
        System.out.println("[代理操作]进行消息发送通道的连接");
        return true;
    }

    public void close() {
        System.out.println("[代理操作]关闭连接通道.");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (this.connect()) {
                return method.invoke(this.target, args);
            } else {
                throw new Exception("[error]消息无法进行发送");
            }
        } finally {
            this.close();
        }
    }
}

class Factory {
    private Factory() {
    }
    public static <T> T getInstance(Class<T> tClass) {       //直接返回一个实例化的操作对象
        try {
            return (T) new MessageProxy().bind(tClass.getDeclaredConstructor().newInstance());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
class MessageService{
    private IMessage message;

    public MessageService() {
        this.message = Factory.getInstance(MessageImpl.class);
    }
    public void send(String msg){
        this.message.send(msg);
    }
}
public class AnnotationFactoryDemo {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
       MessageService service = new MessageService();
       service.send("hello");

    }
}

每次都需要在MessageService类中给出MessageImpl.class的声明,使用注解形式来简化代码。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author : S K Y
 * @version :0.0.1
 */
interface IMessage {
    public void send(String msg);
}

class MessageImpl implements IMessage {
    @Override
    public void send(String msg) {
        System.out.println("[消息发送]" + msg);
    }
}

class MessageProxy implements InvocationHandler {
    private Object target;


    public Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    public boolean connect() {
        System.out.println("[代理操作]进行消息发送通道的连接");
        return true;
    }

    public void close() {
        System.out.println("[代理操作]关闭连接通道.");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (this.connect()) {
                return method.invoke(this.target, args);
            } else {
                throw new Exception("[error]消息无法进行发送");
            }
        } finally {
            this.close();
        }
    }
}

class Factory {
    private Factory() {
    }
    public static <T> T getInstance(Class<T> tClass) {       //直接返回一个实例化的操作对象
        try {
            return (T) new MessageProxy().bind(tClass.getDeclaredConstructor().newInstance());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
@Retention(RetentionPolicy.RUNTIME)
@interface UseMessage{
    public Class<?> thisClass();
}
@UseMessage(thisClass = MessageImpl.class)
class MessageService{
    private IMessage message;

    public MessageService() {
        UseMessage annotation = MessageService.class.getAnnotation(UseMessage.class);
        this.message = (IMessage) Factory.getInstance(annotation.thisClass());
    }
    public void send(String msg){
        this.message.send(msg);
    }
}
public class AnnotationFactoryDemo {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
       MessageService service = new MessageService();
       service.send("hello");

    }
}

依据注解来修改整个程序的功能实现,对于面向接口的配置处理可以直接利用Annotation的属性完成控制,从而使得整体代码变得简洁。

执行结果:

[代理操作]进行消息发送通道的连接
[消息发送]hello
[代理操作]关闭连接通道.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值