面试题:增强一个对象的方法的三种方式

面试题:增强一个对象的方法的三种方式

1. 继承

  • 使用这种方式必须满足的条件是:被增强的方法的所在类能被继承,并且这个对象已经明确知道。
  • 举例:
  • 有一个接口Person,里面有一个方法run()
    package com.itzhouq.demo1;
    
    public interface Person {
      public void run();
    }
  • 类NormalPerson实现了这个接口Person
    package com.itzhouq.demo1;
    
    public class NormalPerson implements Person {
    
      @Override
      public void run() {
          System.out.println("走.......");
      }
    }
  • 现在的需求是,使用继承方式增强NomalPerson中的方法run()
  • 这里需要被增强的方法是run(),所在的类NomalPerson可以被继承并且已经明确。
  • 所以创建一个类Superson继承NormalPerson
    package com.itzhouq.demo1;
    
    public class Superson extends NormalPerson {
      //重写了父类NormalPerson的方法
      @Override
      public void run() {
          super.run();
          System.out.println("增强了,变成飞了。。。");
      }
    }
  • 类Superson通过对父类NormalPerson的run()方法进行重写,实现对run()方法的增强。
  • 测试
    package com.itzhouq.demo1;
    
    import org.junit.Test;
    
    /*
     * 增强一个对象的方法之一:继承方式
     */
    public class Demo {
    
      @Test
      public void test() {
          NormalPerson p = new NormalPerson();
          p.run();//走.......
      }
    
      //需求:对普通人的run方法进行增强,由走变成飞----增强一个对象的方法
      //用继承来实现需求:创建一个类继承NormalPerson
      @Test
      public void test2() {
          Superson superson = new Superson();
          superson.run();
    //        走.......
    //        增强了,变成飞了。。。
      }
    
    }

2. 装饰者模式

  • 装饰者模式实现对方法的增强,不需要知道被增强的方法run()所在的类是哪个类,只需要知道这个类实现了哪个接口即可。
  • 条件:
    • 装饰者和被装饰者需要实现同一个类
    • 装饰者有被装饰者的引用
  • 接口:
    package com.itzhouq.demo2;
    
    public interface Person {
      public void run();
    }
  • 需要被增强的方法run()
    package com.itzhouq.demo2;
    
    public class NormalPerson implements Person {
    
      @Override
      public void run() {
          System.out.println("走.......");
      }
    }
  • 这里被装饰者就是run()方法所在的类
  • 创建一个装饰者类,实现run()所在类,实现的接口Person
    package com.itzhouq.demo2;
    
    public class Superson implements Person {
      //被装饰者的引用
      private NormalPerson p;
      public Superson(NormalPerson p) {
          this.p = p;
      }
    
      @Override
      public void run() {
          //这个是被装饰者以前的方法
          p.run();
          //增强
          System.out.println("增强了,变成飞。。。。");
      }
    }
  • 测试
    package com.itzhouq.demo2;
    
    import org.junit.Test;
    /*
     * 增强一个对象的方法之二:装饰者方式
     */
    public class Demo {
    
      @Test
      public void test() {
          NormalPerson p = new NormalPerson();
          p.run();//走.......
      }
    
      //需求:对普通人的run方法进行增强,由走变成飞
      //假装不知道接口的实现类NormalPerson,但是要对普通人的run方法进行增强
      //不知道实现类就无法使用继承的方式进行增强
      //使用装饰者解决这样的问题:
          //条件1:装饰者()和被装饰者()实现同一个接口Person
          //条件2:装饰者里面有被装饰者的引用         在我出生的时候,你把你给我,我对你进行增强
    
      @Test
      public void test2() {
          NormalPerson p = new NormalPerson();
          Superson superson = new Superson(p);
          superson.run();
    //        走.......
    //        增强了,变成飞。。。。
    
      }
    }

3. 动态代理

  • 通过一张图回顾动态代理

  • 动态代理的条件:必须知道要被代理的类/对象是谁,这里要被代理的类是NoemalPerson
    Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interface, InvocationHander h);
    //返回一个指定接口的代理类实现
  • 接口person,这里再加一个方法sleep
    package com.itzhouq.demo3;
    
    public interface Person {
      public void run();
      public String sleep();
    }
  • 实现类NomalPerson
    package com.itzhouq.demo3;
    
    public class NormalPerson implements Person {
    
      @Override
      public void run() {
          System.out.println("走.......");
      }
    
      @Override
      public String sleep() {
          System.out.println("睡觉了。。。");
          return "sleep";
      }
    }
  • 使用动态代理增强
    package com.itzhouq.demo3;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import org.junit.Test;
    
    public class Demo {
      @Test
      public void test() {
          NormalPerson p = new NormalPerson();
          p.run();//走.......
      }
    
      //需求:使用动态代理的方式对普通人进行增强
      //JDK提供的类和方法可以给咱们动态的生成代理对象/增强对象
    
      /*
       * 参数概述:固定的
       *  参数1:和要被增强的对象,一样的,类加载器
       *  参数2:和要被增强的对象一样的接口
       *          1 根据指定的传递接口返回一个该接口下的实例
       *          2 传递的接口里面的方法就是可以被增强的所有方法
       *  参数3:所有的增强业务的逻辑实现(方法)
       */
      @Test
      public void test1() {
          NormalPerson p = new NormalPerson();
          Person proxyPerson = (Person) Proxy.newProxyInstance(
                  p.getClass().getClassLoader(),
                  p.getClass().getInterfaces(),
                  new InvocationHandler() {
    
                      /*
                       *  参数概述:固定的
                       *  参数1:不用管,永远是固定值  代理对象的类型
                       *  参数2:要被增强的方法
                       *  参数3:要被增强的方法运行过程中需要的参数
                       */
                      @Override   //invoke里面是所有的增强业务的逻辑代码
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          //让以前的方法执行
                              //参数1:本身应该执行这个方法的对象
                              //参数2:执行这个方法需要的参数
                          Object value = method.invoke(p, args);
                          //原来方法的返回值
                          System.out.println(value);
                          // 写增强业务逻辑
                          System.out.println("增强了,变成飞了。。。");
                          //最终的返回值,谁调用返回给谁
                          return "abcd";
                      }
                  });
          proxyPerson.run();//执行接口中的每一个需要增强的方法,invoke都会执行一遍,执行的内容就是针对该方法的增强
    //        走.......
    //        增强了,变成飞了。。。
          String value = proxyPerson.sleep();
          System.out.println(value);
    //        睡觉了。。。
    //        sleep
    //        增强了,变成飞了。。。
    //        abcd
      }
    }

4. 扩展:使用动态代理方式统一字符集编码

  • 新建Web项目,新建一个index.jsp。在JSP中写两个表单,分别为post和get方式提交。后台通过Servlet接收到前台输入的username,打印在控制台会乱码。
  • JSP
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
      <form action="${pageContext.request.contextPath }/sd1" method="get">
          用户名:<input type="text" name="username">
          <input type="submit" value="提交">
      </form>
    
      <hr>
    
      <form action="${pageContext.request.contextPath }/sd1" method="post">
          用户名:<input type="text" name="username">
          <input type="submit" value="提交">
      </form>
    </body>
    </html>
  • Servlet
    package com.itzhouq.web;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class ServletDemo1 extends HttpServlet {
    
      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          String username = request.getParameter("username");
          System.out.println(username);
      }
    
      public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          doGet(request, response);
      }
    }
  • 为解决这个问题使用过滤器,在过滤器中使用动态代理方式解决
  • 新建一个过滤器MyFilter.java,并在xml文件中配置过滤的资源为全部资源
  • web.xml
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.itzhouq.filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  • MyFilter
    package com.itzhouq.filter;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    
    public class MyFilter implements Filter {
        public MyFilter() {
        }
      public void destroy() {
      }
    
      public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          //要增强的方法:request.getparameter
          //被代理的对象:request
          HttpServletRequest request = (HttpServletRequest)req;
    
          //动态的生成代理对象
          HttpServletRequest hsr = (HttpServletRequest) Proxy.newProxyInstance(
                  request.getClass().getClassLoader(),
                  request.getClass().getInterfaces(),
                  new InvocationHandler() {
    
                      @Override
                      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                          //1. 判断是否是要增强的方法getParameter
                          if("getParameter".equals(method.getName())) {
                              //知道getParameter使用的是哪个提交方式
                              String m = request.getMethod();
                              //判断是get还是post
                              if("get".equalsIgnoreCase(m)) {
                                  //  以前方法调用后的乱码
                                  String s = (String)method.invoke(request, args);
                                  // 增强---解决乱码
                                  s = new String(s.getBytes("iso8859-1"),"utf-8");
                                  return s;
                              }
                              if("post".equalsIgnoreCase(m)) {
                                  request.setCharacterEncoding("utf-8");
                                  return method.invoke(request, args);
                              }
                          }
    
                          //  如果是别的方法
                          return method.invoke(request, args);
                      }
                  });
    
          chain.doFilter(hsr, response);
      }
    
      public void init(FilterConfig fConfig) throws ServletException {
      }
    }
  • 后台Servlet接收到的username不在乱码。

原文地址:https://www.cnblogs.com/itzhouq/p/proxy1.html

时间: 2024-10-10 14:34:37

面试题:增强一个对象的方法的三种方式的相关文章

jsp调取java方法的三种方式

DouYin,经常安慰我.现在的困境都是对自己的磨砺,我也常常暗示自己:They are all chosen by themselves..-- 闲扯就到这,笔者决定每天啊,尽量出去拉拉单杠,锻炼下身体.下面,我们以webwork框架的jsp为例,探究一下form表单的回调函数.一.分析框架下的jsp页面组成 <!-- 指定语言和编码 --> <%@ page language="java" pageEncoding="utf-8" conten

Struts2学习(二)运行Action中方法的三种方式

1.运行execute()方法 一般的能够直接在action中书写execute,调用action时会自己主动运行此方法 2.配置method方法 在struts.xml中配置action时.写method属性设置方法.例如以下所看到的.这样写能够在一个action类中写多个运行的方法.简化结构. <package name="user" extends="struts-default" namespace="/user"> <

增强一个Java类中的某个方法的几种方式

      * 一种方式:继承的方式.  * 能够控制这个类的构造的时候,才可以使用继承. Connection是一个接口,实现类不确定(由各厂商提供),无法使用此方法     * 二种方式:装饰者模式方式.         * 包装对象和被包装的对象都要实现相同的接口.         * 包装的对象中需要获得到被包装对象的引用.         ***** 缺点:如果接口的方法比较多,增强其中的某个方法.其他的功能的方法需要原有调用.     * 三种方式:动态代理的方式.         

创建二叉树的两种方法以及三种遍历方法

二叉树的两种创建方法和三种遍历方法 这里的两种创建方法,一种值得是 数据结构上面的创建方法: 方法一 代码如下: 二叉树的结构定义如下: typedef struct BinaryTreeNode{ char value; struct BinaryTreeNode *left; struct BinaryTreeNode *right; }; - c语言版 void CreateBinaryTree(BinaryTreeNode **T) { char data; scanf("%d"

(转)maven怎么 引入(或引用/使用) 自定义(或本地/第三方) jar的三种方式 图文教程 方法二最简单

转:https://blog.csdn.net/wabiaozia/article/details/52798194 准备工作: 假如我有一个自定义jar是:123456.jar,下载地址http://download.csdn.net/detail/wabiaozia/9870838 如果不想下载,可以按照https://jingyan.baidu.com/article/046a7b3ed8b23ef9c27fa9b9.html 操作即可得到jar. jar包里的源码是: public cl

oracle数据的导入导出(两种方法三种方式)

大概了解数据库中数据的导入导出.在oracle中,导入导出数据的方法有两种,一种是使用cmd命令行的形式导入导出数据,另一种是使用PL/SQL工具导入导出数据. 1,使用cmd命令行导入导出数据 1.1整库导出 整库导出:exp 管理员账号/密码 full=y;//参数full表示整库导出.导出后会在当前目录下生成一个EXPDAT.DMP的文件,此文件为备份文件.如果想导出数据到指定位置,并且取个名字,需要添加file参数.例如:exp system/123456 file= C:\person

【Eclipse使用方法】Eclipse中安装插件的三种方式

Eclipse也用了很久,但是都没有怎么去研究过怎么安装插件,今天正好在自己新买的本上试了一下.现在将心得写下,以供参考.目前安装Eclipse插件主要有以下三种方式: 在线安装: 以TestNG的安装为例来阐述其安装过程. 第一步:点击菜单栏中“Help”菜单,在下拉列中选择“Install New Software...”.此时会弹出“install”的安装框. 第二步:在弹出的“install”框,选择之前已经建立好的site,如果没有建立好,则可以点击“Add”按钮,弹出一个添加site

React创建组件的三种方式及区别

React创建组件具体的三种方式: 1.函数式定义的无状态组件 2.es5原生方式React.createClass定义的组件 3.es6形式的extends React.Component定义的组件 虽然有三种方式可以定义React组件,但是它们有什么不同呢?什么情况下应该使用哪种定义方式呢?请继续往下看 接下来我们先说一下三种方式有什么区别? 1.无状态函数式组件 它是为了创建纯展示组件,这种组件只负责根据传入的props来展示,不涉及到要state状态的操作. 无状态函数式组件形式上表现为

宿主机为linux、windows分别实现VMware三种方式上网(转)

一.VMware三种方式工作原理1 Host-only连接方式  让虚机具有与宿主机不同的各自独立IP地址,但与宿主机位于不同网段,同时为宿主主机新增一个IP地址,且保证该IP地址与各虚机IP地址位于同一网段.最终结果是新建了一个由所有虚机与宿主主机所构成的局域网,但该局域网与宿主主机本身所处的现有局域网是相互独立的,如果不做额外路由设置,这两个局域网之间不会连通,因此新建的局域网可以认为是一个单独从属于当前宿主主机的私有网络,其成员为当前宿主主机和相关的所有虚机,这也是Host-only命名的