今天有人问我怎么增强一个类的功能。博客刚好没东西,今天就讲讲增强类。

增强的手段有三种类型:

1、继承或者实现接口:特点是被增强对象不能变,增强的内容不能变。

2、装饰着模式:特点是被增强对象可变,但增强内容不可变。

3、动态代理:特点是被增强对象可变,增强内容可变。

下面是三种对a对象进行增强的手段:
 继承:AA类继承a对象的类型:A类,然后重写fun1()方法,其中重写的fun1()方法就是被增强的方法。但是,继承必须要知道a对象的真实类型,然后才能去继承。如果我们不知道a对象的确切类型,而只知道a对象是IA接口的实现类对象,那么就无法使用继承来增强a对象了;
 装饰者模式:AA类去实现a对象相同的接口:IA接口,还需要给AA类传递a对象,然后在AA类中所有的方法实现都是通过代理a对象的相同方法完成的,只有fun1()方法在代理a对象相同方法的前后添加了一些内容,这就是对fun1()方法进行了增强;
 动态代理:动态代理与装饰者模式比较相似,而且是通过反射来完成的。

详解:

1、继承,java是面向对象的语言,继承机制使他的可扩展性大大增强,我们可以通过继承方式对现有类进行扩展增强。子类继承父类之后可以获得父类所有的公共方法,子类可以进行重写等操作,这种方式简便易学,但是随之而来的是代码的耦合性大大的增强,不利于后期的维护,所以对于继承这种方法,谨慎使用。

2、装饰者设计模式,设计模式是java编程的重要组成部分,确切的说不仅仅是Java,几乎所有的高级编程语言都多多少少会涉及到编程模式。当然想要完全理解设计模式,单纯学习一门语言是完全不够的。百度百科中的解释为:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。java的IO流就是标准的装饰者模式,在这里我们拿出java中的两个类做解释InputStream和BufferedInputStream,两个类分别为字节输入流和字节缓冲输入流,简单来说缓冲流就是字节流的强化版,使用的模式就是装饰者模式,我们可以看到BufferedInputStream的构造方法中BufferedInputStream(InputStream
in)
字节流就是作为参数输入,这样缓冲流中可以通过输入的字节流对象来调用字节流中的所有的方法,同时不会妨碍缓冲流的私有方法,这就是比较经典的装饰者强化。

下面举个例子说明:

需求:处理GET请求参数编码问题,需要在Filter中放行时,把request对象给“调包”了,也就是让目标Servlet使用我们“调包”之后的request对象。这说明我们需要保证“调包”之后的request对象中所有方法都要与“调包”之前一样可以使用,并且getParameter()方法还要有能力返回转码之后的参数。这可能让你想起了“继承”,但是这里不能用继承,而是“装饰者模式(Decorator
Pattern)”!

解决方法:对request对象进行增强的条件,刚好符合装饰者模式的特点!因为我们不知道request对象的具体类型,但我们知道request是HttpServletRequest接口的实现类。这说明我们写一个类EncodingRequest,去实现HttpServletRequest接口,然后再把原来的request传递给EncodingRequest类!在EncodingRequest中对HttpServletRequest接口中的所有方法的实现都是通过代理原来的request对象来完成的,只有对getParameter()方法添加了增强代码!
JavaEE已经给我们提供了一个HttpServletRequestWrapper类,它就是HttpServletRequest的包装类,但它没做任何的增强!你可能会说,写一个装饰类,但不做增强,其目的是什么呢?使用这个装饰类的对象,和使用原有的request有什么分别呢?HttpServletRequestWrapper类虽然是HttpServletRequest的装饰类,但它不是用来直接使用的,而是用来让我们去继承的!当我们想写一个装饰类时,还要对所有不需要增强的方法做一次实现是很心烦的事情,但如果你去继承HttpServletRequestWrapper类,那么就只需要重写需要增强的方法即可了。

EncodingRequest代码:

    /**
* <p>类名称 EncodingRequest </p>
* <p>类描述 装饰者模式:
* 增强request的getParameter方法,使其编码格式为utf-8
* </p>
* @author 裴健
* @date 2017年3月22日 下午1:53:06
*/
public class EncodingRequest extends HttpServletRequestWrapper{ private HttpServletRequest request;
public EncodingRequest(HttpServletRequest request) {
super(request);
this.request=request;
} @Override
public String getParameter(String name) {
String value = request.getParameter(name);
try {
value=new String(value.getBytes("iso-8859-1"),"utf-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return value;
} }

这里是将get请求的request对象对getParameter()方法增强了,直接将“iso-8859-1”的编码改为“utf-8”的编码。

3、代理模式,代理类模式理解起来会比装饰者设计模式更加难,因为这个涉及到反射的原理。学习代理模式最终是学习AOP(面向切面编程),它与装饰者模式有点相似,它比装饰者模式还要灵活!

下面举个例子说明:

需求:(1)定义一个服务员接口

// 服务员
public interface Waiter {
// 服务
public void serve();
}

(2)一个男服务员的类并实现服务员接口。男服务员有个方法是服务。

    public class ManWaiter implements Waiter {
public void serve() {
System.out.println("服务中...");
}
}

现在想让男服务员在服务的时候有礼貌,需要在服务的方法前后加上礼貌用语。此时会想到修改代码实现。如果每次增强一个方法都去修改源代码,通俗点说,显得太low了。这里就需要动态代理增强这个“服务员”。

解决代码:

    public class Demo {
@Test
public void fun1() {
Waiter manWaiter = new ManWaiter();//目标对象
/*
* 给出三个参数,来创建方法,得到代理对象
*/
ClassLoader loader = this.getClass().getClassLoader();
Class[] interfaces = {Waiter.class};
InvocationHandler h = new WaiterInvocationHandler(manWaiter);//参数manWaiter表示目标对象
// 得到代理对象,代理对象就是在目标对象的基础上进行了增强的对象!
Waiter waiterProxy = (Waiter)Proxy.newProxyInstance(loader, interfaces, h); waiterProxy.serve();//前面添加“您好”, 后面添加“再见”
}
} class WaiterInvocationHandler implements InvocationHandler {
private Waiter waiter;//目标对象 public WaiterInvocationHandler(Waiter waiter) {
this.waiter = waiter;
} public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("您好!");
this.waiter.serve();//调用目标对象的目标方法
System.out.println("再见!");
return null;
}
}

Java中增强一个类的几种方法的更多相关文章

  1. Java中获取键盘输入值的三种方法

    Java中获取键盘输入值的三种方法     Java程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值 ...

  2. java中调用dll文件的两种方法

    一中是用JNA方法,另外是用JNative方法,两种都是转载来的, JNA地址:http://blog.csdn.net/shendl/article/details/3589676   JNativ ...

  3. Java中的一个类怎么调用另一个类中的方法

    如果另一个类中的那个方法是私有的话,就不能直接调用到,如果是其他类型的话看情况,如果是静态的(static)话,直接用类名可以调用到,如果是非静态的,就需要利用另一个类的实例(也就是用那个类生成的对象 ...

  4. Java中遍历Map集合的四种方法

    在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍历Map有不少的方法.我们看一下最常用的方法及其优缺点. 既然java中的所有map都 ...

  5. Java中spring读取配置文件的几种方法

    Spring读取配置XML文件分三步: 一.新建一个Java Bean: package springdemo; public class HelloBean { private String hel ...

  6. Java入门:Java中获取键盘输入值的三种方法

    Java程序开发过程中,需要从键盘获取输入值是常有的事,但Java它偏偏就没有像c语言给我们提供的scanf(),C++给我们提供的cin()获取键盘输入值的现成函数!Java没有提供这样的函数也不代 ...

  7. JAVA中创建线程池的五种方法及比较

    之前写过JAVA中创建线程的三种方法及比较.这次来说说线程池. JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用.另一类是通过Thr ...

  8. JAVA中获得一个月最大天数的方法(备忘)

    Calendar 类是一个抽象类,为日历字段之间的转换提供了一些方法.其中有一个重要方法 getActualMaximum ,该方法用于返回指定日历字段实际的最大值. 利用这个方法(Calendar. ...

  9. Java中的Object类的几个方法

    Object类被称为上帝类,也被称为祖宗类.在定义Java类时,如果没有指定父类,那么默认都会去继承Object类.配合Java的向上类型转换,借助Object类就可以完成很多工作了. 在Object ...

随机推荐

  1. java IO流 (四) 缓冲流的使用

    1.缓冲流涉及到的类: * BufferedInputStream* BufferedOutputStream* BufferedReader* BufferedWriter 2.作用:作用:提供流的 ...

  2. Android 性能优化 ---- 启动优化

    Android 性能优化 ---- 启动优化 1.为什么要进行启动优化 一款应用的第一印象很重要,第一印象往往决定了用户的去留.打开一款应用,如果速度很快,很顺畅,那么很容易让人觉得这款应用背后的技术 ...

  3. (4)webpack中配置css,scss,less第三方loader

    为什么要使用第三方loader 一般引入样式文件,我们会在html中引入样式标签. 很明显,这样就跟之前在script中引入js文件一样,会导致二次请求.我们希望webpack像处理js文件一样处理样 ...

  4. Ethical Hacking - GAINING ACCESS(20)

    CLIENT SIDE ATTACKS - Spoofing backdoor extension Change the extension of the trojan from exe to a s ...

  5. python pytest接口自动化框架搭建(一)

    1.首先安装pytest pip install pytest 2.编写单测用例 在pytest框架中,有如下约束: 所有的单测文件名都需要满足test_*.py格式或*_test.py格式. 在单测 ...

  6. vuex : 用vuex控制侧栏点亮状态

    上代码. xxx.vue <template> <div id="xxx"> <div class="layout"> &l ...

  7. Makefile中的目标

    Makefile中的目标 一般目标 目标就是我们需要的最终文件,也是make的最终输出 Makefile的运行机制是:先将目标当成文件,查看文件是否存在,如果存在且是最新,那么直接结束,如果文件不存在 ...

  8. 不是吧,阿sir,2020年程序员要不好过?

    自从网传程序员到了35岁之后必须要转行,现在又有人传言:“疫情之下,程序员今年要过苦日子了,降薪裁员是大趋势.” 不是,我就不明白了,你们怎么就看不得程序员好呢?天天巴望着程序员降薪.转行.裁员…   ...

  9. 题解 洛谷 P6640 【[BJOI2020] 封印】

    设\(lenth_i\)为\(s\)在\(i\)位置的前缀的后缀为\(t\)的一个子串的最长长度,即为从\(i\)位置开始往前和\(t\)的最长公共子串长度.其可以通过对\(t\)建后缀自动机,然后让 ...

  10. CentOS7 firewalld docker 端口映射问题,firewall开放端口后,还是不能访问,解决方案

    # 宿主机ip: 192.168.91.19 docker run -itd --name tomcat -p 8080:8080 tomcat /usr/local/apache-tomcat-9. ...