本篇對一些常用的java知識做一個整合,三大特性、IO操作、線程處理、類集處理,目的在於能用這些只是實現一個網頁爬蟲的功能。
Ⅰ 首先對於一個java開發的項目有一個整體性的了解認知,項目開發流程:
項目階段:
1) 項目准備:
a) 根據開會得到會議紀要,了解客戶的需求情況
b) 需求分析(需求分析文檔)
c) 數據庫設計和網站(產品)原型設計
d) 架構設計
2) 項目開發
a) 項目組長(PM,PL)進行項目的時間規划,並划分好每個人的工作任務
b) 程序員主要完成項目代碼編寫和詳細設計文檔編寫。(用戶手冊)
3) 測試
a) 單元測試
b) 集成測試
c) 壓力測試
d) 回歸測試
4) 上線實施
Ⅱ 三大特性(封裝、繼承、多態)
封裝
1、 封裝重點在於一個private關鍵字,目的是為了讓類中的屬性不能直接修改或取得。也就是說,建立一個類時,所有的屬性都必須通過private進行封裝。
既然屬性被封裝了,那么如果想設置或取得屬性,就必須編寫getter/setter方法。
同時,類中在創建對象時,為了方便,一般建議編寫一些帶參數的構造方法。
如果不編寫構造方法,程序會自動加入一個無參的構造,但是,如果自己聲明了構造方法,那么必須就手工加入一個無參構造,為了讓其他的框架可以通過無參數構造來創建對象。
2、 如果數據庫中有一張表,則需要你能根據表編寫一個這樣的類。
這種類被稱為:VO(Value Object)、TO(Transfer Object)、POJO(Plain Olds Java Object)、DTO(DaTa Object)等
1 class Person { 2 private String name; 3 private Integer age; 4 public Person() { 5 } 6 public Person(String name, Integer age) { 7 this.name = name; 8 this.age = age; 9 } 10 public String getName() { 11 return this.name; 12 } 13 public Integer getAge() { 14 return age; 15 } 16 public void setAge(Integer age) { 17 this.age = age; 18 } 19 public void setName(String name) { 20 this.name = name; 21 } 22 }
技巧:在eclipse中編寫封裝類,可以聲明變量后,按shift+Alt+s鍵出現Generate Getters and Setters提示創建getter和setter方法。
繼承關系
繼承所使用的關鍵字:extends,接口實現所使用的關鍵字是:implements。
Java開發中對於接口和抽象類區別主要是單繼承和多繼承。
真正開發中接口用的更多,幾乎不編寫抽象類。
一般都是以接口作為標准來進行聲明。
這部分我們要求能夠掌握接口的聲明和實現方法。
1 interface Animal { 2 public void cry(); 3 public void run(); 4 } 5 class Cat implements Animal { 6 7 @Override 8 public void cry() { 9 System.out.println("miao"); 10 } 11 @Override 12 public void run() { 13 System.out.println("貓爬樹"); 14 } 15 }
多態
其實就是在繼承的基礎上進行方法覆寫和子類轉型。
1 package org.liky.test; 2 public class InterfaceDemo { 3 public static void main(String[] args) { 4 Animal a1 = new Cat(); 5 Animal a2 = new Dog(); 6 a1.cry(); 7 a2.cry(); 8 } 9 } 10 interface Animal { 11 public void cry(); 12 public void run(); 13 } 14 class Cat implements Animal { 15 @Override 16 public void cry() { 17 System.out.println("miao"); 18 } 19 @Override 20 public void run() { 21 System.out.println("貓爬樹"); 22 } 23 } 24 class Dog implements Animal { 25 @Override 26 public void cry() { 27 System.out.println("Wang"); 28 } 29 @Override 30 public void run() { 31 System.out.println("狗游泳"); 32 } 33 }
單例設計模式
單例模式有以下特點:
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
package org.liky.test; public class TestSingleton { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); Singleton s3 = Singleton.getInstance(); //其實只創建了一個對象 System.out.println(s1 + " --> " + s2 + " --> " + s3); } } class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
Ⅲ IO操作
文件內容讀取:
File、FileReader、FileWriter、BufferedReader、BufferedWriter、Scanner、InputStreamReader
文件夾遍歷:
File
文件復制操作
如果想操作文件的內容(對內容進行寫出和讀取),需要使用到的就是IO流中的輸入輸出操作。
這種輸入和輸出的操作流有兩種:
1) 字符流:主要操作文本文件(編寫爬蟲操作時,肯定要使用字符流來完成)
a) 讀:FileReader
b) 寫:FileWriter
2) 字節流:所有文件都可以使用這種流操作
a) 讀:InputStream
b) 寫:OutputStream
需要能夠通過我們這里的FileReader和FileWriter配合文件類:File,完成內容的讀取和寫出。
/** * IO流操作的演示類,用來演示文本文件的寫出和讀取 * * @author Liky * */ public class FileTest { public static void main(String[] args) { // 寫出內容 // writeData( // "D:/test.txt", // "這是“吉林一號”視頻衛星 8月9日11時25分拍攝的 九寨溝縣視頻 顯示了九寨溝縣的地形地貌 縣城呈狹長分布 周邊山體有明顯滑坡痕跡 視頻中還可見縣城道路大部分完好 有車輛通行 一架飛機飛過 地面與空中交通並未中斷 圖像提供:長光衛星技術有限公司 技術支持:北京愛太空科技發展有限公司"); System.out.println(readData("D:/test.txt")); } /** * 寫出數據 * * @param filePath * 文件保存的位置 * @param data * 要保存的文件內容數據 */ public static void writeData(String filePath, String data) { // 先有一個文件,來保存要寫出的數據 File file = new File(filePath); // 建立輸出流對象 try { FileWriter writer = new FileWriter(file); // 開始完成內容的輸出 writer.write(data); // 資源必須回收,也就是必須將流關閉 writer.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 讀取數據 * * @param filePath * 要讀取的文件所在的完整路徑 * @return 讀取出來的文檔內容 */ public static String readData(String filePath) { // 也要建立文件對象 File file = new File(filePath); // 建立讀取的輸入流對象 try { FileReader reader = new FileReader(file); // 每次調用read可以讀取一個字符, // 按照int類型返回,返回的是字符的編碼, // 需要通過強制類型轉換,變為char類型 // Java中對於String這個類一般不建議反復修改,因為會占用內存。 StringBuilder builder = new StringBuilder(); // 因為文件中有很多的字符,因此需要循環來進行內容的讀取。 // 就需要判斷是否還有字符進行讀取 int value = -1; // 每次讀取時,如果讀到內容,則會返回 0 - 65535 的char類型字符 // 如果沒有讀取到內容,則返回 -1 ,因此我們可以根據這個 -1 來判斷后面是否還有內容 while ((value = reader.read()) != -1) { // 將讀取到的內容保存下來 char c = (char) value; // 把字符放入到StringBuilder里 builder.append(c); } // 沒有讀取到內容,說明循環結束,已經到了文件的末尾 return builder.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } }
目前這樣編寫已經可以實現內容的輸入和輸出操作了。
但是還不支持換行操作,如果想換行,需要人工進行\r\n的編寫。
如果不想人工編寫換行,就可以使用以下兩個類來完成輸入。
PrintWriter(打印流)
BufferedWriter(緩沖流)
1 public static void writeData(String filePath, String... data) { 2 // 先有一個文件,來保存要寫出的數據 3 File file = new File(filePath); 4 // 建立輸出流對象 5 try { 6 // FileWriter writer = new FileWriter(file); 7 PrintWriter pw = new PrintWriter(file); 8 // 開始完成內容的輸出 9 for (String str : data) { 10 pw.println(str); 11 } 12 // 資源必須回收,也就是必須將流關閉 13 pw.close(); 14 } catch (IOException e) { 15 e.printStackTrace(); 16 } 17 }
使用時,注意我們這里加入了可變參數來動態傳入多個字符串(即String... data)。
當讀取數據時,如果我們使用普通的讀取方式,對於換行的處理不方便。
如果想按行讀取內容,可以使用BufferedReader,Scanner
Scanner是JDK1.5新的
BufferedReader是JDK1.0就有的,所以使用BufferedReader。
為什么現在還使用BufferedReader,因為Scanner不支持編碼的轉換。
1 public static String readData(String filePath) { 2 // 也要建立文件對象 3 File file = new File(filePath); 4 // 建立讀取的輸入流對象 5 try { 6 FileReader reader = new FileReader(file); 7 BufferedReader bw = new BufferedReader(reader); 8 // 每次調用read可以讀取一個字符, 9 // 按照int類型返回,返回的是字符的編碼, 10 // 需要通過強制類型轉換,變為char類型 11 // Java中對於String這個類一般不建議反復修改,因為會占用內存。 12 StringBuilder builder = new StringBuilder(); 13 // 因為文件中有很多的字符,因此需要循環來進行內容的讀取。 14 // 就需要判斷是否還有字符進行讀取 15 String line = null; 16 // 每次讀取時,如果讀到內容,則會返回 0 - 65535 的char類型字符 17 // 如果沒有讀取到內容,則返回 -1 ,因此我們可以根據這個 -1 來判斷后面是否還有內容 18 while ((line = bw.readLine()) != null) { 19 // 將讀取到的內容保存下來 20 // 把字符放入到StringBuilder里 21 builder.append(line); 22 System.out.println(line); 23 } 24 // 沒有讀取到內容,說明循環結束,已經到了文件的末尾 25 return builder.toString(); 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 30 return null; 31 }
例如,將一個D盤的test.txt的內容讀取出來,再寫出到E盤的test.txt中
1 public static void copyFile(String inputFile, String outputPath) { 2 // 首先建立輸入和輸出的文件 3 File input = new File(inputFile); 4 File output = new File(outputPath); 5 // 建立輸入和輸出流 6 try { 7 BufferedReader br = new BufferedReader(new FileReader(input)); 8 PrintWriter pw = new PrintWriter(output); 9 // 每次讀入一行,所以准備一個變量來接收 10 String line = null; 11 while ((line = br.readLine()) != null) { 12 pw.println(line); 13 } 14 pw.close(); 15 br.close(); 16 } catch (Exception e) { 17 e.printStackTrace(); 18 } 19 }
文件夾迭代
這里只需要用到一個File類,但需要用里面的一些方法來判斷是文件還是文件夾
isFile():是否是文件
isDirectory():是否是文件夾
還需要通過遞歸操作,將目錄下的所有子目錄也進行迭代。
多線程處理
使用多線程的目的肯定是為了提升程序的效率。
因為在進行網絡數據爬取時,一般都是同時爬取多個網頁的數據,而不是單個網頁,因此在項目開發中我們需要通過多線程,來讓程序同時完成多個操作。
多線程有兩種實現方式:
1) 繼承Thread類
2) 實現Runnable接口
使用多線程時,還有兩個必須注意的方法:
1) start()啟動線程
2) run()編寫線程執行的主體。
先來完成一個倒計時功能:
public class ThreadDemo { public static void main(String[] args) { // new MyThread().start(); // new Thread() { // public void run() { // for (int i = 10; i >= 0; i--) { // System.out.println("剩余時間:" + i); // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // } // } // }.start(); // new Thread(new MyRunnable()).start(); } } //繼承Thread類必須重寫run類 class MyThread extends Thread { @Override public void run() { for (int i = 10; i >= 0; i--) { System.out.println("剩余時間:" + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyRunnable implements Runnable { @Override public void run() { for (int i = 10; i >= 0; i--) { System.out.println("剩余時間:" + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
類集處理
List:允許重復,可以根據下標來取得數據,會按照放入的順序來存儲數據。
ArrayList:以數組的形式存儲,適合不經常變動,但經常查詢的數據集。
LinkedList:以鏈表的形式存儲,適合經常變動的數據集,但是不經常查詢
1 public class ListDemo { 2 public static void main(String[] args) { 3 LinkedList<Integer> list1 = new LinkedList<>(); 4 for (int i = 0; i <= 100000; i++) { 5 list1.add(i); 6 } 7 long start = System.currentTimeMillis(); 8 for (int i = 0; i <= 100000; i++) { 9 list1.get(i); 10 } 11 long end = System.currentTimeMillis(); 12 System.out.println("ArrayList: " + (end - start) + " ms"); 13 } 14 }
Set:不允許重復,存儲順序看心情,沒辦法根據下標取得數據
HashSet:散列排序(沒有順序)
TreeSet:二叉樹排序,按照固定的規則來排序,TreeSet中的內容必須實現一個Comparable的接口,並且必須覆寫compareTo的方法,根據給定的規則來排序。
1 public class SetDemo { 2 public static void main(String[] args) { 3 Set<Person> set = new TreeSet<>(); 4 set.add(new Person("張三", 12)); 5 set.add(new Person("李四", 22)); 6 set.add(new Person("王五", 42)); 7 set.add(new Person("王八", 42)); 8 set.add(new Person("趙六", 32)); 9 System.out.println(set); 10 } 11 } 12 13 class Person implements Comparable<Person> { 14 private String name; 15 private Integer age; 16 17 public Person() { 18 super(); 19 } 20 21 public Person(String name, Integer age) { 22 super(); 23 this.name = name; 24 this.age = age; 25 } 26 27 public String getName() { 28 return name; 29 } 30 31 public void setName(String name) { 32 this.name = name; 33 } 34 35 public Integer getAge() { 36 return age; 37 } 38 39 public void setAge(Integer age) { 40 this.age = age; 41 } 42 43 @Override 44 public String toString() { 45 return "Person [name=" + name + ", age=" + age + "]"; 46 } 47 @Override 48 public int compareTo(Person o) { 49 if (this.age > o.age) { 50 return 1; 51 } else if (this.age < o.age) { 52 return -1; 53 } 54 if (this.name.equals(o.name)) { 55 return 0; 56 } 57 return 1; 58 } 59 }
Map:key-value形式,可以根據key取得value,key按照Set集合的模式來保存。
HashMap:散列
TreeMap:有順序
對於Map集合,要求能夠循環迭代出里面的所有數據。
所以必須掌握Map的循環方法。
1 public class MapDemo { 2 public static void main(String[] args) { 3 Map<String, Integer> map = new HashMap<>(); 4 map.put("方便面", 20); 5 map.put("火腿腸", 120); 6 map.put("礦泉水", 20); 7 map.put("可樂", 30); 8 9 // Map集合如果想迭代必須先按照key來進行迭代, 10 // 再根key查找value 11 Set<String> keySet = map.keySet(); 12 for (String key : keySet) { 13 System.out.println(key + " ---> " + map.get(key)); 14 } 15 } 16 }
小結:
本篇對於java封裝、繼承、多態三大特性,IO操作,線程管理,類集處理(List、Set、Map)進行了闡述以及代碼實現。
到此,對於網頁數據的爬寫的知識准備的可以了,下一篇我會先對一個文件進行數據爬取,然后再對網頁上的數據代碼實現爬蟲功能。