尚學堂JAVA高級學習筆記

尚學堂JAVA高級學習筆記

目錄

寫在前面

學習連接:Java 視頻教程全集javascript

課件連接:Java課件html

聲明:全是本人邊學習邊手打的,但願對你們有幫助。前端

第1章 手寫webserver

1. 靈魂反射

  • 反射Reflection:把java類中的各類結構(方法、屬性、構造器、類名)映射成一個個的java對象。利用反射技術能夠對一個類進行解剖,反射是框架設計的靈魂。java

  • 獲取Class對象(三種方式):推薦使用Class.forName(「完整路徑」)python

  • 能夠動態建立對象:clz.getConstructor().newInstance()mysql

  • 練習git

    package com.sxt.Server_study01;
    
    import java.lang.reflect.InvocationTargetException;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: ReflectTest.java
     * @time: 2019/11/29 14:55
     * @desc:
     */
    
    public class ReflectTest {
        public static void main(String[] args){
            // 1. 對象.getClass() | 買iphone照着作
            Iphone iphone = new Iphone();
            Class clz = iphone.getClass();
            // 2. 類.class()     | 買通工程師給圖紙
            clz = Iphone.class;
            // 3. Class.forName("包名.類名")   | 偷圖紙
            try {
                clz = Class.forName("com.sxt.Server_study01.Iphone");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            // 建立對象
            try {
                Iphone iphone2 = (Iphone)clz.newInstance();
                System.out.println(iphone2);
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            // 建立對象推薦方式
            try {
                Iphone iphone3 = (Iphone)clz.getConstructor().newInstance();
                System.out.println(iphone3);
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }
    
    class Iphone{
        public Iphone(){
        }
    }

2. 高效解析xml

  • XML:Extensible Markup Language,可擴展標記語言,做爲數據的一種存儲格式或用於存儲軟件的參數,程序解析此配置文件,就能夠達到不修改代碼就能更改程序的目的。github

  • 利用Sax解析XMLweb

  • xml例子正則表達式

    <?xml version="1.0" encoding="UTF-8"?>
    <persons>
        <person>
            <name>至尊寶</name>
            <age>9000</age>
        </person>
        <person>
            <name>白晶晶</name>
            <age>7000</age>
        </person>
    </persons>
  • 製做Person類

    package com.sxt.Server_study01;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Person.java
     * @time: 2019/11/29 15:29
     * @desc:
     */
    
    public class Person {
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
  • 熟悉sax解析流程

    package com.sxt.Server_study01;
    
    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParser;
    import javax.xml.parsers.SAXParserFactory;
    import java.io.IOException;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: XmlTest01.java
     * @time: 2019/11/29 15:10
     * @desc: 熟悉sax解析流程
     */
    
    public class XmlTest01 {
        public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
            // SAX解析
            // 1. 獲取解析工廠
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // 2. 從解析工廠獲取解析器
            SAXParser parse = null;
            parse = factory.newSAXParser();
            // 3. 加載文檔Document註冊處理器
            // 4. 編寫處理器
            PHandler handler = new PHandler();
            parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/Server_study01/p.xml"), handler);
        }
    }
    
    class PHandler extends DefaultHandler {
        @Override
        public void startDocument() throws SAXException {
            System.out.println("解析文檔開始");
        }
    
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            System.out.println(qName + "-->解析開始");
        }
    
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String contents = new String(ch, start, length).trim();
            if(contents.length()>0) {
                System.out.println("內容爲:" + contents);
            }else{
                System.out.println("內容爲空!");
            }
        }
    
        @Override
        public void endDocument() throws SAXException {
            System.out.println("解析文檔結束");
        }
    
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            System.out.println(qName + "-->解析結束");
        }
    }
  • 獲取xml中的內容

    package com.sxt.Server_study01;
    
    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParser;
    import javax.xml.parsers.SAXParserFactory;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: XmlTest02.java
     * @time: 2019/11/29 15:31
     * @desc: 獲取xml中的內容
     */
    
    public class XmlTest02 {
        public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
            // SAX解析
            // 1. 獲取解析工廠
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // 2. 從解析工廠獲取解析器
            SAXParser parse = null;
            parse = factory.newSAXParser();
            // 3. 加載文檔Document註冊處理器
            // 4. 編寫處理器
            PersonHandler handler = new PersonHandler();
            parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/Server_study01/p.xml"), handler);
            // 5. 獲取數據
            List<Person> persons = handler.getPersons();
            for (Person p : persons) {
                System.out.println(p.getName() + "-->" + p.getAge());
            }
        }
    }
    
    class PersonHandler extends DefaultHandler {
        private List<Person> persons;
        private Person person;
        private String tag;     // 存儲操做的標籤
    
        @Override
        public void startDocument() throws SAXException {
            System.out.println("解析文檔開始");
            persons = new ArrayList<>();
        }
    
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (null != qName) {
                tag = qName;        // 存儲標籤名
                if (tag.equals("person")) {
                    person = new Person();
                }
            }
        }
    
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String contents = new String(ch, start, length).trim();
            if (contents.length() > 0) {
                if (tag.equals("name")) {
                    person.setName(contents);
                } else if (tag.equals("age")) {
                    person.setAge(Integer.valueOf(contents));
                }
            }
        }
    
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (qName.equals("person")) {
                persons.add(person);
            }
        }
    
        @Override
        public void endDocument() throws SAXException {
            System.out.println("解析文檔結束");
        }
    
        public List<Person> getPersons() {
            return persons;
        }
    }

3. 解析webxml

  • webxml例子

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app>
        <servlet>
            <servlet-name>login</servlet-name>
            <servlet-class>com.sxt.server.basic.servlet.LoginServlet</servlet-class>
        </servlet>
        <servlet>
            <servlet-name>reg</servlet-name>
            <servlet-class>com.sxt.server.basic.servlet.RegisterServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>login</servlet-name>
            <url-pattern>/login</url-pattern>
            <url-pattern>/g</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>reg</servlet-name>
            <url-pattern>/reg</url-pattern>
        </servlet-mapping>
    </web-app>
  • 解析webxml

    package com.sxt.Server_study01.servlet;
    
    import com.sxt.Server_study01.Person;
    import org.xml.sax.Attributes;
    import org.xml.sax.SAXException;
    import org.xml.sax.helpers.DefaultHandler;
    
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParser;
    import javax.xml.parsers.SAXParserFactory;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: XmlTest02.java
     * @time: 2019/11/29 15:31
     * @desc: 解析Webxml
     */
    
    public class XmlTest02 {
        public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
            // SAX解析
            // 1. 獲取解析工廠
            SAXParserFactory factory = SAXParserFactory.newInstance();
            // 2. 從解析工廠獲取解析器
            SAXParser parse = null;
            parse = factory.newSAXParser();
            // 3. 加載文檔Document註冊處理器
            // 4. 編寫處理器
            WebHandler handler = new WebHandler();
            parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/Server_study01/servlet/web.xml"), handler);
            // 5. 獲取數據
            List<Entity> entitys = handler.getEntitys();
            List<Mapping> mappings = handler.getMappings();
            System.out.println(entitys.size());
            System.out.println(mappings.size());
        }
    }
    
    class WebHandler extends DefaultHandler {
        private List<Entity> entitys;
        private List<Mapping> mappings;
        private Entity entity;
        private Mapping mapping;
        private String tag;
        private boolean isMapping = false;
    
        public List<Entity> getEntitys() {
            return entitys;
        }
    
        public void setEntitys(List<Entity> entitys) {
            this.entitys = entitys;
        }
    
        public List<Mapping> getMappings() {
            return mappings;
        }
    
        public void setMappings(List<Mapping> mappings) {
            this.mappings = mappings;
        }
    
        @Override
        public void startDocument() throws SAXException {
            entitys = new ArrayList<>();
            mappings = new ArrayList<>();
        }
    
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (null != qName) {
                tag = qName;        // 存儲標籤名
                if (tag.equals("servlet")) {
                    entity = new Entity();
                    isMapping = false;
                } else if (tag.equals("servlet-mapping")) {
                    mapping = new Mapping();
                    isMapping = true;
                }
            }
        }
    
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String contents = new String(ch, start, length).trim();
            if (contents.length() > 0) {
                if (isMapping) {// 操做servlet-mapping
                    if (tag.equals("servlet-name")) {
                        mapping.setName(contents);
                    } else if (tag.equals("url-pattern")) {
                        mapping.addPattern(contents);
                    }
                } else {// 操做servlet
                    if (tag.equals("servlet-name")) {
                        entity.setName(contents);
                    } else if (tag.equals("servlet-class")) {
                        entity.setClz(contents);
                    }
                }
            }
        }
    
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (qName.equals("servlet")) {
                entitys.add(entity);
            } else if (qName.equals("servlet-mapping")) {
                mappings.add(mapping);
            }
        }
    }

4. 反射webxml

  • xml樣例

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app>
        <servlet>
            <servlet-name>login</servlet-name>
            <servlet-class>com.sxt.Server_study01.servlet.LoginServlet</servlet-class>
        </servlet>
        <servlet>
            <servlet-name>reg</servlet-name>
            <servlet-class>com.sxt.Server_study01.servlet.RegisterServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>login</servlet-name>
            <url-pattern>/login</url-pattern>
            <url-pattern>/g</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>reg</servlet-name>
            <url-pattern>/reg</url-pattern>
        </servlet-mapping>
    </web-app>
  • 解析xml

    • Entity類

      package com.sxt.Server_study01.servlet;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Entity.java
       * @time: 2019/12/2 13:20
       * @desc:
       */
      
      public class Entity {
          private String name;
          private String clz;
      
          public Entity() {
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getClz() {
              return clz;
          }
      
          public void setClz(String clz) {
              this.clz = clz;
          }
      }
    • Mapping類

      package com.sxt.Server_study01.servlet;
      
      import java.util.HashSet;
      import java.util.Set;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Mapping.java
       * @time: 2019/12/2 13:21
       * @desc:
       */
      
      public class Mapping {
          private String name;
          private Set<String> patterns;
      
          public Mapping() {
              patterns = new HashSet<>();
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public Set<String> getPatterns() {
              return patterns;
          }
      
          public void setPatterns(Set<String> patterns) {
              this.patterns = patterns;
          }
      
          public void addPattern(String pattern){
              this.patterns.add(pattern);
          }
      }
    • 經過URL的路徑找到了對應的class

      package com.sxt.Server_study01.servlet;
      
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: WebContext.java
       * @time: 2019/12/3 10:51
       * @desc:
       */
      
      public class WebContext {
          private List<Entity> entitys = null;
          private List<Mapping> mappings = null;
      
          // key-->servlet-name  value-->servlet-class
          private Map<String, String> mappingMap = new HashMap<>();
          // key-->url-pattern  value-->servlet-name
          private Map<String, String> entityMap = new HashMap<>();
      
          public WebContext(List<Entity> entitys, List<Mapping> mappings) {
              this.entitys = entitys;
              this.mappings = mappings;
      
              // 將entity的List轉成了對應的map
              for(Entity entity: entitys){
                  entityMap.put(entity.getName(), entity.getClz());
              }
              // 將map的List轉成了對應的map
              for(Mapping mapping: mappings){
                  for(String pattern: mapping.getPatterns()){
                      mappingMap.put(pattern, mapping.getName());
                  }
              }
          }
      
          // 經過URL的路徑找到了對應的class
          public String getClz(String pattern) {
              String name = mappingMap.get(pattern);
              return entityMap.get(name);
          }
      }
    • 核心代碼,取出數據

      package com.sxt.Server_study01.servlet;
      
      import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
      import org.xml.sax.Attributes;
      import org.xml.sax.SAXException;
      import org.xml.sax.helpers.DefaultHandler;
      
      import javax.xml.parsers.ParserConfigurationException;
      import javax.xml.parsers.SAXParser;
      import javax.xml.parsers.SAXParserFactory;
      import java.io.IOException;
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: XmlTest02.java
       * @time: 2019/11/29 15:31
       * @desc: 解析Webxml
       */
      
      public class XmlTest02 {
          public static void main(String[] args) throws Exception {
              // SAX解析
              // 1. 獲取解析工廠
              SAXParserFactory factory = SAXParserFactory.newInstance();
              // 2. 從解析工廠獲取解析器
              SAXParser parse = null;
              parse = factory.newSAXParser();
              // 3. 加載文檔Document註冊處理器
              // 4. 編寫處理器
              WebHandler handler = new WebHandler();
              parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/Server_study01/servlet/web.xml"), handler);
              // 5. 獲取數據
              WebContext context = new WebContext(handler.getEntitys(), handler.getMappings());
              // 假設你輸入了 /login or /g
              String className = context.getClz("/g");
              Class clz = Class.forName(className);
              Servlet servlet = (Servlet)clz.getConstructor().newInstance();
              System.out.println(servlet);
              servlet.service();
          }
      }
      
      class WebHandler extends DefaultHandler {
          private List<Entity> entitys;
          private List<Mapping> mappings;
          private Entity entity;
          private Mapping mapping;
          private String tag;
          private boolean isMapping = false;
      
          public List<Entity> getEntitys() {
              return entitys;
          }
      
          public void setEntitys(List<Entity> entitys) {
              this.entitys = entitys;
          }
      
          public List<Mapping> getMappings() {
              return mappings;
          }
      
          public void setMappings(List<Mapping> mappings) {
              this.mappings = mappings;
          }
      
          @Override
          public void startDocument() throws SAXException {
              entitys = new ArrayList<>();
              mappings = new ArrayList<>();
          }
      
          @Override
          public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
              if (null != qName) {
                  tag = qName;        // 存儲標籤名
                  if (tag.equals("servlet")) {
                      entity = new Entity();
                      isMapping = false;
                  } else if (tag.equals("servlet-mapping")) {
                      mapping = new Mapping();
                      isMapping = true;
                  }
              }
          }
      
          @Override
          public void characters(char[] ch, int start, int length) throws SAXException {
              String contents = new String(ch, start, length).trim();
              if (contents.length() > 0) {
                  if (isMapping) {// 操做servlet-mapping
                      if (tag.equals("servlet-name")) {
                          mapping.setName(contents);
                      } else if (tag.equals("url-pattern")) {
                          mapping.addPattern(contents);
                      }
                  } else {// 操做servlet
                      if (tag.equals("servlet-name")) {
                          entity.setName(contents);
                      } else if (tag.equals("servlet-class")) {
                          entity.setClz(contents);
                      }
                  }
              }
          }
      
          @Override
          public void endElement(String uri, String localName, String qName) throws SAXException {
              if (qName.equals("servlet")) {
                  entitys.add(entity);
              } else if (qName.equals("servlet-mapping")) {
                  mappings.add(mapping);
              }
          }
      }
    • Servlet接口

      package com.sxt.Server_study01.servlet;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Servlet.java
       * @time: 2019/12/3 15:59
       * @desc:
       */
      
      public interface Servlet {
          void service();
      }
    • 須要被映射的類:Loginservlet

      package com.sxt.Server_study01.servlet;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: LoginServlet.java
       * @time: 2019/12/3 15:59
       * @desc:
       */
      
      public class LoginServlet implements Servlet {
          @Override
          public void service() {
              System.out.println("LoginServlet");
          }
      }
    • 須要被映射的類:RegisterServlet

      package com.sxt.Server_study01.servlet;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: RegisterServlet.java
       * @time: 2019/12/3 15:59
       * @desc:
       */
      
      public class RegisterServlet implements Servlet{
          @Override
          public void service() {
              System.out.println("RegisterServlet");
          }
      }

5. 簡單易學的html

  • HyperText Markup Language ,超文本標記語言,簡單理解爲瀏覽器使用的語言。

  • name是後端用的,id是前端用的

    <html>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <head>
            <title>第一個html登錄</title>
        </head>
        <body>
            <h1>表單的使用</h1>
            <pre>
                post: 提交,基於http協議不一樣  量大  請求參數url不可見  安全<br/>
                get: 默認,獲取,基於http協議不一樣  量小  請求參數url可見  不安全<br/>
                action: 請求web服務器的資源 URL<br/>
                name: 做爲後端使用,區分惟一,請求服務器,必須存在,數據不能提交<br/>
                id: 做爲前端使用,區分惟一<br/>
            </pre>
            <form method="post" action="http://localhost:8888/index.html">
                用戶名: <input type="text" name="uname" id="uname"/>
                密碼: <input type="password" name="pwd" id="pwd"/>
                <input type="submit" value="登錄"/>
            </form>
        </body>
    </html>

6. 不得不提的http協議

  • http請求協議

    img

  • http響應協議

    img

7. 獲取請求協議

  • 使用ServerSocket創建與瀏覽器的鏈接,獲取請求協議

    package com.sxt.Server_study02;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server01.java
     * @time: 2019/12/4 9:26
     * @desc: 使用ServerSocket創建與瀏覽器的鏈接,獲取請求協議
     */
    
    public class Server01 {
        private ServerSocket serverSocket;
        public static void main(String[] args) {
            Server01 server = new Server01();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            try {
                Socket client = serverSocket.accept();
                System.out.println("一個客戶端創建了鏈接...");
                // 獲取請求協議
                InputStream is = client.getInputStream();
                byte[] datas = new byte[1024*1024];
                int len = is.read(datas);
                String requstInfo = new String(datas, 0, len);
                System.out.println(requstInfo);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("客戶端錯誤...");
            }
        }
    
        // 中止服務
        public void stop() {
    
        }
    }
  • 兩種請求方法get和post:GET和POST兩種基本請求方法的區別GET 和 POST 到底有什麼區別?

8. 返回響應協議

package com.sxt.Server_study02;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/**
 * @author: Li Tian
 * @contact: litian_cup@163.com
 * @software: IntelliJ IDEA
 * @file: Server02.java
 * @time: 2019/12/4 9:26
 * @desc: 返回響應協議
 */

public class Server02 {
    private ServerSocket serverSocket;
    public static void main(String[] args) {
        Server02 server = new Server02();
        server.start();
    }

    // 啓動服務
    public void start() {
        try {
            serverSocket = new ServerSocket(8888);
            receive();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("服務器啓動失敗...");
        }
    }

    // 接受鏈接處理
    public void receive() {
        try {
            Socket client = serverSocket.accept();
            System.out.println("一個客戶端創建了鏈接...");
            // 獲取請求協議
            InputStream is = client.getInputStream();
            byte[] datas = new byte[1024*1024];
            int len = is.read(datas);
            String requstInfo = new String(datas, 0, len);
            System.out.println(requstInfo);

            StringBuilder content = new StringBuilder();
            content.append("<html>");
            content.append("<head>");
            content.append("<title>");
            content.append("服務器響應成功");
            content.append("</title>");
            content.append("</head>");
            content.append("<body>");
            content.append("終於回來了...");
            content.append("</body>");
            content.append("</html>");
            int size = content.toString().getBytes().length;
            StringBuilder responseInfo = new StringBuilder();
            String blank = " ";
            String CRLF = "\r\n";
            // 返回
            // 1. 響應行:HTTP/1.1 200 OK
            responseInfo.append("HTTP/1.1").append(blank);
            responseInfo.append(200).append(blank);
            responseInfo.append("OK").append(CRLF);
            // 2. 響應頭(最後一行存在空行):
            responseInfo.append("Date:").append(new Date()).append(CRLF);
            responseInfo.append("Server:").append("shsxt Server/0.0.1;charset=GBK").append(CRLF);
            responseInfo.append("Content-type:text/html").append(CRLF);
            responseInfo.append("Content-length:").append(size).append(CRLF);
            responseInfo.append(CRLF);
            // 3. 正文
            responseInfo.append(content.toString());
            // 寫出到客戶端
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
            bw.write(responseInfo.toString());
            bw.flush();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("客戶端錯誤...");
        }
    }

    // 中止服務
    public void stop() {

    }
}

9. 封裝響應信息

  1. 動態添加內容print

  2. 累加字節數的長度

  3. 根據狀態碼拼接響應頭協議

  4. 根據狀態碼統一推送出去

    package com.sxt.Server_study02;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server03.java
     * @time: 2019/12/4 9:26
     * @desc: 封裝響應信息
     */
    
    public class Server03 {
        private ServerSocket serverSocket;
        public static void main(String[] args) {
            Server03 server = new Server03();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            try {
                Socket client = serverSocket.accept();
                System.out.println("一個客戶端創建了鏈接...");
                // 獲取請求協議
                InputStream is = client.getInputStream();
                byte[] datas = new byte[1024*1024];
                int len = is.read(datas);
                String requstInfo = new String(datas, 0, len);
                System.out.println(requstInfo);
    
                Response response = new Response(client);
    
                // 關注了內容
                response.print("<html>");
                response.print("<head>");
                response.print("<title>");
                response.print("服務器響應成功");
                response.print("</title>");
                response.print("</head>");
                response.print("<body>");
                response.print("終於回來了...");
                response.print("</body>");
                response.print("</html>");
    
                // 關注了狀態碼
                response.pushToBrowser(200);
    
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("客戶端錯誤...");
            }
        }
    
        // 中止服務
        public void stop() {
        }
    }
    package com.sxt.Server_study02;
    
    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.util.Date;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Response.java
     * @time: 2019/12/5 9:17
     * @desc:
     */
    
    public class Response {
        private BufferedWriter bw;
        // 正文
        private StringBuilder content;
        // 協議頭信息:狀態行與請求頭、回車
        private StringBuilder headInfo;
        // 正文的字節數
        private int len;
    
        private final String BLANK = " ";
        private final String CRLF = "\r\n";
    
        public Response() {
            content = new StringBuilder();
            headInfo = new StringBuilder();
            len = 0;
        }
    
        public Response(Socket client) {
            this();
            try {
                bw = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
            } catch (IOException e) {
                e.printStackTrace();
                headInfo = null;
            }
        }
    
        public Response(OutputStream os) {
            this();
            bw = new BufferedWriter(new OutputStreamWriter(os));
        }
        // 動態添加內容
        public Response print(String info){
            content.append(info);
            len += info.getBytes().length;
            return this;
        }
        public Response println(String info){
            content.append(info).append(CRLF);
            len += (info + CRLF).getBytes().length;
            return this;
        }
    
        // 推送響應信息
        public void pushToBrowser(int code) throws IOException {
            if (null == headInfo){
                code = 505;
            }
            createHeadInfo(code);
            bw.append(headInfo);
            bw.append(content);
            bw.flush();
        }
    
        // 構建頭信息
        private void createHeadInfo(int code) {
            // 1. 響應行:HTTP/1.1 200 OK
            headInfo.append("HTTP/1.1").append(BLANK);
            headInfo.append(code).append(BLANK);
            switch (code) {
                case 200:
                    headInfo.append("OK").append(CRLF);
                    break;
                case 404:
                    headInfo.append("NOT FOUND").append(CRLF);
                    break;
                case 505:
                    headInfo.append("SERVER ERROR").append(CRLF);
                    break;
            }
    
            // 2. 響應頭(最後一行存在空行):
            headInfo.append("Date:").append(new Date()).append(CRLF);
            headInfo.append("Server:").append("shsxt Server/0.0.1;charset=GBK").append(CRLF);
            headInfo.append("Content-type:text/html").append(CRLF);
            headInfo.append("Content-length:").append(len).append(CRLF);
            headInfo.append(CRLF);
        }
    }

10. 封裝請求信息

  • 經過分解字符串獲取method、URL和請求參數

  • POST請求參數可能在請求體中存在

  • 分解協議

    package com.sxt.Server_study02;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Request.java
     * @time: 2019/12/5 10:15
     * @desc:
     */
    
    public class Request {
        private String requestInfo;
        // 請求方式
        private String method;
        // 請求url
        private String url;
        // 請求參數
        private String queryStr;
    
        private final String BLANK = " ";
        private final String CRLF = "\r\n";
    
        public Request(Socket client) throws IOException {
            this(client.getInputStream());
        }
    
        public Request(InputStream is) {
            byte[] datas = new byte[1024 * 1024];
            int len;
            try {
                len = is.read(datas);
                this.requestInfo = new String(datas, 0, len);
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
            // 分解字符串
            parseRequestInfo();
        }
    
        private void parseRequestInfo() {
            System.out.println("---分解---");
            System.out.println("1. 獲取請求方式:開頭到第一個/");
            this.method = this.requestInfo.substring(0, this.requestInfo.indexOf("/")).toLowerCase().trim();
            System.out.println("2. 獲取請求url:第一個/ 到HTTP/");
            System.out.println("可能包含請求參數?前面的url");
            // 1. 獲取/的位置
            int startIdx = this.requestInfo.indexOf("/") + 1;
            // 2. 獲取HTTP/的位置
            int endIdx = this.requestInfo.indexOf("HTTP/");
            // 3. 分割字符串
            this.url = this.requestInfo.substring(startIdx, endIdx);
            // 4. 獲取?的位置
            int queryIdx = this.url.indexOf("?");
            if (queryIdx >= 0) {
                // 表示存在請求參數
                String[] urlArray = this.url.split("\\?");
                this.url = urlArray[0].trim();
                queryStr = urlArray[1].trim();
            }
            System.out.println(this.url);
    
            System.out.println("3. 獲取請求參數:若果Get已經獲取,若是post可能在請求體中");
            if (method.equals("post")) {
                String qStr = this.requestInfo.substring(this.requestInfo.lastIndexOf(CRLF)).trim();
                if (null == queryStr) {
                    queryStr = qStr;
                } else {
                    queryStr += "&" + qStr;
                }
            }
            queryStr = null == queryStr?"": queryStr;
            System.out.println(method + "-->" + url + "-->" + queryStr);
        }
    }
    package com.sxt.Server_study02;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server04.java
     * @time: 2019/12/4 9:26
     * @desc: 封裝請求協議:獲取method uri以及請求參數
     */
    
    public class Server04 {
        private ServerSocket serverSocket;
        public static void main(String[] args) {
            Server04 server = new Server04();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            try {
                Socket client = serverSocket.accept();
                System.out.println("一個客戶端創建了鏈接...");
                // 獲取請求協議
                Request request = new Request(client);
    
                Response response = new Response(client);
    
                // 關注了內容
                response.print("<html>");
                response.print("<head>");
                response.print("<title>");
                response.print("服務器響應成功");
                response.print("</title>");
                response.print("</head>");
                response.print("<body>");
                response.print("終於回來了...");
                response.print("</body>");
                response.print("</html>");
    
                // 關注了狀態碼
                response.pushToBrowser(200);
    
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("客戶端錯誤...");
            }
        }
    
        // 中止服務
        public void stop() {
    
        }
    }
  • 分解參數

    package com.sxt.Server_study02;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.UnsupportedEncodingException;
    import java.net.Socket;
    import java.util.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Request2.java
     * @time: 2019/12/5 10:15
     * @desc: 封裝請求協議:封裝請求參數爲Map
     */
    
    public class Request2 {
        private String requestInfo;
        // 請求方式
        private String method;
        // 請求url
        private String url;
        // 請求參數
        private String queryStr;
        // 存儲參數
        private Map<String, List<String>> parameterMap;
    
        private final String BLANK = " ";
        private final String CRLF = "\r\n";
    
        public Request2(Socket client) throws IOException {
            this(client.getInputStream());
        }
    
        public String getMethod() {
            return method;
        }
    
        public String getUrl() {
            return url;
        }
    
        public String getQueryStr() {
            return queryStr;
        }
    
        public Request2(InputStream is) {
            parameterMap = new HashMap<>();
            byte[] datas = new byte[1024 * 1024];
            int len;
            try {
                len = is.read(datas);
                this.requestInfo = new String(datas, 0, len);
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
            // 分解字符串
            parseRequestInfo();
        }
    
        private void parseRequestInfo() {
            System.out.println("---分解---");
            System.out.println("1. 獲取請求方式:開頭到第一個/");
            this.method = this.requestInfo.substring(0, this.requestInfo.indexOf("/")).toLowerCase().trim();
            System.out.println("2. 獲取請求url:第一個/ 到HTTP/");
            System.out.println("可能包含請求參數?前面的url");
            // 1. 獲取/的位置
            int startIdx = this.requestInfo.indexOf("/") + 1;
            // 2. 獲取HTTP/的位置
            int endIdx = this.requestInfo.indexOf("HTTP/");
            // 3. 分割字符串
            this.url = this.requestInfo.substring(startIdx, endIdx);
            // 4. 獲取?的位置
            int queryIdx = this.url.indexOf("?");
            if (queryIdx >= 0) {
                // 表示存在請求參數
                String[] urlArray = this.url.split("\\?");
                this.url = urlArray[0].trim();
                queryStr = urlArray[1].trim();
            }
            System.out.println(this.url);
    
            System.out.println("3. 獲取請求參數:若果Get已經獲取,若是post可能在請求體中");
            if (method.equals("post")) {
                String qStr = this.requestInfo.substring(this.requestInfo.lastIndexOf(CRLF)).trim();
                if (null == queryStr) {
                    queryStr = qStr;
                } else {
                    queryStr += "&" + qStr;
                }
            }
            queryStr = null == queryStr?"": queryStr;
            System.out.println(method + "-->" + url + "-->" + queryStr);
            // 轉成Map    fav=1&fav=2&uname=shsxt&age=18&other=
            convertMap();
        }
    
        // 處理請求參數爲Map
        private void convertMap(){
            // 分割字符串 &
            String[] keyValues = this.queryStr.split("&");
            for(String queryStr: keyValues){
                // 再次分割字符串 =
                String[] kv = queryStr.split("=");
                // 保持兩個長度 key 和 value
                kv = Arrays.copyOf(kv, 2);
                // 獲取key 和 value
                String key = kv[0];
                String value = kv[1]==null? null: decode(kv[1], "utf-8");
                // 存儲在Map中
                if(!parameterMap.containsKey(key)){
                    // 容器裏面沒有,第一次
                    parameterMap.put(key, new ArrayList<String>());
                }
                parameterMap.get(key).add(value);
            }
        }
    
        // 處理中文
        private String decode(String value, String enc){
            try {
                return java.net.URLDecoder.decode(value, enc);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        // 經過name獲取對應的多個值
        public String[] getParameterValues(String key){
            List<String> values = this.parameterMap.get(key);
            if(null == values || values.size()<1){
                return null;
            }
            return values.toArray(new String[0]);
        }
    
        // 經過name獲取對應的一個值
        public String getParameter(String key){
            String[] values = getParameterValues(key);
            return values == null?null: values[0];
        }
    }
    package com.sxt.Server_study02;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server05.java
     * @time: 2019/12/4 9:26
     * @desc: 封裝請求信息中參數轉成map
     */
    
    public class Server05 {
        private ServerSocket serverSocket;
        public static void main(String[] args) {
            Server05 server = new Server05();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            try {
                Socket client = serverSocket.accept();
                System.out.println("一個客戶端創建了鏈接...");
                // 獲取請求協議
                Request2 request = new Request2(client);
    
                Response response = new Response(client);
    
                // 關注了內容
                response.print("<html>");
                response.print("<head>");
                response.print("<title>");
                response.print("服務器響應成功");
                response.print("</title>");
                response.print("</head>");
                response.print("<body>");
                response.print("終於回來了..." + request.getParameter("uname"));
                response.print("</body>");
                response.print("</html>");
    
                // 關注了狀態碼
                response.pushToBrowser(200);
    
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("客戶端錯誤...");
            }
        }
    
        // 中止服務
        public void stop() {
    
        }
    }

11. 引入servlet

  • 加入了Servlet解耦了業務代碼

    package com.sxt.Server_study03;
    
    import com.sxt.tcp.Server;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server06.java
     * @time: 2019/12/4 9:26
     * @desc: 加入了Servlet解耦了業務代碼
     */
    
    public class Server06 {
        private ServerSocket serverSocket;
        public static void main(String[] args) {
            Server06 server = new Server06();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            try {
                Socket client = serverSocket.accept();
                System.out.println("一個客戶端創建了鏈接...");
                // 獲取請求協議
                Request request = new Request(client);
                Response response = new Response(client);
    
                // 關注了內容
                Servlet servlet = null;
                if(request.getUrl().equals("login")){
                    servlet = new LoginServlet();
                }else if (request.getUrl().equals("reg")){
                    servlet = new RegisterServlet();
                }else {
                    // 首頁
                }
    
                servlet.service(request, response);
    
    
                // 關注了狀態碼
                response.pushToBrowser(200);
    
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("客戶端錯誤...");
            }
        }
    
        // 中止服務
        public void stop() {
    
        }
    }
  • Request和Response同上

  • Servlet

    package com.sxt.Server_study03;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Servlet.java
     * @time: 2019/12/9 12:03
     * @desc: 服務器小腳本接口
     */
    
    public interface Servlet {
        void service(Request request, Response response);
    }
  • RegisterServlet

    package com.sxt.Server_study03;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: RegisterServlet.java
     * @time: 2019/12/3 15:59
     * @desc:
     */
    
    public class RegisterServlet implements Servlet{
        @Override
        public void service(Request request, Response response){
            response.print("註冊成功...");
        }
    }
  • LoginServlet

    package com.sxt.Server_study03;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: LoginServlet.java
     * @time: 2019/12/3 15:59
     * @desc:
     */
    
    public class LoginServlet implements Servlet {
        @Override
        public void service(Request request, Response response) {
            response.print("<html>");
            response.print("<head>");
            response.print("<title>");
            response.print("第一個servlet");
            response.print("</title>");
            response.print("</head>");
            response.print("<body>");
            response.print("歡迎回來..." + request.getParameter("uname"));
            response.print("</body>");
            response.print("</html>");
        }
    }

12. 整合webxml

  • 將以前的Mapping,Entity,WebContext,WebHandler拷貝到工程中

  • 解析web.xml代碼

    package com.sxt.Server_study03;
    
    import com.sxt.Server_study01.servlet.WebContext;
    
    import javax.xml.parsers.SAXParser;
    import javax.xml.parsers.SAXParserFactory;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: WebApp.java
     * @time: 2019/12/9 12:37
     * @desc: 解析代碼
     */
    
    public class WebApp {
        private static WebContext context;
    
        static {
            try {
                // SAX解析
                // 1. 獲取解析工廠
                SAXParserFactory factory = SAXParserFactory.newInstance();
                // 2. 從解析工廠獲取解析器
                SAXParser parse = null;
                parse = factory.newSAXParser();
                // 3. 加載文檔Document註冊處理器
                // 4. 編寫處理器
                WebHandler handler = new WebHandler();
                parse.parse(Thread.currentThread().getContextClassLoader().getResourceAsStream("web.xml"), handler);
                // 5. 獲取數據
                context = new WebContext(handler.getEntitys(), handler.getMappings());
    
            } catch (Exception e) {
                System.out.println("解析配置文件錯誤!");
            }
        }
    
        // 經過url獲取配置文件對應的servlet
        public static Servlet getServletFromUrl(String url) {
            // 假設你輸入了 /login or /g or /reg
            String className = context.getClz("/" + url);
            Class clz = null;
            try {
                clz = Class.forName(className);
                Servlet servlet = (Servlet) clz.getConstructor().newInstance();
                return servlet;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
  • 新增OthersServlet

    package com.sxt.Server_study03;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: OthersServlet.java
     * @time: 2019/12/9 13:17
     * @desc: 其餘測試頁面
     */
    
    public class OthersServlet implements Servlet{
        @Override
        public void service(Request request, Response response) {
            response.print("其餘測試頁面...");
        }
    }
  • 修改xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app>
        <servlet>
            <servlet-name>login</servlet-name>
            <servlet-class>com.sxt.Server_study03.LoginServlet</servlet-class>
        </servlet>
        <servlet>
            <servlet-name>reg</servlet-name>
            <servlet-class>com.sxt.Server_study03.RegisterServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>login</servlet-name>
            <url-pattern>/login</url-pattern>
            <url-pattern>/g</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>reg</servlet-name>
            <url-pattern>/reg</url-pattern>
        </servlet-mapping>
        <servlet>
            <servlet-name>others</servlet-name>
            <servlet-class>com.sxt.Server_study03.OthersServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>others</servlet-name>
            <url-pattern>/o</url-pattern>
        </servlet-mapping>
    </web-app>
  • 整合配置文件

    package com.sxt.Server_study03;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server06.java
     * @time: 2019/12/4 9:26
     * @desc: 整合配置文件
     */
    
    public class Server07 {
        private ServerSocket serverSocket;
        public static void main(String[] args) {
            Server07 server = new Server07();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            try {
                Socket client = serverSocket.accept();
                System.out.println("一個客戶端創建了鏈接...");
                // 獲取請求協議
                Request request = new Request(client);
                Response response = new Response(client);
    
                Servlet servlet = WebApp.getServletFromUrl(request.getUrl());
                if(null != servlet){
                    servlet.service(request, response);
                    // 關注了狀態碼
                    response.pushToBrowser(200);
                }else {
                    // 錯誤頁面...
                    response.pushToBrowser(404);
                }
    
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("客戶端錯誤...");
            }
        }
    
        // 中止服務
        public void stop() {
    
        }
    }

13. 高效分發器

  • 多線程處理,加入分發器

    package com.sxt.Server_study03;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server08.java
     * @time: 2019/12/4 9:26
     * @desc: 多線程處理,加入分發器
     */
    
    public class Server08 {
        private ServerSocket serverSocket;
        private boolean isRunning;
    
        public static void main(String[] args) {
            Server08 server = new Server08();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                isRunning = true;
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
                stop();
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            while (isRunning) {
                try {
                    Socket client = serverSocket.accept();
                    System.out.println("一個客戶端創建了鏈接...");
                    // 多線程處理
                    new Thread(new Dispatcher(client)).start();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("客戶端錯誤...");
                }
            }
        }
    
        // 中止服務
        public void stop() {
            isRunning = false;
            try {
                this.serverSocket.close();
                System.out.println("服務器已中止...");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  • 分發器

    package com.sxt.Server_study03;
    
    import java.io.IOException;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Dispatcher.java
     * @time: 2019/12/12 16:36
     * @desc: 分發器
     */
    
    public class Dispatcher implements Runnable {
        private Socket client;
        private Request request;
        private Response response;
    
        public Dispatcher(Socket client) {
            this.client = client;
            try {
                // 獲取請求和響應
                request = new Request(client);
                response = new Response(client);
            } catch (IOException e) {
                e.printStackTrace();
                this.release();
            }
        }
    
        @Override
        public void run() {
            try {
                Servlet servlet = WebApp.getServletFromUrl(request.getUrl());
                if (null != servlet) {
                    servlet.service(request, response);
                    // 關注了狀態碼
                    response.pushToBrowser(200);
                } else {
                    // 錯誤頁面...
                    response.pushToBrowser(404);
                }
            }catch (Exception e){
                try {
                    response.pushToBrowser(500);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            release();
        }
    
        // 釋放資源
        private void release() {
            try {
                client.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

14. 經典404及首頁處理

  • 這一部分有問題is.readAllBytes()這裏報錯,而且也沒有找到解決辦法

  • 分發器

    package com.sxt.Server_study03;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.Socket;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Dispatcher.java
     * @time: 2019/12/12 16:36
     * @desc: 分發器
     */
    
    public class Dispatcher implements Runnable {
        private Socket client;
        private Request request;
        private Response response;
    
        public Dispatcher(Socket client) {
            this.client = client;
            try {
                // 獲取請求和響應
                request = new Request(client);
                response = new Response(client);
            } catch (IOException e) {
                e.printStackTrace();
                this.release();
            }
        }
    
        @Override
        public void run() {
            try {
                if (null == request.getUrl() || request.getUrl().equals("")) {
                    InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("index.html");
                    response.print(new String(is.readAllBytes()));
                    response.println(new String(Files.readAllBytes(Paths.get("index.html"))));
                    response.pushToBrowser(200);
                    is.close();
                    return;
                }
                Servlet servlet = WebApp.getServletFromUrl(request.getUrl());
                if (null != servlet) {
                    servlet.service(request, response);
                    // 關注了狀態碼
                    response.pushToBrowser(200);
                } else {
                    InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("error.html");
                    response.print(new String(is.readAllBytes()));
                    response.pushToBrowser(404);
                    is.close();
                }
            } catch (Exception e) {
                try {
                    response.print("你好我很差,我會立刻好");
                    response.pushToBrowser(500);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            release();
        }
    
        // 釋放資源
        private void release() {
            try {
                client.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
  • 處理404/505和首頁

    package com.sxt.Server_study03;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Server08.java
     * @time: 2019/12/4 9:26
     * @desc: 處理404/505和首頁
     */
    
    public class Server09 {
        private ServerSocket serverSocket;
        private boolean isRunning;
    
        public static void main(String[] args) {
            Server09 server = new Server09();
            server.start();
        }
    
        // 啓動服務
        public void start() {
            try {
                serverSocket = new ServerSocket(8888);
                isRunning = true;
                receive();
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服務器啓動失敗...");
                stop();
            }
        }
    
        // 接受鏈接處理
        public void receive() {
            while (isRunning) {
                try {
                    Socket client = serverSocket.accept();
                    System.out.println("一個客戶端創建了鏈接...");
                    // 多線程處理
                    new Thread(new Dispatcher(client)).start();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("客戶端錯誤...");
                }
            }
        }
    
        // 中止服務
        public void stop() {
            isRunning = false;
            try {
                this.serverSocket.close();
                System.out.println("服務器已中止...");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

第2章 註解+反射+字節碼+類加載機制

1. 內置註解

  • Annotation

  • 做用

    • 不是程序自己,能夠對程序做出解釋。
    • 能夠被其餘程序(好比:編譯器等)讀取。
  • 格式

    • @註解名
    package com.sxt.test.annotation;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2019/12/16 9:34
     * @desc:
     */
    
    public class Demo01 {
        @Override
        // 重寫父類方法
        public String toString() {
            return "";
        }
    
        @Deprecated
        // 該方法不建議使用
        public static void test1() {
            System.out.println("你大爺");
        }
    
        @SuppressWarnings("all")
        // 不顯示全部警告信息
        public static void test2() {
            List list = new ArrayList();
        }
    
        @SuppressWarnings(value = {"unchecked", "deprecation"})
        // 不顯示某幾個警告信息
        public static void main(String[] args) {
            test1();
        }
    }

2. 自定義註解

  • 使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口
  • 要點
    • interface用來聲明一個註解
    • 格式爲:public @interface 註解名 {定義體}
  • 其中的每個方法其實是聲明瞭一個配置參數
    • 方法的名稱就是參數的名稱
    • 返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。
    • 能夠經過default來聲明參數的默認值
    • 若是隻有一個參數成員,通常參數名爲value
  • 元註解
    • 負責註解其餘註解。
    • @Target:用於描述註解的使用範圍
    • @Retention:表示須要在什麼級別保存該註解信息,用於描述註解的生命週期
    • @Documented
    • @Inherited

3. 反射機制讀取註解

  • ORM:Object Relationship Mapping,對象關係映射

    • 類和表結構對應
    • 屬性和字段對應
    • 對象和記錄對應
  • 使用註解完成類和表結構的映射關係

    • 類註解

      package com.sxt.test.annotation;
      
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: LTTable.java
       * @time: 2019/12/16 12:44
       * @desc:
       */
      
      @Target(value = {ElementType.TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface LTTable {
          String value();
      }
    • 屬性註解

      package com.sxt.test.annotation;
      
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: LTField.java
       * @time: 2019/12/16 12:46
       * @desc: 說明屬性的特徵
       */
      
      @Target(value = {ElementType.FIELD})
      @Retention(RetentionPolicy.RUNTIME)
      public @interface LTField {
          String columnName();
          String type();
          int length();
      }
    • 學生類

      package com.sxt.test.annotation;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Student.java
       * @time: 2019/12/16 12:43
       * @desc:
       */
      
      @LTTable("tb_student")
      public class Student {
      
          @LTField(columnName = "id", type = "int", length = 10)
          private int id;
          @LTField(columnName = "sname", type = "varchar", length = 10)
          private String studentName;
          @LTField(columnName = "age", type = "int", length = 3)
          private int age;
      
          public int getId() {
              return id;
          }
      
          public void setId(int id) {
              this.id = id;
          }
      
          public String getStudentName() {
              return studentName;
          }
      
          public void setStudentName(String studentName) {
              this.studentName = studentName;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      }
    • 使用反射讀取註解的信息,模擬處理註解信息的流程

      package com.sxt.test.annotation;
      
      import java.lang.annotation.Annotation;
      import java.lang.reflect.Field;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo03.java
       * @time: 2019/12/16 12:49
       * @desc: 使用反射讀取註解的信息,模擬處理註解信息的流程
       */
      
      public class Demo03 {
          public static void main(String[] args) {
              try {
                  Class clazz = Class.forName("com.sxt.test.annotation.Student");
      
                  // 得到類的全部有效註解
                  Annotation[] annotations = clazz.getAnnotations();
                  for (Annotation a : annotations) {
                      System.out.println(a);
                  }
      
                  // 得到類的指定的註解
                  LTTable st = (LTTable) clazz.getAnnotation(LTTable.class);
                  System.out.println(st.value());
      
                  // 得到類的屬性的註解
                  Field f = clazz.getDeclaredField("studentName");
                  LTField ltField = f.getAnnotation(LTField.class);
                  System.out.println(ltField.columnName()+"-->"+ltField.type()+"-->"+ltField.length());
      
                  // 根據得到的代表、字段的信息,拼出DDL語句,而後使用JDBC執行這個SQL,在數據庫中生成相關的表
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }

4. 反射機制

  • 介紹+Class對象獲取

    • 動態語言:程序運行時,能夠改變程序結構或變態類型。(如python、javascript)

    • Java不是動態語言,但能夠稱爲「準動態語言」。Java有必定的動態性,咱們能夠利用反射機制、字節碼操做得到相似動態語言的特性。

    • 反射機制

      • 指的是能夠於運行時加載、探知、使用編譯期間徹底未知的類。
    • 測試各類類型對應的java.lang.Class對象的獲取方式

      package com.sxt.test.reflection;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo01.java
       * @time: 2019/12/17 18:59
       * @desc: 測試各類類型對應的java.lang.Class對象的獲取方式
       */
      
      public class Demo01 {
          public static void main(String[] args) {
              String path = "com.sxt.test.bean.User";
              try {
                  Class clazz = Class.forName(path);
                  // 對象是表示或封裝一些數據。一個類被加載後,JVM會建立一個對應該類的Class對象,
                  // 類的整個結構信息會放到對應的Class對象中。這個Class對象就像一面鏡子同樣,
                  // 經過這面鏡子咱們能夠看到對應類的所有信息。
                  System.out.println(clazz);
                  System.out.println(clazz.hashCode());
      
                  // 一個類只對應一個Class對象
                  Class clazz2 = Class.forName(path);
                  System.out.println(clazz2.hashCode());
      
                  Class strClazz = String.class;
                  Class strClazz2 = path.getClass();
                  // 得到的都是String的Class對象
                  System.out.println(strClazz==strClazz2);
      
                  Class intClazz = int.class;
                  System.out.println(intClazz.hashCode());
      
                  // 數組跟維度、類型都有關
                  int[] arr01 = new int[10];
                  int[] arr02 = new int[30];
                  int[][] arr03 = new int[10][10];
                  double[] arr04 = new double[10];
                  System.out.println(arr01.getClass().hashCode() == arr02.getClass().hashCode());
                  System.out.println(arr01.getClass().hashCode() == arr03.getClass().hashCode());
                  System.out.println(arr01.getClass().hashCode() == arr04.getClass().hashCode());
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
  • 動態操做+構造器+方法+屬性

    • 應用反射的API獲取類的信息(類的名字、屬性、方法、構造器等)

      package com.sxt.test.reflection;
      
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Field;
      import java.lang.reflect.Method;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo02.java
       * @time: 2020/1/7 14:33
       * @desc: 應用反射的API獲取類的信息(類的名字、屬性、方法、構造器等)
       */
      
      public class Demo02 {
          public static void main(String[] args) {
              String path = "com.sxt.test.bean.User";
              try {
                  Class clazz = Class.forName(path);
      
                  // 獲取類的全名
                  System.out.println(clazz.getName());
                  // 獲取類的包名+全名
                  System.out.println(clazz.getSimpleName());
      
                  // 獲取屬性信息
                  // 只能得到public的field
                  // Field[] fields = clazz.getFields();
                  // 返回全部的field
                  Field[] fields = clazz.getDeclaredFields();
                  System.out.println(fields.length);
                  for(Field temp: fields){
                      System.out.println("屬性:" + temp);
                  }
      
                  // 經過名稱獲取屬性
                  Field f = clazz.getDeclaredField("uname");
                  System.out.println(f);
      
                  // 獲取方法信息
                  Method[] methods = clazz.getDeclaredMethods();
                  Method m1 = clazz.getDeclaredMethod("getUname");
                  Method m2 = clazz.getDeclaredMethod("setUname", String.class);
                  for(Method temp: methods){
                      System.out.println("方法:" + temp);
                  }
      
                  // 獲取構造器信息
                  Constructor[] constructors = clazz.getDeclaredConstructors();
                  for(Constructor temp: constructors){
                      System.out.println("構造器:" + temp);
                  }
                  // 獲取某個特定的構造器
                  Constructor c1 = clazz.getDeclaredConstructor();
                  System.out.println("無參構造器:" + c1);
                  Constructor c2 = clazz.getDeclaredConstructor(int.class, int.class, String.class);
                  System.out.println("有參構造器:" + c2);
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
    • 經過反射API動態的操做:構造器、方法、屬性

      package com.sxt.test.reflection;
      
      import com.sxt.test.bean.User;
      
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Field;
      import java.lang.reflect.Method;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo3.java
       * @time: 2020/1/8 17:03
       * @desc: 經過反射API動態的操做:構造器、方法、屬性
       */
      
      public class Demo3 {
          public static void main(String[] args) {
              String path = "com.sxt.test.bean.User";
              try {
                  Class<User> clazz = (Class<User>) Class.forName(path);
      
                  // 經過反射API調用構造方法,構造對象
                  // 實際上是調用了User的無參構造方法
                  User u = clazz.newInstance();
                  System.out.println(u);
      
                  // 指定構造器的調用
                  Constructor<User> c = clazz.getDeclaredConstructor(int.class, int.class, String.class);
                  User u2 = c.newInstance(1001, 18, "李英俊");
                  System.out.println(u2.getUname());
      
                  // 經過反射API調用普通方法
                  User u3 = clazz.newInstance();
                  u3.setUname("李不羈");
                  // 上一句用反射來寫以下
                  Method method = clazz.getDeclaredMethod("setUname", String.class);
                  method.invoke(u3, "李不羈");
                  System.out.println(u3.getUname());
      
                  // 經過反射API操做屬性
                  User u4 = clazz.newInstance();
                  Field f = clazz.getDeclaredField("uname");
                  // 這個屬性不須要作安全檢查了,能夠直接訪問
                  f.setAccessible(true);
                  // 經過反射直接寫屬性
                  f.set(u4, "李傻瓜");
                  System.out.println(u4.getUname());
                  // 經過反射直接讀屬性的值
                  System.out.println(f.get(u4));
      
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
  • 提升反射效率+操做泛型+操做註解

    • setAccessible

      • 啓用和禁用訪問安全檢查的開關,值爲true則指示反射的對象在使用時應該取消java語言訪問檢查。值爲false則指示反射的對象應該實施java語言訪問檢查。並非爲true就能訪問,爲false就不能訪問。
      • 禁止安全檢查,能夠提升反射的運行速度。
    • 反射操做泛型(Generic)

      • java採用泛型擦除的機制來引入泛型。java中的泛型僅僅是給編譯器javac使用的,確保數據的安全性和免去強制類型轉換的麻煩。可是,一旦變異完成,全部的和泛型有關的類型所有擦除。
      • 爲了經過反射操做這些類型以迎合實際開發的須要,java就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType幾種類型來表明不能被歸一到Class類中的類型可是又和原始類型齊名的類型
    • 操做泛型

      package com.sxt.test.reflection;
      
      import com.sxt.test.bean.User;
      
      import java.lang.reflect.Method;
      import java.lang.reflect.ParameterizedType;
      import java.lang.reflect.Type;
      import java.util.List;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo4.java
       * @time: 2020/1/9 10:44
       * @desc: 操做泛型
       */
      
      public class Demo4 {
          public void test01(Map<String, User> map, List<User> list){
              System.out.println("Demo04.test01()");
          }
      
          public Map<Integer, User> test02(){
              System.out.println("Demo04.test02()");
              return null;
          }
      
          public static void main(String[] args){
              try{
                  // 得到指定方法參數泛型信息
                  Method m = Demo4.class.getMethod("test01", Map.class, List.class);
                  Type[] t = m.getGenericParameterTypes();
                  for (Type paramType: t){
                      System.out.println("#" + paramType);
                      if(paramType instanceof ParameterizedType){
                          Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
                          for (Type genericType: genericTypes){
                              System.out.println("泛型類型:" + genericType);
                          }
                      }
                  }
      
                  // 得到指定方法返回值泛型信息
                  Method m2 = Demo4.class.getMethod("test02");
                  Type returnType = m2.getGenericReturnType();
                  if(returnType instanceof ParameterizedType){
                      Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();
      
                      for (Type genericType: genericTypes){
                              System.out.println("返回值,泛型類型:" + genericType);
                          }
                  }
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              }
          }
      }
    • 操做註解

      package com.sxt.test.annotation;
      
      import java.lang.annotation.Annotation;
      import java.lang.reflect.Field;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo5.java
       * @time: 2020/1/9 10:57
       * @desc: 操做註解
       */
      
      public class Demo5 {
          public static void main(String[] args) {
              try {
                  Class clazz = Class.forName("com.sxt.test.annotation.Student");
      
                  // 得到類的全部有效註解
                  Annotation[] annotations = clazz.getAnnotations();
                  for (Annotation a : annotations) {
                      System.out.println(a);
                  }
      
                  // 得到類指定的註解
                  LTTable st = (LTTable) clazz.getAnnotation(LTTable.class);
                  System.out.println(st.value());
      
                  // 得到類的屬性的註解
                  Field f = clazz.getDeclaredField("studentName");
                  LTField ltField = f.getAnnotation(LTField.class);
                  System.out.println(ltField.columnName() + "--" + ltField.type() + "--" + ltField.length());
      
                  // 根據得到的表名、字段的信息,拼出DDL語句,而後,使用JDBC執行這個SQL,在數據庫中生成相關的表
      
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }

5. 動態編譯

  • 經過JavaCompiler動態編譯

    public static int compileFile(String sourceFile){
    	// 動態編譯
    	JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    	int result = compiler.run(null, null, null, sourceFile);
    	System.out.println(result==0?"編譯成功": "編譯失敗");
    	return result;
    }
    • 參數1:爲java編譯器提供參數
    • 參數2:獲得Java編譯器的輸出信息
    • 參數3:接受編譯器的錯誤信息
    • 參數4:可變參數(是一個String數組)能傳入一個或多個java源文件
    • 返回值:0表示編譯成功,非0表示編譯失敗
    package com.sxt.test.testDynamicCompile;
    
    import javax.tools.JavaCompiler;
    import javax.tools.ToolProvider;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.lang.reflect.Method;
    import java.net.URL;
    import java.net.URLClassLoader;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo1.java
     * @time: 2020/1/9 14:38
     * @desc: 動態編譯
     */
    
    public class Demo1 {
        public static void main(String[] args) throws IOException {
    
            // 若是是給的字符串的話,能夠
            // 經過IO流操做,將字符串存儲成一個臨時文件,而後調用動態編譯方法!
            // 若是是文件的話,就按下面的方法
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            int result = compiler.run(null, null, null, "D:\\java_test\\HelloWorld.java");
            System.out.println(result == 0 ? "編譯成功" : "編譯失敗");
    
            // 上面只是進行了編譯,下面還要進行動態運行編譯好的類
            
            // 1. 經過Runtime調用執行類
            Runtime run = Runtime.getRuntime();
            Process process = run.exec("java -cp D:\\java_test HelloWorld");
            InputStream in = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            String info = "";
            while ((info = reader.readLine()) != null) {
                System.out.println(info);
            }
    
            // 2. 經過反射調用執行類
            try {
                URL[] urls = new URL[]{new URL("file:/" + "D:\\java_test\\")};
                URLClassLoader loader = new URLClassLoader(urls);
                Class c = loader.loadClass("HelloWorld");
                // 調用加載類的main方法
                Method m = c.getMethod("main", String[].class);
                // 若是這裏不用Object強制轉型的話,invoke後面傳入的就不是一個String
                // 的字符串數組,而是兩個字符串,認爲是兩個參數,因而就會發生參數個數不匹配的問題
                m.invoke(null, (Object) new String[]{});
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

6. 腳本引擎執行JavaScript代碼__Rhino引擎

  • JAVA腳本引擎是從JDK6.0以後添加的新功能

    • 使得Java應用程序能夠經過一套固定的藉口與各類腳本引擎交互,從而達到在Java平臺上調用各類腳本語言的目的。
    • Java腳本API是連通Java平臺和腳本語言的橋樑。
    • 能夠把一些複雜異變的業務邏輯交給腳本語言處理,這又大大提升了開發效率。
  • 在寫Demo的時候存在一些報錯的狀況,這是由於JDK6.0的語法在JDK8.0中已通過時了

    • println報錯:改爲print
    • importPackage報錯:改爲不導入包,後面直接使用java.util.Arrays.asList
    function test(){
        var a = 3;
        var b = 4;
        print("invoke js file: " + (a+b));
    }
    
    // 執行test方法
    test();
    package com.sxt.test.testRhino;
    
    import javax.script.Invocable;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import java.io.FileReader;
    import java.net.URL;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2020/1/13 15:58
     * @desc: 測試腳本引擎執行JavaScript代碼
     */
    
    public class Demo01 {
        public static void main(String[] args) throws Exception {
            // 得到腳本引擎的對象
            ScriptEngineManager sem = new ScriptEngineManager();
            ScriptEngine engine = sem.getEngineByName("javascript");
    
            // 定義變量,存儲到引擎上下文中
            engine.put("msg", "liyingjun is g good man!");
            String str = "var user = {name: 'litian', age: 18, schools: ['清華大學', '北京尚學堂']};";
            str += "print(user.name);";
    
            // 執行腳本
            engine.eval(str);
            engine.eval("msg = 'sxt is a good school!'");
            System.out.println(engine.get("msg"));
            System.out.println("########################");
    
            // 定義函數
            engine.eval("function add(a, b){var sum = a + b; return sum;}");
            // 取得調用接口
            Invocable jsInvoke = (Invocable) engine;
            // 執行腳本中定義的方法
            Object result1 = jsInvoke.invokeFunction("add", new Object[]{13, 20});
            System.out.println(result1);
    
            // 導入其餘java包,使用其餘包中的java類
            String jsCode = "var list=java.util.Arrays.asList([\"北京尚學堂\", \"清華大學\", \"中國石油大學\"]);";
            engine.eval(jsCode);
    
            List<String> list2 = (List<String>) engine.get("list");
            for (String temp : list2) {
                System.out.println(temp);
            }
    
            // 執行一個js文件
            URL url = Demo01.class.getClassLoader().getResource("com/sxt/test/testRhino/test.js");
            FileReader fr = new FileReader(url.getPath());
            engine.eval(fr);
            fr.close();
        }
    }

7. 字節碼操做__javassist庫

  • Java動態性的兩種常見實現方式:

    • 字節碼操做
    • 反射
  • 運行時操做字節碼可讓咱們實現以下功能:

    • 動態生成新的類
    • 動態改變某個類的結構(添加/刪除/修改 新的屬性/方法)
  • 優點

    • 比反射開銷小,性能高
    • JAVAssist性能高於反射,低於ASM
  • 常見的字節碼操做類庫

    • BCEL
    • ASM
    • CGLIB
    • Javassist
  • 測試使用javassist生成一個新的類

    package com.sxt.test.testJavassist;
    
    import javassist.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo1.java
     * @time: 2020/1/14 9:21
     * @desc: 測試使用javassist生成一個新的類
     */
    
    public class Demo1 {
        public static void main(String[] args) throws Exception {
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.makeClass("com.sxt.test.testJavassist.EmpTest");
    
            // 建立屬性
            CtField f1 = CtField.make("private int empno;", cc);
            CtField f2 = CtField.make("private String ename;", cc);
            cc.addField(f1);
            cc.addField(f2);
    
            // 建立方法
            CtMethod m1 = CtMethod.make("public int getEmpno(){return empno;}", cc);
            CtMethod m2 = CtMethod.make("public void setEmpno(int empno){this.empno=empno;}", cc);
            cc.addMethod(m1);
            cc.addMethod(m2);
    
            // 添加構造器
            CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String")}, cc);
            constructor.setBody("{this.empno = empno; this.ename = ename;}");
            cc.addConstructor(constructor);
    
            // 將上面構造好的類寫入到下面的工做空間下面
            cc.writeFile("D:\\java_test");
            System.out.println("生成類,成功!");
    
        }
    }
  • 測試javassist的API

    • Emp.java

      package com.sxt.test.testJavassist;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Emp.java
       * @time: 2020/1/14 9:28
       * @desc:
       */
      
      @Author(name="litian", year=2020)
      public class Emp {
          private int empno;
          private String name;
      
          public int getEmpno() {
              return empno;
          }
      
          public void setEmpno(int empno) {
              this.empno = empno;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public Emp() {
          }
      
          public Emp(int empno, String name) {
              this.empno = empno;
              this.name = name;
          }
      
          public void sayHello(int a){
              System.out.println("Hello!: " + a);
          }
      }
    • Author.java

      package com.sxt.test.testJavassist;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Author.java
       * @time: 2020/1/14 14:50
       * @desc:
       */
      
      public @interface Author {
          String name();
          int year();
      }
    • 測試javassist的API

      package com.sxt.test.testJavassist;
      
      import javassist.*;
      
      import java.lang.reflect.Method;
      import java.util.Arrays;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo2.java
       * @time: 2020/1/14 10:45
       * @desc: 測試javassist的API
       */
      
      public class Demo2 {
          /*
            處理類的基本用法
           */
          public static void test1() throws Exception {
              ClassPool pool = ClassPool.getDefault();
              CtClass cc = pool.get("com.sxt.test.testJavassist.Emp");
      
              byte[] bytes = cc.toBytecode();
              System.out.println(Arrays.toString(bytes));
      
              // 獲取類名
              System.out.println(cc.getName());
              // 獲取簡要類名
              System.out.println(cc.getSimpleName());
              // 得到父類
              System.out.println(cc.getSuperclass());
              // 得到接口
              System.out.println(cc.getInterfaces());
          }
      
          public static void test2() throws Exception {
              /*
              測試產生新的方法
               */
              ClassPool pool = ClassPool.getDefault();
              CtClass cc = pool.get("com.sxt.test.testJavassist.Emp");
      
              // 新建方法1
              CtMethod m1 = CtNewMethod.make("public int add(int a, int b){return a+b;}", cc);
              // 新建方法2
              CtMethod m2 = new CtMethod(CtClass.intType, "add", new CtClass[]{CtClass.intType, CtClass.intType}, cc);
              // 設置權限
              m2.setModifiers(Modifier.PUBLIC);
              // 設置方法體 $1等等表明的是形參的佔位符
              m2.setBody("{System.out.println(\"沖沖衝!\"); return $1+$2;}");
      
              cc.addMethod(m2);
      
              // 經過反射調用新生成的方法
              Class clazz = cc.toClass();
              // 經過調用Emp無參構造器,建立新的Emp對象
              Object obj = clazz.newInstance();
              Method method = clazz.getDeclaredMethod("add", int.class, int.class);
              Object result = method.invoke(obj, 200, 300);
              System.out.println(result);
          }
      
          public static void test3() throws Exception {
              /*
              對已有的方法進行修改
               */
              ClassPool pool = ClassPool.getDefault();
              CtClass cc = pool.get("com.sxt.test.testJavassist.Emp");
      
              CtMethod cm = cc.getDeclaredMethod("sayHello", new CtClass[]{CtClass.intType});
              cm.insertBefore("System.out.println($1);System.out.println(\"start!!!\");");
              cm.insertAfter("System.out.println(\"end!!!\");");
              // 在某一行前面加代碼,從1開始計數,不存在迭代效應,也就是改行代碼的行數不會因加入了新的代碼而改變
              cm.insertAt(41, "System.out.println(\"???\");");
              cm.insertAt(42, "System.out.println(\"!!!\");");
      
              // 經過反射調用新生成的方法
              Class clazz = cc.toClass();
              Object obj = clazz.newInstance();
              Method method = clazz.getDeclaredMethod("sayHello", int.class);
              Object result = method.invoke(obj, 200);
              System.out.println(result);
      
          }
      
          public static void test4() throws Exception {
              /*
              對已有的屬性進行修改
               */
              ClassPool pool = ClassPool.getDefault();
              CtClass cc = pool.get("com.sxt.test.testJavassist.Emp");
      
              // CtField f1 = CtField.make("private int empno;", cc);
              CtField f1 = new CtField(CtClass.intType, "salary", cc);
              f1.setModifiers(Modifier.PRIVATE);
              // 後面的參數是默認值,若是不寫的話,就沒有默認值
              cc.addField(f1, "1000");
              // 獲取指定的屬性
              cc.getDeclaredField("salary");
      
              // 除了直接經過增長方法的方式提供getter和setter方法,還能夠經過如下方式
              cc.addMethod(CtNewMethod.getter("getSalary", f1));
              cc.addMethod(CtNewMethod.setter("setSalary", f1));
          }
      
          public static void test5() throws Exception {
              /*
              查看已有構造方法,並進行修改
               */
              ClassPool pool = ClassPool.getDefault();
              CtClass cc = pool.get("com.sxt.test.testJavassist.Emp");
      
              CtConstructor[] cs = cc.getConstructors();
              for (CtConstructor c : cs) {
                  System.out.println(c.getLongName());
                  c.insertBefore("System.out.println(\"what?\");");
              }
              // 經過反射調用新生成的方法
              Class clazz = cc.toClass();
              Object obj = clazz.newInstance();
          }
      
          public static void test6() throws Exception {
              /*
              註解
               */
              ClassPool pool = ClassPool.getDefault();
              CtClass cc = pool.get("com.sxt.test.testJavassist.Emp");
              Object[] all = cc.getAnnotations();
              Author a = (Author) all[0];
              String name = a.name();
              int year = a.year();
              System.out.println("name: " + name + "year: " + year);
          }
      
          public static void main(String[] args) throws Exception {
              test6();
          }
      }
  • Javassist庫的侷限性

    • 不支持泛型、枚舉等新語法,不支持註解修改,但能夠經過底層的javassist類來解決。
    • 不支持數組的初始化
    • 不支持內部類和匿名類
    • 不支持continue和break表達式
    • 對於集成關係,有些不支持

8. JVM核心機制

  • JVM運行和類加載全過程

    • 類的加載機制(JVM內存分析+反射機制核心原理+常量池理解)

      • JVM把class文件加載到內存,並對數據進行校驗、解析和初始化,最終造成JVM能夠直接使用的Java類型的過程。

      • 加載:將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲方法區類數據的訪問入口。

      • 連接:將Java類的二進制代碼合併到JVM的運行狀態之中的過程

        • 驗證:確保加載的類信息符合JVM規範,沒有安全方面的問題。
        • 準備:正式爲類變量(static變量)分配內存並設置類變量初始值的階段,這些內存都將在方法去中進行分配
        • 解析:虛擬機常量池內的符號引用替換爲直接引用的過程。
      • 初始化

        • 初始化階段是執行類構造器<clinit>()方法的過程。類構造器<clinit>()方法是由編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊(static塊)中的語句合併產生的。
        • 當初始化一個類額時候,若是發現其父類尚未進行過初始化、則須要先進行其父類的初始化
        • 虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確加鎖和同步。
        • 當訪問一個Java類的靜態域時,只有真正聲明這個域的類纔會被初始化。
      • 過程圖解

        過程圖解

    • 初始化時機+靜態初始化塊執行的順序問題

      • 靜態變量、靜態域、field本質上是一回事
      • 類構造方法就是把靜態域複製的動做以及靜態初始化塊合併在一塊兒造成一個類構造器
      • 先調類構造方法,而後才調普通方法;先調父類的類構造方法,而後才調子類的
      • 類的加載和初始化只執行一次。
    • 類的主動引用和被動引用

      • 類的主動引用(必定會發生類的初始化)
        • new一個類的對象
        • 調用類的靜態成員(除了final常量)和靜態方法
        • 使用java.lang.reflect包的方法對類進行反射調用
        • 當虛擬機啓動,java Hello,則必定會初始化Hello類,說白了就是先啓動main方法所在的類
        • 當初始化一個類,若是其父類沒有被初始化,則先會初始化它的父類
      • 類的被動引用(不會發生類的初始化)
        • 當訪問一個靜態域時,只有真正聲明這個域的類纔會被初始化(經過子類引用父類的靜態變量,不會致使子類初始化)
        • 經過數組定義類引用,不會觸發此類的初始化
        • 引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中了)
  • 深刻類加載器

    • 類加載器原理

      • 類加載器的做用:將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲方法區數據的訪問入口。
    • 類加載器樹狀結構、雙親委託(代理)機制

      • 分類:

        • 引導類加載器(C)
        • 擴展類加載器(JAVA)
        • 應用程序類加載器(JAVA)
        • 自定義類加載器(JAVA)
        • 除了引導類,其餘的類都要繼承ClassLoader,其基本職責就是根據一個指定的類的名稱,找到或者生成相應的字節代碼,而後從這些字節代碼中定義出一個Java類,即java.lang.Class類的一個實例;除此以外,ClassLoader還負責加載java應用所需的資源,如圖像文件和配置文件等。
      • 類加載器的代理模式

        • 交給其餘加載器來加載指定的類
        • 雙親委託機制代理模式的一種,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次追溯,直到最高的爺爺輩的,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。
        • 雙親委託機制是爲了保證Java核心庫的類型安全。這種機制就保證不會出現用戶本身能定義java.lang.Obejct類的狀況。
        • 類加載器除了用於加載類,也是安全的最基本的保障。
        • 並非全部的類加載器都採用雙親委託機制。(tomcat的加載順序是相反的)
      • 類加載器實戰

        package com.sxt.test.classLoader;
        
        /**
         * @author: Li Tian
         * @contact: litian_cup@163.com
         * @software: IntelliJ IDEA
         * @file: Demo01.java
         * @time: 2020/1/30 21:40
         * @desc:
         */
        
        public class Demo01 {
            public static void main(String[] args){
                // 應用加載器
                System.out.println(ClassLoader.getSystemClassLoader());
                // 擴展加載器(上一個的父類)
                System.out.println(ClassLoader.getSystemClassLoader().getParent());
                // 引導加載器,可是是C寫的,因此爲null(上一個的父類)
                System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
        
                // 系統中類的路徑
                System.out.println(System.getProperty("java.class.path"));
            }
        }
    • 自定義類加載器(文件、網路、加密)

      • 自定義加載器的流程:

        • 繼承java.lang.ClassLoader
        • 檢查請求的類型是否已經被這個類加載器裝載到命名空間了,若是已經裝載,直接返回;
        • 委派類加載請求給父類加載器,若是父類加載器可以完成,則返回父類加載器加載的Class實例;
        • 調用本類加載器的findClass(...)方法,試圖獲取對應的字節碼,若是獲取的到,則調用defineClass(...)導入類型到方法區;若是獲取不到對應的字節碼或者其餘緣由失敗,則返回異常給loadClass(...),loadClass(...)轉拋異常,終止加載過程。
        • 注意:被兩個類加載器加載的同一個類,JVM不認爲是相同的類。
      • 自定義文件系統類加載器

        • FileSystemClassLoader

          package com.sxt.test.classLoader;
          
          import java.io.*;
          
          /**
           * @author: Li Tian
           * @contact: litian_cup@163.com
           * @software: IntelliJ IDEA
           * @file: FileSystemClassLoader.java
           * @time: 2020/1/31 20:58
           * @desc: 自定義文件系統類加載器
           */
          
          public class FileSystemClassLoader extends ClassLoader {
              // com.sxt.test.bean.User --> F:/BookStudy/else/JAVAPro/src/com/sxt/test/bean/User.class
              private String rootDir;
          
              public FileSystemClassLoader(String rootDir) {
                  this.rootDir = rootDir;
              }
          
              @Override
              protected Class<?> findClass(String name) throws ClassNotFoundException {
                  Class<?> c = findLoadedClass(name);
          
                  // 應該要先查詢有沒有加載過這個類,若是已經加載,則直接返回加載好的類,若是沒有,則加載新的類。
                  if (c != null) {
                      return c;
                  } else {
                      ClassLoader parent = this.getParent();
                      // 委派給父類加載
                      try {
                          c = parent.loadClass(name);
                      }catch (Exception e){
                          System.out.println("父類加載器沒有加載到這個類哦!");
                      }
                      if (c != null) {
                          return c;
                      } else {
                          byte[] classData = getClassData(name);
                          if (classData == null) {
                              throw new ClassNotFoundException();
                          } else {
                              c = defineClass(name, classData, 0, classData.length);
                          }
                      }
                  }
                  return c;
              }
          
              private byte[] getClassData(String classname) {
                  String path = rootDir + "/" + classname.replace('.', '/') + ".class";
                  // 能夠使用IOUtils將流中的數據轉成字節數組,這裏採用手寫了
                  InputStream is = null;
                  ByteArrayOutputStream baos = null;
                  try {
                      is = new FileInputStream(path);
                      baos = new ByteArrayOutputStream();
                      byte[] buffer = new byte[1024];
                      int temp = 0;
                      while ((temp = is.read(buffer)) != -1) {
                          baos.write(buffer, 0, temp);
                      }
                      return baos.toByteArray();
                  } catch (IOException e) {
                      e.printStackTrace();
                      return null;
                  } finally {
                      try {
                          if (is != null) {
                              is.close();
                          }
                      }catch(IOException e){
                          e.printStackTrace();
                      }
                      try {
                          if (baos != null) {
                              baos.close();
                          }
                      }catch(IOException e){
                          e.printStackTrace();
                      }
                  }
              }
          }
        • 測試自定義類加載器FileSystemClassLoader

          package com.sxt.test.classLoader;
          
          /**
           * @author: Li Tian
           * @contact: litian_cup@163.com
           * @software: IntelliJ IDEA
           * @file: Demo02.java
           * @time: 2020/1/31 21:17
           * @desc: 測試自定義類加載器FileSystemClassLoader
           */
          
          public class Demo02 {
              public static void main(String[] args) throws ClassNotFoundException {
                  FileSystemClassLoader loader1 = new FileSystemClassLoader("F:\\Java WorkSpace");
                  FileSystemClassLoader loader2 = new FileSystemClassLoader("F:\\Java WorkSpace");
                  System.out.println(loader1 == loader2);
          
          
                  Class<?> c1 = loader1.loadClass("NewClass");
                  Class<?> c2 = loader1.loadClass("NewClass");
                  Class<?> c3 = loader2.loadClass("NewClass");
                  Class<?> c4 = loader1.loadClass("java.lang.String");
                  Class<?> c5 = loader1.loadClass("com.sxt.test.classLoader.Demo01");
          
                  System.out.println(c1);
                  System.out.println(c1.hashCode());
                  System.out.println(c2);
                  System.out.println(c2.hashCode());
          
                  // 注意:被兩個類加載器加載的同一個類,JVM不認爲是相同的類。
                  System.out.println(c3);
                  System.out.println(c3.hashCode());
          
                  System.out.println(c4);
                  System.out.println(c4.hashCode());
          
                  System.out.println(c1.getClassLoader());
                  System.out.println(c2.getClassLoader());
                  System.out.println(c3.getClassLoader());    // 自定義的類加載器
                  System.out.println(c4.getClassLoader());    // 引導類加載器
                  System.out.println(c5.getClassLoader());    // 系統默認的類加載器
              }
          }
      • 自定義網路類加載器

        package com.sxt.test.classLoader;
        
        import java.io.ByteArrayOutputStream;
        import java.io.IOException;
        import java.io.InputStream;
        import java.net.URL;
        
        /**
         * @author: Li Tian
         * @contact: litian_cup@163.com
         * @software: IntelliJ IDEA
         * @file: FileSystemClassLoader.java
         * @time: 2020/1/31 20:58
         * @desc: 自定義網路類加載器
         */
        
        public class NetSystemClassLoader extends ClassLoader {
            // com.sxt.test.bean.User --> www.sxt.cn/JAVAPro/src/com/sxt/test/bean/User.class
            private String rootUrl;
        
            public NetSystemClassLoader(String rootDir) {
                this.rootUrl = rootDir;
            }
        
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                Class<?> c = findLoadedClass(name);
        
                // 應該要先查詢有沒有加載過這個類,若是已經加載,則直接返回加載好的類,若是沒有,則加載新的類。
                if (c != null) {
                    return c;
                } else {
                    ClassLoader parent = this.getParent();
                    // 委派給父類加載
                    try {
                        c = parent.loadClass(name);
                    }catch (Exception e){
                        System.out.println("父類加載器沒有加載到這個類哦!");
                    }
                    if (c != null) {
                        return c;
                    } else {
                        byte[] classData = getClassData(name);
                        if (classData == null) {
                            throw new ClassNotFoundException();
                        } else {
                            c = defineClass(name, classData, 0, classData.length);
                        }
                    }
                }
                return c;
            }
        
            private byte[] getClassData(String classname) {
                String path = rootUrl + "/" + classname.replace('.', '/') + ".class";
                // 能夠使用IOUtils將流中的數據轉成字節數組,這裏採用手寫了
                InputStream is = null;
                ByteArrayOutputStream baos = null;
                try {
                    URL url = new URL(path);
                    is = url.openStream();
                    baos = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int temp = 0;
                    while ((temp = is.read(buffer)) != -1) {
                        baos.write(buffer, 0, temp);
                    }
                    return baos.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    try {
                        if (is != null) {
                            is.close();
                        }
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                    try {
                        if (baos != null) {
                            baos.close();
                        }
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                }
            }
        }
      • 自定義加密解密類加載器

        • 加密工具類

          package com.sxt.test.classLoader;
          
          import java.io.*;
          
          /**
           * @author: Li Tian
           * @contact: litian_cup@163.com
           * @software: IntelliJ IDEA
           * @file: EncrpUtil.java
           * @time: 2020/2/1 22:52
           * @desc: 加密工具類
           */
          
          public class EncrpUtil {
              public static void main(String[] args) {
                  encrpt("F:/Java WorkSpace/NewClass.class", "F:/Java WorkSpace/temp/NewClass.class");
              }
          
              public static void encrpt(String src, String dest) {
                  FileInputStream fis = null;
                  FileOutputStream fos = null;
          
                  try {
                      fis = new FileInputStream(src);
                      fos = new FileOutputStream(dest);
          
                      int temp = -1;
                      while ((temp = fis.read()) != -1) {
                          // 取反操做
                          fos.write(temp ^ 0xff);
                      }
          
                  } catch (IOException e) {
                      e.printStackTrace();
                  } finally {
                      try {
                          if (fis != null) {
                              fis.close();
                          }
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
                  try {
                      if (fos != null) {
                          fos.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
        • 解密工具類:加載文件系統中加密後的class字節碼的類加載器

          package com.sxt.test.classLoader;
          
          import java.io.ByteArrayOutputStream;
          import java.io.FileInputStream;
          import java.io.IOException;
          import java.io.InputStream;
          
          /**
           * @author: Li Tian
           * @contact: litian_cup@163.com
           * @software: IntelliJ IDEA
           * @file: DecrptClassLoader.java
           * @time: 2020/2/1 23:03
           * @desc: 解密工具類:加載文件系統中加密後的class字節碼的類加載器
           */
          
          public class DecrptClassLoader extends ClassLoader {
              // com.sxt.test.bean.User --> F:/BookStudy/else/JAVAPro/src/com/sxt/test/bean/User.class
              private String rootDir;
          
              public DecrptClassLoader(String rootDir) {
                  this.rootDir = rootDir;
              }
          
              @Override
              protected Class<?> findClass(String name) throws ClassNotFoundException {
                  Class<?> c = findLoadedClass(name);
          
                  // 應該要先查詢有沒有加載過這個類,若是已經加載,則直接返回加載好的類,若是沒有,則加載新的類。
                  if (c != null) {
                      return c;
                  } else {
                      ClassLoader parent = this.getParent();
                      // 委派給父類加載
                      try {
                          c = parent.loadClass(name);
                      } catch (Exception e) {
                          System.out.println("父類加載器沒有加載到這個類哦!");
                      }
                      if (c != null) {
                          return c;
                      } else {
                          byte[] classData = getClassData(name);
                          if (classData == null) {
                              throw new ClassNotFoundException();
                          } else {
                              c = defineClass(name, classData, 0, classData.length);
                          }
                      }
                  }
                  return c;
              }
          
              private byte[] getClassData(String classname) {
                  String path = rootDir + "/" + classname.replace('.', '/') + ".class";
                  // 能夠使用IOUtils將流中的數據轉成字節數組,這裏採用手寫了
                  InputStream is = null;
                  ByteArrayOutputStream baos = null;
                  try {
                      is = new FileInputStream(path);
                      baos = new ByteArrayOutputStream();
                      int temp = -1;
                      while ((temp = is.read()) != -1) {
                          // 取反操做,進行解密
                          baos.write(temp ^ 0xff);
                      }
                      return baos.toByteArray();
                  } catch (IOException e) {
                      e.printStackTrace();
                      return null;
                  } finally {
                      try {
                          if (is != null) {
                              is.close();
                          }
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                      try {
                          if (baos != null) {
                              baos.close();
                          }
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
        • 測試簡單加密解密(取反)操做

          package com.sxt.test.classLoader;
          
          /**
           * @author: Li Tian
           * @contact: litian_cup@163.com
           * @software: IntelliJ IDEA
           * @file: Demo03.java
           * @time: 2020/2/1 22:49
           * @desc: 測試簡單加密解密(取反)操做
           */
          
          public class Demo03 {
              public static void main(String[] args) throws ClassNotFoundException {
                  int a = 3;  // 00000011
                  System.out.println(Integer.toBinaryString(a ^ 0xff));
          
                  // 加載這個加密的類會報類格式錯誤ClassFormatError
                  FileSystemClassLoader loader1 = new FileSystemClassLoader("F:/Java WorkSpace");
                  Class<?> c1 = loader1.loadClass("NewClass_encrp");
                  System.out.println(c1);
          
                  // 使用解密類加載器加載加密後的類
                  DecrptClassLoader loader = new DecrptClassLoader("F:/Java WorkSpace/temp");
                  Class<?> c = loader.loadClass("NewClass");
                  System.out.println(c);
              }
          }
    • 線程上下文類加載器

      • 雙親委託機制以及類加載器的問題
        • 通常狀況下,保證同一個類中所關聯的其餘類都是由當前類的類加載器所加載的。(好比,ClassA自己在Ext下找到,那麼它裏面new出來的一些類也就只能用Ext去查找了(不會低一個級別),因此有些明明App能夠找到的,卻找不到了。)
        • JDBC API有時限的driver部分(mysql/sql server),咱們的JDBC API都是由Boot或者Ext來載入的,可是JDBC driver倒是由Ext或者App來載入,那麼就有可能找不到driver了。在Java中,其實只要分紅這種api+SPI(Service Provide Interface,特定廠商提供)的,都會遇到此問題。
        • 常見的SPI有JDBC、JCE、JNDL、JAXP和JBI等,這些SPI接口由JAVA核心庫來提供,如JAXP的SPI接口定義包含在javax.mxl.parsers包中。SPI的接口是JAVA核心庫的一部分,是由引導類加載器來加載的;SPI實現的JAVA類通常是由系統類加載器來加載的;引導類加載器是沒法找到SPI的實現類的,由於它只加載了JAVA的核心庫。
      • 一般當你須要動態加載資源的時候,你至少有三個ClassLoader能夠選擇:
        • 系統類加載器或者叫作應用類加載器(system classloader or application classloader)
        • 當前類加載器
        • 當前線程類加載器
      • 線程類加載器是爲了拋棄雙親委派機制加載鏈模式。
        • 每個線程都有一個關聯的上下文類加載器,若是你使用new Thread()方式生成新的線程,新線程將繼承其父線程的上下文類加載器,若是程序對線程上下文類加載器沒有任何改動的話,程序中全部的線程都將使用系統類加載器做爲上下文類加載器。
      • Thread.currentThread().getContextClassLoader()
      • Thread.currentThread().setContextClassLoader()
    • 線程上下文類加載器測試

      package com.sxt.test.classLoader;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo05.java
       * @time: 2020/2/2 22:51
       * @desc: 線程上下文類加載器測試
       */
      
      public class Demo05 {
          public static void main(String[] args) throws ClassNotFoundException {
              ClassLoader loader = Demo05.class.getClassLoader();
              System.out.println(loader);
      
              ClassLoader loader2 = Thread.currentThread().getContextClassLoader();
              System.out.println(loader2);
      
              Thread.currentThread().setContextClassLoader(new FileSystemClassLoader("F:/Java WorkSpace"));
              System.out.println(Thread.currentThread().getContextClassLoader());
      
              Class<Demo01> c = (Class<Demo01>) Thread.currentThread().getContextClassLoader().loadClass("com.sxt.test.classLoader.Demo01");
              System.out.println(c);
              System.out.println(c.getClassLoader());
          }
      }
    • 服務器類加載原理和OSGI介紹

      • TOMCAT服務器類加載機制
        • TOMCAT不能使用系統默認的類加載器(每個項目都有本身獨立的類加載器,不是雙親委派機制,而是反過來,先從子類再找父類)
        • 爲了安全TOMCAT須要實現本身的類加載器
      • OSGI(Open Service Gateway Initative)
        • 是面向Java的動態模塊系統。它爲開發人員提供了面向服務和基於組件的運行環境,並提供標準的方式用來管理軟件的生命週期。
        • OSGI已經被實現和部署在不少產品上,在開源社區也獲得了普遍的支持。Eclipse就是基於OSGI技術來構建的
        • 原理:OSGI中的每一個模塊(bundle)都包含java包和類。模塊能夠聲明它所依賴的須要導入(import)的其餘模塊的java包和類(經過import-package),也能夠聲明導出(export)本身的包和類,供其它模塊使用(經過export-package)。也就是說須要可以隱藏和共享一個模塊中的某些java包和類。這是經過OSGI特有的類加載器機制來實現的。OSGI中的每一個模塊都有對應的一個類加載器。它負責加載模塊本身包含的java包和類。當它須要加載java核心庫時(以java開頭的包和類),它會代理給父類加載器(一般是啓動類加載器)來完成。當它須要加載所導入的java類時,它會代理給導出java類的模塊來完成加載。模塊也能夠是顯式的聲明某些java包和類,必須由父類加載器來加載。只須要設置系統屬性org.osgi.framework.bootdelegation的值便可。
        • Equinox是OSGI的一個實現。

第3章 設計模式GOF23

  • 建立型模式:單例模式、工廠模式、抽象工廠模式、建造者模式、原型模式
  • 結構型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式
  • 行爲型模式:模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式。
  • 各類模式的區別:
    • 行爲型模式關注系統中對象之間的相互交互,研究系統在運行時對象之間的相互通訊和協做,進一步明確對象的職責,共有11種模式
    • 建立型模式關注對象的建立過程
    • 結構型模式關注對象和類的組織

1. 單例模式

  • 核心做用:保證一個類只有一個實例,而且提供一個訪問該實例的全局訪問點。

  • 常見的應用場景:

    • Windows的Task Manager(任務管理器)就是很典型的單例模式
    • Windows的Recycle Bin(回收站)也是典型的單例應用。在整個系統運行過程當中,回收站一直維護着僅有的一個實例。
    • 項目中,讀取配置文件的類,通常也只有一個對象。沒有必要每次使用配置文件數據,每次new一個對象去讀取。
    • 網站的計數器,通常也是採用單例模式實現,不然難以同步。
    • 應用程序的日誌應用,通常都經過單例模式實現,着通常是因爲共享的日誌文件一直處於打開狀態,由於只能有一個實例去操做,不然內容很差追加。
    • 數據庫鏈接池的設計通常也是採用單例模式,由於數據庫鏈接是一種數據庫資源。
    • 操做系統的文件系統,也是大的單例模式實現的具體例子,一個操做系統只能有一個文件系統。
    • Application也是單例的典型應用(Servlet編程中會涉及到)
    • 在Spring中,每一個Bean默認就是單例的,這樣作的優勢是Spring容器能夠管理
    • 在servlet編程中,每一個servlet也是單例的
    • 在spring MVC框架/struts1框架中,控制對象也是單例
  • 單例模式的優勢:

    • 因爲單例模式只生成一個實例,減小了系統性能開銷,當一個對象的產生須要比較多的資源時,如讀取配置、產生其餘依賴對象時,則能夠經過在應用啓動時直接產生一個單例對象,而後永久駐留內存的方式來解決
    • 單例模式能夠在系統設置全局的訪問點,優化環共享資源訪問,例如能夠設計一個單例類,負責全部數據表的映射處理
  • 常見的五種單例模式實現方式:

    • 主要:
      • 餓漢式(線程安全,調用效率高。可是,不能延時加載。)
      • 懶漢式(線程安全,調用效率不高。可是,能夠延時加載。)
    • 其餘:
      • 雙重檢測鎖式(因爲JVM底層內部模型的緣由,偶爾會出問題。不建議使用)
      • 靜態內部類式(線程安全,調用效率高。可是,能夠延時加載)
      • 枚舉單例(線程安全,調用效率高,不能延時加載,但能夠自然的防止反射和反序列化漏洞!

1.1 餓漢式

  • (單例對象當即加載)

  • 代碼

    package com.sxt.singleton;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: SingletonDemo01.java
     * @time: 2020/2/3 18:41
     * @desc: 測試餓漢式單例模式
     */
    
    public class SingletonDemo01 {
        // 上來就把這個對象先new了,無論後面你使不使用它。因此叫作餓漢式。
        // 類初始化時,當即加載這個對象。(因此不能延時加載)
        // 因爲加載類時,自然的是線程安全的!
        private static SingletonDemo01 instance = new SingletonDemo01();
        private SingletonDemo01(){
        }
    
        // 方法沒有同步,調用效率高!
        public static SingletonDemo01 getInstance(){
            return instance;
        }
    }
  • 餓漢式單例模式代碼中,static變量會在類裝載時初始化,此時也不會涉及多個線程對象訪問該對象的問題。虛擬機保證只會裝載一次該類,確定不會發生併發訪問的問題。所以,能夠省略synchronized關鍵字。

  • 問題:若是隻是加載本類,而不是要調用getInstance(),甚至永遠沒有調用,則會形成資源浪費!

1.2 懶漢式

  • (單例對象延遲加載)

  • 代碼

    package com.sxt.singleton;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: SingletonDemo01.java
     * @time: 2020/2/3 18:41
     * @desc: 測試懶漢式單例模式
     */
    
    public class SingletonDemo02 {
        // 類初始化時,不初始化這個對象。(延時加載,真正用的時候再建立)
        private static SingletonDemo02 instance;
    
        private SingletonDemo02() {
        }
    
        // 方法同步,調用效率低!
        public static synchronized SingletonDemo02 getInstance() {
            if (instance == null){
                instance = new SingletonDemo02();
            }
            return instance;
        }
    }
  • 要點:lazy load!延遲加載,懶加載!真正用的時候才加載!

  • 問題:資源利用率高了。可是,每次調用getInstance()方法都要同步,併發效率較低。

1.3 雙重檢測鎖

  • 代碼

    package com.sxt.singleton;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: SingletonDemo01.java
     * @time: 2020/2/3 18:41
     * @desc: 測試雙重檢測鎖實現單例模式
     */
    
    public class SingletonDemo03 {
        private static SingletonDemo03 instance = null;
    
        private SingletonDemo03() {
        }
    
        public static SingletonDemo03 getInstance() {
            if (instance == null){
                SingletonDemo03 sc;
                synchronized (SingletonDemo03.class){
                    sc = instance;
                    if(sc == null){
                        synchronized (SingletonDemo03.class){
                            if(sc == null){
                                sc = new SingletonDemo03();
                            }
                        }
                        instance = sc;
                    }
                }
            }
            return instance;
        }
    }
  • 這個模式將同步內容下放到if內部,提升了執行的效率,沒必要每次獲取對象時都進行同步,只有第一次才同步,建立了之後就不必了。

  • 問題:因爲編輯器優化緣由和JVM底層內部模型緣由,偶爾會出問題,不建議使用。

1.4 靜態內部類

  • (也是一種懶加載方式)

  • 代碼

    package com.sxt.singleton;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: SingletonDemo01.java
     * @time: 2020/2/3 18:41
     * @desc: 測試靜態內部類實現單例模式,這種方式線程安全,調用效率高,而且實現了延時加載
     */
    
    public class SingletonDemo04 {
        private static class SingletonClassInstance{
            private static final SingletonDemo04 instance = new SingletonDemo04();
        }
        private SingletonDemo04(){
        }
    
        // 方法沒有同步,調用效率高!
        public static SingletonDemo04 getInstance(){
            return SingletonClassInstance.instance;
        }
    }
  • 要點

    • 外部類沒有static屬性,則不會像餓漢式那樣當即家在對象。
    • 只有真正調用了getInstance()纔會加載靜態內部類,加載類時時線程安全的。instance是static final類型,保證了內存中只有這樣一個實例存在,並且只能被賦值一次,從而保證了線程安全性。
    • 兼備了併發高效調用和延遲加載的優點
  • 問題:

    • 反射能夠破解上面幾種實現方式!(能夠在構造方法中手動拋出異常控制)
    • 反序列化能夠破解上面幾種實現方式!能夠經過定義readResolve()防止得到不一樣對象。反序列化時,若是對象所在類定義了readResolve()(實際是一種回調),定義返回哪一個對象。

1.5 枚舉

  • 代碼

    package com.sxt.singleton;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: SingletonDemo01.java
     * @time: 2020/2/3 18:41
     * @desc: 測試枚舉實現單例模式(沒有延時加載)
     */
    
    public enum  SingletonDemo05 {
        // 這個枚舉元素,自己就是單例對象!
        INSTANCE;
    
        // 添加本身須要的操做!
        public void singletonOperation(){
    
        }
    }
  • 優勢

    • 實現簡單
    • 枚舉自己就是單例模式。由JVM從根本上提供保障!避免經過反射和反序列化的漏洞!
  • 缺點

    • 無延遲加載
  • UML類圖

    img

  • 如何選用?

    • 單例對象,佔用資源少,不須要延時加載:枚舉式好於餓漢式
    • 單例對象,佔用資源大,須要延時加載:靜態內部類好於懶漢式

1.6 如何防止反射和反序列化

  • 測試懶漢式單例模式

    package com.sxt.singleton;
    
    import java.io.Serializable;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: SingletonDemo01.java
     * @time: 2020/2/3 18:41
     * @desc: 測試懶漢式單例模式(如何防止反射和反序列化)
     */
    
    public class SingletonDemo06 implements Serializable {
        // 類初始化時,不初始化這個對象。(延時加載,真正用的時候再建立)
        private static SingletonDemo06 instance;
    
        private SingletonDemo06() {
            // 經過檢查是否已經建立對象了,若是有了,則拋出異常
            if (instance != null) {
                throw new RuntimeException();
            }
        }
    
        // 方法同步,調用效率低!
        public static synchronized SingletonDemo06 getInstance() {
            if (instance == null) {
                instance = new SingletonDemo06();
            }
            return instance;
        }
    
        // 反序列化時,若是定了readResolve方法,則直接返回此方法指定的對象,而不須要單獨再建立新對象!
        private Object readResolve() throws Exception{
            return instance;
        }
    }
  • 測試反射和反序列化被破解單例模式

    package com.sxt.singleton;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Client.java
     * @time: 2020/2/3 20:13
     * @desc: 測試反射和反序列化被破解單例模式
     */
    
    public class Client2 {
        public static void main(String[] args) throws Exception {
            // 測試餓漢實現單例
            SingletonDemo06 s1 = SingletonDemo06.getInstance();
            SingletonDemo06 s2 = SingletonDemo06.getInstance();
            System.out.println(s1);
            System.out.println(s2);
    
            // 經過反射的方式直接調用私有構造器
            Class<SingletonDemo06> clazz = (Class<SingletonDemo06>) Class.forName("com.sxt.singleton.SingletonDemo06");
            Constructor<SingletonDemo06> c = clazz.getDeclaredConstructor(null);
            // 這樣設置就能夠訪問private的構造器了,這種方式則跳過了單例模式的限制
            c.setAccessible(true);
    
            SingletonDemo06 s3 = c.newInstance();
            SingletonDemo06 s4 = c.newInstance();
    
            System.out.println(s3);
            System.out.println(s4);
    
            // 經過反序列化的方式構造多個對象
            FileOutputStream fos = new FileOutputStream("a.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s1);
            oos.close();
            fos.close();
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
            SingletonDemo06 s5 = (SingletonDemo06) ois.readObject();
            System.out.println(s5);
        }
    }
  • CountDownLatch

    • 同步輔助類,在完成一組正在其餘線程中執行的操做以前,它容許一個或多個線程一直等待。
    • countDown():當前線程調此方法,則計數減一(減一放在finally裏進行)
    • await():調用此方法會一直阻塞當前線程,直到計時器的值爲0
  • 測試五種建立單例模式的效率

    package com.sxt.singleton;
    
    import java.util.concurrent.CountDownLatch;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Client.java
     * @time: 2020/2/3 20:13
     * @desc: 測試五種建立單例模式的效率
     */
    
    public class Client03 {
        public static void main(String[] args) throws Exception {
    
            long start = System.currentTimeMillis();
            int threadNum = 10;
            final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
            for (int i = 0; i < threadNum; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 100000; i++) {
    //                        Object o = SingletonDemo04.getInstance();
                            Object o = SingletonDemo05.INSTANCE;
                        }
                        countDownLatch.countDown();
                    }
                }).start();
            }
            // main線程阻塞,直到計數器變爲0,纔會繼續往下執行!
            countDownLatch.await();
            long end = System.currentTimeMillis();
            System.out.println("總耗時:" + (end - start));
        }
    }

2. 工廠模式

  • 實現了建立者和調用者的分離
  • 詳細分類:
    • 簡單工廠模式:用來生產同一等級結構中的任意產品。(對於增長新的產品,須要修改已有代碼)
    • 簡單方法模式:用來生產同一等級結構中的固定產品。(支持增長任意產品)
    • 抽象工廠模式:用來生產不一樣產品族的所有產品。(對於增長的新產品,無能爲力,支持增長產品族)
  • 面向對象設計基本原則:
    • OCP(開閉原則,Open-Closed Principle):一個軟件的實體應當對擴展開放,對修改關閉
    • DIP(依賴倒轉原則,Dependence Inversion Principle):要針對接口編程,不要針對實現編程
    • LoD(迪米特法則,Law of Demeter):只與你直接的朋友通訊,而避免和陌生人通訊
  • 核心本質:
    • 實例化對象,用工廠方法代替new操做
    • 將選擇實現類、建立對象統一管理和控制。從而將調用者跟咱們的實現類解耦。

2.1 簡單工廠模式

  • 要點:

    • 簡單工廠模式也叫靜態工廠模式,就是工廠類通常是使用靜態方法,經過接受的參數的不一樣來返回不一樣的對象實例。
    • 對於增長新產品無能爲力!不修改代碼的話,是沒法擴展的。
  • 代碼:

    • Car接口

      package com.sxt.factory.simplefactor;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Car.java
       * @time: 2020/2/5 15:59
       * @desc:
       */
      
      public interface Car {
          void run();
      }
    • 奧迪車

      package com.sxt.factory.simplefactor;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Audi.java
       * @time: 2020/2/5 16:00
       * @desc:
       */
      
      public class Audi implements Car {
          @Override
          public void run() {
              System.out.println("奧迪在跑!");
          }
      }
    • 比亞迪車

      package com.sxt.factory.simplefactor;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Byd.java
       * @time: 2020/2/5 16:01
       * @desc:
       */
      
      public class Byd implements Car {
          @Override
          public void run() {
              System.out.println("比亞迪在跑!");
          }
      }
    • 測試在沒有工廠模式的狀況下

      package com.sxt.factory.simplefactor;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client01.java
       * @time: 2020/2/5 16:02
       * @desc: 測試在沒有工廠模式的狀況下
       */
      
      public class Client01 { // 調用者
          public static void main(String[] args){
              Car c1 = new Audi();
              Car c2 = new Byd();
              c1.run();
              c2.run();
          }
      }
    • 簡單工廠類1

      package com.sxt.factory.simplefactor;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: CarFactory.java
       * @time: 2020/2/5 16:04
       * @desc:
       */
      
      public class CarFactory {
          public static Car createCar(String type){
              if("奧迪".equals(type)){
                  return new Audi();
              }else if("比亞迪".equals(type)){
                  return new Byd();
              }else{
                  // 簡單工廠仍是有點小問題的,這裏若是要加新的車輛的話,就須要改代碼,違反了開閉原則OCP
                  return null;
              }
          }
      }
    • 簡單工廠2

      package com.sxt.factory.simplefactor;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: CarFactory.java
       * @time: 2020/2/5 16:04
       * @desc: 簡單工廠類2
       */
      
      public class CarFactory2 {
          public static Car createAudi() {
              return new Audi();
          }
      
          public static Car createByd() {
              return new Byd();
          }
      }
    • 測試在簡單工廠模式的狀況下

      package com.sxt.factory.simplefactor;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client01.java
       * @time: 2020/2/5 16:02
       * @desc: 測試在簡單工廠模式的狀況下
       */
      
      public class Client02 { // 調用者
          public static void main(String[] args){
              Car c1 = CarFactory.createCar("奧迪");
              Car c2 = CarFactory.createCar("比亞迪");
              c1.run();
              c2.run();
          }
      }
  • UML類圖

    img

2.2 工廠方法模式

  • 爲了不簡單工廠模式的缺點,不徹底知足OCP。

  • 工廠方法模式和簡單工廠模式最大的不一樣在於,簡單工廠模式只有一個(對於一個項目或者一個獨立模塊而言)工廠類,而工廠方法模式有一組實現了相同接口的工廠類。

  • 代碼:

    • 新增了車的工廠interface

      package com.sxt.factory.factorymethod;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: CarFactory.java
       * @time: 2020/2/5 19:42
       * @desc:
       */
      
      public interface CarFactory {
          Car createCar();
      }
    • 實現了車工廠的audi工廠

      package com.sxt.factory.factorymethod;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: AudiFactory.java
       * @time: 2020/2/5 19:42
       * @desc:
       */
      
      public class AudiFactory implements CarFactory {
          @Override
          public Car createCar() {
              return new Audi();
          }
      }
    • 實現了車工廠的byd工廠

      package com.sxt.factory.factorymethod;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: BydFactory.java
       * @time: 2020/2/5 19:43
       * @desc:
       */
      
      public class BydFactory extends Byd implements CarFactory {
          @Override
          public Car createCar() {
              return new Byd();
          }
      }
    • 客戶端

      package com.sxt.factory.factorymethod;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/5 19:44
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              Car c1 = new AudiFactory().createCar();
              Car c2 = new BydFactory().createCar();
              Car c3 = new BenzFactory().createCar();
      
              c1.run();
              c2.run();
              c3.run();
          }
      }
    • 若是須要新增車的類型的話,不須要修改原來的代碼,只須要增長類就行,即知足了OCP

    • 新增benz類

      package com.sxt.factory.factorymethod;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Benz.java
       * @time: 2020/2/5 19:45
       * @desc:
       */
      
      public class Benz extends BenzFactory implements Car {
          @Override
          public void run() {
              System.out.println("奔馳在跑!");
          }
      }
    • 新增benz工廠

      package com.sxt.factory.factorymethod;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Benz.java
       * @time: 2020/2/5 19:45
       * @desc:
       */
      
      public class Benz extends BenzFactory implements Car {
          @Override
          public void run() {
              System.out.println("奔馳在跑!");
          }
      }
  • 簡單工廠模式和工廠方法模式PK:

    img

  • 根據設計理論建議:工廠方法模式。但實際上,咱們通常都用簡單工廠模式

2.3 抽象工廠模式(第23個模式)

  • 用來生產不一樣產品族的所有產品。(對於增長新的產品,無能爲力;支持增長產品族)

  • 抽象工廠模式是工廠方法模式的升級版本,在有多個業務品種、業務分類時,經過抽象工廠模式產生須要的對象是一種很是好的解決方式。

  • 代碼:

    • 發動機

      package com.sxt.factory.abstractfactory;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Engine.java
       * @time: 2020/2/6 12:40
       * @desc:
       */
      
      public interface Engine {
          void run();
          void start();
      }
      
      class LuxuryEngine implements Engine{
      
          @Override
          public void run() {
              System.out.println("轉得快!");
          }
      
          @Override
          public void start() {
              System.out.println("啓動快!能夠自動啓停!");
          }
      }
      class LowerEngine implements Engine{
      
          @Override
          public void run() {
              System.out.println("轉得慢!");
          }
      
          @Override
          public void start() {
              System.out.println("啓動慢!能夠自動啓停!");
          }
      }
    • 座位

      package com.sxt.factory.abstractfactory;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Seat.java
       * @time: 2020/2/6 12:42
       * @desc:
       */
      
      public interface Seat {
          void massage();
      }
      
      class LuxurySeat implements Seat{
      
          @Override
          public void massage() {
              System.out.println("能夠按摩!");
          }
      }
      class LowerSeat implements Seat{
      
          @Override
          public void massage() {
              System.out.println("不能夠按摩!");
          }
      }
    • 輪胎

      package com.sxt.factory.abstractfactory;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Tyre.java
       * @time: 2020/2/6 12:43
       * @desc:
       */
      
      public interface Tyre {
          void revolve();
      }
      
      class LuxuryTyre implements Tyre{
      
          @Override
          public void revolve() {
              System.out.println("磨損慢!");
          }
      }
      class LowerTyre implements Tyre{
      
          @Override
          public void revolve() {
              System.out.println("磨損快!");
          }
      }
    • 車工廠

      package com.sxt.factory.abstractfactory;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: CarFactory.java
       * @time: 2020/2/6 12:44
       * @desc:
       */
      
      public interface CarFactory {
          Engine createEngine();
          Seat createSeat();;
          Tyre createTyre();
      }
    • 高端車工廠

      package com.sxt.factory.abstractfactory;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: LuxuryCarFactory.java
       * @time: 2020/2/6 12:45
       * @desc:
       */
      
      public class LuxuryCarFactory implements CarFactory {
      
          @Override
          public Engine createEngine() {
              return new LuxuryEngine();
          }
      
          @Override
          public Seat createSeat() {
              return new LuxurySeat();
          }
      
          @Override
          public Tyre createTyre() {
              return new LuxuryTyre();
          }
      }
    • 低端車工廠

      package com.sxt.factory.abstractfactory;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: LowerCarFactory.java
       * @time: 2020/2/6 12:45
       * @desc:
       */
      
      public class LowerCarFactory implements CarFactory {
      
          @Override
          public Engine createEngine() {
              return new LowerEngine();
          }
      
          @Override
          public Seat createSeat() {
              return new LowerSeat();
          }
      
          @Override
          public Tyre createTyre() {
              return new LowerTyre();
          }
      }
    • 客戶端

      package com.sxt.factory.abstractfactory;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/6 12:47
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              CarFactory factory = new LuxuryCarFactory();
              Engine e = factory.createEngine();
              e.run();
              e.start();
          }
      }
  • 工廠模式要點

    • 簡單工廠模式(靜態工廠模式):雖然某種程度不符合實際原則,但實際使用最多
    • 工廠方法模式:不修改已有類的前提下,經過增長新的工廠實現擴展。
    • 抽象工廠模式:不能夠增長產品,但能夠增長產品族。
  • 應用場景

    • JDK中Calendar的getInstance方法
    • JDBC中Connection對象的獲取
    • Hibernate中SessionFactory建立Session
    • spring中IOC容器建立管理bean對象
    • XML解析時的DocumentBuilderFactory建立解析器對象
    • 反射中Class對象的newInstance

3. 建造者模式

  • 場景:

    • 咱們要建造一個複雜的產品。好比神舟飛船、Iphone。這個複雜的產品的建立,有這樣的一個問題要處理:裝配這些子組件是否是有個步驟問題?
    • 實際開發中,咱們所須要的對象構建時,也很是複雜,有不少步驟須要處理時。
  • 建造者模式的本質

    • 分離了對象子組件的單獨構造(由Builder來負責)和裝配(由Director負責)。從而能夠構造出複雜的對象。這個模式適用於:某個對象的構造過程複雜的狀況下使用。
    • 因爲實現了構造和裝配的解耦。不一樣的構造器,相同的裝配,也能夠作出不一樣的對象;相同的裝備構造器,不一樣的裝配順序也能夠作出不一樣的對象。也就是實現了構造算法、裝配算法的解耦,實現了更好的複用。
  • 代碼:

    • 宇宙飛船

      package com.sxt.builder;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: AirShip.java
       * @time: 2020/2/6 13:48
       * @desc: 宇宙飛船
       */
      
      public class AirShip {
          // 軌道艙
          private OrbitalModule orbitalModule;
          // 發動機
          private Engine engine;
          // 逃逸倉
          private EscapeTower escapeTower;
      
          public void launch(){
              System.out.println("發動機【" + engine.getName() + "】" + "軌道艙【" + orbitalModule.getName() + "】" + "逃離塔【" + escapeTower.getName() + "】" + "-->發射!");
          }
      
          public OrbitalModule getOrbitalModule() {
              return orbitalModule;
          }
      
          public void setOrbitalModule(OrbitalModule orbitalModule) {
              this.orbitalModule = orbitalModule;
          }
      
          public Engine getEngine() {
              return engine;
          }
      
          public void setEngine(Engine engine) {
              this.engine = engine;
          }
      
          public EscapeTower getEscapeTower() {
              return escapeTower;
          }
      
          public void setEscapeTower(EscapeTower escapeTower) {
              this.escapeTower = escapeTower;
          }
      }
      
      class OrbitalModule {
          private String name;
      
          public OrbitalModule(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
      
      class Engine {
          private String name;
      
          public Engine(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
      
      class EscapeTower{
          private String name;
      
          public EscapeTower(String name) {
              this.name = name;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      }
    • 構建者接口

      package com.sxt.builder;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: AirShipBuilder.java
       * @time: 2020/2/6 13:53
       * @desc:
       */
      
      public interface AirShipBuilder {
          Engine buildEngine();
          OrbitalModule builderOrbitalModule();
          EscapeTower buildEscapeTower();
      }
    • 裝配者接口

      package com.sxt.builder;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: AirShipDirector.java
       * @time: 2020/2/6 13:54
       * @desc: 組裝飛船對象
       */
      
      public interface AirShipDirector {
          // 組裝飛船對象
          AirShip directAirShip();
      }
    • 構建者

      package com.sxt.builder;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: MyAirShipBuilder.java
       * @time: 2020/2/6 13:55
       * @desc:
       */
      
      public class MyAirShipBuilder implements AirShipBuilder{
      
          @Override
          public Engine buildEngine() {
              System.out.println("構建發動機!");
              return new Engine("個人發動機");
          }
      
          @Override
          public OrbitalModule builderOrbitalModule() {
              System.out.println("構建軌道艙!");
              return new OrbitalModule("個人軌道艙");
          }
      
          @Override
          public EscapeTower buildEscapeTower() {
              System.out.println("構建逃逸塔!");
              return new EscapeTower("個人逃逸塔");
          }
      }
    • 裝配者

      package com.sxt.builder;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: MyAirShipDirector.java
       * @time: 2020/2/6 13:59
       * @desc:
       */
      
      public class MyAirShipDirector implements AirShipDirector{
      
          private AirShipBuilder builder;
      
          public MyAirShipDirector(AirShipBuilder builder) {
              this.builder = builder;
          }
      
          @Override
          public AirShip directAirShip() {
              // 從構建者獲取組件
              Engine e = builder.buildEngine();
              OrbitalModule o = builder.builderOrbitalModule();
              EscapeTower es = builder.buildEscapeTower();
      
              // 進行組裝
              AirShip ship = new AirShip();
              ship.setEngine(e);
              ship.setOrbitalModule(o);
              ship.setEscapeTower(es);
      
              return ship;
          }
      }
    • 客戶端

      package com.sxt.builder;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/6 14:02
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args) {
              AirShipDirector director = new MyAirShipDirector(new MyAirShipBuilder());
      
              AirShip ship = director.directAirShip();
      
              ship.launch();
          }
      }
    • UML類圖

      img

  • 開發中應用場景:

    • StringBuilder類的append方法
    • SQL中的PreparedStatement
    • JDOM中,DomBuilder、SAXBuilder

4. 原型模式

  • 又叫克隆模式、拷貝模式,prototype
  • 根據拷貝方式不一樣分爲
  • 場景:
    • 經過new產生一個對象須要很是繁瑣的數據準備或訪問權限,則能夠使用原型模式。
    • 就是java中的克隆技術,以某個對象爲原型,複製出新的對象。顯然,新的對象具有原型對象的特色。
    • 優點有:效率高(直接克隆,避免了從新執行構造過程步驟)。
    • 克隆相似於new,可是不一樣於new。new建立新的對象屬性採用的是默認值。克隆出的對象的屬性值徹底和原型對象相同。而且克隆出的新對象改變不會影響原型對象。而後,再修改克隆對象的值。
  • 實現:
    • Cloneable接口和clone方法
    • Prototype模式中實現起來最困難的地方就是內存複製操做,所幸在java中提供了clone()方法替咱們作了絕大部分事情。
  • 注意用詞:克隆和拷貝一回事。

4.1 淺克隆

  • 代碼:

    • package com.sxt.prototype;
      
      import java.util.Date;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Sheep1.java
       * @time: 2020/2/6 15:53
       * @desc: 測試淺複製
       */
      
      public class Sheep1 implements Cloneable{
          private String sname;
          private Date birthday;
      
          public String getSname() {
              return sname;
          }
      
          public void setSname(String sname) {
              this.sname = sname;
          }
      
          public Date getBirthday() {
              return birthday;
          }
      
          public void setBirthday(Date birthday) {
              this.birthday = birthday;
          }
      
          public Sheep1(String sname, Date birthday) {
              this.sname = sname;
              this.birthday = birthday;
          }
      
          public Sheep1() {
          }
      
          @Override
          protected Object clone() throws CloneNotSupportedException {
              // 直接調用object對象的clone()方法
              Object obj = super.clone();
              return obj;
          }
      }
    • 客戶端

      package com.sxt.prototype;
      
      import java.util.Date;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client2.java
       * @time: 2020/2/6 15:57
       * @desc: 測試原型模式(淺複製)
       */
      
      public class Client1 {
          public static void main(String[] args) throws CloneNotSupportedException {
              Date d = new Date(3333333333L);
              Sheep1 s1 = new Sheep1("少理", d);
              System.out.println(s1);
              System.out.println(s1.getSname());
              System.out.println(s1.getBirthday());
      
              // 兩個是不一樣的對象,可是值是同樣的!
              Sheep1 s2 = (Sheep1) s1.clone();
      
              // 修改s1生日
              d.setTime(22222222222L);
              System.out.println("--------------------------");
              System.out.println(s1.getBirthday());
      
              System.out.println(s2);
              System.out.println(s2.getSname());
              System.out.println(s2.getBirthday());
      
              // 修改s2的值
              System.out.println("--------------------------");
              s2.setSname("多裏");
              System.out.println(s2);
              System.out.println(s2.getSname());
              System.out.println(s2.getBirthday());
          }
      }

4.2 深克隆

  • 代碼

    • package com.sxt.prototype;
      
      import java.util.Date;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Sheep2.java
       * @time: 2020/2/6 15:53
       * @desc: 測試深複製
       */
      
      public class Sheep2 implements Cloneable{
          private String sname;
          private Date birthday;
      
          public String getSname() {
              return sname;
          }
      
          public void setSname(String sname) {
              this.sname = sname;
          }
      
          public Date getBirthday() {
              return birthday;
          }
      
          public void setBirthday(Date birthday) {
              this.birthday = birthday;
          }
      
          public Sheep2(String sname, Date birthday) {
              this.sname = sname;
              this.birthday = birthday;
          }
      
          public Sheep2() {
          }
      
          @Override
          protected Object clone() throws CloneNotSupportedException {
              // 直接調用object對象的clone()方法
              Object obj = super.clone();
      
              // 添加以下代碼實現深複製
              Sheep2 s = (Sheep2) obj;
              // 把屬性也進行克隆
              s.birthday = (Date) this.birthday.clone();
      
              return obj;
          }
      }
    • 客戶端同上

  • 區別

    • 淺克隆和深克隆的區別

      img

    • 運行結果區別:淺克隆只是複製了生日變量對應的地址,這樣即便值改變了,地址相同,則得到的生日的值也相同;深克隆將生日變量複製了一份,兩個地址不一樣,所以原型的值變了,與複製的值無關,所以生日的值依然是以前複製的值。

  • 利用序列化和反序列化的技術實現深克隆!

    package com.sxt.prototype;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.util.Date;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Client.java
     * @time: 2020/2/6 15:57
     * @desc: 測試原型模式(深複製),使用序列化和反序列化的方式實現深複製
     */
    
    public class Client3 {
        public static void main(String[] args) throws Exception {
            Date d = new Date(3333333333L);
            Sheep1 s1 = new Sheep1("少理", d);
            System.out.println(s1);
            System.out.println(s1.getSname());
            System.out.println(s1.getBirthday());
    
            // 兩個是不一樣的對象,可是值是同樣的!
            // Sheep1 s2 = (Sheep1) s1.clone();
            // 這裏用序列化和反序列化實現深複製,因此用的是Sheep1,即淺複製的類
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(s1);
            byte[] bytes = bos.toByteArray();
    
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            Sheep1 s2 = (Sheep1) ois.readObject();
    
            // 修改s1生日
            d.setTime(22222222222L);
            System.out.println("--------------------------");
            System.out.println(s1.getBirthday());
    
            System.out.println(s2);
            System.out.println(s2.getSname());
            System.out.println(s2.getBirthday());
        }
    }
  • 短期大量建立對象時,原型模式和普通new方式效率測試:若是須要短期建立大量對象,而且new的過程比較耗時。則能夠考慮使用原型模式!

  • 開發中的應用場景

    • 原型模式不多單獨出現,通常是和工廠方法模式一塊兒出現,經過clone的方法建立一個對象,而後由工廠方法提供給調用者。
    • spring中bean的建立實際就是兩種:單例模式和原型模式。(固然原型模式須要和工廠模式搭配起來,即原來是new一個對象,改成clone一個對象)

建立型模式總結

  • 建立型模式:都是來幫助咱們建立對象的!

    img

5. 適配器模式

  • 結構型模式:

    • 核心做用:是從程序的結構上實現送耦合,從而能夠擴大總體的類結構,用來解決更大的問題
    • 分類:適配器模式、代理模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式
  • 什麼是適配器模式(adapter):將一個類的接口轉換成客戶但願的另一個接口。Adapter模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠在一塊兒工做。

  • 模式中的角色:

    • 目標接口(Target):客戶所期待的接口。目標能夠是具體的或抽象的類,也能夠是接口。
    • 須要適配的類(Adaptee):須要適配的類或適配者類。
    • 適配器(Adapter):經過包裝一個須要適配的對象,把原接口轉換成目標接口。
  • 代碼:

    • 被適配的類(沒有usb插口的鍵盤)

      package com.sxt.adapter;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Adaptee.java
       * @time: 2020/2/7 11:01
       * @desc: 被適配的類,至關於PS/2鍵盤
       */
      
      public class Adaptee {
          public void request(){
              System.out.println("能夠完成客戶請求的須要的功能!");
          }
      }
    • 客戶端(只有usb接口的筆記本電腦)

      package com.sxt.adapter;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/7 11:02
       * @desc: 客戶類,至關於筆記本,只有USB接口
       */
      
      public class Client {
          public void test1(Target t){
              t.handleReq();
          }
      
          public static void main(String[] args){
              Client c = new Client();
              Adaptee a = new Adaptee();
      
              // 方式1:類適配器方式
              Target t1 = new Adapter();
              // 方式2:對象適配器方式
              Target t2 = new Adapter2(a);
      
              c.test1(t2);
          }
      }
    • 接口:usb插口

      package com.sxt.adapter;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Target.java
       * @time: 2020/2/7 11:02
       * @desc: 至關於USB插口
       */
      
      public interface Target {
          void handleReq();
      }

5.1 類適配器方式

package com.sxt.adapter;

/**
 * @author: Li Tian
 * @contact: litian_cup@163.com
 * @software: IntelliJ IDEA
 * @file: Adapter.java
 * @time: 2020/2/7 11:03
 * @desc: 【類適配器方式】適配器,至關於把鍵盤轉換成usb接口的轉接器
 */

public class Adapter extends Adaptee implements Target{
    // 個人理解:把適配器變成鍵盤(子類),並實現USB接口-->打字

    @Override
    public void handleReq() {
        super.request();
    }
}

5.2 對象適配器方式

package com.sxt.adapter;

/**
 * @author: Li Tian
 * @contact: litian_cup@163.com
 * @software: IntelliJ IDEA
 * @file: Adapter2.java
 * @time: 2020/2/7 11:07
 * @desc: 【對象組合的方式,對象適配器方式】適配器
 */

public class Adapter2 implements Target{
    // 個人理解:把原來不兼容的鍵盤接口變成Target(即USB接口)

    private Adaptee adaptee;

    @Override
    public void handleReq() {
        adaptee.request();
    }

    public Adapter2(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
}
  • 工做中的場景:

    • 常常用來作舊系統改造和升級
    • 若是咱們的系統開發以後不再須要維護,那麼不少模式都是不必的,可是不幸的是,事實倒是維護一個系統的代價每每是開發一個系統的數倍。
  • 咱們學習中見過的場景

    • java.io.InputStreamReader(InputStream)
    • java.io.OutputStreamWriter(OutputStream)
  • 以對象適配器爲例,繪製UML類圖

    img

6. 代理模式

  • Proxy Pattern
  • 核心做用
    • 經過代理,控制對對象的訪問
    • 能夠詳細控制訪問某個(某類)對象的方法,在調用這個方法前作前置處理,調用這個方法後作後置處理。(即AOP的微觀實現);從而實現將統一流程代碼放到代理類中處理。
    • AOP(Aspect Oriented Programming):面向切面編程的核心實現機制。
      • 它是能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一添加功能的一種技術。它是一種新的方法論,它是對傳統OOP編程的一種補充。
      • 經常使用術語:
        • 切面:Aspect,其實就是共有功能的實現。
        • 通知:Advice,是切面的具體實現。
        • 鏈接點:Jointpoint,就是程序在運行過程當中可以插入切面的地點。
        • 切入點:Pointcut,用於定義通知應該切入到哪些鏈接點上。
        • 目標對象:Target,就是那些即將切入切面的對象,也就是那些被通知的對象。
        • 代理對象:Proxy,將通知應用到目標對象以後被動態建立的對象。
        • 織入:Weaving,將切面應用到目標對象從而建立一個新的代理對象的過程。
      • 開源的AOP框架:AspectJ
  • 核心角色
    • 抽象角色:
      • 定義代理角色和真是角色的公共對外方法
    • 真實角色:
      • 實現抽象角色,定義真實角色所要實現的業務邏輯,供代理角色調用。
      • 關注真正的業務邏輯!
    • 代理角色:
      • 實現抽象角色,是真實角色的代理,經過真實角色的業務邏輯方法來實現抽象方法,並能夠附加本身的操做。
      • 將統一的流程控制放到代理角色中處理。
    • 應用場景:
      • 安全代理:屏蔽對真實角色的直接訪問。
      • 遠程代理:經過代理類處理遠程方法調用(RMI)。
      • 延遲代理:先加載輕量級的代理對象,真正須要再加載真實對象。(好比你要開發一個大文檔查看系統,大文檔中有大的圖片,有可能一個圖片有100MB,在打開文件時不可能將全部的圖片都顯示出來,這樣就能夠使用代理模式,當須要查看圖片時,用proxy來進行大圖片的打開。)
    • 分類:
      • 靜態代理(靜態定義代理類)
      • 動態代理(動態生成代理類)
        • JDK自帶的動態代理
        • javassist字節碼操做庫實現
        • CGLIB
        • ASM(底層使用指令,可維護性較差)

6.1 靜態代理

  • 代碼:

    • 明星接口

      package com.sxt.proxy.staticproxy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Star.java
       * @time: 2020/2/7 13:51
       * @desc:
       */
      
      public interface Star {
          // 面談
          void confer();
          // 籤合同
          void signContract();
          // 訂票
          void bookTicket();
          // 唱歌
          void sing();
          // 收錢
          void collectMoney();
      }
    • 真實明星

      package com.sxt.proxy.staticproxy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: RealStar.java
       * @time: 2020/2/7 13:53
       * @desc:
       */
      
      public class RealStar implements Star {
      
          private String name = "真實明星";
          @Override
          public void confer() {
              System.out.println(name + "面談!");
          }
      
          @Override
          public void signContract() {
              System.out.println(name + "籤合同");
          }
      
          @Override
          public void bookTicket() {
              System.out.println(name + "訂票");
          }
      
          @Override
          public void sing() {
              System.out.println(name + "唱歌");
          }
      
          @Override
          public void collectMoney() {
              System.out.println(name + "收錢");
          }
      }
    • 代理經紀人

      package com.sxt.proxy.staticproxy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ProxyStar.java
       * @time: 2020/2/7 13:55
       * @desc:
       */
      
      public class ProxyStar implements Star {
          private String name = "經紀人";
          private Star star;
      
          public ProxyStar(Star star) {
              this.star = star;
          }
      
          @Override
          public void confer() {
              System.out.println(name + "面談!");
          }
      
          @Override
          public void signContract() {
              System.out.println(name + "籤合同");
          }
      
          @Override
          public void bookTicket() {
              System.out.println(name + "訂票");
          }
      
          @Override
          public void sing() {
              star.sing();
          }
      
          @Override
          public void collectMoney() {
              System.out.println(name + "收錢");
          }
      }
    • 客戶端

      package com.sxt.proxy.staticproxy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/7 13:57
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              Star real = new RealStar();
              Star proxy = new ProxyStar(real);
              proxy.confer();
              proxy.signContract();
              proxy.bookTicket();
              proxy.sing();
              proxy.collectMoney();
          }
      }

6.2 動態代理

  • 動態生成代理類

  • 動態代理相比於靜態代理的優勢:抽象角色中(接口)聲明的全部方法都被轉移到調用處理器一個集中的方法中處理,這樣咱們能夠更加靈活和統一的處理衆多的方法。

  • JDK自帶的動態代理

    • java.lang.reflect.Proxy
      • 做用:動態生成代理類和對象
    • java.lang.reflect.InvocationHandler(處理器接口)
      • 能夠經過invoke方法實現對真實角色的代理訪問。
      • 每次經過Proxy生成代理類對象時都要指出對應的處理器對象。
  • 代碼:

    • 明星接口和真實明星接口同上

    • 動態代理類

      package com.sxt.proxy.dynamicproxy;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: StarHandler.java
       * @time: 2020/2/7 16:19
       * @desc:
       */
      
      public class StarHandler implements InvocationHandler {
      
          Star realStar;
      
          public StarHandler(Star realStar) {
              this.realStar = realStar;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              Object obj = null;
              System.out.println("真正的方法執行前!");
              System.out.println("面談,籤合同等。。。");
              if (method.getName().equals("sing")) {
                  obj = method.invoke(realStar, args);
              }
              System.out.println("真正的方法執行後!");
              System.out.println("收錢!");
      
              return obj;
          }
      }
    • 客戶端

      package com.sxt.proxy.dynamicproxy;
      
      import java.lang.reflect.Proxy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/7 16:22
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              Star realStar = new RealStar();
              StarHandler handler = new StarHandler(realStar);
      
              Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Star.class}, handler);
      
              proxy.sing();
          }
      }

7. 橋接模式

  • bridge pattern

  • 場景:商場系統中常見的商品分類,以電腦爲類,如何良好的處理商品分類銷售的問題?

  • 咱們能夠用多繼承結構實現下圖關係

    graph TB A[電腦] --> B[臺式機] A --> C[筆記本] A --> D[平板電腦] B --> B1[聯想臺式機] B --> B2[戴爾臺式機] B --> B3[神舟臺式機] C --> C1[聯想筆記本] C --> C2[戴爾筆記本] C --> C3[神舟筆記本] D --> D1[聯想平板] D --> D2[戴爾平板] D --> D3[神舟平板]
  • 問題:

    • 擴展性問題(類個數膨脹問題):
      • 若是要增長一個新的電腦類型智能手機,則要增長各個品牌下面的類。
      • 若是要增長一個新的品牌,也要增長各類電腦類型的類。
    • 違反單一職責原則:一個類:聯想筆記本,有兩個引發這個類變化的緣由。
  • 橋接模式核心要點:處理多層集成結構,處理多維度變化的場景,將各個維度設計成獨立的繼承結構,使各個維度能夠獨立的擴展在抽象層創建關聯。

    img

  • 橋接模式總結:

    • 橋接模式能夠取代多層繼承的方案。多層繼承違背了單一職責原則,複用性較差,類的個數也很是多。橋接模式能夠極大地減小子類的個數,從而下降管理和維護的成本。
    • 橋接模式極大的提升了系統可擴展性,在兩個變化維度中任意擴展一個維度,都不須要修改原有的系統,符合開閉原則。
    • 橋接模式能夠完成java不能多重繼承的功能。
  • 橋接模式實際開發中應用場景

    • JDBC驅動查程序
    • AWT中的Peer架構
    • 銀行日誌管理:
      • 格式分類:操做日誌、交易日誌、異常日誌
      • 距離分類:本地記錄日誌、異地記錄日誌
    • 人力資源系統中的獎金計算模塊:
      • 獎金分類:我的獎金、團體獎金、激勵獎金
      • 部分分類:人事部門、銷售部門、研發部門
    • OA系統中的消息處理:
      • 業務類型:普通消息、加急消息、特急消息
      • 發送消息方式:系統內消息、手機短信、郵件
  • 不用橋接模式的話

    package com.sxt.bridge;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Computer.java
     * @time: 2020/2/9 14:44
     * @desc:
     */
    
    public interface Computer {
        void sale();
    }
    
    class Desktop implements Computer{
    
        @Override
        public void sale() {
            System.out.println("銷售臺式機!");
        }
    }
    
    class Laptop implements Computer{
    
        @Override
        public void sale() {
            System.out.println("銷售筆記本!");
        }
    }
    
    class Pad implements Computer{
    
        @Override
        public void sale() {
            System.out.println("銷售平板電腦!");
        }
    }
    
    class LenovoDesktop extends Desktop{
        @Override
        public void sale() {
            System.out.println("銷售聯想臺式機!");;
        }
    }
    
    class LenovoLaptop extends Laptop{
        @Override
        public void sale() {
            System.out.println("銷售聯想筆記本!");;
        }
    }
    
    class LenovoPad extends Pad{
        @Override
        public void sale() {
            System.out.println("銷售聯想平板電腦!");;
        }
    }
    
    class ShenzhouDesktop extends Desktop{
        @Override
        public void sale() {
            System.out.println("銷售神舟臺式機!");;
        }
    }
    
    class ShenzhouLaptop extends Laptop{
        @Override
        public void sale() {
            System.out.println("銷售神舟筆記本!");;
        }
    }
    
    class ShenzhouPad extends Pad{
        @Override
        public void sale() {
            System.out.println("銷售神舟平板電腦!");;
        }
    }
    
    class DellDesktop extends Desktop{
        @Override
        public void sale() {
            System.out.println("銷售戴爾臺式機!");;
        }
    }
    
    class DellLaptop extends Laptop{
        @Override
        public void sale() {
            System.out.println("銷售戴爾筆記本!");;
        }
    }
    
    class DellPad extends Pad{
        @Override
        public void sale() {
            System.out.println("銷售戴爾平板電腦!");;
        }
    }
  • 使用橋接模式

    • 品牌

      package com.sxt.bridge;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Brand.java
       * @time: 2020/2/9 14:56
       * @desc: 品牌
       */
      
      public interface Brand {
          void sale();
      }
      
      class Lenovo implements Brand{
      
          @Override
          public void sale() {
              System.out.println("銷售聯想電腦!");
          }
      }
      
      class Dell implements Brand{
      
          @Override
          public void sale() {
              System.out.println("銷售戴爾電腦!");
          }
      }
    • 電腦類型的維度

      package com.sxt.bridge;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ComputerBridge.java
       * @time: 2020/2/9 14:58
       * @desc: 電腦類型的維度
       */
      
      public class ComputerBridge {
          protected Brand brand;
      
          public ComputerBridge(Brand brand) {
              this.brand = brand;
          }
      
          public void sale(){
              brand.sale();
          }
      }
      
      class Desktop2 extends ComputerBridge{
      
          public Desktop2(Brand brand) {
              super(brand);
          }
      
          @Override
          public void sale() {
              super.sale();
              System.out.println("銷售臺式機!");
          }
      }
      
      class Laptop2 extends ComputerBridge{
      
          public Laptop2(Brand brand) {
              super(brand);
          }
      
          @Override
          public void sale() {
              super.sale();
              System.out.println("銷售筆記本電腦!");
          }
      }
    • 客戶頓

      package com.sxt.bridge;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/9 15:01
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              // 銷售聯想的筆記本電腦
              ComputerBridge c = new Laptop2(new Lenovo());
              c.sale();
              // 銷售戴爾的臺式機
              ComputerBridge c2 = new Desktop2(new Dell());
              c2.sale();
          }
      }

8. 組合模式

  • composite patern

  • 使用組合模式的場景:把部分和總體的關係用樹形結構來表示,從而使得客戶端能夠使用統一的方式處理部分對象和總體對象

  • 組合模式核心:

    • 抽象(Component)構建角色:定義了葉子和容器構建的共同點
    • 葉子(Leaf)構建角色:無子節點
    • 容器(Composite)構建角色:有容器特徵,能夠包含子節點
  • 抽象組件構成代碼

    package com.sxt.composite;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Component.java
     * @time: 2020/2/9 16:39
     * @desc: 抽象組件
     */
    
    public interface Component {
        void operation();
    }
    
    // 葉子組件
    interface Leaf extends Component {
    
    }
    // 容器組件
    interface Composite extends Component{
        void add(Component c);
        void remove(Component c);
        Component getChild(int index);
    }
  • 組合模式工做流程分析:

    • 組合模式爲處理樹形結構提供了完美的解決方法,描述瞭如何將容器和葉子進行遞歸組合,使得用戶在使用時能夠一致性的對待容器和葉子。
    • 當容器對象的指定方法被調用時,將遍歷整個樹形結構,尋找也包含這個方法的成員,並調用執行。其中,使用了遞歸調用的機制對整個結構進行處理。
  • 使用組合模式,模擬殺毒軟件架構設計

    • 抽象構建

      package com.sxt.composite;
      
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: AbstarctFile.java
       * @time: 2020/2/9 16:46
       * @desc: 抽象構建
       */
      
      public interface AbstarctFile {
          void killVirus();
      }
      
      class ImageFile implements AbstarctFile{
          private String name;
      
          public ImageFile(String name) {
              this.name = name;
          }
      
          @Override
          public void killVirus() {
              System.out.println("---圖像文件:" + name + ",進行查殺!");
          }
      }
      
      class TextFile implements AbstarctFile{
          private String name;
      
          public TextFile(String name) {
              this.name = name;
          }
      
          @Override
          public void killVirus() {
              System.out.println("---文本文件:" + name + ",進行查殺!");
          }
      }
      
      class VideoFile implements AbstarctFile{
          private String name;
      
          public VideoFile(String name) {
              this.name = name;
          }
      
          @Override
          public void killVirus() {
              System.out.println("---視頻文件:" + name + ",進行查殺!");
          }
      }
      
      class Folder implements AbstarctFile{
          private String name;
          // 定義容器,用來存放本容器構建下的子節點
          private List<AbstarctFile> list = new ArrayList<>();
      
          public Folder(String name) {
              this.name = name;
          }
      
          public void add(AbstarctFile file){
              list.add(file);
          }
      
          public void remove(AbstarctFile file){
              list.remove(file);
          }
      
          public AbstarctFile getChild(int index){
              return list.get(index);
          }
      
          @Override
          public void killVirus() {
              System.out.println("---文件夾:" + name + ",進行查殺!");
              for (AbstarctFile file: list){
                  file.killVirus();
              }
          }
      }
    • 客戶端

      package com.sxt.composite;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/9 16:54
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              AbstarctFile f2, f3, f5, f6;
              Folder f1 = new Folder("個人收藏");
              f2 = new ImageFile("個人頭像.jpg");
              f3 = new TextFile("Hello.txt");
              f1.add(f2);
              f1.add(f3);
      
              Folder f4 = new Folder("電影");
              f5 = new VideoFile("神鵰俠侶.avi");
              f6 = new VideoFile("笑傲江湖.avi");
      
              f4.add(f5);
              f4.add(f6);
              f1.add(f4);
      
              f1.killVirus();
          }
      }
  • 開發中的應用場景

    • 操做系統的資源管理器
    • GUI中的容器層次圖
    • XML文件解析
    • OA系統中,組織結構的處理
    • Junit單元測試框架:底層設計就是典型的組合模式,TestCase(葉子)、TestSuite(容器)、Test接口(抽象)

9. 裝飾器模式

  • decorator pattern

  • 職責:

    • 動態的爲一個對象增長新的功能。
    • 裝飾器模式是一種用於代替繼承的技術,無須經過繼承增長子類就能擴展對象的新功能。使用對象的關聯關係代替繼承關係,更加靈活,同時避免類型體系的快速膨脹。
  • 實現細節:

    • Component抽象構件角色:真實對象和裝飾對象有相同的接口。這樣,客戶端對象就可以以與真實對象相同的方式同裝飾對象交互。
    • ConcreteComponent具體構件角色(真實對象):io流中的FileInputStream、FileOutoutStream
    • Decorator裝飾角色:持有一個抽象構件的應用。裝飾對象接受全部客戶端的請求,並把這些請求轉發給真實的對象。這樣,就能在真實對象調動先後增長新的功能。
    • ConcreteDecorator具體裝飾角色:負責給構件對象增長新的責任。
  • 代碼

    • 抽象組件

      package com.sxt.decorator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ICar.java
       * @time: 2020/2/9 18:07
       * @desc: 抽象組件
       */
      
      public interface ICar {
          void move();
      }
      
      // 真實對象
      class Car implements ICar{
      
          @Override
          public void move() {
              System.out.println("陸地上跑");
          }
      }
      
      //裝飾角色
      class SuperCar implements ICar{
          private ICar car;
      
          public SuperCar(ICar car) {
              this.car = car;
          }
      
          @Override
          public void move() {
              car.move();
          }
      }
      
      // 具體裝飾對象
      class FlyCar extends SuperCar{
      
          public FlyCar(ICar car) {
              super(car);
          }
      
          public void fly(){
              System.out.println("天上飛!");
          }
      
          @Override
          public void move() {
              super.move();
              fly();
          }
      }
      
      class WaterCar extends SuperCar{
      
          public WaterCar(ICar car) {
              super(car);
          }
      
          public void swim(){
              System.out.println("水上游!");
          }
      
          @Override
          public void move() {
              super.move();
              swim();
          }
      }
      
      class AICar extends SuperCar{
      
          public AICar(ICar car) {
              super(car);
          }
      
          public void autoMove(){
              System.out.println("無人駕駛!");
          }
      
          @Override
          public void move() {
              super.move();
              autoMove();
          }
      }
    • 客戶端

      package com.sxt.decorator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/9 18:13
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              Car car = new Car();
              car.move();
      
              System.out.println("增長新的功能,飛行!");
              FlyCar flyCar = new FlyCar(car);
              flyCar.move();
      
              System.out.println("增長新的功能,游泳!");
              WaterCar waterCar = new WaterCar(car);
              waterCar.move();
      
              System.out.println("增長新的功能,飛行和游泳!");
              WaterCar car2 = new WaterCar(new FlyCar(car));
              car2.move();
          }
      }
  • UML類圖

    img

  • 開發中使用的場景

    • IO中輸入流和輸出流的設計
    • Swing包中圖形界面構件功能
    • Servlet API中提供了一個request對象的Decorator設計模式的默認實現類HTTPServletRequestWrapper, HTTPServletRequestWrapper類,加強了request對象的功能
    • Struts2中,request,response,session對象的處理
  • IO流實現細節

    • Component抽象構件角色:io流中的InputStream、OutputStream、Reader、Writer
    • ConcreteComponent具體構件角色:io流中的FileInputStream、FileOutputStream
    • Decorator裝飾角色:持有一個抽象構件的引用:io流中的FilterInputStream、FilterOutputStream
    • ConcreteDecorator具體裝飾角色:負責給構件對象增長新的責任。io流中的BufferedOutputStream、BufferedInputStream等。
  • 總結:

    • 裝飾模式(Decorator)也叫包裝器模式(Wrapper)
    • 裝飾模式下降系統的耦合度,能夠動態的增長或刪減對象的職責,並使得須要裝飾的具體構件類和具體裝飾類能夠獨立變化,以便增長新的具體構件類和具體裝飾類。
  • 優勢:

    • 擴展對象功能,比繼承靈活,不會致使類的個數急劇增長
    • 能夠對一個對象進行屢次裝飾,創造出不一樣行爲的組合,獲得功能更增強大的對象
    • 具體構件類和具體裝飾類能夠獨立變化,用戶能夠根據須要本身增長新的具體構件子類和具體裝飾子類
  • 缺點:

    • 產生不少小對象。大量小對象佔據內存,必定程度上影響性能。
    • 裝飾模式容易出錯,調試排查比較麻煩。
  • 裝飾模式和橋接模式的區別

    兩個模式都是爲了解決過多子類對象的問題。可是他們的誘因不同。

    • 橋接模式是對象自身現有機制沿多個維度變化,是既有部分不穩定
    • 裝飾模式是爲了增長新的功能。
  • 個人理解是:

    • 橋接模式把A維度(品牌)接口當作(電腦)的屬性,而後B維度(電腦的類別)繼承類,這樣A維度經過實現不一樣的品牌來擴展,B維度經過生成不一樣的類別的子類來擴展。
    • 裝飾器模式的主角是(汽車),爲了給這個類增長新的功能,新建了一個裝飾器類,裝飾器類的屬性就是主角類。新功能裝飾器至關於實現了新功能的主角類,並繼承裝飾器類,所以實現了給主角類添加新功能的目的。
    • 沒有裝飾者和被裝飾者的主次區別,橋接和被橋接者是平等的,不用繼承自同一個父類。(即橋接能夠互換)
    • 橋接模式不用使用同一個接口;裝飾模式用同一個接口裝飾,接口在父類中定義。
    • 均可以處理類擴散的狀況。

10. 外觀模式

  • 迪米特法則(最少知識原則):一個軟件實體應當儘量少的與其餘實體發生相互做用。

  • 外觀模式核心:爲子系統提供統一的入口。封裝子系統的複雜性,便於客戶端調用。

  • 公司註冊流程:

    • 不使用外觀模式

      img

    • 使用外觀模式

      img

  • 開發中常見的場景

    • 頻率很高。哪裏都會遇到。各類技術和框架中,都有外觀模式的使用。
    • JDBC封裝後的,commons提供的DBUtils類,Hibernate提供的工具類、Spring JDBC工具類等。

11. 享元模式

  • FlyWeight Pattern

  • 場景:內存屬於稀缺資源,不要隨便浪費。若是有不少個徹底相同或類似的對象,咱們能夠經過享元模式,節省內存。

  • 核心:

    • 享元模式以共享的方式高效地支持大量細粒度對象的重用。
    • 享元對象可以作到共享的關鍵是區分了內部狀態和外部狀態。
      • 內部狀態:能夠共享,不會隨環境變化而改變
      • 外部狀態:不能夠共享,會隨環境變化而改變
  • 圍棋軟件設計:每一個圍棋棋子都是一個對象,有以下屬性:

    graph LR A[顏色] --> station[*內部狀態*能夠共享] B[形狀] --> station C[大小] --> station D[位置] --> station_non[*外部狀態*不能夠共享]
  • 享元模式實現:

    • FlyweightFactory享元工廠類:

      建立並管理享元對象,享元池通常設計成鍵值對

    • FlyWeight抽象享元類

      一般是一個接口或抽象類,聲明公共方法,這些方法能夠向外界提供對象的內部狀態,設置外部狀態。

    • ConcreteFlyWeight具體享元類

      爲內部狀態提供成員變量進行存儲

    • UnsharedConcreteFlyWeight非共享享元類

      不能被共享的子類能夠設計爲非共享享元類

  • 享元模式實現的UML圖

img

  • 代碼:

    • 享元類和實際享元實現類(這裏實現了顏色共享)

      package com.sxt.flyweight;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ChessFlyWeight.java
       * @time: 2020/2/10 12:37
       * @desc: 享元類
       */
      
      public interface ChessFlyWeight {
          void setColor(String c);
          String getColor();
          void display(Coordinate c);
      }
      
      class ConcreteChess implements ChessFlyWeight{
          private String color;
      
          public ConcreteChess(String color) {
              this.color = color;
          }
      
          @Override
          public void setColor(String c) {
              this.color = c;
          }
      
          @Override
          public String getColor() {
              return color;
          }
      
          @Override
          public void display(Coordinate c) {
              System.out.println("棋子顏色:" + color);
              System.out.println("棋子位置:" + c.getX() + ", " + c.getY());
          }
      }
    • 外部類,這裏是座標,即非享元類

      package com.sxt.flyweight;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Coordinate.java
       * @time: 2020/2/10 12:37
       * @desc: 外部狀態,UnsharedConcreteFlyWeight非共享享元類
       */
      
      public class Coordinate {
          private int x, y;
      
          public Coordinate(int x, int y) {
              this.x = x;
              this.y = y;
          }
      
          public int getX() {
              return x;
          }
      
          public void setX(int x) {
              this.x = x;
          }
      
          public int getY() {
              return y;
          }
      
          public void setY(int y) {
              this.y = y;
          }
      }
    • 享元工廠類(用來生成享元對象)

      package com.sxt.flyweight;
      
      
      import java.util.HashMap;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ChessFlyWeightFactory.java
       * @time: 2020/2/10 12:43
       * @desc: 享元工廠類
       */
      
      public class ChessFlyWeightFactory {
          // 享元池
          private static Map<String, ChessFlyWeight> map = new HashMap<>();
          public static ChessFlyWeight getChess(String color){
              if(map.get(color) != null){
                  return map.get(color);
              }else{
                  ChessFlyWeight cfw = new ConcreteChess(color);
                  map.put(color, cfw);
                  return cfw;
              }
          }
      }
    • 客戶端

      package com.sxt.flyweight;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/10 12:47
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("black");
              ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("black");
              System.out.println(chess1);
              System.out.println(chess2);
      
              // 增長外部狀態的處理
              chess1.display(new Coordinate(10, 10));
              chess1.display(new Coordinate(20, 20));
          }
      }
  • 享元模式開發中應用的場景:

    • 享元模式因爲其共享的特徵,能夠在任何「池」中操做,好比:線程池、數據庫鏈接池。
    • String類的設計也是享元模式。
  • 享元模式的優勢:

    • 極大減小內存中對象的數量
    • 相同或類似對象內存中只存一份,極大的節約資源,提升系統性能
    • 外部狀態相對獨立,不影響內部狀態
  • 享元模式的缺點:

    • 模式較複雜,使程序邏輯複雜化
    • 爲了節省內存,共享了內部狀態,分離出外部狀態,而讀取外部狀態使運行時間邊長。用時間換取了空間。

結構型模式總結

img

12. 責任鏈模式

  • chain of responsibility pattern
  • 定義:將可以處理同一類請求的對象連成一條鏈,所提交的請求沿着鏈傳遞,鏈上的對象逐個判斷是否有能力處理該請求,若是能則處理,若是不能則傳遞給鏈上的下一個對象。
  • 場景:
    • 打牌時,輪流出牌
    • 接力賽跑
    • 大學中,獎學金審批
    • 公司中,公文審批
  • 開發中常見的場景:
    • java中,異常機制就是一種責任鏈模式。一個try能夠對應多個catch,當第一個catch不匹配類型,則自動跳到第二個catch。
    • JavaScript語言中,事件的冒泡和捕獲機制。java語言中,事件的處理採用觀察者模式。
    • Servlet開發中,過濾器的鏈式處理
    • Struts2中,攔截器的調用也是經典的責任鏈模式

12.1 鏈表方式img

  • 封裝請假的基本信息

    package com.sxt.chainofresp;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: LeaveRequest.java
     * @time: 2020/2/10 18:01
     * @desc: 封裝請假的基本信息
     */
    
    public class LeaveRequest {
        private String empName;
        private int leaveDays;
        private String reason;
    
        public LeaveRequest(String empName, int leaveDays, String reason) {
            this.empName = empName;
            this.leaveDays = leaveDays;
            this.reason = reason;
        }
    
        public String getEmpName() {
            return empName;
        }
    
        public void setEmpName(String empName) {
            this.empName = empName;
        }
    
        public int getLeaveDays() {
            return leaveDays;
        }
    
        public void setLeaveDays(int leaveDays) {
            this.leaveDays = leaveDays;
        }
    
        public String getReason() {
            return reason;
        }
    
        public void setReason(String reason) {
            this.reason = reason;
        }
    }
  • 抽象類領導

    package com.sxt.chainofresp;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Leader.java
     * @time: 2020/2/10 18:02
         * @desc: 抽象類 領導
     */
    
    public abstract class Leader {
        protected String name;
            // 領導的下一個責任領導
        protected Leader nextLeader;
    
        public Leader(String name) {
            this.name = name;
        }
    
        // 設定責任鏈上的後繼對象
        public void setNextLeader(Leader nextLeader) {
            this.nextLeader = nextLeader;
        }
    
        // 處理請求的核心業務方法
        public abstract void handleRequest(LeaveRequest request);
    }
  • 上級

    package com.sxt.chainofresp;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Director.java
     * @time: 2020/2/10 18:06
     * @desc:
     */
    
    public class Director extends Leader {
    
        public Director(String name) {
            super(name);
        }
    
        @Override
        public void handleRequest(LeaveRequest request) {
            if(request.getLeaveDays() < 3){
                System.out.println("員工:" + request.getEmpName() + "請假:" + request.getLeaveDays() + "天,理由是:" + request.getReason());
                System.out.println("主任:" + this.name + "審批經過!");
            }else{
                if(this.nextLeader != null){
                    this.nextLeader.handleRequest(request);
                }
            }
        }
    }
  • 經理

    package com.sxt.chainofresp;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Manager.java
     * @time: 2020/2/10 18:06
     * @desc: 經理
     */
    
    public class Manager extends Leader {
    
        public Manager(String name) {
            super(name);
        }
    
        @Override
        public void handleRequest(LeaveRequest request) {
            if(request.getLeaveDays() < 10){
                System.out.println("員工:" + request.getEmpName() + "請假:" + request.getLeaveDays() + "天,理由是:" + request.getReason());
                System.out.println("經理:" + this.name + "審批經過!");
            }else{
                if(this.nextLeader != null){
                    this.nextLeader.handleRequest(request);
                }
            }
        }
    }
  • 總經理

    package com.sxt.chainofresp;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Manager.java
     * @time: 2020/2/10 18:06
     * @desc: 總經理
     */
    
    public class GeneralManager extends Leader {
    
        public GeneralManager(String name) {
            super(name);
        }
    
        @Override
        public void handleRequest(LeaveRequest request) {
            if(request.getLeaveDays() < 30){
                System.out.println("員工:" + request.getEmpName() + "請假:" + request.getLeaveDays() + "天,理由是:" + request.getReason());
                System.out.println("總經理:" + this.name + "審批經過!");
            }else{
                System.out.println("莫非" + request.getEmpName() + "想辭職!居然請假" + request.getLeaveDays() + "天!");
            }
        }
    }
  • 客戶端

    package com.sxt.chainofresp;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Client.java
     * @time: 2020/2/10 18:11
     * @desc:
     */
    
    public class Client {
        public static void main(String[] args){
            Leader a = new Director("張三");
            Leader b = new Manager("李四");
            Leader c = new GeneralManager("王五");
    
            // 組織責任鏈對象關係
            a.setNextLeader(b);
            b.setNextLeader(c);
    
            // 開始請假操做
            LeaveRequest req1 = new LeaveRequest("Tom", 10, "回家睡覺!");
            a.handleRequest(req1);
        }
    }

12.2 非鏈表方式

  • 經過集合、數組生成責任鏈更加實用!實際上,不少項目中,每一個具體的Handler並非由開發團隊定義的,而是項目上線後由外部單位追加的,因此實用鏈表方式定義COR鏈就很困難。

13. 迭代器模式

  • iterator pattern

  • 場景:

    • 提供一種能夠遍歷聚合對象的方式。又稱爲:遊標cursor模式
    • 聚合對象:存儲數據
    • 迭代器:遍歷數據
  • 基本案例:

    • 實現正向遍歷的迭代器

      • 自定義的迭代器接口

        package com.sxt.iterator;
        
        /**
         * @author: Li Tian
         * @contact: litian_cup@163.com
         * @software: IntelliJ IDEA
         * @file: MyIterator.java
         * @time: 2020/2/14 18:42
         * @desc: 自定義的迭代器接口
         */
        
        public interface MyIterator {
            // 將遊標指向第一個元素
            void first();
            // 將遊標指向下一個元素
            void next();
            // 判斷是否存在下一個元素
            boolean hasNext();
        
            boolean ifFirst();
            boolean isLast();
        
            // 獲取當前遊標指向的對象
            Object getCurrentObj();
        }
      • 自定義的聚合類

        package com.sxt.iterator;
        
        import java.util.ArrayList;
        import java.util.List;
        
        /**
         * @author: Li Tian
         * @contact: litian_cup@163.com
         * @software: IntelliJ IDEA
         * @file: ConcreteMyAggregate.java
         * @time: 2020/2/14 18:45
         * @desc: 自定義的聚合類
         */
        
        public class ConcreteMyAggregate {
            private List<Object> list = new ArrayList<Object>();
        
            public void addObject(Object obj) {
                this.list.add(obj);
            }
        
            public void removeObject(Object obj) {
                this.list.remove(obj);
            }
        
            public List<Object> getList() {
                return list;
            }
        
            public void setList(List<Object> list) {
                this.list = list;
            }
        
            // 得到迭代器
            public MyIterator createIterator(){
                return new ConcreteIterator();
            }
        
            // 使用內部類定義迭代器,能夠直接使用外部類的屬性
            private class ConcreteIterator implements MyIterator {
                // 定義遊標用於記錄遍歷時的位置
                private int cursor;
        
                @Override
                public void first() {
                    cursor = 0;
                }
        
                @Override
                public void next() {
                    if (cursor < list.size()) {
                        cursor++;
                    }
                }
        
                @Override
                public boolean hasNext() {
                    return cursor < list.size();
                }
        
                @Override
                public boolean ifFirst() {
                    return cursor == 0;
                }
        
                @Override
                public boolean isLast() {
                    return cursor == (list.size() - 1);
                }
        
                @Override
                public Object getCurrentObj() {
                    return list.get(cursor);
                }
            }
        }
      • 客戶端

        package com.sxt.iterator;
        
        /**
         * @author: Li Tian
         * @contact: litian_cup@163.com
         * @software: IntelliJ IDEA
         * @file: Client.java
         * @time: 2020/2/14 18:53
         * @desc:
         */
        
        public class Client {
            public static void main(String[] args){
                ConcreteMyAggregate cma = new ConcreteMyAggregate();
                cma.addObject("aa");
                cma.addObject("bb");
                cma.addObject("cc");
        
                MyIterator iter = cma.createIterator();
                while(iter.hasNext()){
                    System.out.println(iter.getCurrentObj());
                    iter.next();
                }
            }
        }
    • 實現逆向遍歷的迭代器

  • 開發中常見的場景:

    • JDK內置的迭代器(List/Set)

14. 中介者模式

  • Mediator Pattern

  • 場景:總經理協調各個部門之間的關係,起到一箇中介的做用。

  • 核心:

    • 若是一個系統中對象之間的聯繫呈現網狀結構,對象之間存在大量多對多關係,將致使關係及其複雜,這些對象稱爲「同事對象」
    • 咱們能夠引入一箇中介者對象,使各個同事對象只跟中介者對象打交道,將複雜的網絡結構化解爲星形結構。
  • 代碼類圖

    img

  • 代碼:

    • 總經理類的接口

      package com.sxt.mediator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Mediator.java
       * @time: 2020/2/14 19:07
       * @desc: 總經理類的接口
       */
      
      public interface Mediator {
          void regitster(String dname, Department d);
          void command(String dname);
      }
    • 同事類接口

      package com.sxt.mediator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Department.java
       * @time: 2020/2/14 19:08
       * @desc: 同事類的接口
       */
      
      public interface Department {
          // 作本部門的事情
          void selfAction();
          // 向總經理髮出申請
          void outAction();
      }
    • 研發部門

      package com.sxt.mediator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Development.java
       * @time: 2020/2/14 19:09
       * @desc:
       */
      
      public class Development implements Department {
          // 持有中介者(總經理)的引用
          private Mediator m;
      
          public Development(Mediator m) {
              this.m = m;
              m.regitster("development", this);
          }
      
          @Override
          public void selfAction() {
              System.out.println("專心搞科研!");
          }
      
          @Override
          public void outAction() {
              System.out.println("向總經理彙報工做!須要資金支持!");
          }
      }
    • 財務部門

      package com.sxt.mediator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Finacial.java
       * @time: 2020/2/14 19:11
       * @desc:
       */
      
      public class Finacial implements Department {
          // 持有中介者(總經理)的引用
          private Mediator m;
      
          public Finacial(Mediator m) {
              this.m = m;
              m.regitster("finacial", this);
          }
      
          @Override
          public void selfAction() {
              System.out.println("數錢!");
          }
      
          @Override
          public void outAction() {
              System.out.println("錢太多了啊總經理!怎麼花!");
          }
      }
    • 市場部門

      package com.sxt.mediator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Market.java
       * @time: 2020/2/14 19:13
       * @desc:
       */
      
      public class Market implements Department {
          // 持有中介者(總經理)的引用
          private Mediator m;
      
          public Market(Mediator m) {
              this.m = m;
              m.regitster("market", this);
          }
      
          @Override
          public void selfAction() {
              System.out.println("跑去接項目!");
          }
      
          @Override
          public void outAction() {
              System.out.println("承接項目的進度!須要資金支持!");
              m.command("finacial");
          }
      }
    • 總經理

      package com.sxt.mediator;
      
      import java.util.HashMap;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: President.java
       * @time: 2020/2/14 19:14
       * @desc:
       */
      
      public class President implements Mediator {
          private Map<String, Department> map = new HashMap<String, Department>();
      
          @Override
          public void regitster(String dname, Department d) {
              map.put(dname, d);
          }
      
          @Override
          public void command(String dname) {
              map.get(dname).selfAction();
          }
      }
    • 客戶端

      package com.sxt.mediator;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/14 19:17
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              Mediator m = new President();
              Market market = new Market(m);
              Development devp = new Development(m);
              Finacial f = new Finacial(m);
      
              market.selfAction();
              market.outAction();
          }
      }
  • 中介者模式的本質:解耦多個同事對象之間的交互關係。每一個對象都持有中介者對象的引用,只跟中介者對象打交道。咱們經過中介者對象統一管理這些交互關係。

  • 開發中常見的場景:

    • MVC模式(其中的C,控制器就是一箇中介者對象。M和V都和它打交道)
    • 窗口遊戲程序,窗口軟件開發中窗口也是一箇中介者對象。
    • 圖形界面開發GUI中,多個組件之間的交互,能夠經過引入一箇中介者對象來解決,能夠是總體的窗口對象或者DOM對象。
    • java.lang.reflect.Method#invoke()

15. 命令模式

  • command pattern

  • 介紹:將一個請求封裝爲一個對象,從而使咱們可用不一樣的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。也稱爲:動做Action模式、事務transaction模式。

  • 結構:

    • Command抽象命令類

    • ConcreteCommand具體命令類

    • Invoker調用者/請求者:請求的發送者,它經過命令對象來執行請求。一個調用者並不須要在設計時肯定其接受者,所以它只與抽象命令類之間存在關聯。在程序運行時,將調用命令對象的execute(),間接調用接受者的相關操做。

    • Receiver接受者

      • 接受者執行與請求相關的操做,具體實現對請求的業務處理。
      • 未抽象前,實際執行操做內容的對象。
    • Client客戶類:在客戶類中須要建立調用者對象、具體命令類對象,在建立具體命令對象時指定對應的接受者。發送者和接受者之間沒有直接聯繫,都經過命令對象間接調用。

    • 類圖

      img

  • 代碼:

    • 真正的命令執行者

      package com.sxt.command;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Receiver.java
       * @time: 2020/2/15 14:28
       * @desc: 真正的命令執行者
       */
      
      public class Receiver {
          public void action(){
              System.out.println("Receiver.action()");
          }
      }
    • 命令管理

      package com.sxt.command;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Command.java
       * @time: 2020/2/15 14:29
       * @desc: 命令管理
       */
      
      public interface Command {
          // 這個方法是一個返回結果爲空的方法
          // 實際項目中,能夠根據需求設計多個不一樣的方法
          void execute();
      }
      
      class ConcreteCommand implements Command{
          // 命令的真正執行者
          private Receiver receiver;
      
          public ConcreteCommand(Receiver receiver) {
              this.receiver = receiver;
          }
      
          @Override
          public void execute() {
              // 命令真正執行前或後,執行相關的處理
              receiver.action();
          }
      }
    • 命令的調用者和發起者

      package com.sxt.command;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Invoke.java
       * @time: 2020/2/15 14:34
       * @desc: 命令的調用者和發起者
       */
      
      public class Invoke {
          // 也能夠經過容器放不少不少命令,進行批處理。好比數據庫底層的事務管理
          private Command command;
      
          public Invoke(Command command) {
              this.command = command;
          }
      
          // 業務方法,用於調用命令類的方法
          public void call(){
              command.execute();
          }
      }
    • 客戶端

      package com.sxt.command;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/15 14:36
       * @desc:
       */
      
      public class Client {
          public static void main(String[] args){
              Command c = new ConcreteCommand(new Receiver());
              Invoke i = new Invoke(c);
              i.call();
          }
      }
  • 開發中常見的場景:

    • Struts2中,action的整個調用過程當中就有命令模式
    • 數據庫事務機制的底層實現
    • 命令的撤銷和恢復,word那種,執行前保存,執行後能夠回滾

16. 解釋器模式

  • interpreter pattern
  • 介紹:
    • 是一種不經常使用的設計模式
    • 用於描述如何構成一個簡單的語言解釋器,主要用於使用面嚮對象語言開發的編譯器和計時器設計。
    • 當咱們須要開發一種新的語言時,能夠考慮使用解釋器模式。
    • 儘可能不要使用解釋器模式,後期維護會有很大麻煩。在項目中,能夠使用Jruby,Groovy,java的js引擎來替代解釋器的做用,彌補java語言的不足。
  • 開發中常見的場景:
    • EL表達式的處理
    • 正則表達式解釋器
    • SQL語法的解釋器
    • 數學表達式解析器:如現成的工具包:Math Expression String Parser、Expression4J等。

17. 訪問者模式

  • Visitor Pattern
  • 模式動機:對於存儲在一個集合中的對象,他們可能具備不一樣的類型(即便有一個公共的接口),對於該集合中的對象,能夠接受一類稱爲訪問者的對象來訪問,不一樣的訪問者其訪問方式也有不一樣。
  • 定義:表示一個做用於某對象結果中的各元素的操做,它使咱們能夠在不改變每一個元素的類的前提下定義做用於這些元素的新操做。
  • 開發中的場景(應用範圍很是窄,瞭解便可):
    • XML文檔解析器設計
    • 編譯器的設計
    • 複雜集合對象的處理

18. 策略模式

  • strategy pattern

  • 場景:

    • 某個市場人員接到單後的報價策略(CRM系統中常見問題)。報價策略很複雜,能夠簡單做以下分類:
      • 普通客戶小批量報價
      • 普通客戶大批量報價
      • 老客戶小批量報價
      • 老客戶大批量報價
    • 具體選用哪一個報價策略,這須要根據實際狀況來肯定。這時候,咱們採用策略模式便可。
    • 通常能夠先採用條件語句處理,但類型特別多,算法比較複雜時,整個條件控制代碼會變得很長,難於維護。
  • 本質:分離算法,選擇實現

  • 開發中常見的場景

    • JAVASE中GUI編程中的佈局管理
    • Spring框架中,Resource接口,資源訪問策略
    • javax.servlet.http.HttpServlet#service()
  • 策略模式對應於解決某一個問題的一個算法族,容許用戶從該算法族中任選一個算法解決某以問題,同時能夠方便的更換算法或者增長新的算法。而且由客戶端決定調用哪一個算法。

  • 代碼

    • 策略接口

      package com.sxt.strategy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Strategy.java
       * @time: 2020/2/29 16:37
       * @desc:
       */
      
      public interface Strategy {
          public double getPrice(double standarPrice);
      }
    • 普通客戶小批量購買

      package com.sxt.strategy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @time: 2020/2/29 16:37
       * @desc: 普通客戶小批量購買
       */
      
      public class NewCustomerFewStrategy implements Strategy{
      
          @Override
          public double getPrice(double standarPrice) {
              System.out.println("不打折原價!");
              return standarPrice;
          }
      }
    • 普通客戶大批量購買

      package com.sxt.strategy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @time: 2020/2/29 16:37
       * @desc: 普通客戶大批量購買
       */
      
      public class NewCustomerManyStrategy implements Strategy{
      
          @Override
          public double getPrice(double standarPrice) {
              System.out.println("打九折!");
              return standarPrice*0.9;
          }
      }
    • 老客戶小批量購買

      package com.sxt.strategy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @time: 2020/2/29 16:37
       * @desc: 老客戶小批量購買
       */
      
      public class OldCustomerFewStrategy implements Strategy{
      
          @Override
          public double getPrice(double standarPrice) {
              System.out.println("打八五折!");
              return standarPrice*0.85;
          }
      }
    • 老客戶大批量購買

      package com.sxt.strategy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @time: 2020/2/29 16:37
       * @desc: 老客戶大批量購買
       */
      
      public class OldCustomerManyStrategy implements Strategy{
      
          @Override
          public double getPrice(double standarPrice) {
              System.out.println("打八折!");
              return standarPrice*0.8;
          }
      }
    • 負責和具體的策略類交互

      package com.sxt.strategy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Context.java
       * @time: 2020/2/29 16:40
       * @desc: 負責和具體的策略類交互,這樣的話,具體的算法和直接的客戶端調用分類了,使得算法能夠獨立於客戶端獨立的變化。
       *        若是使用Spring的依賴注入功能,還能夠經過配置文件,動態的注入不一樣策略對象,動態的切換不一樣的算法。
       */
      
      public class Context {
          // 當前採用的算法對象
          private Strategy strategy;
      
          // 能夠經過構造器來注入
          public Context(Strategy strategy) {
              this.strategy = strategy;
          }
      
          // 或者經過加一個set方法來注入
          public void setStrategy(Strategy strategy) {
              this.strategy = strategy;
          }
      
          public void printPrice(double s){
              System.out.println("您的報價:"  + strategy.getPrice(s));
          }
      }
    • 客戶端

      package com.sxt.strategy;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/29 16:56
       * @desc: |
       */
      
      public class Client {
          public static void main(String[] args){
              Strategy s1 = new OldCustomerManyStrategy();
              Context ctx = new Context(s1);
              ctx.printPrice(998);
          }
      }

19. 模板方法模式

  • template method pattern

  • 場景:客戶到銀行辦理業務:

    • 取號排隊
    • 辦理具體業務
    • 給銀行工做人員評分
  • 代碼

    • 模擬銀行業務流程

      package com.sxt.template;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: BankTemplateMethod.java
       * @time: 2020/2/29 17:10
       * @desc: |
       */
      
      public abstract class BankTemplateMethod {
          // 具體方法
          public void takeNumber(){
              System.out.println("取號排隊!");
          }
      
          // 辦理具體的業務,鉤子方法
          public abstract void transact();
      
          public void evaluate(){
              System.out.println("反饋評分!");
          }
      
          // 模板方法
          public final void process(){
              this.takeNumber();
              this.transact();
              this.evaluate();
          }
      }
    • 客戶端

      package com.sxt.template;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/29 17:11
       * @desc: |
       */
      
      public class Client {
          public static void main(String[] args){
              BankTemplateMethod btm = new DrawMoney();
              btm.process();
      
              // 一般採用匿名內部類
              BankTemplateMethod btm2 = new BankTemplateMethod() {
                  @Override
                  public void transact() {
                      // 存錢
                      System.out.println("我要存錢");
                  }
              };
              btm2.process();
          }
      }
      
      // 取款
      class DrawMoney extends BankTemplateMethod{
      
          @Override
          public void transact() {
              System.out.println("我要取款!");
          }
      }
  • 方法回調(鉤子方法)

    • 好萊塢原則「Don't call me, we'll call you back.」
    • 在好萊塢,當藝人把簡歷遞交給好萊塢的娛樂公司時,所能作的就是等待,整個過程由娛樂公司控制,演員只能被動的服務安排,在須要的時候再由公司安排具體環節的演出。
    • 在軟件開發中,咱們能夠將call翻譯爲調用。子類不能調用父類,而經過父類調用子類。這些調用步驟已經在父類中寫好了,徹底由父類控制整個過程。
  • 何時用到模板方法模式:實現一個算法時,總體步驟很固定,可是,某些部分易變。易變的部分能夠抽象出來,供子類實現。

  • 開發中常見的場景:很是頻繁。各個框架、類庫中都有影子。好比常見的優:

    • 數據庫訪問的封裝
    • Junit單元測試
    • servlet中關於doGet/doPost方法調用
    • Hibernate中模板程序
    • spring中JDBCTemplate、HibernateTemplate等。

20. 狀態模式

  • state pattern

  • 場景:

    • 電梯的運行
    • 紅綠燈
    • 企業或政府系統、公文的審批狀態
    • 網上購物時,訂單的狀態
    • 酒店系統中,房間的狀態變化:已預訂、已入住、空閒
  • 核心:用於解決系統中複雜對象的狀態轉換以及不一樣狀態下行爲的封裝問題

  • 結構:

    • Context環境類:環境類中維護一個State對象,它是定義了當前的狀態。
    • State抽象狀態類
    • ConcreteState具體狀態類:每個類封裝了一個狀態對應的行爲。
  • 類圖:

    img

  • 代碼

    • 狀態接口

      package com.sxt.state;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: State.java
       * @time: 2020/2/29 18:11
       * @desc: |
       */
      
      public interface State {
          void handle();
      }
    • 空閒狀態

      package com.sxt.state;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @time: 2020/2/29 18:11
       * @desc: |空閒狀態
       */
      
      public class FreeState implements State{
      
          @Override
          public void handle() {
              System.out.println("房間空閒!沒人住!");
          }
      }
    • 預約狀態

      package com.sxt.state;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @time: 2020/2/29 18:11
       * @desc: |預約狀態
       */
      
      public class BookedState implements State{
      
          @Override
          public void handle() {
              System.out.println("房間已預訂!");
          }
      }
    • 已入住狀態

      package com.sxt.state;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @time: 2020/2/29 18:11
       * @desc: |已入住狀態
       */
      
      public class CheckedState implements State{
      
          @Override
          public void handle() {
              System.out.println("房間已入住!");
          }
      }
    • 上下文環境類:表明當前的狀態和狀態之間切換的核心方法

      package com.sxt.state;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Context.java
       * @time: 2020/2/29 18:15
       * @desc: |
       */
      
      public class HomeContext {
          // 當前狀態
          private State state;
      
          public HomeContext(State state) {
              this.state = state;
              this.state.handle();
          }
      
          // 設置不一樣狀態
          public void setState(State s){
              System.out.println("修改狀態!");
              state = s;
              state.handle();
          }
      }
    • 客戶端

      package com.sxt.state;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/2/29 18:17
       * @desc: |
       */
      
      public class Client {
          public static void main(String[] args) {
              HomeContext ctx = new HomeContext(new FreeState());
              ctx.setState(new BookedState());
              ctx.setState(new CheckedState());
          }
      }
  • 開發中常見的場景:

    • 銀行系統中帳號狀態的管理
    • OA系統中公文狀態的管理
    • 酒店系統中,房間狀態的管理
    • 線程對象各狀態之間的切換

21. 觀察者模式

  • Observer Pattern

  • 場景

    • 廣播機制
    • 聊天室
    • 新聞訂閱
    • CS遊戲
  • 上面這些場景,咱們均可以使用觀察者模式處理。咱們能夠把多個訂閱者、客戶稱之爲觀察者;須要同步給多個訂閱者的數據封裝到對象中,稱之爲目標。

  • 核心:

    • 觀察者模式主要用於1:N的通知。當一個對象(目標對象Subject或Objservable,消息發佈)的狀態發生變化時,他須要及時告知一系列對象(觀察者對象,Observer,消息訂閱),令他們作出響應。
    • 通知觀察者的方式
      • 推:每次都會把通知以廣播方式發送給全部觀察者,全部觀察者只能被動接受。
      • 拉:觀察者只要知道有狀況便可。至於何時獲取內容,獲取什麼內容,均可以自主決定。
  • UML類圖

    img

  • 代碼:

    • 消息發佈對象

      package com.sxt.observer;
      
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Subject.java
       * @time: 2020/3/1 13:36
       * @desc: |
       */
      
      public class Subject {
          protected List<Observer> list = new ArrayList<>();
      
          public void registerObserver(Observer obs) {
              list.add(obs);
          }
      
          public void removeObserver(Observer obs) {
              list.remove(obs);
          }
      
          // 通知全部的觀察者更新狀態
          public void notifyAllObservers() {
              for (Observer obs : list) {
                  obs.update(this);
              }
          }
      }
    • 消息發佈對象的具體實現

      package com.sxt.observer;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ConcreteSubject.java
       * @time: 2020/3/1 13:41
       * @desc: |
       */
      
      public class ConcreteSubject extends Subject {
          private int state;
      
          public int getState() {
              return state;
          }
      
          public void setState(int state) {
              this.state = state;
              // 主題對象/目標對象的值發生了變化,請通知全部的觀察者
              this.notifyAllObservers();
          }
      }
    • 觀察者接口

      package com.sxt.observer;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Observer.java
       * @time: 2020/3/1 13:36
       * @desc: |
       */
      
      public interface Observer {
          void update(Subject subject);
      }
    • 觀察者

      package com.sxt.observer;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ObserverA.java
       * @time: 2020/3/1 13:43
       * @desc: |
       */
      
      public class ObserverA implements Observer {
          // myState須要跟目標對象的值保持一致
          private int myState;
      
          public int getMyState() {
              return myState;
          }
      
          public void setMyState(int myState) {
              this.myState = myState;
          }
      
          @Override
          public void update(Subject subject) {
              myState = ((ConcreteSubject)subject).getState();
          }
      }
    • 客戶端

      package com.sxt.observer;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/3/1 13:45
       * @desc: |
       */
      
      public class Client {
          public static void main(String[] args){
              // 建立目標對象
              ConcreteSubject subject = new ConcreteSubject();
              // 建立多個觀察者
              ObserverA obs1 = new ObserverA();
              ObserverA obs3 = new ObserverA();
              ObserverA obs2 = new ObserverA();
      
              // 讓這三個觀察者添加到subject對象的觀察者隊伍中
              subject.registerObserver(obs1);
              subject.registerObserver(obs2);
              subject.registerObserver(obs3);
      
              // 改變subject的狀態
              subject.setState(3000);
              // 查看觀察者的狀態
              System.out.println(obs1.getMyState());
              System.out.println(obs2.getMyState());
              System.out.println(obs3.getMyState());
          }
      }
  • JAVASE中提供了java.util.Observable和java.util.Observer來實現觀察者模式

    • 目標對象

      package com.sxt.observer2;
      
      import java.util.Observable;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ConcreteSubject.java
       * @time: 2020/3/1 14:05
       * @desc: |
       */
      
      public class ConcreteSubject extends Observable {
          private int state;
      
          public int getState() {
              return state;
          }
      
          public void setState(int state) {
              this.state = state;
          }
      
          public void set(int s){
              // 目標對象的狀態發生了改變
              state = s;
              // 表示目標對象已經發生了更改
              setChanged();
              // 通知全部的觀察者
              notifyObservers(state);
          }
      }
    • 觀察者對象

      package com.sxt.observer2;
      
      import java.util.Observable;
      import java.util.Observer;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ObserverA.java
       * @time: 2020/3/1 14:07
       * @desc: |
       */
      
      public class ObserverA implements Observer {
          private int myState;
      
          public int getMyState() {
              return myState;
          }
      
          public void setMyState(int myState) {
              this.myState = myState;
          }
      
          @Override
          public void update(Observable o, Object arg) {
              myState = ((ConcreteSubject)o).getState();
          }
      }
    • 客戶端

      package com.sxt.observer2;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/3/1 14:09
       * @desc: |
       */
      
      public class Client {
          public static void main(String[] args){
              // 建立目標對象Observable
              ConcreteSubject subject = new ConcreteSubject();
      
              // 建立觀察者
              ObserverA obs1 = new ObserverA();
              ObserverA obs2 = new ObserverA();
              ObserverA obs3 = new ObserverA();
      
              // 將上面三個觀察者對象加到目標對象subject的觀察者容器中
              subject.addObserver(obs1);
              subject.addObserver(obs2);
              subject.addObserver(obs3);
      
              // 改變subject對象的狀態
              subject.set(300);
      
              // 看看觀察者的狀態發生變化了沒
              System.out.println(obs1.getMyState());
              System.out.println(obs2.getMyState());
              System.out.println(obs3.getMyState());
          }
      }
  • 開發中常見的場景

    • 聊天室程序的,服務器轉發給全部客戶端
    • 網絡遊戲(多人聯機對戰)場景中,服務器將客戶端的狀態進行分發
    • 郵件訂閱
    • Servlet中,監聽器的實現
    • Android中,廣播機制
    • JDK的AWT中事件處理模型,基於觀察者模式的委派事件模型(Delegation Event Model)
      • 事件源 --> 目標對象
      • 事件監聽器 --> 觀察者
    • 京東商城中,羣發某商品打折信息

22. 備忘錄模式

  • memento pattern

  • 場景:

    • 錄入大批人員資料。正在錄入當前人資料時,發現上一我的錄錯了,此時須要恢復上一我的的資料,再進行修改。
    • Word文檔編輯時,突然電腦死機或斷電,再打開時,能夠看到word提示你恢復到之前的文檔。
    • 管理系統中,公文撤回功能。公文發送出去後,想撤回來。
  • 核心:就是保存某個對象內部狀態的拷貝,這樣之後就能夠將該對象恢復到原先的狀態。

  • 結構

    • 源發器類Originator
    • 備忘錄類Memento
    • 負責人類CareTaker
  • 代碼:

    • 源發器類

      package com.sxt.memento;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Emp.java
       * @time: 2020/3/1 14:38
       * @desc: |源發器類
       */
      
      public class Emp {
          private String name;
          private int age;
          private double salary;
      
          // 進行備忘操做,並返回備忘錄對象
          public EmpMemento memento() {
              return new EmpMemento(this);
          }
      
          // 進行數據恢復,恢復成制定備忘錄對象的值
          public void recovery(EmpMemento mmt){
              this.name = mmt.getName();
              this.age = mmt.getAge();
              this.salary = mmt.getSalary();
          }
      
          public Emp(String name, int age, double salary) {
              this.name = name;
              this.age = age;
              this.salary = salary;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public double getSalary() {
              return salary;
          }
      
          public void setSalary(double salary) {
              this.salary = salary;
          }
      }
    • 備忘錄類

      package com.sxt.memento;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: EmpMemento.java
       * @time: 2020/3/1 14:54
       * @desc: |備忘錄類
       */
      
      public class EmpMemento {
          private String name;
          private int age;
          private double salary;
      
          public EmpMemento(Emp e){
              this.name = e.getName();
              this.age = e.getAge();
              this.salary = e.getSalary();
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public double getSalary() {
              return salary;
          }
      
          public void setSalary(double salary) {
              this.salary = salary;
          }
      }
    • 負責人類:管理備忘錄對象

      • 負責保存好的備忘錄對象
      • 能夠經過增長容器,設置多個備忘點
      package com.sxt.memento;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: CareTaker.java
       * @time: 2020/3/1 14:58
       * @desc: |負責人類:管理備忘錄對象
       */
      
      public class CareTaker {
          private EmpMemento memento;
      
          public EmpMemento getMemento() {
              return memento;
          }
      
          public void setMemento(EmpMemento memento) {
              this.memento = memento;
          }
      }
    • 客戶端

      package com.sxt.memento;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Client.java
       * @time: 2020/3/1 14:58
       * @desc: |
       */
      
      public class Client {
          public static void main(String[] args) {
              CareTaker taker = new CareTaker();
              Emp emp = new Emp("李英俊", 18, 900);
              System.out.println("第一次建立對象:" + emp.getName() + emp.getAge() + emp.getSalary());
      
              // 進行一次備份
              taker.setMemento(emp.memento());
      
              emp.setAge(38);
              emp.setName("哈哈");
              emp.setSalary(10);
              System.out.println("第二次建立對象:" + emp.getName() + emp.getAge() + emp.getSalary());
      
              // 恢復到備忘錄對象保存的狀態
              emp.recovery(taker.getMemento());
              System.out.println("第三次建立對象:" + emp.getName() + emp.getAge() + emp.getSalary());
          }
      }
  • UML類圖

    img

  • 備忘點較多時

    • 將備忘錄壓棧
    • 將多個備忘錄對象,序列化和持久化
  • 開發中常見的應用場景:

    • 棋類遊戲中的悔棋操做
    • 普通軟件中的,撤銷操做
    • 數據庫管理軟件中的,事務管理中的回滾操做
    • PS中的歷史記錄

第4章 正則表達式

  • 文本的複雜處理
  • regular expression
  • 開發中使用正則表達式的流程:
    • 分析全部匹配的數據,寫出測試用的典型數據
    • 在工具軟件中進行匹配測試
    • 在程序中調用經過測試的正則表達式
  • regexbuddy工具

1. 正則表達式語法

  • 普通字符:匹配與之相同的一個字符

  • 簡單的轉義字符:

    img

  • 標準字符集合

    • 可以與「多種字符」匹配的表達式
    • 注意區分帶小寫,大寫是相反的意思(好比\d是匹配一個數字,\D就是匹配非數字)
    • 小數點不能匹配換行符\n
    • [\s\S]用來匹配任意字符,包括換行符

    img

  • 自定義字符集合

    • []方括號匹配方式,可以匹配方括號中任意一個字符

    img

    • 正則表達式的特殊符號,被包含到中括號中,則失去特殊意義,除了^,-以外
    • 標準字符集合,除小數點外,若是被包含於中括號,自定義字符集合將包含該集合。好比:[\d.\-+]將匹配:數字、小數點、+、-
  • 量詞 | Quantifier

    • 修飾匹配次數的特殊符號

      img

      \d\d{6} != {\d\d}{6}

    • 匹配次數中的貪婪模式(匹配字符越多越好,默認!)

    • 匹配次數中的非貪婪模式(匹配字符越少越好,修飾匹配次數的特殊符號後再加上一個「?」號)例:\d{2,3}?

  • 字符邊界(零寬)

    • 本組標記匹配的不是字符而是位置,符合某 種條件的位置

    • \b匹配這樣一個位置:前面的字符和後面的字符不全是\w

      img

    • gaoqi\b測試:

      img

    • \bgaoqi\b測試:

      img

  • IGNORECASE:忽略大小寫模式

    • 匹配時忽略大小寫
    • 默認狀況下,正則表達式是要區分大小寫的
  • SINGLELINE:單行模式

    • 整個文本看做一個字符串,只有一個開頭,一個結尾。
    • 使小數點「.」能夠匹配包含換行符(\n)在內的任意字符。
  • MULTILINE:多行模式

    • 每行都是一個字符串,都有開頭和結尾
    • 在制定了MULTILINE以後,若是須要僅匹配字符串開始和結束位置,能夠使用\A和\Z
  • 選擇符和分組

    img

  • 反向引用(\nnm)

    • 每一對()會分配一個編號,使用()的捕獲根據左括號的順序從1開始自動編號。
    • 經過反向應用,能夠對分組已捕獲的字符串進行應用。
  • 預搜索(零寬斷言)

    • 只進行子表達式的匹配,匹配內容不計入最終的匹配結果,是零寬度

    • 這個位置應該符合某個條件。判斷當前位置的先後字符,是否符合指定的條件,但不匹配先後的字符。是對位置的匹配。

    • 正則表達式匹配過程當中,若是子表達式匹配到的是字符的內容,而非位置,並被保存到最終的匹配結果中,那麼就認爲這個子表達式是佔有字符的;若是子表達式匹配的僅僅是位置,或者匹配的內容並不保存到最終的匹配結果中,那麼就認爲這個子表達式是零寬度的。佔有字符仍是零寬度,是針對匹配的內容是否保存到最終的匹配結果中而言的。

      img

2. 正則表達式的練習

  • 練習1

    img

  • 解答1:(0\d{2,3}-\d{7,8})|(1[35789]\d{9})

  • 練習2

    img

  • 解答2:[\w\-]+@[a-z0-9A-Z]+(\.[A-Za-z]{2,4}){1,2}

3. 經常使用正則表達式列表

img

4. JAVA程序中使用正則表達式

  • 開發環境

    • 開發環境和文本編譯器中使用正則
    • 數據庫中也能夠使用正則
  • JAVA相關類位於java.util.regex包下面

  • 類Pattern:

    • 正則表達式的編譯表現形式
    • 創建正則表達式,並啓用相應模式
    • Pattern p = Pattern.compile(r, int);
  • 類Matcher:

    • 經過解釋Pattern對character sequence執行匹配操做的引擎
    • 匹配str字符串
    • Matcher m = p.matcher(str);
  • 測試

    • 匹配整個正則表達式

      package com.sxt.regex;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Demo01.java
       * @time: 2020/3/3 14:10
       * @desc: |
       */
      
      public class Demo01 {
          public static void main(String[] args){
              // 在這個字符串:asdfsadf2323,是否符合制定的正則表達式:\w+
              Pattern p = Pattern.compile("\\w+");
              // 建立Matcher對象
              Matcher m = p.matcher("asdfsadf@@2323");
              // 嘗試將整個字符序列與該模式匹配
              // boolean yo = m.matches();
              // 該方法掃描輸入的序列,查找與該模式匹配的下一個子序列
              while(m.find()){
                  // group()和group(0)都是匹配整個表達式的子字符串
                  System.out.println(m.group());
                  System.out.println(m.group(0));
              }
          }
      }
    • 測試正則表達式分組的處理

      package com.sxt.regex;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @desc: | 測試正則表達式分組的處理
       */
      
      public class Demo02 {
          public static void main(String[] args) {
              // 在這個字符串:asdfsadf2323,是否符合制定的正則表達式:\w+
              Pattern p = Pattern.compile("([a-z]+)([0-9]+)");
              // 建立Matcher對象
              Matcher m = p.matcher("asdfsa12**asd233**dsd11");
              // 嘗試將整個字符序列與該模式匹配
              while (m.find()) {
                  // group()和group(0)都是匹配整個表達式的子字符串
                  System.out.println("start---");
                  System.out.println("知足整個表達式的子字符串:");
                  System.out.println(m.group());
                  System.out.println("知足第1個括號中表達式的字符串:");
                  System.out.println(m.group(1));
                  System.out.println("知足第2個括號中表達式的字符串:");
                  System.out.println(m.group(2));
              }
          }
      }
    • 測試正則表達對象替換操做

      package com.sxt.regex;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @desc: | 測試正則表達對象替換操做
       */
      
      public class Demo03 {
          public static void main(String[] args) {
              // 在這個字符串:asdfsadf2323,是否符合制定的正則表達式:\w+
              Pattern p = Pattern.compile("[0-9]");
              // 建立Matcher對象
              Matcher m = p.matcher("asdfsa12**asd233**dsd11");
      
              // 替換
              String newStr = m.replaceAll("#");
              System.out.println(newStr);
          }
      }
    • 測試正則表達對象分割字符串的操做

      package com.sxt.regex;
      
      import java.util.Arrays;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @desc: | 測試正則表達對象分割字符串的操做
       */
      
      public class Demo04 {
          public static void main(String[] args) {
              String str = "asdfsa12asd233dsd11";
      
              // 切割
              String[] arrs = str.split("\\d+");
              System.out.println(Arrays.toString(arrs));
          }
      }

5. 手寫網絡爬蟲

package com.sxt.regex;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author: Li Tian
 * @contact: litian_cup@163.com
 * @software: IntelliJ IDEA
 * @file: WebSpider.java
 * @time: 2020/3/4 17:29
 * @desc: |網絡爬蟲取數據
 */

public class WebSpider {
    public static void main(String[] args) {
        String url = "http://www.163.com";
        String destStr = getURLContent(url);

        // 取到的超連接的整個內容
        // Pattern p = Pattern.compile("<a[\\s\\S]+?</a>");
        // 取到的超連接的地址
        // Pattern p = Pattern.compile("href=\"(.+?)\"");
        // 注意:上述?是非貪婪模式
        // Matcher m = p.matcher(destStr);
        // while (m.find()) {
        //     System.out.println(m.group());
        //     System.out.println("-----");
        //     System.out.println(m.group(1));
        // }

        List<String> result = getMatherSubstrs(destStr, "href=\"(http://[\\w\\s./]+?)\"");
        for (String temp : result) {
            System.out.println(temp);
        }
    }

    public static String getURLContent(String loc) {
        /*得到url對應的網頁源碼內容*/
        StringBuilder sb = new StringBuilder();
        try {
            URL url = new URL(loc);
            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Charset.forName("gbk")));
            String temp = "";
            while ((temp = reader.readLine()) != null) {
                sb.append(temp);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    public static List<String> getMatherSubstrs(String destStr, String regexStr) {
        // 取到的超連接地址
        Pattern p = Pattern.compile(regexStr);
        Matcher m = p.matcher(destStr);
        List<String> result = new ArrayList<>();
        while (m.find()) {
            result.add(m.group(1));
        }
        return result;
    }
}

第5章 JDBC

1. Mysql引入

  • 經常使用命令行操做

    img

2. JDBC簡述

  • JDBC(Java Database Connection)爲java開發者使用數據庫提供了統一的編程接口,它由一組java類和接口組成。是java程序與數據庫系統通訊的標準api。JDBC API使得開發人員能夠使用純java的方式來鏈接數據庫,並執行操做。

  • 訪問數據庫流程

    img

  • Driver接口

    • Driver接口由數據庫廠家提供,對於java開發者而言,只須要使用Driver接口就能夠了。
    • 在編程中要鏈接數據庫,必須先裝載特定廠商的數據庫驅動程序。不一樣的數據庫有不一樣的裝載方法。
    • 驅動:就是各個數據庫廠商實現的Sun公司提出的JDBC接口。即對Connection等接口的實現類的jar文件。
    • 裝載MySQL驅動
      • Class.forName("com.mysql.jdbc.Driver");
    • 裝載Oracle驅動
      • Class.forName("oracle.jdbc.driver.OracleDriver")
  • DriverManager接口

    • DriverManager是JDBC的管理層,做用於用戶和驅動程序之間。
    • DriverManager跟蹤可用的驅動程序,並在數據庫和相應的驅動程序之間創建鏈接。
  • Connection接口

    • Connection與特定數據庫的鏈接(會話),在鏈接上下文中執行SQL語句並返回結果。
    • DriverManager的getConnection()方法創建在JDBC URL中定義的數據庫Connection鏈接上。
    • 鏈接Mysql數據:
      • Connection con = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
    • 鏈接Oracle數據庫:
      • Connection con = DriverManager.gtConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
  • JDBC詳細操做

    • 靈活指定SQL語句中的變量:PreparedStatement
    • 對存儲過程進行調用:CallableStatement
    • 運用事務處理:Transaction
    • 批處理:Batch,對於大量的批處理,建議使用Statement,由於PreparedStatement的預編譯空間有限,當數據量特別大時,會發生異常。
  • 鏈接測試

  • 報錯參考鏈接:解決方案

    package com.sxt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2020/3/5 12:48
     * @desc: | 測試跟數據庫創建鏈接
     * 若是報錯:參考鏈接:https://www.cnblogs.com/cn-chy-com/p/10145690.html
     */
    
    public class Demo01 {
        public static void main(String[] args) {
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                long start = System.currentTimeMillis();
                // 創建鏈接(鏈接對象內部其實包含了Socket對象,是一個遠程的鏈接。比較耗時!這是Connection對象管理的一個要點!)
                // 真正開發中,爲了提升效率,都會使用鏈接池來管理鏈接對象!
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
                long end = System.currentTimeMillis();
                System.out.println(conn);
                System.out.println("創建鏈接耗時:" + (end - start) + "ms毫秒");
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }

3. JDBC經常使用接口

  • Statement接口

    • 用於執行靜態SQL語句並返回它所生成結果的對象。
    • 三種Statement類:
      • Statement:由createStatement建立,用於發送簡單的SQL語句。(不帶參數的)
      • PreparedStatement:繼承自Statement接口,由prepareStatement建立,用於發送含有一個或多個輸入參數的sql語句。PreparedStatement對象比Statement對象的效率更高,而且能夠防止SQL注入。咱們通常都用PreparedStatement
      • CallableStatement:集成自PreparedStatement。由方法prePareCall建立,用於調用存儲過程。
    • 經常使用的Statement方法:
      • execute():運行語句,返回是否有結果集。
      • executeQuery():運行select語句,返回ResultSet結果集。
      • executeUpadate():運行insert/update/delete操做,返回更新的行數。
  • Statement測試執行sql語句以及sql注入問題

    package com.sxt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo02.java
     * @time: 2020/3/5 12:48
     * @desc: | 測試執行sql語句以及sql注入問題
     */
    
    public class Demo02 {
        public static void main(String[] args) {
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                // 創建鏈接(鏈接對象內部其實包含了Socket對象,是一個遠程的鏈接。比較耗時!這是Connection對象管理的一個要點!)
                // 真正開發中,爲了提升效率,都會使用鏈接池來管理鏈接對象!
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
                Statement stmt = conn.createStatement();
                String sql = "insert into t_user (username, pwd, regTime) values ('趙六', 6666, now())";
                stmt.execute(sql);
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
  • sql注入問題:若要根據id刪除一行記錄,很容易出現數據庫危險,好比要刪除id=5的記錄,傳入的時候爲id = 5 or 1 = 1,最終致使數據庫都被刪除。

  • 測試PreparedStatement的用法

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2020/3/5 12:48
     * @desc: | 測試PreparedStatement的基本用法
     */
    
    public class Demo03 {
        public static void main(String[] args) {
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // ?是佔位符
                String sql = "insert into t_user (username, pwd, regTime) values (?, ?, ?)";
                PreparedStatement ps = conn.prepareStatement(sql);
                // 參數索引是從1開始計算,而不是0
                // ps.setString(1, "傻瓜");
                // ps.setString(2, "12345");
    
                // 還能夠無論類型直接setObject
                // ps.setObject(1, "傻瓜2");
                // ps.setObject(2, "12344");
    
                // 設置時間:注意該時間的格式應該是java.sql.Date
                ps.setObject(1, "傻瓜3");
                ps.setObject(2, "12343");
                ps.setObject(3, new java.sql.Date(System.currentTimeMillis()));
    
                System.out.println("插入一行記錄");
                // 返回是否有結果集
                // ps.execute();
                // 返回更新的行數
                int count = ps.executeUpdate();
                System.out.println(count);
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        }
    }
  • 關閉順序:resultset-->statement-->connection

  • 測試ResultSet結果集的用法

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 測試ResultSet結果集的用法
     * 記得要關閉打開的接口
     */
    
    public class Demo04 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // ?是佔位符
                String sql = "select id, username, pwd from t_user where id>?";
                ps = conn.prepareStatement(sql);
                // 把id>2的記錄都取出來
                ps.setObject(1, 2);
                rs = ps.executeQuery();
    
                while (rs.next()) {
                    // 數字表明哪一列
                    System.out.println(rs.getInt(1) + "-->" + rs.getString(2) + "-->" + rs.getString(3));
                }
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            } finally {
                // 必定要將三個try catch分開寫
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
  • Batch批處理,儘可能使用Statement而不是PreparedStatement

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 批處理
     */
    
    public class Demo05 {
        public static void main(String[] args) {
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
                // 設爲手動提交
                conn.setAutoCommit(false);
                long start = System.currentTimeMillis();
                stmt = conn.createStatement();
                for (int i = 0; i < 20000; i++) {
                    stmt.addBatch("insert into t_user (username, pwd, regTime) values ('li'" + ", 666666, now())");
                    stmt.executeBatch();
                }
                // 提交事務
                conn.commit();
                long  end = System.currentTimeMillis();
                System.out.println("插入20000條數據,耗時(毫秒):" + (end - start));
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            } finally {
                // 必定要將三個try catch分開寫
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (stmt != null) {
                    try {
                        stmt.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

4. 事務

  • 事務的基本概念:一組要麼同時執行成功,要麼同時執行失敗的SQL語句。是數據庫操做的一個執行單元。

  • 事務開始於:

    • 鏈接到數據庫上,並執行一條DML語句(INSERT、UPDATE或DELETE)
    • 前一個事務結束後,又輸入了另一條DML語句
  • 事務結束於:

    • 執行COMMIT或ROLLBACK語句
    • 執行一條DDL語句,如CREATE TABLE語句;這種狀況下,會自動執行COMMIT語句
    • 執行一條DCL語句,例如GRANT語句;這種狀況下,會自動執行COMMIT語句
    • 斷開與數據庫的鏈接
    • 執行了一條DML語句,該語句卻失敗了;在這種狀況下,會爲這個無效的DML語句執行ROLLBACK語句
  • 事務的四大特性(ACID)

    • atomicity(原子性):表示一個事務內的全部操做是一個總體,要麼所有成功,要麼所有失敗
    • consistency(一致性):表示一個事務內有一個操做失敗時,全部的更改過的數據都必須回滾到修改前的狀態
    • isolation(隔離性):事務查看數據時數據所處的狀態,要麼是另外一併發事務修改它以前的狀態,要麼是另外一事務修改它以後的狀態,事務不會查看中間狀態的數據。
      • 事務隔離級別從低到高:
        • 讀取未提交(Read Uncommitted)
        • 讀取已提交(Read Committed)
        • 可重複讀(Repeatable Read)
        • 序列化(Serializable)
    • durability(持久性):持久性事務完成以後,它對系統的影響是永久的
  • 測試事務的基本用法

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 測試事務的基本用法
     */
    
    public class Demo06 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps1 = null;
            PreparedStatement ps2 = null;
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // JDBC默認是自動提交
                conn.setAutoCommit(false);
    
                ps1 = conn.prepareStatement("insert into t_user (username, pwd) values (?, ?)");
                ps1.setObject(1, "狗子");
                ps1.setObject(2, "111");
                ps1.execute();
                System.out.println("插入一個用戶1");
                Thread.sleep(6000);
    
                ps2 = conn.prepareStatement("insert into t_user (username, pwd) values (?, ?, ?)");
                ps2.setObject(1, "狗子2");
                ps2.setObject(2, "111");
                ps2.execute();
                System.out.println("插入一個用戶2");
    
                conn.commit();
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 必定要將三個try catch分開寫
                if (ps1 != null) {
                    try {
                        ps1.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps2 != null) {
                    try {
                        ps2.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

5. 時間處理

  • 時間類型

    • java.util.Date
      • 子類:java.sql.Date:表示年月日
      • 子類:java.sql.Time:表示時分秒
      • 子類:java.sql.Timestamp:表示年月日時分秒
    • 日期比較處理
      • 插入隨機日期
      • 取出指定日期範圍的記錄
  • Date、Timestamp比較和插入隨機日期

    package com.sxt.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.Random;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 測試時間處理(java.sql.Date, java.sql.Time, java.sql.Timestamp)
     */
    
    public class Demo07 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps1 = null;
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                for (int i = 0; i < 1000; i++) {
                    ps1 = conn.prepareStatement("insert into t_user (username, pwd, regTime, lastLoginTime) values (?, ?, ?, ?)");
                    ps1.setObject(1, "狗子" + i);
                    ps1.setObject(2, "111");
    
                    // 定義隨機數
                    int rand = 10000000 + new Random().nextInt(1000000000);
    
                    java.sql.Date date = new java.sql.Date(System.currentTimeMillis() - rand);
                    ps1.setDate(3, date);
    
                    // 若是須要插入制定日期,能夠使用Calendar或DateFormat
                    java.sql.Timestamp stamp = new java.sql.Timestamp(System.currentTimeMillis());
                    ps1.setTimestamp(4, stamp);
    
                    ps1.execute();
                }
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } finally {
                // 必定要將三個try catch分開寫
                if (ps1 != null) {
                    try {
                        ps1.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
  • 取出指定日期範圍的記錄

    package com.sxt.jdbc;
    
    import java.sql.*;
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 測試時間處理,取出指定時間段的數據
     */
    
    public class Demo08 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // 選擇知足regTime條件的記錄,Timestamp格式同理,把java.sql.Date改爲java.sql.Timestamp便可getDate改爲getTimestamp
                ps = conn.prepareStatement("select * from t_user where regTime>? and regTime<?");
                java.sql.Date start = new java.sql.Date(str2Date("2020-3-1 10:23:45"));
                java.sql.Date end = new java.sql.Date(str2Date("2020-3-3 10:23:45"));
                ps.setObject(1, start);
                ps.setObject(2, end);
                rs = ps.executeQuery();
                while(rs.next()){
                    System.out.println(rs.getInt("id") + "-->" + rs.getString("username") + "-->" + rs.getDate("regTime"));
                }
    
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } finally {
                // 必定要將三個try catch分開寫
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static long str2Date(String dateStr){
            /*將字符串表明的日期轉爲long數字(格式:yyyy-MM-dd hh:mm:ss)*/
            DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            try {
                return format.parse(dateStr).getTime();
            } catch (ParseException e) {
                e.printStackTrace();
                return 0;
            }
        }
    }

6. 大對象操做

6.1 CLOB文本大對象操做

  • Character Large Object

  • 用於存儲大量的文本數據

  • 大字段有些特殊,不一樣數據庫處理的方式不同,大字段的操做經常是以流的方式來處理的。而非通常的字段,一次便可讀出數據。

  • Mysql中相關類型:

    • TINYTEXT最大長度爲255(2[1]-1)字符的TEXT列。
    • TEXT[(M)]最大長度爲65535(2[2]-1)字符的TEXT列。
    • MEDIUMTEXT最大長度爲16777215(2[3]-1)字符的TEXT列。
    • LONGTEXT最大長度爲4294967295或4GB(2[4]-1)字符的TEXT列。
  • 測試CLOB文本大對象的使用

  • 包含:將字符串、文件內容插入數據庫中的CLOB字段,將CLOB字段值取出來操做

    package com.sxt.jdbc;
    
    import java.io.*;
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 測試CLOB文本大對象的使用
     * 包含:將字符串、文件內容插入數據庫中的CLOB字段,將CLOB字段值取出來操做
     */
    
    public class Demo09 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                ps = conn.prepareStatement("insert into t_user2 (username, myInfo) values (?, ?)");
                ps.setString(1, "狗子");
                // 將文本文件的內容直接輸入到數據庫中
                // ps.setClob(2, new FileReader(new File("a1.txt")));
                // 經過流的操做寫入字符串內容
                // ps.setClob(2, new BufferedReader(new InputStreamReader(new ByteArrayInputStream("aaaabbbb".getBytes()))));
                // ps.executeUpdate();
    
                // 讀取Clob字段
                ps = conn.prepareStatement("select * from t_user2 where username=?");
                ps.setObject(1, "狗子");
                rs = ps.executeQuery();
                while (rs.next()) {
                    Clob c = rs.getClob("myInfo");
                    Reader r = c.getCharacterStream();
                    int temp = 0;
                    while((temp = r.read()) != -1){
                        System.out.print((char)temp);
                    }
                    System.out.println();
                }
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 必定要將三個try catch分開寫
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

6.2 BLOB二進制大對象操做

  • Binary Large Object

  • 用於存儲大量的二進制數據

  • 大字段有些特殊,不一樣數據庫處理的方式不同,大字段的操做經常是以流的方式來處理的。而非通常的字段,一次便可獨處數據。

  • Mysql中相關類型與CLOB相似,只是將CLOB改成BLOB

  • 測試BLOB二進制大對象的使用

    package com.sxt.jdbc;
    
    import java.io.*;
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 測試BLOB二進制大對象的使用
     */
    
    public class Demo10 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                // 加載驅動類
                Class.forName("com.mysql.cj.jdbc.Driver");
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC", "root", "123456");
    
                // ps = conn.prepareStatement("insert into t_user2 (username, headImg) values (?, ?)");
                // ps.setString(1, "狗子2");
                // 將圖片文件的內容直接輸入到數據庫中
                // ps.setBlob(2, new FileInputStream("test.png"));
                // ps.executeUpdate();
    
                // 讀取Blob字段
                ps = conn.prepareStatement("select * from t_user2 where username=?");
                ps.setObject(1, "狗子2");
                rs = ps.executeQuery();
                while (rs.next()) {
                    Blob b = rs.getBlob("headImg");
                    InputStream is = b.getBinaryStream();
                    OutputStream os = new FileOutputStream("a1_input.png");
                    int temp = 0;
                    while((temp = is.read()) != -1){
                        os.write(temp);
                    }
                }
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 必定要將三個try catch分開寫
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

7. 經典JDBC代碼總結

  • JDBC工具類

  • 包括返回數據庫驅動鏈接,關閉各個接口

    package com.sxt.jdbc;
    
    import java.io.IOException;
    import java.sql.*;
    import java.util.Properties;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: JDBCUTIL.java
     * @time: 2020/3/8 19:43
     * @desc: |JDBC工具類
     */
    
    public class JDBCUtil {
        // 能夠幫助咱們讀取和處理資源文件中的信息
        private static Properties pros = null;
        static {
            /*靜態代碼塊:只有在加載JDBCUtil類的時候調用一次*/
            pros = new Properties();
            try {
                pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/jdbc/db.properties"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static Connection getMysqlConn() {
            /*獲取數據庫(mysql)驅動鏈接*/
            // 加載驅動類
            try {
                Class.forName(pros.getProperty("mysqlDriver"));
                return DriverManager.getConnection(
                        pros.getProperty("mysqlURL"),
                        pros.getProperty("mysqlUser"),
                        pros.getProperty("mysqlPwd"));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public static Connection getOracleConn() {
            /*獲取數據庫(oracle)驅動鏈接*/
            // 加載驅動類
            try {
                Class.forName(pros.getProperty("oracleDriver"));
                return DriverManager.getConnection(
                        pros.getProperty("oracleURL"),
                        pros.getProperty("oracleUser"),
                        pros.getProperty("oraclePwd"));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        public static void close(ResultSet rs, Statement ps, Connection conn){
            /*關閉接口方法*/
            try {
                if (rs != null){
                    rs.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (conn != null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void close(Statement ps, Connection conn){
            /*關閉接口方法,重載*/
            try {
                if (ps != null){
                    ps.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (conn != null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void close(Connection conn){
            /*關閉接口方法,重載*/
            try {
                if (conn != null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  • 配置文件封裝

    mysqlURL=jdbc:mysql://localhost:3306/testjdbc?serverTimezone=UTC
    #mysqlURL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
    mysqlDriver=com.mysql.cj.jdbc.Driver
    mysqlUser=root
    mysqlPwd=123456
    
    oracleDriver=oracle.jdbc.driver.OracleDriver
    oracleURL=jdbc:oracle:thin:@localhost:1521:database
    oracleUser=scott
    oraclePwd=tiger
  • 測試使用JDBCUtil工具類來簡化JDBC開發

    package com.sxt.jdbc;
    
    import java.sql.*;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: | 測試使用JDBCUtil工具類來簡化JDBC開發
     */
    
    public class Demo11 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
    
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("insert into t_user (username) values (?)");
                ps.setString(1, "hehe");
                ps.execute();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
        }
    }

8. ORM原理

  • ORM的基本思想

    • Object Relationship Mapping:對象關係映射
    • 表結構跟類對應;表中字段和類的屬性對應;表中記錄和對象對應。
    • 讓javabean的屬性名和類型儘可能和數據庫保持一致!
    • 一條記錄對應一個對象。將這些查詢到的對象放到容器中(List,Set,Map)
  • 將表中的一條記錄封裝到Object數組中

  • 將表中的一條記錄封裝到map中

  • 將表中的一條記錄封裝到javabean對象中

  • 測試使用Object數組來封裝一條記錄,使用List<Object[]>存儲多條記錄

    package com.sxt.testORM;
    
    import com.sxt.jdbc.JDBCUtil;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Demo01.java
     * @time: 2020/3/10 13:29
     * @desc: |測試使用Object數組來封裝一條記錄
     * 使用List<Object[]>存儲多條記錄
     */
    
    public class Demo01 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<Object[]> list = new ArrayList<>();
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
                ps.setObject(1, 0);
                rs = ps.executeQuery();
                while (rs.next()) {
                    // System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
                    Object[] objs = new Object[3];
                    objs[0] = rs.getObject(1);
                    objs[1] = rs.getObject(2);
                    objs[2] = rs.getObject(3);
    
                    list.add(objs);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
    
            for (Object[] objs : list) {
                System.out.println(objs[0] + "-->" + objs[1] + "-->" + objs[2]);
            }
        }
    }
  • 測試使用Map來封裝一條記錄,使用List<Map>存儲多條記錄(也可用Map<Map>

    package com.sxt.testORM;
    
    import com.sxt.jdbc.JDBCUtil;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: |測試使用Map來封裝一條記錄
     * 使用List<Map>存儲多條記錄(也可用Map<Map>)
     */
    
    public class Demo02 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            // 使用一個Map封裝一條記錄
            List<Map<String, Object>> list = new ArrayList<>();
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
                ps.setObject(1, 0);
                rs = ps.executeQuery();
                while (rs.next()) {
                    // System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
                    Map<String, Object> row = new HashMap<>();
                    row.put("empname", rs.getString(1));
                    row.put("salary", rs.getString(2));
                    row.put("age", rs.getString(3));
                    list.add(row);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
    
            // 遍歷List和Map
            for (Map<String, Object> row : list) {
                for (String key : row.keySet()) {
                    System.out.print(key + "-->" + row.get(key) + "\t\t");
                }
                System.out.println();
            }
        }
    }
  • 使用Javabean對象來封裝一條記錄,使用List<Javabean>存儲多條記錄

    package com.sxt.testORM;
    
    import com.sxt.jdbc.JDBCUtil;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @desc: |使用Javabean對象來封裝一條記錄
     * 使用List<Javabean>存儲多條記錄
     */
    
    public class Demo03 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            List<Emp> list = new ArrayList<>();
            try {
                conn = JDBCUtil.getMysqlConn();
                ps = conn.prepareStatement("select empname, salary, age from emp where id > ?");
                ps.setObject(1, 0);
                rs = ps.executeQuery();
                while (rs.next()) {
                    // System.out.println(rs.getString(1) + "-->" + rs.getDouble(2) + "-->" + rs.getInt(3));
                    Emp emp = new Emp(rs.getString(1), rs.getInt(2), rs.getDouble(3));
                    list.add(emp);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JDBCUtil.close(rs, ps, conn);
            }
    
            for (Emp e: list) {
                System.out.println(e);
            }
        }
    }
  • 其中須要爲每個表定義相同結構的類

    • Emp

      package com.sxt.testORM;
      
      import java.sql.Date;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Emp.java
       * @time: 2020/3/10 14:35
       * @desc: |表結構和類對應
       */
      
      public class Emp {
          private Integer id;
          private String empname;
          private Integer age;
          private Double salary;
          private Date birthday;
          private Integer deptId;
      
          public Emp(String empname, Integer age, Double salary) {
              this.empname = empname;
              this.age = age;
              this.salary = salary;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getEmpname() {
              return empname;
          }
      
          public void setEmpname(String empname) {
              this.empname = empname;
          }
      
          public Integer getAge() {
              return age;
          }
      
          public void setAge(Integer age) {
              this.age = age;
          }
      
          public Double getSalary() {
              return salary;
          }
      
          public void setSalary(Double salary) {
              this.salary = salary;
          }
      
          public Date getBirthday() {
              return birthday;
          }
      
          public void setBirthday(Date birthday) {
              this.birthday = birthday;
          }
      
          public Integer getDeptId() {
              return deptId;
          }
      
          public void setDeptId(Integer deptId) {
              this.deptId = deptId;
          }
      
          public Emp(String empname, Integer age, Double salary, Date birthday, Integer deptId) {
              this.empname = empname;
              this.age = age;
              this.salary = salary;
              this.birthday = birthday;
              this.deptId = deptId;
          }
      
          public Emp(Integer id, String empname, Integer age, Double salary, Date birthday, Integer deptId) {
              this.id = id;
              this.empname = empname;
              this.age = age;
              this.salary = salary;
              this.birthday = birthday;
              this.deptId = deptId;
          }
      
          public Emp() {
          }
      
          @Override
          public String toString() {
              return empname + "-->" + age + "-->" + salary;
          }
      }
    • Dept

      package com.sxt.testORM;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Dept.java
       * @time: 2020/3/10 14:38
       * @desc: |
       */
      
      public class Dept {
          private Integer id;
          private String dname;
          private String address;
      
          public Dept() {
          }
      
          public Dept(String dname, String address) {
              this.dname = dname;
              this.address = address;
          }
      
          public Dept(Integer id, String dname, String address) {
              this.id = id;
              this.dname = dname;
              this.address = address;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getDname() {
              return dname;
          }
      
          public void setDname(String dname) {
              this.dname = dname;
          }
      
          public String getAddress() {
              return address;
          }
      
          public void setAddress(String address) {
              this.address = address;
          }
      }

9. 手寫SORM框架

  • Simple Object Relationship Mapping

  • 咱們但願設計一個能夠實現對象和SQL自動映射的框架,可是總體用法和設計比Hibernate簡單。砍掉沒必要要的功能。

  • 會穿插使用設計模式

  • 從對象到sql

    • 增長:將對象對應成sql語句,執行sql,插入數據庫中
    • 刪除:根據對象主鍵的值,生成sql,執行,從庫中刪除
    • 修改:根據對象須要修改屬性的值,生成sql,執行
  • 從sql到對象

    • 查詢:根據結果分類
      • 多行多列:List<Javabean>
      • 一行多列:Javabean
      • 一行一列:普通對象:Object,數字:Number
  • 核心架構:

    • Query接口:負責查詢(對外提供服務的核心類)
    • QueryFactory類:負責根據配置信息建立query對象
    • TypeConvertor類:負責類型轉換
    • TableContext類:負責獲取管理數據庫全部表結構和類結構的關係,並能夠根據表結構生成類結結構。
    • DBManager類:根據配置信息,維持鏈接對象的管理(增長鏈接池功能)
    • 工具類:
      • JDBCUtils封裝經常使用JDBC操做
      • StringUtils封裝經常使用字符串操做
      • JavaFileUtils封裝Java文件操做
      • ReflectUtils封裝經常使用反射操做
  • 架構圖

    img

  • 核心bean,封裝相關數據

    • ColumnInfo:封裝表中一個字段的信息(字段類型、字段名、鍵類型)
    • Configuration:封裝配置文件信息
    • TableInfo:封裝一張表的信息
  • 針對SORM框架的說明:

    • 核心思想:使用簡單、性能高、極易上手!
    • 配置文件:目前使用資源文件、後期項目複雜後能夠增長XML文件配置和註解
    • 類名由表名生成,只有首字母大寫有區別,其餘無區別
    • Java對象的屬性由表中字段生成,徹底對應
    • 目前,只支持表中只有一個主鍵,聯合主鍵不支持

9.1 初版SORM

  • bean

    • ColumnInfo

      package com.sxt.SORM.bean;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ColumnInfo.java
       * @time: 2020/3/11 13:46
       * @desc: |封裝表中一個字段的信息
       */
      
      public class ColumnInfo {
          // 字段名稱
          private String name;
          // 字段數據類型
          private String dataType;
          // 字段的鍵類型(0普通鍵;1主鍵;2外鍵)
          private int keyType;
      
          public ColumnInfo() {
          }
      
          public ColumnInfo(String name, String dataType, int keyType) {
              this.name = name;
              this.dataType = dataType;
              this.keyType = keyType;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getDataType() {
              return dataType;
          }
      
          public void setDataType(String dataType) {
              this.dataType = dataType;
          }
      
          public int getKeyType() {
              return keyType;
          }
      
          public void setKeyType(int keyType) {
              this.keyType = keyType;
          }
      }
    • Configuration

      package com.sxt.SORM.bean;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Configuration.java
       * @time: 2020/3/11 13:55
       * @desc: |管理配置信息
       */
      
      public class Configuration {
          // 正在使用哪一個數據庫
          private String usingDb;
          // jdbc的url
          private String URL;
          // 驅動類
          private String driver;
          // 數據庫的用戶名
          private String user;
          // 數據庫的密碼
          private String pwd;
          // 項目的源碼路徑
          private String srcPath;
          // 掃描生成java類的包(po的意思是Persistence Object持久化對象)
          private String poPackage;
      
          public Configuration() {
          }
      
          public Configuration(String usingDb, String URL, String driver, String user, String pwd, String srcPath, String poPackage) {
              this.usingDb = usingDb;
              this.URL = URL;
              this.driver = driver;
              this.user = user;
              this.pwd = pwd;
              this.srcPath = srcPath;
              this.poPackage = poPackage;
          }
      
          public String getUsingDb() {
              return usingDb;
          }
      
          public void setUsingDb(String usingDb) {
              this.usingDb = usingDb;
          }
      
          public String getURL() {
              return URL;
          }
      
          public void setURL(String URL) {
              this.URL = URL;
          }
      
          public String getDriver() {
              return driver;
          }
      
          public void setDriver(String driver) {
              this.driver = driver;
          }
      
          public String getUser() {
              return user;
          }
      
          public void setUser(String user) {
              this.user = user;
          }
      
          public String getPwd() {
              return pwd;
          }
      
          public void setPwd(String pwd) {
              this.pwd = pwd;
          }
      
          public String getSrcPath() {
              return srcPath;
          }
      
          public void setSrcPath(String srcPath) {
              this.srcPath = srcPath;
          }
      
          public String getPoPackage() {
              return poPackage;
          }
      
          public void setPoPackage(String poPackage) {
              this.poPackage = poPackage;
          }
      }
    • JavaFieldGetSet

      package com.sxt.SORM.bean;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: JavaFieldGetSet.java
       * @time: 2020/3/11 18:24
       * @desc: |封裝了java屬性和get、set方法的源代碼
       */
      
      public class JavaFieldGetSet {
          // 屬性源碼信息。如:private int userId;
          private String fieldInfo;
          // get方法的源碼信息。如:public int getUserId;
          private String getInfo;
          // set方法的源碼信息。如:public void setUserId(int id){this.id = id;}
          private String setInfo;
      
          public JavaFieldGetSet() {
          }
      
          public JavaFieldGetSet(String fieldInfo, String getInfo, String setInfo) {
              this.fieldInfo = fieldInfo;
              this.getInfo = getInfo;
              this.setInfo = setInfo;
          }
      
          public String getFieldInfo() {
              return fieldInfo;
          }
      
          public void setFieldInfo(String fieldInfo) {
              this.fieldInfo = fieldInfo;
          }
      
          public String getGetInfo() {
              return getInfo;
          }
      
          public void setGetInfo(String getInfo) {
              this.getInfo = getInfo;
          }
      
          public String getSetInfo() {
              return setInfo;
          }
      
          public void setSetInfo(String setInfo) {
              this.setInfo = setInfo;
          }
      
          @Override
          public String toString() {
              // System.out.println(fieldInfo);
              // System.out.println(getInfo);
              // System.out.println(setInfo);
              return super.toString();
          }
      }
    • TableInfo

      package com.sxt.SORM.bean;
      
      import java.util.List;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: TableInfo.java
       * @time: 2020/3/11 13:56
       * @desc: |存儲表結構信息
       */
      
      public class TableInfo {
          // 表名
          private String tname;
          // 全部字段的信息
          private Map<String, ColumnInfo> columns;
          // 惟一主鍵(目前只能處理表中有且只有一個的狀況)
          private ColumnInfo onlyPriKey;
          // 若是聯合主鍵,則在這裏存儲
          private List<ColumnInfo> priKeys;
      
          public TableInfo() {
          }
      
          public TableInfo(String tname, List<ColumnInfo> priKeys, Map<String, ColumnInfo> columns) {
              this.tname = tname;
              this.columns = columns;
              this.priKeys = priKeys;
          }
      
          public String getTname() {
              return tname;
          }
      
          public void setTname(String tname) {
              this.tname = tname;
          }
      
          public Map<String, ColumnInfo> getColumns() {
              return columns;
          }
      
          public void setColumns(Map<String, ColumnInfo> columns) {
              this.columns = columns;
          }
      
          public ColumnInfo getOnlyPriKey() {
              return onlyPriKey;
          }
      
          public void setOnlyPriKey(ColumnInfo onlyPriKey) {
              this.onlyPriKey = onlyPriKey;
          }
      
          public List<ColumnInfo> getPriKeys() {
              return priKeys;
          }
      
          public void setPriKeys(List<ColumnInfo> priKeys) {
              this.priKeys = priKeys;
          }
      }
  • core

    • Query

      package com.sxt.SORM.core;
      
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Query.java
       * @time: 2020/3/10 17:31
       * @desc: |負責查詢(對外提供服務的核心類)
       */
      
      public interface Query {
      
          /**
           * 直接執行一個DML語句
           *
           * @param sql    sql語句
           * @param params 參數
           * @return 執行sql語句後影響記錄的行數
           */
          public int executeDML(String sql, Object[] params);
      
          /**
           * 將一個對象存儲到數據庫中
           *
           * @param obj 要存儲的對象
           */
          public void insert(Object obj);
      
          /**
           * 刪除clazz表示類對應的表中的記錄(指定主鍵id的記錄)
           * 把對象中不爲null的屬性往數據庫中存儲!若是數字爲null則放0
           * @param clazz 跟表對應的類的Class對象
           * @param id    主鍵的值
           */
          // delete from User where id = 2;
          public void delete(Class clazz, Object id);
      
          /**
           * 刪除對象在數據庫中對應的記錄(對象所在類對應到表,對象的主鍵對應到的記錄)
           *
           * @param obj
           */
          public void delete(Object obj);
      
          /**
           * 更新對象對應的記錄,而且只更新指定的字段的值
           *
           * @param obj        索要更新的對象
           * @param fieldNames 更新的屬性列表
           * @return 執行sql語句後影響記錄的行數
           */
          // update user set uname=?, pwe=?
          public int update(Object obj, String[] fieldNames);
      
          /**
           * 查詢返回多行記錄,並將每行記錄封裝到clazz指定的類的對象中
           *
           * @param sql    查詢語句
           * @param clazz  封裝數據的javabean類的Class對象
           * @param params sql的參數
           * @return 返回查詢到的結果
           */
          public List queryRows(String sql, Class clazz, Object[] params);
      
          /**
           * 查詢返回一行記錄,並將該記錄封裝到clazz指定的類的對象中
           *
           * @param sql    查詢語句
           * @param clazz  封裝數據的javabean類的Class對象
           * @param params sql的參數
           * @return 返回查詢到的結果
           */
          public Object queryUniqueRows(String sql, Class clazz, Object[] params);
      
          /**
           * 查詢返回一個值(一行一列),並將該值返回
           *
           * @param sql    查詢語句
           * @param params sql的參數
           * @return 返回查詢到的結果
           */
          public Object queryValue(String sql, Object[] params);
      
          /**
           * 查詢返回一個數字(一行一列),並將該值返回
           *
           * @param sql    查詢語句
           * @param params sql的參數
           * @return 返回查詢到的數字
           */
          public Number queryNumber(String sql, Object[] params);
      }
    • MysqlQuery

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.ColumnInfo;
      import com.sxt.SORM.bean.TableInfo;
      import com.sxt.SORM.po.Emp;
      import com.sxt.SORM.utils.JDBCUtils;
      import com.sxt.SORM.utils.ReflectUtils;
      import com.sxt.SORM.vo.EmpVO;
      
      import java.lang.reflect.Field;
      import java.sql.*;
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: MysqlQuery.java
       * @time: 2020/3/13 16:54
       * @desc: |負責針對mysql數據庫的查詢
       */
      
      public class MysqlQuery implements Query {
      
          public static void main(String[] args) {
              Object obj = new MysqlQuery().queryValue("select count(*) from emp where salary>?", new Object[]{1000});
              System.out.println(obj);
          }
      
          /**
           * 複雜多行查詢測試
           */
          public static void testQueryRows() {
              List<Emp> list = new MysqlQuery().queryRows("select id,empname,age from emp where age>? and salary<?", Emp.class,
                      new Object[]{1, 9000});
              for (Emp e : list) {
                  System.out.println(e.getEmpname());
              }
      
              String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                      + " " + "join dept d on e.deptId=d.id;";
              List<EmpVO> list2 = new MysqlQuery().queryRows(sql2, EmpVO.class, null);
              for (EmpVO e : list2) {
                  System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
              }
          }
      
          /**
           * 增刪改操做測試
           */
          public static void testDML() {
              Emp e = new Emp();
              e.setEmpname("Tom");
              e.setBirthday(new java.sql.Date(System.currentTimeMillis()));
              e.setAge(30);
              e.setSalary(8888.0);
              e.setId(1);
      
              // new MysqlQuery().delete(e);
              // new MysqlQuery().insert(e);
              new MysqlQuery().update(e, new String[]{"empname", "age", "salary"});
          }
      
          @Override
          public int executeDML(String sql, Object[] params) {
              Connection conn = DBManager.getConn();
              int count = 0;
              PreparedStatement ps = null;
              try {
                  ps = conn.prepareStatement(sql);
                  // 給sql設置參數,就是?位置的參數
                  JDBCUtils.handleParams(ps, params);
                  count = ps.executeUpdate();
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  DBManager.close(ps, conn);
              }
              return count;
          }
      
          @Override
          public void insert(Object obj) {
              // obj --> 表中。 insert into 表名(id, name, pwd) values (?, ?, ?)
              Class c = obj.getClass();
              // 存儲sql的參數對象
              List<Object> params = new ArrayList<>();
              TableInfo tableInfo = TableContext.poClassTableMap.get(c);
              StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");
      
              // 計算不爲空的屬性值
              int countNotNullField = 0;
      
              // 目前只能處理數據庫來維護自增的方式
              Field[] fs = c.getDeclaredFields();
              for (Field f : fs) {
                  String fieldName = f.getName();
                  Object fieldValue = ReflectUtils.invokeGet(fieldName, obj);
                  if (fieldValue != null) {
                      // 若是該屬性值不爲空
                      countNotNullField++;
                      sql.append(fieldName + ",");
                      params.add(fieldValue);
                  }
              }
      
              // 把最後一個屬性後面的,換成)
              sql.setCharAt(sql.length() - 1, ')');
              sql.append(" values (");
      
              for (int i = 0; i < countNotNullField; i++) {
                  sql.append("?,");
              }
              sql.setCharAt(sql.length() - 1, ')');
      
              executeDML(sql.toString(), params.toArray());
          }
      
          @Override
          public void delete(Class clazz, Object id) {
              // Emp.class, 2 --> delete from emp where id=2
              // 經過Class對象找TableInfo
              TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
              // 得到主鍵
              ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
      
              String sql = "delete from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "=?;";
              executeDML(sql, new Object[]{id});
          }
      
          @Override
          public void delete(Object obj) {
              Class c = obj.getClass();
              TableInfo tableInfo = TableContext.poClassTableMap.get(c);
              // 得到主鍵
              ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
              // 經過反射機制,調用屬性對應的get方法或set方法
              Object priKeyValue = ReflectUtils.invokeGet(onlyPriKey.getName(), obj);
              delete(obj.getClass(), priKeyValue);
          }
      
          @Override
          public int update(Object obj, String[] fieldNames) {
              // obj{"uname", "pwd} --> update 表名 set uname=?, pwd=? where id=?
              Class c = obj.getClass();
              List<Object> params = new ArrayList<>();
              TableInfo tableInfo = TableContext.poClassTableMap.get(c);
              ColumnInfo priKey = tableInfo.getOnlyPriKey();
              StringBuilder sql = new StringBuilder("update " + tableInfo.getTname() + " set ");
      
              for (String fname : fieldNames) {
                  Object fvalue = ReflectUtils.invokeGet(fname, obj);
                  params.add(fvalue);
                  sql.append(fname + "=?,");
              }
              sql.setCharAt(sql.length() - 1, ' ');
              sql.append(" where ");
              sql.append(priKey.getName() + "=?");
              params.add(ReflectUtils.invokeGet(priKey.getName(), obj));
      
              return executeDML(sql.toString(), params.toArray());
          }
      
          @Override
          public List queryRows(String sql, Class clazz, Object[] params) {
              Connection conn = DBManager.getConn();
              // 存放查詢結果的容器
              List list = null;
              PreparedStatement ps = null;
              ResultSet rs = null;
              try {
                  ps = conn.prepareStatement(sql);
                  // 給sql設置參數,就是?位置的參數
                  JDBCUtils.handleParams(ps, params);
                  rs = ps.executeQuery();
                  ResultSetMetaData metaData = rs.getMetaData();
                  // 多行
                  while (rs.next()) {
                      if (list == null) {
                          list = new ArrayList();
                      }
                      // 調用javabean的無參構造器
                      Object rowObj = clazz.newInstance();
                      // 多列 select username, pwd, age from user where id>? and age>?
                      for (int i = 0; i < metaData.getColumnCount(); i++) {
                          // username
                          String columnName = metaData.getColumnLabel(i + 1);
                          Object columnValue = rs.getObject(i + 1);
      
                          // 調用rowObj對象的setUsername(String uname)方法,將columnValue的值設置進去
                          ReflectUtils.invokeSet(rowObj, columnName, columnValue);
                      }
                      list.add(rowObj);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  DBManager.close(ps, conn);
              }
              return list;
          }
      
          @Override
          public Object queryUniqueRows(String sql, Class clazz, Object[] params) {
              List list = queryRows(sql, clazz, params);
              return (list == null && list.size() > 0) ? null : list.get(0);
          }
      
          @Override
          public Object queryValue(String sql, Object[] params) {
              Connection conn = DBManager.getConn();
              // 存儲查詢結果的對象
              Object value = null;
              PreparedStatement ps = null;
              ResultSet rs = null;
              try {
                  ps = conn.prepareStatement(sql);
                  // 給sql設置參數,就是?位置的參數
                  JDBCUtils.handleParams(ps, params);
                  rs = ps.executeQuery();
                  // 多行
                  while (rs.next()) {
                      // select count(*) from user
                      value = rs.getObject(1);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  DBManager.close(ps, conn);
              }
              return value;
          }
      
          @Override
          public Number queryNumber(String sql, Object[] params) {
              return (Number) queryValue(sql, params);
          }
      }
    • TypeConvertor

      package com.sxt.SORM.core;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: TypeConvertor.java
       * @time: 2020/3/11 13:39
       * @desc: |負責java數據類型和數據庫數據類型的互相轉換
       */
      
      public interface TypeConvertor {
          /**
           * 將數據庫數據類型轉化成java的數據類型
           * @param columnType 數據庫字段的數據類型
           * @return java的數據類型
           */
          public String databaseType2JavaType(String columnType);
      
          /**
           * 將java數據類型轉化爲數據庫數據類型
           * @param javaDataType java數據類型
           * @return 數據庫數據類型
           */
          public String javaType2DatabaseType(String javaDataType);
      }
    • MySqlTypeConvertor

      package com.sxt.SORM.core;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: MySqlTypeConvertor.java
       * @time: 2020/3/11 18:16
       * @desc: |mysql數據類型和java數據類型的轉換
       */
      
      public class MySqlTypeConvertor implements TypeConvertor {
          @Override
          public String databaseType2JavaType(String columnType) {
              // varchar --> String
              if ("varchar".equalsIgnoreCase(columnType) || "char".equalsIgnoreCase(columnType)) {
                  return "String";
              } else if ("int".equalsIgnoreCase(columnType)
                      || "tinyint".equalsIgnoreCase(columnType)
                      || "smallint".equalsIgnoreCase(columnType)
                      || "integer".equalsIgnoreCase(columnType)) {
                  return "Integer";
              } else if ("bigint".equalsIgnoreCase(columnType)) {
                  return "long";
              } else if ("double".equalsIgnoreCase(columnType) || "float".equalsIgnoreCase(columnType)) {
                  return "Double";
              } else if ("clob".equalsIgnoreCase(columnType)) {
                  return "java.sql.Clob";
              } else if ("blob".equalsIgnoreCase(columnType)) {
                  return "java.sql.Blob";
              }else if("date".equalsIgnoreCase(columnType)){
                  return "java.sql.Date";
              }else if("time".equalsIgnoreCase(columnType)){
                  return "java.sql.Time";
              }else if("timestamp".equalsIgnoreCase(columnType)){
                  return "java.sql.Timestamp";
              }
              return null;
          }
      
          @Override
          public String javaType2DatabaseType(String javaDataType) {
              return null;
          }
      }
    • DBManager

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.Configuration;
      
      import java.io.IOException;
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.ResultSet;
      import java.sql.Statement;
      import java.util.Properties;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: DBManager.java
       * @time: 2020/3/11 13:43
       * @desc: |根據配置信息,維持鏈接對象的管理(增長鏈接池功能)
       */
      
      public class DBManager {
          private static Configuration conf;
      
          static {
              // 靜態代碼塊
              Properties pros = new Properties();
              try {
                  pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/SORM/db.properties"));
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              conf = new Configuration();
              conf.setDriver(pros.getProperty("driver"));
              conf.setPoPackage(pros.getProperty("poPackage"));
              conf.setPwd(pros.getProperty("pwd"));
              conf.setSrcPath(pros.getProperty("srcPath"));
              conf.setURL(pros.getProperty("URL"));
              conf.setUser(pros.getProperty("user"));
              conf.setUsingDb(pros.getProperty("usingDB"));
          }
      
          public static Connection getConn() {
              /*獲取數據庫(mysql)驅動鏈接*/
              // 加載驅動類
              try {
                  Class.forName(conf.getDriver());
                  // 直接創建鏈接,後期增長鏈接池處理,提升效率!
                  return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      
          public static void close(ResultSet rs, Statement ps, Connection conn) {
              /*關閉接口方法*/
              try {
                  if (rs != null) {
                      rs.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (conn != null) {
                      conn.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Statement ps, Connection conn) {
              /*關閉接口方法,重載*/
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (conn != null) {
                      conn.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Connection conn) {
              /*關閉接口方法,重載*/
              try {
                  if (conn != null) {
                      conn.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static Configuration getConf(){
              return conf;
          }
      }
    • TableContext

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.ColumnInfo;
      import com.sxt.SORM.bean.TableInfo;
      import com.sxt.SORM.utils.JavaFileUtils;
      import com.sxt.SORM.utils.StringUtils;
      
      import java.sql.Connection;
      import java.sql.DatabaseMetaData;
      import java.sql.ResultSet;
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.Map;
      
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: TableContext.java
       * @time: 2020/3/11 13:42
       * @desc: |負責獲取管理數據庫全部表結構和類結構的關係,並能夠根據表結構生成類結結構
       */
      
      public class TableContext {
          // 表名爲key,表信息對象爲value
          public static Map<String, TableInfo> tables = new HashMap<>();
          // 將po的calss對象和表信息對象關聯起來,便於重用。
          public static Map<Class, TableInfo> poClassTableMap = new HashMap<>();
      
          private TableContext() {
          }
      
          static {
              try {
                  // 初始化得到的表信息
                  Connection conn = DBManager.getConn();
                  DatabaseMetaData dbmd = conn.getMetaData();
                  ResultSet tableSet = dbmd.getTables("", "%", "%", new String[]{"TABLE"});
      
                  while (tableSet.next()) {
                      // 循環每一個表名
                      String tableName = (String) tableSet.getObject("TABLE_NAME");
                      TableInfo ti = new TableInfo(tableName, new ArrayList<ColumnInfo>(), new HashMap<String, ColumnInfo>());
                      tables.put(tableName, ti);
                      // 查詢表中的全部字段
                      ResultSet set = dbmd.getColumns("", "%", tableName, "%");
                      while (set.next()) {
                          // 循環每一個列名
                          ColumnInfo ci = new ColumnInfo(set.getString("COLUMN_NAME"), set.getString("TYPE_NAME"), 0);
                          ti.getColumns().put(set.getString("COLUMN_NAME"), ci);
                      }
      
                      // 查詢表中的主鍵
                      // System.out.println(tableName);
                      ResultSet set2 = dbmd.getPrimaryKeys("", "%", tableName);
                      while (set2.next()) {
                          ColumnInfo ci2 = (ColumnInfo) ti.getColumns().get(set2.getObject("COLUMN_NAME"));
                          // 設置爲主鍵類型
                          ci2.setKeyType(1);
                          ti.getPriKeys().add(ci2);
                      }
                      if (ti.getPriKeys().size() > 0) {
                          // 取惟一主鍵。方便使用。若是是聯合主鍵。則爲空!
                          ti.setOnlyPriKey(ti.getPriKeys().get(0));
                      }
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
      
              // 更新類結構
              updateJavaPOFile();
              // 加載po包下面的全部類,便於重用,提升效率!
              loadPOTables();
          }
      
          /**
           * 根據表結構,更新配置的po包下面的java類
           */
          public static void updateJavaPOFile() {
              Map<String, TableInfo> map = TableContext.tables;
              for (TableInfo t : map.values()) {
                  JavaFileUtils.createJavaPOFile(t, new MySqlTypeConvertor());
              }
          }
      
          /**
           * 加載po包下面的類
           */
          public static void loadPOTables() {
              for (TableInfo tableInfo : tables.values()) {
                  try {
                      Class c = Class.forName(DBManager.getConf().getPoPackage() + "." + StringUtils.firstChar2UpperCase(tableInfo.getTname()));
                      poClassTableMap.put(c, tableInfo);
                  } catch (ClassNotFoundException e) {
                      e.printStackTrace();
                  }
              }
          }
      
          public static void main(String[] args) {
              Map<String, TableInfo> tables = TableContext.tables;
              System.out.println(tables);
          }
      
      }
  • utils

    • JavaFileUtils

      package com.sxt.SORM.utils;
      
      import com.sxt.SORM.bean.ColumnInfo;
      import com.sxt.SORM.bean.JavaFieldGetSet;
      import com.sxt.SORM.bean.TableInfo;
      import com.sxt.SORM.core.DBManager;
      import com.sxt.SORM.core.MySqlTypeConvertor;
      import com.sxt.SORM.core.TableContext;
      import com.sxt.SORM.core.TypeConvertor;
      
      import java.io.*;
      import java.util.ArrayList;
      import java.util.List;
      import java.util.Map;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: JavaFileUtils.java
       * @time: 2020/3/11 13:44
       * @desc: | 封裝了生成java文件(源代碼)經常使用操做
       */
      
      public class JavaFileUtils {
          /**
           * 根據字段信息生成java屬性信息。如varchar username --> private String username;以及相應的set和get方法源碼
           *
           * @param column    字段信息
           * @param convertor 類型轉化器
           * @return java屬性和set/get方法源碼
           */
          public static JavaFieldGetSet createFieldGetSetSRC(ColumnInfo column, TypeConvertor convertor) {
              JavaFieldGetSet jfgs = new JavaFieldGetSet();
              String javaFieldType = convertor.databaseType2JavaType(column.getDataType());
              jfgs.setFieldInfo("\tprivate " + javaFieldType + " " + column.getName() + ";\n");
      
              // public String getUsername(){return username;}
              StringBuilder getSrc = new StringBuilder();
              getSrc.append("\tpublic " + javaFieldType + " get" + StringUtils.firstChar2UpperCase(column.getName()) + "(){\n");
              getSrc.append("\t\treturn " + column.getName() + ";\n");
              getSrc.append("\t}\n");
              jfgs.setGetInfo(getSrc.toString());
      
              // public void setUsername(String username){this.username = username;}
              StringBuilder setSrc = new StringBuilder();
              setSrc.append("\tpublic void set" + StringUtils.firstChar2UpperCase(column.getName()) + "(");
              setSrc.append(javaFieldType + " " + column.getName() + "){\n");
              setSrc.append("\t\tthis." + column.getName() + " = " + column.getName() + ";\n");
              setSrc.append("\t}\n");
              jfgs.setSetInfo(setSrc.toString());
      
              return jfgs;
          }
      
          /**
           * 根據表信息生成java類的源代碼
           *
           * @param tableInfo 表信息
           * @param convertor 數據類型轉化器
           * @return java類的源代碼
           */
          public static String createJavaSrc(TableInfo tableInfo, TypeConvertor convertor) {
              Map<String, ColumnInfo> columns = tableInfo.getColumns();
              List<JavaFieldGetSet> javaFields = new ArrayList<>();
      
              for (ColumnInfo c : columns.values()) {
                  javaFields.add(createFieldGetSetSRC(c, convertor));
              }
              StringBuilder src = new StringBuilder();
      
              // 生成package語句
              src.append("package " + DBManager.getConf().getPoPackage() + ";\n\n");
              // 生成import語句
              src.append("import java.sql.*;\n");
              src.append("import java.util.*;\n\n");
              // 生成類聲明語句
              src.append("public class " + StringUtils.firstChar2UpperCase(tableInfo.getTname()) + " {\n\n");
              // 生成屬性列表
              for (JavaFieldGetSet f : javaFields) {
                  src.append(f.getFieldInfo());
              }
              src.append("\n\n");
              // 生成set方法列表
              for (JavaFieldGetSet f : javaFields) {
                  src.append(f.getSetInfo());
              }
              // 生成get方法列表
              for (JavaFieldGetSet f : javaFields) {
                  src.append(f.getGetInfo());
              }
              // 生成類結束
              src.append("}\n");
              // System.out.println(src);
              return src.toString();
          }
      
          public static void createJavaPOFile(TableInfo tableInfo, TypeConvertor convertor) {
              String src = createJavaSrc(tableInfo, convertor);
              String srcPath = DBManager.getConf().getSrcPath() + "\\";
              String packagePath = DBManager.getConf().getPoPackage().replaceAll("\\.", "/");
              // 修正poPackage路徑,由於沒有從新建立項目
              String[] packagePath_list = packagePath.split("/");
              packagePath = packagePath_list[packagePath_list.length - 1];
      
              File f = new File(srcPath + packagePath);
              // System.out.println(f.getAbsolutePath());
      
              if (!f.exists()) {
                  // 指定目錄不存在則幫助用戶創建該目錄
                  f.mkdirs();
              }
      
              BufferedWriter bw = null;
              try {
                  bw = new BufferedWriter(new FileWriter(f.getAbsolutePath() + "/" + StringUtils.firstChar2UpperCase(tableInfo.getTname()) + ".java"));
                  bw.write(src);
                  System.out.println("創建表" + tableInfo.getTname() + "對應的java類");
                  bw.flush();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      if (bw != null) {
                          bw.close();
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
      
          }
      
          public static void main(String[] args) {
              // 測試每個表的field,set、get方法源碼生成
              // ColumnInfo ci = new ColumnInfo("username", "int", 0);
              // JavaFieldGetSet f = createFieldGetSetSRC(ci, new MySqlTypeConvertor());
              // System.out.println(f);
              // System.out.println("\n--------------------" + "分割線" + "--------------------\n");
      
              // 測試每個表的從頭至尾徹底源碼生成
              // Map<String, TableInfo> map = TableContext.tables;
              // TableInfo t = map.get("emp");
              // createJavaSrc(t, new MySqlTypeConvertor());
      
              Map<String, TableInfo> map = TableContext.tables;
              // TableInfo t = map.get("emp");
              for(TableInfo t: map.values()) {
                  createJavaPOFile(t, new MySqlTypeConvertor());
              }
          }
      }
    • JDBCUtils

      package com.sxt.SORM.utils;
      
      import java.sql.PreparedStatement;
      import java.sql.SQLException;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: JDBCUtils.java
       * @time: 2020/3/11 13:43
       * @desc: | 封裝了JDBC查詢經常使用的操做
       */
      
      public class JDBCUtils {
          /**
           * 給sql設置參數,就是?位置的參數
           * @param ps 預編譯sql語句對象
           * @param params 參數
           */
          public static void handleParams(PreparedStatement ps, Object[] params) {
              if (params != null) {
                  for (int i = 0; i < params.length; i++) {
                      try {
                          ps.setObject(1 + i, params[i]);
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
      }
    • StringUtils

      package com.sxt.SORM.utils;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: StringUtils.java
       * @time: 2020/3/11 13:44
       * @desc: | 封裝了字符串經常使用的操做
       */
      
      public class StringUtils {
          /**
           * 將目標字符串首字母變爲大寫
           * @param str 目標字符串
           * @return 首字母變爲大寫的字符串
           */
          public static String firstChar2UpperCase(String str){
              // abcd-->Abcd
              return str.toUpperCase().substring(0, 1) + str.substring(1);
          }
      }
    • ReflectUtils

      package com.sxt.SORM.utils;
      
      import java.lang.reflect.Method;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: ReflectUtils.java
       * @time: 2020/3/11 13:44
       * @desc: | 封裝了反射的經常使用操做
       */
      
      public class ReflectUtils {
          /**
           * 調用obj對象對應屬性fieldName的get方法
           *
           * @param fieldName 屬性名
           * @param obj       Object對象
           * @return
           */
          public static Object invokeGet(String fieldName, Object obj) {
              // 經過反射機制,調用屬性對應的get方法或set方法
              try {
                  Class c = obj.getClass();
                  Method m = c.getDeclaredMethod("get" + StringUtils.firstChar2UpperCase(fieldName), null);
                  return m.invoke(obj, null);
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      
          public static void invokeSet(Object obj, String columnName, Object columnValue) {
              try {
                  if (columnValue != null) {
                      Method m = obj.getClass().getDeclaredMethod("set" + StringUtils.firstChar2UpperCase(columnName),
                              columnValue.getClass());
                      m.invoke(obj, columnValue);
                  }
              } catch (Exception e) {
                  e.printStackTrace();
      
              }
          }
      }
  • po(自動生成的代碼保存位置)

    • Dept

      package com.sxt.SORM.po;
      
      public class Dept {
      
      	private String address;
      	private Integer id;
      	private String dname;
      
      
      	public void setAddress(String address){
      		this.address = address;
      	}
      	public void setId(Integer id){
      		this.id = id;
      	}
      	public void setDname(String dname){
      		this.dname = dname;
      	}
      	public String getAddress(){
      		return address;
      	}
      	public Integer getId(){
      		return id;
      	}
      	public String getDname(){
      		return dname;
      	}
      }
    • Emp

      package com.sxt.SORM.po;
      
      public class Emp {
      
          private String empname;
          private java.sql.Date birthday;
          private Double bonus;
          private Integer deptId;
          private Integer id;
          private Double salary;
          private Integer age;
      
          public String getEmpname() {
              return empname;
          }
      
          public void setEmpname(String empname) {
              this.empname = empname;
          }
      
          public java.sql.Date getBirthday() {
              return birthday;
          }
      
          public void setBirthday(java.sql.Date birthday) {
              this.birthday = birthday;
          }
      
          public Double getBonus() {
              return bonus;
          }
      
          public void setBonus(Double bonus) {
              this.bonus = bonus;
          }
      
          public Integer getDeptId() {
              return deptId;
          }
      
          public void setDeptId(Integer deptId) {
              this.deptId = deptId;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public Double getSalary() {
              return salary;
          }
      
          public void setSalary(Double salary) {
              this.salary = salary;
          }
      
          public Integer getAge() {
              return age;
          }
      
          public void setAge(Integer age) {
              this.age = age;
          }
      }
  • vo(複雜查詢生成的類所保存的位置)

    • EmpVO

      package com.sxt.SORM.vo;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: EmpVO.java
       * @time: 2020/3/15 12:44
       * @desc: |
       */
      
      public class EmpVO {
          // select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e
          // join dept d on e.deptId=d.id;
          private Integer id;
          private String empname;
          private Double xinshui;
          private Integer age;
          private String deptName;
          private String deptAddr;
      
          public EmpVO() {
          }
      
          public EmpVO(Integer id, String empname, Double xinshui, Integer age, String deptName, String deptAddr) {
              this.id = id;
              this.empname = empname;
              this.xinshui = xinshui;
              this.age = age;
              this.deptName = deptName;
              this.deptAddr = deptAddr;
          }
      
          public Integer getId() {
              return id;
          }
      
          public void setId(Integer id) {
              this.id = id;
          }
      
          public String getEmpname() {
              return empname;
          }
      
          public void setEmpname(String empname) {
              this.empname = empname;
          }
      
          public Double getXinshui() {
              return xinshui;
          }
      
          public void setXinshui(Double xinshui) {
              this.xinshui = xinshui;
          }
      
          public Integer getAge() {
              return age;
          }
      
          public void setAge(Integer age) {
              this.age = age;
          }
      
          public String getDeptName() {
              return deptName;
          }
      
          public void setDeptName(String deptName) {
              this.deptName = deptName;
          }
      
          public String getDeptAddr() {
              return deptAddr;
          }
      
          public void setDeptAddr(String deptAddr) {
              this.deptAddr = deptAddr;
          }
      }
  • 配置文件(db.properties)

    usingDB=mysql
    URL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
    driver=com.mysql.cj.jdbc.Driver
    user=root
    pwd=123456
    srcPath=F:/BookStudy/else/JAVAPro/src/com/sxt/SORM
    poPackage=com.sxt.SORM.po
  • README(說明文件)

    1. 在src下創建db.properties
    2. 每張表只有一個主鍵,不能處理多個主鍵的狀況
    3. po儘可能使用包裝類,不要使用基本數據類型
    4. 目前只能處理數據庫來維護自增的方式

9.2 經過模板方法模式對Query進行優化

  • Query

    package com.sxt.SORM.core;
    
    import com.sxt.SORM.bean.ColumnInfo;
    import com.sxt.SORM.bean.TableInfo;
    import com.sxt.SORM.utils.JDBCUtils;
    import com.sxt.SORM.utils.ReflectUtils;
    
    import java.lang.reflect.Field;
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Query.java
     * @time: 2020/3/10 17:31
     * @desc: |負責查詢(對外提供服務的核心類)
     */
    
    public abstract class Query implements Cloneable {
    
        /**
         * 採用模板方法模式將JDBC操做封裝成模板,變於重用
         *
         * @param sql    sql語句
         * @param params sql的參數
         * @param clazz  記錄要封裝到的java類
         * @param back   CallBack的實現類,實現回調
         * @return 返回查詢結果
         */
        public Object executeQueryTemplate(String sql, Object[] params, Class clazz, CallBack back) {
            Connection conn = DBManager.getConn();
            // 存放查詢結果的容器
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                ps = conn.prepareStatement(sql);
                // 給sql設置參數,就是?位置的參數
                JDBCUtils.handleParams(ps, params);
                rs = ps.executeQuery();
                ResultSetMetaData metaData = rs.getMetaData();
    
                return back.doExecute(conn, ps, rs);
    
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {
                DBManager.close(ps, conn);
            }
        }
    
        /**
         * 直接執行一個DML語句
         *
         * @param sql    sql語句
         * @param params 參數
         * @return 執行sql語句後影響記錄的行數
         */
        public int executeDML(String sql, Object[] params) {
            Connection conn = DBManager.getConn();
            int count = 0;
            PreparedStatement ps = null;
            try {
                ps = conn.prepareStatement(sql);
                // 給sql設置參數,就是?位置的參數
                JDBCUtils.handleParams(ps, params);
                count = ps.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                DBManager.close(ps, conn);
            }
            return count;
        }
    
        /**
         * 將一個對象存儲到數據庫中
         *
         * @param obj 要存儲的對象
         */
        public void insert(Object obj) {
            // obj --> 表中。 insert into 表名(id, name, pwd) values (?, ?, ?)
            Class c = obj.getClass();
            // 存儲sql的參數對象
            List<Object> params = new ArrayList<>();
            TableInfo tableInfo = TableContext.poClassTableMap.get(c);
            StringBuilder sql = new StringBuilder("insert into " + tableInfo.getTname() + " (");
    
            // 計算不爲空的屬性值
            int countNotNullField = 0;
    
            // 目前只能處理數據庫來維護自增的方式
            Field[] fs = c.getDeclaredFields();
            for (Field f : fs) {
                String fieldName = f.getName();
                Object fieldValue = ReflectUtils.invokeGet(fieldName, obj);
                if (fieldValue != null) {
                    // 若是該屬性值不爲空
                    countNotNullField++;
                    sql.append(fieldName + ",");
                    params.add(fieldValue);
                }
            }
    
            // 把最後一個屬性後面的,換成)
            sql.setCharAt(sql.length() - 1, ')');
            sql.append(" values (");
    
            for (int i = 0; i < countNotNullField; i++) {
                sql.append("?,");
            }
            sql.setCharAt(sql.length() - 1, ')');
    
            executeDML(sql.toString(), params.toArray());
        }
    
        /**
         * 刪除clazz表示類對應的表中的記錄(指定主鍵id的記錄)
         * 把對象中不爲null的屬性往數據庫中存儲!若是數字爲null則放0
         *
         * @param clazz 跟表對應的類的Class對象
         * @param id    主鍵的值
         */
        // delete from User where id = 2;
        public void delete(Class clazz, Object id) {
            // Emp.class, 2 --> delete from emp where id=2
            // 經過Class對象找TableInfo
            TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
            // 得到主鍵
            ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
    
            String sql = "delete from " + tableInfo.getTname() + " where " + onlyPriKey.getName() + "=?;";
            executeDML(sql, new Object[]{id});
        }
    
        /**
         * 刪除對象在數據庫中對應的記錄(對象所在類對應到表,對象的主鍵對應到的記錄)
         *
         * @param obj
         */
        public void delete(Object obj) {
            Class c = obj.getClass();
            TableInfo tableInfo = TableContext.poClassTableMap.get(c);
            // 得到主鍵
            ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
            // 經過反射機制,調用屬性對應的get方法或set方法
            Object priKeyValue = ReflectUtils.invokeGet(onlyPriKey.getName(), obj);
            delete(obj.getClass(), priKeyValue);
        }
    
        /**
         * 更新對象對應的記錄,而且只更新指定的字段的值
         *
         * @param obj        索要更新的對象
         * @param fieldNames 更新的屬性列表
         * @return 執行sql語句後影響記錄的行數
         */
        // update user set uname=?, pwe=?
        public int update(Object obj, String[] fieldNames) {
            // obj{"uname", "pwd} --> update 表名 set uname=?, pwd=? where id=?
            Class c = obj.getClass();
            List<Object> params = new ArrayList<>();
            TableInfo tableInfo = TableContext.poClassTableMap.get(c);
            ColumnInfo priKey = tableInfo.getOnlyPriKey();
            StringBuilder sql = new StringBuilder("update " + tableInfo.getTname() + " set ");
    
            for (String fname : fieldNames) {
                Object fvalue = ReflectUtils.invokeGet(fname, obj);
                params.add(fvalue);
                sql.append(fname + "=?,");
            }
            sql.setCharAt(sql.length() - 1, ' ');
            sql.append(" where ");
            sql.append(priKey.getName() + "=?");
            params.add(ReflectUtils.invokeGet(priKey.getName(), obj));
    
            return executeDML(sql.toString(), params.toArray());
        }
    
        /**
         * 查詢返回多行記錄,並將每行記錄封裝到clazz指定的類的對象中
         *
         * @param sql    查詢語句
         * @param clazz  封裝數據的javabean類的Class對象
         * @param params sql的參數
         * @return 返回查詢到的結果
         */
        public List queryRows(final String sql, final Class clazz, final Object[] params) {
            // 存放查詢結果的容器
            return (List) executeQueryTemplate(sql, params, clazz, new CallBack() {
                @Override
                public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
                    List list = null;
                    try {
                        ResultSetMetaData metaData = rs.getMetaData();
                        // 多行
                        while (rs.next()) {
                            if (list == null) {
                                list = new ArrayList();
                            }
                            // 調用javabean的無參構造器
                            Object rowObj = clazz.newInstance();
                            // 多列 select username, pwd, age from user where id>? and age>?
                            for (int i = 0; i < metaData.getColumnCount(); i++) {
                                // username
                                String columnName = metaData.getColumnLabel(i + 1);
                                Object columnValue = rs.getObject(i + 1);
    
                                // 調用rowObj對象的setUsername(String uname)方法,將columnValue的值設置進去
                                ReflectUtils.invokeSet(rowObj, columnName, columnValue);
                            }
                            list.add(rowObj);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return list;
                }
            });
        }
    
        /**
         * 查詢返回一行記錄,並將該記錄封裝到clazz指定的類的對象中
         *
         * @param sql    查詢語句
         * @param clazz  封裝數據的javabean類的Class對象
         * @param params sql的參數
         * @return 返回查詢到的結果
         */
        public Object queryUniqueRows(String sql, Class clazz, Object[] params) {
            List list = queryRows(sql, clazz, params);
            return (list != null || list.size() > 0) ? list.get(0) : null;
        }
    
        /**
         * 根據主鍵的值直接查找對應的對象
         * @param clazz
         * @param id
         * @return
         */
        public Object queryById(Class clazz, Object id){
            // select * from emp where id=?
            TableInfo tableInfo = TableContext.poClassTableMap.get(clazz);
            ColumnInfo onlyPriKey = tableInfo.getOnlyPriKey();
            String sql = "select * from " + tableInfo.getTname() +  " where " + onlyPriKey.getName() + "=?";
            return queryUniqueRows(sql, clazz, new Object[]{id});
        }
    
        /**
         * 查詢返回一個值(一行一列),並將該值返回
         *
         * @param sql    查詢語句
         * @param params sql的參數
         * @return 返回查詢到的結果
         */
        public Object queryValue(String sql, Object[] params) {
            return executeQueryTemplate(sql, params, null, new CallBack() {
                @Override
                public Object doExecute(Connection conn, PreparedStatement ps, ResultSet rs) {
                    Object value = null;
                    try {
                        // 多行
                        while (rs.next()) {
                            // select count(*) from user
                            value = rs.getObject(1);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return value;
                }
            });
        }
    
        /**
         * 查詢返回一個數字(一行一列),並將該值返回
         *
         * @param sql    查詢語句
         * @param params sql的參數
         * @return 返回查詢到的數字
         */
        public Number queryNumber(String sql, Object[] params) {
            return (Number) queryValue(sql, params);
        }
    
        /**
         * 分頁查詢
         *
         * @param pageNum 第幾頁數據
         * @param size    每頁顯示多少記錄
         * @return
         */
        public abstract Object queryPagenate(int pageNum, int size);
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
  • MysqlQuery

    package com.sxt.SORM.core;
    
    import com.sxt.SORM.po.Emp;
    import com.sxt.SORM.vo.EmpVO;
    
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: MysqlQuery.java
     * @time: 2020/3/13 16:54
     * @desc: |負責針對mysql數據庫的查詢
     */
    
    public class MysqlQuery extends Query {
    
        public static void main(String[] args) {
            Object obj = new MysqlQuery().queryValue("select count(*) from emp where salary>?", new Object[]{1000});
            System.out.println(obj);
        }
    
        /**
         * 複雜多行查詢測試
         */
        public static void testQueryRows() {
            List<Emp> list = new MysqlQuery().queryRows("select id,empname,age from emp where age>? and salary<?", Emp.class,
                    new Object[]{1, 9000});
            for (Emp e : list) {
                System.out.println(e.getEmpname());
            }
    
            String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                    + " " + "join dept d on e.deptId=d.id;";
            List<EmpVO> list2 = new MysqlQuery().queryRows(sql2, EmpVO.class, null);
            for (EmpVO e : list2) {
                System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
            }
        }
    
        /**
         * 增刪改操做測試
         */
        public static void testDML() {
            Emp e = new Emp();
            e.setEmpname("Tom");
            e.setBirthday(new java.sql.Date(System.currentTimeMillis()));
            e.setAge(30);
            e.setSalary(8888.0);
            e.setId(1);
    
            // new MysqlQuery().delete(e);
            // new MysqlQuery().insert(e);
            new MysqlQuery().update(e, new String[]{"empname", "age", "salary"});
        }
    
        @Override
        public Object queryPagenate(int pageNum, int size) {
            return null;
        }
    }

9.3 工廠模式+單例模式+克隆模式構建QueryFactory

  • 修改db.properties,增長queryClass的路徑

    queryClass=com.sxt.SORM.core.MysqlQuery
  • 修改Configuration,增長queryClass的屬性,getset方法

    // 項目使用的查詢類的路徑
    private String queryClass;
    
    public String getQueryClass() {
    	return queryClass;
    }
    
    public void setQueryClass(String queryClass) {
    	this.queryClass = queryClass;
    }
  • Query實現implements Cloneable接口

    public abstract class Query implements Cloneable
    
    protected Object clone() throws CloneNotSupportedException {
    	return super.clone();
    }
  • QueryFactory

    package com.sxt.SORM.core;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: QueryFactory.java
     * @time: 2020/3/11 13:33
     * @desc: |建立Query對象的工廠類:單例+克隆+工廠
     */
    
    public class QueryFactory {
        private static QueryFactory factory = new QueryFactory();
        // 原型對象
        private static Query prototypeObj;
    
        static{
            try {
                // 加載指定的Query類
                Class c = Class.forName(DBManager.getConf().getQueryClass());
                prototypeObj = (Query) c.newInstance();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        // 私有構造器
        private QueryFactory(){}
    
        public static Query createQuery(){
            try {
                return (Query) prototypeObj.clone();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
  • 客戶端調用測試類

    package com.sxt.SORM.test;
    
    import com.sxt.SORM.core.MysqlQuery;
    import com.sxt.SORM.core.Query;
    import com.sxt.SORM.core.QueryFactory;
    import com.sxt.SORM.vo.EmpVO;
    
    import java.util.List;
    
    /**
     * @author: Li Tian
     * @contact: litian_cup@163.com
     * @software: IntelliJ IDEA
     * @file: Test.java
     * @time: 2020/3/17 12:55
     * @desc: |客戶端調用測試類
     */
    
    public class Test {
        public static void main(String[] args){
            Query q = QueryFactory.createQuery();
            String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                    + " " + "join dept d on e.deptId=d.id;";
            List<EmpVO> list2 = q.queryRows(sql2, EmpVO.class, null);
            for (EmpVO e : list2) {
                System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
            }
        }
    }

9.4 增長鏈接池提升效率

  • Connection Pool

  • 就是將Connection對象放入List中,反覆重用!

  • 鏈接池的初始化:事先放入多個鏈接對象

  • 從鏈接池中取鏈接對象

    • 若是池中有可用鏈接,則將池中最後一個返回。同時,講該鏈接從池中remove,表示正在使用。
    • 若是池中無可用鏈接,則建立一個新的。
  • 關閉鏈接

  • 不是真正關閉鏈接,而是將用完的鏈接放入鏈接池中。

  • 市面上的鏈接池產品:

    • DBCP
    • c3p0
    • proxool
  • 代碼

    • 配置文件新增鏈接池參數

      poolMinSize=10
      poolMaxSize=100
    • Configuration增長上述參數的屬性和getset方法

      // 鏈接池最小限制
      private int poolMinSize;
      // 鏈接池最大限制
      private int poolMaxSize;
      
      public int getPoolMinSize() {
      	return poolMinSize;
      }
      
      public void setPoolMinSize(int poolMinSize) {
      	this.poolMinSize = poolMinSize;
      }
      
      public int getPoolMaxSize() {
      	return poolMaxSize;
      }
      
      public void setPoolMaxSize(int poolMaxSize) {
      	this.poolMaxSize = poolMaxSize;
      }
    • 新增鏈接池類:DBConnPool

      package com.sxt.SORM.pool;
      
      import com.sxt.SORM.core.DBManager;
      
      import java.sql.Connection;
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: DBConnPool.java
       * @time: 2020/3/17 14:03
       * @desc: |鏈接池的類
       */
      
      public class DBConnPool {
          // 最大鏈接數
          private static final int POOL_MAX_SIZE = DBManager.getConf().getPoolMaxSize();
          // 最小鏈接數
          private static final int POOL_MIN_SIZE = DBManager.getConf().getPoolMinSize();
          // 鏈接池對象
          private List<Connection> pool;
      
          public DBConnPool() {
              initPool();
          }
      
          /**
           * 初始化鏈接池,使池中的鏈接數達到最小值
           */
          public void initPool() {
              if (pool == null) {
                  pool = new ArrayList<Connection>();
              }
      
              while (pool.size() < DBConnPool.POOL_MIN_SIZE) {
                  pool.add(DBManager.createConn());
                  System.out.println("初始化池,池中鏈接數:" + pool.size());
              }
          }
      
          /**
           * 從鏈接池中取出一個鏈接
           */
          public synchronized Connection getConnection() {
              int last_index = pool.size() - 1;
              // 得到一個鏈接
              Connection conn = pool.get(last_index);
              pool.remove(last_index);
              return conn;
          }
      
          /**
           * 將鏈接放回池中
           *
           * @param conn
           */
          public synchronized void close(Connection conn) {
              if (pool.size() >= POOL_MAX_SIZE) {
                  try {
                      if (conn != null) {
                          conn.close();
                      }
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              } else {
                  pool.add(conn);
              }
          }
      }
    • 修改DBManager方法,把原來獲取鏈接的方式,改成使用鏈接池,關閉鏈接的方式也作一樣的修改

      package com.sxt.SORM.core;
      
      import com.sxt.SORM.bean.Configuration;
      import com.sxt.SORM.pool.DBConnPool;
      
      import java.io.IOException;
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.ResultSet;
      import java.sql.Statement;
      import java.util.Properties;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: DBManager.java
       * @time: 2020/3/11 13:43
       * @desc: |根據配置信息,維持鏈接對象的管理(增長鏈接池功能)
       */
      
      public class DBManager {
          // 配置信息
          private static Configuration conf;
          // 鏈接池對象
          private static DBConnPool pool;
      
          static {
              // 靜態代碼塊
              Properties pros = new Properties();
              try {
                  pros.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("com/sxt/SORM/db.properties"));
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
              conf = new Configuration();
              conf.setDriver(pros.getProperty("driver"));
              conf.setPoPackage(pros.getProperty("poPackage"));
              conf.setPwd(pros.getProperty("pwd"));
              conf.setSrcPath(pros.getProperty("srcPath"));
              conf.setURL(pros.getProperty("URL"));
              conf.setUser(pros.getProperty("user"));
              conf.setUsingDb(pros.getProperty("usingDB"));
              conf.setQueryClass(pros.getProperty("queryClass"));
              conf.setPoolMaxSize(Integer.parseInt(pros.getProperty("poolMaxSize")));
              conf.setPoolMinSize(Integer.parseInt(pros.getProperty("poolMinSize")));
      
              // 加載TableContext
              System.out.println(TableContext.class);
          }
      
          public static Connection getConn() {
              /*獲取數據庫(mysql)驅動鏈接*/
              // 加載驅動類
      
              /* 第一種方法:直接取
              try {
                  Class.forName(conf.getDriver());
                  // 直接創建鏈接,後期增長鏈接池處理,提升效率!
                  return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
               */
      
              // 第二種方法,經過鏈接池
              if(pool == null){
                   pool = new DBConnPool();
              }
              return pool.getConnection();
          }
      
          /**
           * 建立新的Connection鏈接
           *
           * @return
           */
          public static Connection createConn() {
              /*獲取數據庫(mysql)驅動鏈接*/
              // 加載驅動類
              try {
                  Class.forName(conf.getDriver());
                  // 直接創建鏈接,後期增長鏈接池處理,提升效率!
                  return DriverManager.getConnection(conf.getURL(), conf.getUser(), conf.getPwd());
              } catch (Exception e) {
                  e.printStackTrace();
                  return null;
              }
          }
      
          public static void close(ResultSet rs, Statement ps, Connection conn) {
              /**關閉接口方法*/
              try {
                  if (rs != null) {
                      rs.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  //     if (conn != null) {
                  //         conn.close();
                  //     }
                  pool.close(conn);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Statement ps, Connection conn) {
              /**關閉接口方法,重載*/
              try {
                  if (ps != null) {
                      ps.close();
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
              try {
                  //     if (conn != null) {
                  //         conn.close();
                  //     }
                  pool.close(conn);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static void close(Connection conn) {
              /*關閉接口方法,重載*/
              try {
                  //     if (conn != null) {
                  //         conn.close();
                  //     }
                  pool.close(conn);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      
          public static Configuration getConf() {
              return conf;
          }
      }
    • 客戶端測試

      package com.sxt.SORM.test;
      
      import com.sxt.SORM.core.Query;
      import com.sxt.SORM.core.QueryFactory;
      import com.sxt.SORM.vo.EmpVO;
      
      import java.util.List;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Test2.java
       * @time: 2020/3/17 16:40
       * @desc: |測試鏈接池的調用效率
       */
      
      public class Test2 {
          public static void test1(){
              Query q = QueryFactory.createQuery();
              String sql2 = "select e.id,e.empname,salary+bonus 'xinshui',age,d.dname 'deptName',d.address 'deptAddr' from emp e"
                      + " " + "join dept d on e.deptId=d.id;";
              List<EmpVO> list2 = q.queryRows(sql2, EmpVO.class, null);
              for (EmpVO e : list2) {
                  System.out.println(e.getEmpname() + "-" + e.getDeptAddr() + "-" + e.getXinshui());
              }
          }
      
          public static void main(String[] args){
              long a = System.currentTimeMillis();
              for (int i = 0; i < 3000; i++) {
                  test1();
              }
              long b = System.currentTimeMillis();
              // 不加鏈接池的耗時:13077ms,增長鏈接池以後,耗時爲2478
              System.out.println(b-a);
          }
      }

9.5 jar包和API文檔的生成

  • idea導出參考鏈接:https://blog.csdn.net/rico_rico/article/details/84936785

  • javadoc導出參考鏈接:https://blog.csdn.net/qq_29347295/article/details/78635861

  • 其中編碼須要改成:-encoding utf-8 -charset utf-8

  • 測試使用

    • 配置配置文件

      usingDB=mysql
      URL=jdbc:mysql://localhost:3306/sorm?serverTimezone=UTC
      driver=com.mysql.cj.jdbc.Driver
      user=root
      pwd=123456
      srcPath=F:/BookStudy/else/SORMDemo/src
      poPackage=po
      queryClass=com.sxt.SORM.core.MysqlQuery
      poolMinSize=10
      poolMaxSize=100
    • 測試po類的生成,增刪改查

      package test;
      
      import com.sxt.SORM.core.Query;
      import com.sxt.SORM.core.QueryFactory;
      import po.Emp;
      
      /**
       * @author: Li Tian
       * @contact: litian_cup@163.com
       * @software: IntelliJ IDEA
       * @file: Test.java
       * @time: 2020/3/17 17:38
       * @desc: |
       */
      
      public class Test {
          public static void main(String[] args) {
              // 經過這個方法能夠生成po類
              // TableContext.updateJavaPOFile();
      
              // add();
              // select();
              // delete();
              update();
      
          }
      
          public static void add() {
              // 測試插入對象
              Emp e = new Emp();
              e.setAge(18);
              e.setEmpname("我");
              e.setSalary(2000.0);
      
              Query q = QueryFactory.createQuery();
              q.insert(e);
          }
      
          public static void delete(){
              // 測試刪除對象
              Emp e = new Emp();
              e.setId(12);
              Query q = QueryFactory.createQuery();
              q.delete(e);
          }
      
          public static void update(){
              // 測試刪除對象
              Emp e = new Emp();
              e.setId(1);
              e.setAge(1);
              Query q = QueryFactory.createQuery();
              q.update(e, new String[]{"age"});
          }
      
          public static void select(){
              // 測試查詢
              Query q = QueryFactory.createQuery();
              Number n = q.queryNumber("select count(*) from emp where salary>?", new Object[]{100});
              System.out.println(n);
          }
      }
  • 關於連表查詢,我看操做實在是過於複雜,還要本身創建EmpVO,想一想就算了。


個人CSDN:https://blog.csdn.net/qq_21579045

個人博客園:https://www.cnblogs.com/lyjun/

個人Github:https://github.com/TinyHandsome

紙上得來終覺淺,絕知此事要躬行~

歡迎你們過來OB~

by 李英俊小朋友


  1. 8 ↩︎

  2. 16 ↩︎

  3. 24 ↩︎

  4. 32 ↩︎

相關文章
相關標籤/搜索