學習連接:Java 視頻教程全集javascript
課件連接:Java課件html
聲明:全是本人邊學習邊手打的,但願對你們有幫助。前端
反射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(){ } }
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; } }
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); } } }
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"); } }
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>
http請求協議
http響應協議
使用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 到底有什麼區別?
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() { } }
動態添加內容print
累加字節數的長度
根據狀態碼拼接響應頭協議
根據狀態碼統一推送出去
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); } }
經過分解字符串獲取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() { } }
加入了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>"); } }
將以前的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() { } }
多線程處理,加入分發器
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(); } } }
這一部分有問題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(); } } }
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(); } }
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(); } } }
介紹+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
反射操做泛型(Generic)
操做泛型
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(); } } }
經過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; }
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(); } } }
JAVA腳本引擎是從JDK6.0以後添加的新功能
在寫Demo的時候存在一些報錯的狀況,這是由於JDK6.0的語法在JDK8.0中已通過時了
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(); } }
Java動態性的兩種常見實現方式:
運行時操做字節碼可讓咱們實現以下功能:
優點
常見的字節碼操做類庫
測試使用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庫的侷限性
JVM運行和類加載全過程
類的加載機制(JVM內存分析+反射機制核心原理+常量池理解)
JVM把class文件加載到內存,並對數據進行校驗、解析和初始化,最終造成JVM能夠直接使用的Java類型的過程。
加載:將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲方法區類數據的訪問入口。
連接:將Java類的二進制代碼合併到JVM的運行狀態之中的過程
初始化
<clinit>()
方法的過程。類構造器<clinit>()
方法是由編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊(static塊)中的語句合併產生的。<clinit>()
方法在多線程環境中被正確加鎖和同步。過程圖解
初始化時機+靜態初始化塊執行的順序問題
類的主動引用和被動引用
深刻類加載器
類加載器原理
類加載器樹狀結構、雙親委託(代理)機制
分類:
類加載器的代理模式
類加載器實戰
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")); } }
自定義類加載器(文件、網路、加密)
自定義加載器的流程:
自定義文件系統類加載器
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); } }
線程上下文類加載器
線程上下文類加載器測試
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介紹
核心做用:保證一個類只有一個實例,而且提供一個訪問該實例的全局訪問點。
常見的應用場景:
單例模式的優勢:
常見的五種單例模式實現方式:
(單例對象當即加載)
代碼
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(),甚至永遠沒有調用,則會形成資源浪費!
(單例對象延遲加載)
代碼
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()方法都要同步,併發效率較低。
代碼
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底層內部模型緣由,偶爾會出問題,不建議使用。
(也是一種懶加載方式)
代碼
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; } }
要點
問題:
代碼
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(){ } }
優勢
缺點
UML類圖
如何選用?
測試懶漢式單例模式
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
測試五種建立單例模式的效率
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)); } }
要點:
代碼:
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類圖
爲了不簡單工廠模式的缺點,不徹底知足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:
根據設計理論建議:工廠方法模式。但實際上,咱們通常都用簡單工廠模式。
用來生產不一樣產品族的所有產品。(對於增長新的產品,無能爲力;支持增長產品族)
抽象工廠模式是工廠方法模式的升級版本,在有多個業務品種、業務分類時,經過抽象工廠模式產生須要的對象是一種很是好的解決方式。
代碼:
發動機
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(); } }
工廠模式要點
應用場景
場景:
建造者模式的本質
代碼:
宇宙飛船
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類圖
開發中應用場景:
代碼:
羊
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()); } }
代碼
羊
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; } }
客戶端同上
區別
淺克隆和深克隆的區別
運行結果區別:淺克隆只是複製了生日變量對應的地址,這樣即便值改變了,地址相同,則得到的生日的值也相同;深克隆將生日變量複製了一份,兩個地址不一樣,所以原型的值變了,與複製的值無關,所以生日的值依然是以前複製的值。
利用序列化和反序列化的技術實現深克隆!
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的過程比較耗時。則能夠考慮使用原型模式!
開發中的應用場景
建立型模式:都是來幫助咱們建立對象的!
結構型模式:
什麼是適配器模式(adapter):將一個類的接口轉換成客戶但願的另一個接口。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(); }
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(); } }
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; } }
工做中的場景:
咱們學習中見過的場景
以對象適配器爲例,繪製UML類圖
代碼:
明星接口
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(); } }
動態生成代理類
動態代理相比於靜態代理的優勢:抽象角色中(接口)聲明的全部方法都被轉移到調用處理器一個集中的方法中處理,這樣咱們能夠更加靈活和統一的處理衆多的方法。
JDK自帶的動態代理
代碼:
明星接口和真實明星接口同上
動態代理類
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(); } }
bridge pattern
場景:商場系統中常見的商品分類,以電腦爲類,如何良好的處理商品分類銷售的問題?
咱們能夠用多繼承結構實現下圖關係
問題:
橋接模式核心要點:處理多層集成結構,處理多維度變化的場景,將各個維度設計成獨立的繼承結構,使各個維度能夠獨立的擴展在抽象層創建關聯。
橋接模式總結:
橋接模式實際開發中應用場景
不用橋接模式的話
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(); } }
composite patern
使用組合模式的場景:把部分和總體的關係用樹形結構來表示,從而使得客戶端能夠使用統一的方式處理部分對象和總體對象。
組合模式核心:
抽象組件構成代碼
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(); } }
開發中的應用場景
decorator pattern
職責:
實現細節:
代碼
抽象組件
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類圖
開發中使用的場景
IO流實現細節
總結:
優勢:
缺點:
裝飾模式和橋接模式的區別:
兩個模式都是爲了解決過多子類對象的問題。可是他們的誘因不同。
個人理解是:
迪米特法則(最少知識原則):一個軟件實體應當儘量少的與其餘實體發生相互做用。
外觀模式核心:爲子系統提供統一的入口。封裝子系統的複雜性,便於客戶端調用。
公司註冊流程:
不使用外觀模式
使用外觀模式
開發中常見的場景
FlyWeight Pattern
場景:內存屬於稀缺資源,不要隨便浪費。若是有不少個徹底相同或類似的對象,咱們能夠經過享元模式,節省內存。
核心:
圍棋軟件設計:每一個圍棋棋子都是一個對象,有以下屬性:
享元模式實現:
FlyweightFactory享元工廠類:
建立並管理享元對象,享元池通常設計成鍵值對
FlyWeight抽象享元類
一般是一個接口或抽象類,聲明公共方法,這些方法能夠向外界提供對象的內部狀態,設置外部狀態。
ConcreteFlyWeight具體享元類
爲內部狀態提供成員變量進行存儲
UnsharedConcreteFlyWeight非共享享元類
不能被共享的子類能夠設計爲非共享享元類
享元模式實現的UML圖
代碼:
享元類和實際享元實現類(這裏實現了顏色共享)
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)); } }
享元模式開發中應用的場景:
享元模式的優勢:
享元模式的缺點:
封裝請假的基本信息
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); } }
iterator pattern
場景:
基本案例:
實現正向遍歷的迭代器
自定義的迭代器接口
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(); } } }
實現逆向遍歷的迭代器
開發中常見的場景:
Mediator Pattern
場景:總經理協調各個部門之間的關係,起到一箇中介的做用。
核心:
代碼類圖
代碼:
總經理類的接口
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(); } }
中介者模式的本質:解耦多個同事對象之間的交互關係。每一個對象都持有中介者對象的引用,只跟中介者對象打交道。咱們經過中介者對象統一管理這些交互關係。
開發中常見的場景:
command pattern
介紹:將一個請求封裝爲一個對象,從而使咱們可用不一樣的請求對客戶進行參數化;對請求排隊或者記錄請求日誌,以及支持可撤銷的操做。也稱爲:動做Action模式、事務transaction模式。
結構:
Command抽象命令類
ConcreteCommand具體命令類
Invoker調用者/請求者:請求的發送者,它經過命令對象來執行請求。一個調用者並不須要在設計時肯定其接受者,所以它只與抽象命令類之間存在關聯。在程序運行時,將調用命令對象的execute(),間接調用接受者的相關操做。
Receiver接受者
Client客戶類:在客戶類中須要建立調用者對象、具體命令類對象,在建立具體命令對象時指定對應的接受者。發送者和接受者之間沒有直接聯繫,都經過命令對象間接調用。
類圖
代碼:
真正的命令執行者
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(); } }
開發中常見的場景:
strategy pattern
場景:
本質:分離算法,選擇實現
開發中常見的場景
策略模式對應於解決某一個問題的一個算法族,容許用戶從該算法族中任選一個算法解決某以問題,同時能夠方便的更換算法或者增長新的算法。而且由客戶端決定調用哪一個算法。
代碼
策略接口
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); } }
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("我要取款!"); } }
方法回調(鉤子方法)
何時用到模板方法模式:實現一個算法時,總體步驟很固定,可是,某些部分易變。易變的部分能夠抽象出來,供子類實現。
開發中常見的場景:很是頻繁。各個框架、類庫中都有影子。好比常見的優:
state pattern
場景:
核心:用於解決系統中複雜對象的狀態轉換以及不一樣狀態下行爲的封裝問題
結構:
類圖:
代碼
狀態接口
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()); } }
開發中常見的場景:
Observer Pattern
場景
上面這些場景,咱們均可以使用觀察者模式處理。咱們能夠把多個訂閱者、客戶稱之爲觀察者;須要同步給多個訂閱者的數據封裝到對象中,稱之爲目標。
核心:
UML類圖
代碼:
消息發佈對象
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()); } }
開發中常見的場景
memento pattern
場景:
核心:就是保存某個對象內部狀態的拷貝,這樣之後就能夠將該對象恢復到原先的狀態。
結構
代碼:
源發器類
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類圖
備忘點較多時
開發中常見的應用場景:
普通字符:匹配與之相同的一個字符
簡單的轉義字符:
標準字符集合
自定義字符集合
[\d.\-+]
將匹配:數字、小數點、+、-量詞 | Quantifier
修飾匹配次數的特殊符號
\d\d{6} != {\d\d}{6}
匹配次數中的貪婪模式(匹配字符越多越好,默認!)
匹配次數中的非貪婪模式(匹配字符越少越好,修飾匹配次數的特殊符號後再加上一個「?」號)例:\d{2,3}?
字符邊界(零寬)
本組標記匹配的不是字符而是位置,符合某 種條件的位置
\b匹配這樣一個位置:前面的字符和後面的字符不全是\w
gaoqi\b
測試:
\bgaoqi\b
測試:
IGNORECASE:忽略大小寫模式
SINGLELINE:單行模式
MULTILINE:多行模式
選擇符和分組
反向引用(\nnm)
預搜索(零寬斷言)
只進行子表達式的匹配,匹配內容不計入最終的匹配結果,是零寬度
這個位置應該符合某個條件。判斷當前位置的先後字符,是否符合指定的條件,但不匹配先後的字符。是對位置的匹配。
正則表達式匹配過程當中,若是子表達式匹配到的是字符的內容,而非位置,並被保存到最終的匹配結果中,那麼就認爲這個子表達式是佔有字符的;若是子表達式匹配的僅僅是位置,或者匹配的內容並不保存到最終的匹配結果中,那麼就認爲這個子表達式是零寬度的。佔有字符仍是零寬度,是針對匹配的內容是否保存到最終的匹配結果中而言的。
練習1
解答1:(0\d{2,3}-\d{7,8})|(1[35789]\d{9})
練習2
解答2:[\w\-]+@[a-z0-9A-Z]+(\.[A-Za-z]{2,4}){1,2}
開發環境
JAVA相關類位於java.util.regex包下面
類Pattern:
Pattern p = Pattern.compile(r, int);
類Matcher:
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)); } }
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; } }
經常使用命令行操做
JDBC(Java Database Connection)爲java開發者使用數據庫提供了統一的編程接口,它由一組java類和接口組成。是java程序與數據庫系統通訊的標準api。JDBC API使得開發人員能夠使用純java的方式來鏈接數據庫,並執行操做。
訪問數據庫流程
Driver接口
Class.forName("com.mysql.jdbc.Driver");
Class.forName("oracle.jdbc.driver.OracleDriver")
DriverManager接口
Connection接口
Connection con = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
Connection con = DriverManager.gtConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
JDBC詳細操做
鏈接測試
報錯參考鏈接:解決方案
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(); } } }
Statement接口
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(); } } } } }
事務的基本概念:一組要麼同時執行成功,要麼同時執行失敗的SQL語句。是數據庫操做的一個執行單元。
事務開始於:
事務結束於:
事務的四大特性(ACID)
測試事務的基本用法
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(); } } } } }
時間類型
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; } } }
Character Large Object
用於存儲大量的文本數據
大字段有些特殊,不一樣數據庫處理的方式不同,大字段的操做經常是以流的方式來處理的。而非通常的字段,一次便可讀出數據。
Mysql中相關類型:
測試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(); } } } } }
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(); } } } } }
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); } } }
ORM的基本思想
將表中的一條記錄封裝到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; } }
Simple Object Relationship Mapping
咱們但願設計一個能夠實現對象和SQL自動映射的框架,可是總體用法和設計比Hibernate簡單。砍掉沒必要要的功能。
會穿插使用設計模式
從對象到sql
從sql到對象
List<Javabean>
核心架構:
架構圖
核心bean,封裝相關數據
針對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. 目前只能處理數據庫來維護自增的方式
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; } }
修改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()); } } }
Connection Pool
就是將Connection對象放入List中,反覆重用!
鏈接池的初始化:事先放入多個鏈接對象
從鏈接池中取鏈接對象
關閉鏈接
不是真正關閉鏈接,而是將用完的鏈接放入鏈接池中。
市面上的鏈接池產品:
代碼
配置文件新增鏈接池參數
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); } }
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 李英俊小朋友