本篇對一些常用的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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class Person { private String name; private Integer age; public Person() { } public Person(String name, Integer age) { this .name = name; this .age = age; } public String getName() { return this .name; } public Integer getAge() { return age; } public void setAge(Integer age) { this .age = age; } public void setName(String name) { this .name = name; } } |
技巧:在eclipse中編寫封裝類,可以聲明變量后,按shift+Alt+s鍵出現Generate Getters and Setters提示創建getter和setter方法。
繼承關系
繼承所使用的關鍵字:extends,接口實現所使用的關鍵字是:implements。
Java開發中對于接口和抽象類區別主要是單繼承和多繼承。
真正開發中接口用的更多,幾乎不編寫抽象類。
一般都是以接口作為標準來進行聲明。
這部分我們要求能夠掌握接口的聲明和實現方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
interface Animal { public void cry(); public void run(); } class Cat implements Animal { @Override public void cry() { System.out.println( "miao" ); } @Override public void run() { System.out.println( "貓爬樹" ); } } |
多態
其實就是在繼承的基礎上進行方法覆寫和子類轉型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package org.liky.test; public class InterfaceDemo { public static void main(String[] args) { Animal a1 = new Cat(); Animal a2 = new Dog(); a1.cry(); a2.cry(); } } interface Animal { public void cry(); public void run(); } class Cat implements Animal { @Override public void cry() { System.out.println( "miao" ); } @Override public void run() { System.out.println( "貓爬樹" ); } } class Dog implements Animal { @Override public void cry() { System.out.println( "Wang" ); } @Override public void run() { System.out.println( "狗游泳" ); } } |
單例設計模式
單例模式有以下特點:
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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,完成內容的讀取和寫出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
/** * 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public static void writeData(String filePath, String... data) { // 先有一個文件,來保存要寫出的數據 File file = new File(filePath); // 建立輸出流對象 try { // FileWriter writer = new FileWriter(file); PrintWriter pw = new PrintWriter(file); // 開始完成內容的輸出 for (String str : data) { pw.println(str); } // 資源必須回收,也就是必須將流關閉 pw.close(); } catch (IOException e) { e.printStackTrace(); } } |
使用時,注意我們這里加入了可變參數來動態傳入多個字符串(即String... data)。
當讀取數據時,如果我們使用普通的讀取方式,對于換行的處理不方便。
如果想按行讀取內容,可以使用BufferedReader,Scanner
Scanner是JDK1.5新的
BufferedReader是JDK1.0就有的,所以使用BufferedReader。
為什么現在還使用BufferedReader,因為Scanner不支持編碼的轉換。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public static String readData(String filePath) { // 也要建立文件對象 File file = new File(filePath); // 建立讀取的輸入流對象 try { FileReader reader = new FileReader(file); BufferedReader bw = new BufferedReader(reader); // 每次調用read可以讀取一個字符, // 按照int類型返回,返回的是字符的編碼, // 需要通過強制類型轉換,變為char類型 // Java中對于String這個類一般不建議反復修改,因為會占用內存。 StringBuilder builder = new StringBuilder(); // 因為文件中有很多的字符,因此需要循環來進行內容的讀取。 // 就需要判斷是否還有字符進行讀取 String line = null ; // 每次讀取時,如果讀到內容,則會返回 0 - 65535 的char類型字符 // 如果沒有讀取到內容,則返回 -1 ,因此我們可以根據這個 -1 來判斷后面是否還有內容 while ((line = bw.readLine()) != null ) { // 將讀取到的內容保存下來 // 把字符放入到StringBuilder里 builder.append(line); System.out.println(line); } // 沒有讀取到內容,說明循環結束,已經到了文件的末尾 return builder.toString(); } catch (Exception e) { e.printStackTrace(); } return null ; } |
例如,將一個D盤的test.txt的內容讀取出來,再寫出到E盤的test.txt中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public static void copyFile(String inputFile, String outputPath) { // 首先建立輸入和輸出的文件 File input = new File(inputFile); File output = new File(outputPath); // 建立輸入和輸出流 try { BufferedReader br = new BufferedReader( new FileReader(input)); PrintWriter pw = new PrintWriter(output); // 每次讀入一行,所以準備一個變量來接收 String line = null ; while ((line = br.readLine()) != null ) { pw.println(line); } pw.close(); br.close(); } catch (Exception e) { e.printStackTrace(); } } |
文件夾迭代
這里只需要用到一個File類,但需要用里面的一些方法來判斷是文件還是文件夾
isFile():是否是文件
isDirectory():是否是文件夾
還需要通過遞歸操作,將目錄下的所有子目錄也進行迭代。
多線程處理
使用多線程的目的肯定是為了提升程序的效率。
因為在進行網絡數據爬取時,一般都是同時爬取多個網頁的數據,而不是單個網頁,因此在項目開發中我們需要通過多線程,來讓程序同時完成多個操作。
多線程有兩種實現方式:
1)繼承Thread類
2)實現Runnable接口
使用多線程時,還有兩個必須注意的方法:
1)start()啟動線程
2)run()編寫線程執行的主體。
先來完成一個倒計時功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class ListDemo { public static void main(String[] args) { LinkedList<Integer> list1 = new LinkedList<>(); for ( int i = 0 ; i <= 100000 ; i++) { list1.add(i); } long start = System.currentTimeMillis(); for ( int i = 0 ; i <= 100000 ; i++) { list1.get(i); } long end = System.currentTimeMillis(); System.out.println( "ArrayList: " + (end - start) + " ms" ); } } |
Set:不允許重復,存儲順序看心情,沒辦法根據下標取得數據
HashSet:散列排序(沒有順序)
TreeSet:二叉樹排序,按照固定的規則來排序,TreeSet中的內容必須實現一個Comparable的接口,并且必須覆寫compareTo的方法,根據給定的規則來排序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
public class SetDemo { public static void main(String[] args) { Set<Person> set = new TreeSet<>(); set.add( new Person( "張三" , 12 )); set.add( new Person( "李四" , 22 )); set.add( new Person( "王五" , 42 )); set.add( new Person( "王八" , 42 )); set.add( new Person( "趙六" , 32 )); System.out.println(set); } } class Person implements Comparable<Person> { private String name; private Integer age; public Person() { super (); } public Person(String name, Integer age) { super (); this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this .age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]" ; } @Override public int compareTo(Person o) { if ( this .age > o.age) { return 1 ; } else if ( this .age < o.age) { return - 1 ; } if ( this .name.equals(o.name)) { return 0 ; } return 1 ; } } |
Map:key-value形式,可以根據key取得value,key按照Set集合的模式來保存。
HashMap:散列
TreeMap:有順序
對于Map集合,要求能夠循環迭代出里面的所有數據。
所以必須掌握Map的循環方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class MapDemo { public static void main(String[] args) { Map<String, Integer> map = new HashMap<>(); map.put( "方便面" , 20 ); map.put( "火腿腸" , 120 ); map.put( "礦泉水" , 20 ); map.put( "可樂" , 30 ); // Map集合如果想迭代必須先按照key來進行迭代, // 再根key查找value Set<String> keySet = map.keySet(); for (String key : keySet) { System.out.println(key + " ---> " + map.get(key)); } } } |
小結:
本篇對于java封裝、繼承、多態三大特性,IO操作,線程管理,類集處理(List、Set、Map)進行了闡述以及代碼實現。
到此,對于網頁數據的爬寫的知識準備的可以了,下一篇我會先對一個文件進行數據爬取,然后再對網頁上的數據代碼實現爬蟲功能
到此這篇關于Java實現網絡數據提取所需知識點的文章就介紹到這了,更多相關Java實現網絡數據提取內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://www.cnblogs.com/1996swg/p/7354661.html