1. 程式人生 > >面試題:增強一個對象的方法的三種方式

面試題:增強一個對象的方法的三種方式

解決 oct rip owa 返回值 dex tty unit web項目

面試題:增強一個對象的方法的三種方式

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不在亂碼。

面試題:增強一個對象的方法的三種方式