一篇文章搞定java反射,回调,事件委托附实践代码

文章目录

      • 反射
        • 1.什么是反射?
        • 2. 为什么使用反射?
        • 3. 优缺点和使用场景
      • 回调
        • 1.是什么?
        • 2.有什么优势,场景
        • 3.回调与接口,抽象类
      • 委托
        • 1.是什么?
        • 2.由来?
        • 3.java委托事件:
      • 抽象
        • 1.甄别好文章:
        • 2.学会看文章
      • 参考

反射

1.什么是反射?

官方:主要指应用程序访问、检测、修改自身状态与行为的能力。JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法
个人理解:java 反射机制知道类的结构,能够在程序运行过程中,动态地根据.class文件获取类信息的一种手段,包括类属性,方法,修饰符,接口等信息。

2. 为什么使用反射?

动态开放性质,运行时装配代码,灵活。
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。

3. 优缺点和使用场景

优点:
能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
功能强大
缺点:
性能低
安全限制
程序健壮性,破坏类的封装性


回调

1.是什么?

官方解释:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
个人理解:
这个被传入的、后又被调用的函数就称为回调函数。

简单来讲就是A类调用了B类的方法,然后再B类的方法中返回来又调用了A的方法。

2.有什么优势,场景

灵活,易于扩展。

3.回调与接口,抽象类

public class Main implements InterfaceExample{

    public static void main(String[] args) {
        System.out.println("------接口使用测试--------");
        InterfaceTest test = new InterfaceTest();
        //调用InterfaceTest的handleThings方法,并传递Main的实例
        test.handleThings(new Main());
        System.out.println("------异步回调测试--------");
    }

    @Override   //重写接口方法
    public void sendMessage(String string) {
        System.out.println("接口回调成功,利用 " + string + " 做一些事");
    }

}

//接口也可以写在一个独立的.java文件里
interface InterfaceExample {
    void sendMessage(String string);
}
public class InterfaceTest {

    //注意这里Main实例向上转型,接口变量引用了Main实例
    public void handleThings(InterfaceExample example) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("-----做一些事------");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //回调接口方法
                example.sendMessage("接口传的参数");
            }
        }).start();

    }
}

委托

1.是什么?

委托是一个类型(定义了方法的类型),代表着具有相同参数列表和返回类型的方法的类型,一个委托变量可以搭载多个方法,并且可以使得委托对象所搭载的方法并不属于同一个类,当调用此变量时,可以依次调用所绑定的方法。

委托是一种引用类型,作为引用,指向特定的方法,可以用于将方法作为参数传递给其他方法。

  • 委托类似于 C++ 函数指针
  • 委托允许将方法作为参数进行传递
  • 托可用于定义回调方法。
  • 一个委托可以绑定多个方法

2.由来?

为什么需要委托呢?为了使代码解耦,更具有扩展性,更灵活,具体体现在哪里呢?

举例
在观察者模式中,主题对象和观察者紧密耦合。类图如下:
一篇文章搞定java反射,回调,事件委托附实践代码_第1张图片

代码:
抽象观察者

public abstract class Observer {
    public abstract void update();
}

抽象主题对象
需要知道具体观察者,并且在自己改变时通知观察者对象,让观察者执行update操作。

public class Subject {
    List<Observer> observerList=new ArrayList<Observer>();
    public void add(Observer observer){
        observerList.add(observer);
    }

    public void remove(Observer observer){
        observerList.remove(observer);
    }

    public void notify1(){
        for (Observer observer:observerList) {
            observer.update();
        }
    }
}

具体观察者,重写update操作,并且要知道具体的主题对象

public class ConcreteObserver extends Observer {
    private String name;
    private String observerState;
    private ConcreteSubject subject;

    public ConcreteObserver(String name,ConcreteSubject subject){
        this.name=name;
        this.subject=subject;
    }

    @Override
    public void update() {
        observerState=subject.getSubjectState();
        System.out.println("观察者"+name+"状态是"+observerState);
    }
}

具体主题对象

public class ConcreteSubject extends Subject {
    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;
    }

    private String subjectState;
}

通过上面的分析,虽然可以实现通知的功能,但是有以下不足
(1)观察者需要知道抽象的通知者,和通知者之间紧密耦合,互相知道
(2)通知者是调用的都是观察者的更新操作,如果想通知的时候,让不同的观察者执行自己不同的行为操作,不能满足。

例如老师来了,一个放哨的同学发送通知说:让玩手机的同学放下手机,睡觉的同学醒来,并且让放哨的同学和睡觉玩手机的同学解耦。如何实现?

实现思路
在观察者和通知者之间添加一个中间对象,实现通知和观察者的松耦合
观察者通知中间对象,让中间这个对象管理起来所有的观察者,然后根据不同的观察者对象,执行不同的观察者对象的方法,有没有发现就是委托呢?上面提到委托就是方法的引用,然后去执行方法,下面就分析c#版和java版如何实现。

3.java委托事件:

委托在c#中是一个语言特性,但是在java中没有直接的对应。
在java中可以通过反射拿到对象方法的引用来实现委托。

需求:
热水器加热水,当水的温度达到设定的98的时候,让铃响发出通知告诉主人,并且热水器上的显示器显示水温为98度。热水器和铃,显示器解耦。

首先通知者类Notifier:

public abstract class Notifier {
    private EventHandler eventHandler=new EventHandler();

    public EventHandler getEventHandler() {
        return eventHandler;
    }

    public void setEventHandler(EventHandler eventHandler) {
        this.eventHandler = eventHandler;
    }

    /**
     * 增加listener
     */
    public abstract void addListener(Object o,String methodName,Object... args);


    public abstract void notifyAllListeners();
}

热水器通知者HeaterNotifier:

public class HeaterNotifier extends Notifier {

    private Integer temperature;
    @Override
    public void addListener(Object o, String methodName, Object... args) {
        EventHandler eventHandler=this.getEventHandler();
        eventHandler.addEvent(o,methodName,args);
    }

    @Override
    public void notifyAllListeners() {
        try {
            this.getEventHandler().notifyX();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void boilWater(Integer warningTemperature){
        for (int i = 0; i <100 ; i++) {
            this.temperature=i;
            if (this.temperature>warningTemperature) {
                notifyAllListeners();
                break;
            }
        }
    }
}

这个通知者和我们的观察者模式的通知者是不是很像,只不过观察者模式是通知具体的观察者对象,而这里我们是交给了EventHandler来进行管理。

EventHandler类
在EventHandler中遍历每一个事件对象进行通知,类似观察者模式的通知者

public class HeaterNotifier extends Notifier {

    private Integer temperature;
    @Override
    public void addListener(Object o, String methodName, Object... args) {
        EventHandler eventHandler=this.getEventHandler();
        eventHandler.addEvent(o,methodName,args);
    }

    @Override
    public void notifyAllListeners() {
        try {
            this.getEventHandler().notifyX();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void boilWater(Integer warningTemperature){
        for (int i = 0; i <100 ; i++) {
            this.temperature=i;
            if (this.temperature>warningTemperature) {
                notifyAllListeners();
                break;
            }
        }
    }
}

接下来看Event事件。在每一个Event对象中都保存着监听者对象和方法名称。Event的invoke方法通过反射获取要执行的方法。也就是委托。

public class Event {
    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Object[] getParams() {

        return params;
    }

    public void setParams(Object[] params) {
        this.params = params;
    }

    private Object object;
    private String methodName;
    private Object[] params;

    public Class[] getParamTypes() {
        return paramTypes;
    }

    public void setParamTypes(Class[] paramTypes) {
        this.paramTypes = paramTypes;
    }

    private Class[] paramTypes;

    public Event() {
    }

    public Event(Object o, String methodName, Object... args) {
        this.object = o;
        this.methodName = methodName;
        this.params = args;
        contractParamTypes(this.params);
    }

    private void contractParamTypes(Object[] params) {
        this.paramTypes = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            this.paramTypes[i] = params[i].getClass();
        }
    }

    public void invoke() throws Exception {
        Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
        if (null == method) {
            return;
        }
        method.invoke(this.getObject(), this.getParams());
    }
}

监听者铃AlarmList

public class AlarmListener
{
    public void alarm(Integer data){
        System.out.println("水开了,当前温度是:"+data);
    }
}

监听者显示器DisplayListener

public class DisplayListener {
    public void disPlay(Integer data) {
        System.out.println("主人,水开了,显示的温度是:"+data);
    }
}

灵活扩展体现在了哪里?
假如我想让热水器水烧开的时候也自动关闭电源,这时候我只需要添加一个关闭电源的类,然后在客户端中把监听者添加进去即可。
添加一个TurnOffListener类

public class TurnOffListener {
    public void turnOff(Integer data) {
        System.out.println("水开了,自动关闭电源了,水温度是:"+data );
    }
}

修改客户端:只需添加

 heaterNotifier.addListener(new TurnOffListener(),"turnOff",temperature);
public class HeaterEventTest {
    public static void main(String[] args) {
        Integer temperature=98;
        HeaterNotifier heaterNotifier=new HeaterNotifier();
        heaterNotifier.addListener(new AlarmListener(),"alarm",temperature);
        heaterNotifier.addListener(new DisplayListener(),"disPlay",temperature);
        heaterNotifier.addListener(new TurnOffListener(),"turnOff",temperature);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        heaterNotifier.boilWater(temperature);
    }
}

委托能做什么?
委托就是自己把这件事委托给别人去做。
1、通知者完全不知道坚挺着,监听者也不知道通知者,实现了完全的解耦
2,通知者和观察者之间没有依赖,扩展性好
3. 一次通知执行,可以执行不同类的不同方法。


抽象

1.甄别好文章:

跟购物一样,先看大家的评论以及作者的资历。

2.学会看文章

挑选出好文章之后,先全局看一遍,然后再仔细看,我觉得是需要看懂的,如果看不懂就看下一篇,效率不会太高。看懂的话,再看别的,也能去对比,看到最后其实发现大家写的意思都差不多,这样看懂了之后再去看别的就会很容易。

参考

JAVA回调机制
JAVA回调机制(CallBack)详解
Java 利用反射实现C#的委托
C# 委托(delegate)和事件(event)详解

你可能感兴趣的:(——JAVA)