java回顧(專案前期的基本準備)
一、 基礎回顧
1 集合
1.1 集合的型別與各自的特性
---|Collection: 單列集合 ---|List: 有儲存順序, 可重複 ---|ArrayList: 陣列實現, 查詢快, 增刪慢 由於是陣列實現, 在增和刪的時候會牽扯到陣列 增容, 以及拷貝元素. 所以慢。陣列是可以直接按索引查詢, 所以查詢時較快 ---|LinkedList: 連結串列實現, 增刪快, 查詢慢由於連結串列實現, 增加時只要讓前一個元素記住自己就可以, 刪除時讓前一個元素記住後一個元素, 後一個元素記住前一個元素. 這樣的增刪效率較高但查詢時需要一個一個的遍歷, 所以效率較低 ---|Vector 和ArrayList實現方式相同, 但考慮了執行緒安全問題, 所以效率略低 ---|Set: 無儲存順序, 不可重複 ---|HashSet 執行緒不安全,存取速度快。底層是以雜湊表實現的。 ---|TreeSet 序(String)。如果在比較的時候兩個物件 返回值為0,那麼元素重複。 ---| Map: 鍵值對 鍵不可重複,鍵可以重複 ---|HashMap 執行緒不安全,存取速度快。底層是以雜湊表實現的. ---|TreeMap 紅-黑樹的資料結構,預設對元素進行自然排 序(String)。如果在比較的時候兩個物件 返回值為0,那麼元素重複 ---|HashTable 底層也是使用了雜湊表 維護的,存取的讀取快,儲存元素是 無序的。 |
1.2 遍歷集合
1.2.1遍歷集合的幾種方式
1, 使用迭代器Iterator的方式。
2, 使用增強for迴圈的方式。
3, 如果有下標,則可以使用下標的方式。
1.2.2遍歷陣列
1.2.3遍歷List
1.2.4遍歷Set
1.2.5遍歷Map
2 泛型(Generic)
當集合中儲存的物件型別不同時,那麼會導致程式在執行的時候的轉型異常
import java.util.ArrayList; import java.util.Iterator;
public class Demo5 { public static void main(String[] args) { ArrayList arr = new ArrayList(); arr.add(new Tiger("華南虎")); arr.add(new Tiger("東北虎")); arr.add(new Sheep("喜羊羊")); System.out.println(arr); Iterator it = arr.iterator(); while (it.hasNext()) { Object next = it.next(); Tiger t = (Tiger) next; t.eat(); }
} } class Tiger { String name;
public Tiger() {
}
public Tiger(String name) { this.name = name; }
@Override public String toString() {
return "[email protected]:" + this.name; }
public void eat() { System.out.println(this.name + "吃羊"); } }
class Sheep { String name;
public Sheep() {
}
public Sheep(String name) { this.name = name; }
@Override public String toString() { return "[email protected]:" + this.name; }
public void eat() { System.out.println(this.name + "吃青草"); } } |
原因 :發現雖然集合可以儲存任意物件,但是如果需要使用物件的特有方法,那麼就需要型別轉換,如果集合中存入的物件不同,可能引發型別轉換異常.
[[email protected]:華南虎, [email protected]:東北虎, [email protected]:喜羊羊] 華南虎吃羊 東北虎吃羊 Exception in thread "main" java.lang.ClassCastException: cn.itcast.gz.map.Sheep cannot be cast to cn.itcast.gz.map.Tiger at cn.itcast.gz.map.Demo5.main(Demo5.java:17) |
出現問題:
存入的是特定的物件,取出的時候是Object物件,需要強制型別轉換,可能誘發型別轉換異常.
無法控制存入的是什麼型別的物件,取出物件的時候進行強轉時可能誘發異常.而且在編譯時期無法發現問題.
雖然可以再型別轉換的時候通過if語句進行型別檢查(instanceof),但是效率較低.(例如吃飯的時候,還需要判斷米飯裡有沒有沙子,吃飯效率低).可以通過給容器加限定的形式規定容器只能儲存一種型別的物件.
就像給容器貼標籤說明該容器中只能儲存什麼樣型別的物件。
所以在jdk5.0後出現了泛型
泛型應用:
格式
- 集合類<類型別> 變數名 = new 集合類<類型別>();
public class Demo5 { public static void main(String[] args) { // 使用泛型後,規定該集合只能放羊,老虎就進不來了. ArrayList<Sheep> arr = new ArrayList<Sheep>(); arr.add(new Sheep("美羊羊")); arr.add(new Sheep("懶洋洋")); arr.add(new Sheep("喜羊羊")); // 編譯失敗 // arr.add(new Tiger("東北虎")); System.out.println(arr); Iterator<Sheep> it = arr.iterator(); while (it.hasNext()) { // 使用泛型後,不需要強制型別轉換了 Sheep next = it.next(); next.eat(); }
} } |
1. 將執行時的異常提前至編譯時發生。
2. 獲取元素的時候無需強轉型別,就避免了型別轉換的異常問題
格式 通過<> 來指定容器中元素的型別.
什麼時候使用泛型:當類中操作的引用資料型別不確定的時候,就可以使用泛型類.
JDK5.0之前的Comparable
package java.lang; public interface Comparable {
public int compareTo(Object o); } |
JDK5.0之後的Comparable
package java.lang; public interface Comparable<T> {
public int compareTo(T o); } |
這裡的<T>表示泛型型別,隨後可以傳入具體的型別來替換它.
細節一
宣告好泛型型別之後,集合中只能存放特定型別元素
public class Demo6 { public static void main(String[] args) { //建立一個儲存字串的list ArrayList<String> arr=new ArrayList<String>(); arr.add("gz"); arr.add("itcast"); //儲存非字串編譯報錯. arr.add(1); } } |
細節二:
泛型型別必須是引用型別
public class Demo6 { public static void main(String[] args) { // 泛型型別必須是引用型別,也就是說集合不能儲存基本資料型別 // ArrayList<int> arr2=new ArrayList<int>();
// 使用基本資料型別的包裝類 ArrayList<Integer> arr2 = new ArrayList<Integer>();
} }
|
細節三: 使用泛型後取出元素不需要型別轉換.
public class Demo6 { public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>(); arr.add("gzitcast"); arr.add("cditcast"); arr.add("bjitcast"); //使用泛型後取出元素不需要型別轉換. String str=arr.get(0); System.out.println(); } } |
1.1. 泛型方法
需求:寫一個函式,呼叫者傳遞什麼型別的變數,該函式就返回什麼型別的變數?
實現一:
由於無法確定具體傳遞什麼型別的資料.那麼方法的形參就定義為Object型別.返回值也就是Object型別.但是使用該函式時需要強制型別轉換.
private Object getDate(Object obj) { return obj; } |
當不進行強制型別轉換能否寫出該功能.?
目前所學的知識無法解決該問題
就需要使用泛型類解決
使用的泛型的自定義來解決以上問題。
泛型: 就是將型別當作變數處理。規範泛型的定義一般是一個大寫的任意字母。
1. 函式上的泛型定義
當函式中使用了一個不明確的資料型別,那麼在函式上就可以進行泛型的定義。
public <泛型的宣告> 返回值型別 函式名( 泛型 變數名 ){
}
|
public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 };
new Demo6().getData(5);
}
public <T> T getData(T data) { return data; } |
細節:
使用泛型方法前需要進行泛型宣告,使用一對尖括號 <泛型>,宣告的位置在static後返回值型別前。
當一個類中有多個函式聲明瞭泛型,那麼該泛型的宣告可以宣告在類上。
1.2. 泛型類
格式
2. 類上的泛型宣告
修飾符 class 類名<泛型>{
} |
import java.util.Arrays;
public class Demo6<T> { public static void main(String[] args) { // 使用泛型類,建立物件的時候需要指定具體的型別 new Demo6<Integer>().getData(5); }
public T getData(T data) { return data; }
// 反序任意型別陣列 public void reverse(T[] arr) { int start = 0; int end = arr.length - 1; for (int i = 0; i < arr.length; i++) { if (start < end) { T temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; } }
}
|
在泛型類中定義一個靜態方法
public class Demo6<T> { public static void main(String[] args) { System.out.println(getData2(100)); }
public T getData(T data) { return data; }
//靜態方法 public static T getData2(T data) { return data; }
} |
注意:靜態方法不可以使用類中定義的泛型
因為類中的泛型需要在物件初始化時指定具體的型別,而靜態優先於物件存在。那麼類中的靜態方法就需要單獨進行泛型宣告,宣告泛型一定要寫在static後,返回值型別之前
泛型類細節:
1、建立物件的時候要指定泛型的具體型別 2、建立物件時可以不指定泛型的具體型別(和建立集合物件一眼)。預設是Object,例如我們使用集合儲存元素的時候沒有使用泛型就是那麼引數的型別就是Object 3、類上面宣告的泛型只能應用於非靜態成員函式,如果靜態函式需要使用泛型,那麼 需要在函式上獨立宣告。 4、如果建立物件後指定了泛型的具體型別,那麼該物件操作方法時,這些方法只能操作一種資料型別。 5、所以既可以在類上的泛型宣告,也可以在同時在該類的方法中宣告泛型。 |
泛型練習:
定義泛型成員
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); }
}
class Father<T> { private T t;
public Father() {
}
public Father(T t) { super(); this.t = t; }
public T getT() { return t; }
public void setT(T t) { this.t = t; }
} |
如果Father類有子類,子類該如何實現
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); }
}
class Father<T> { private T t;
public Father() {
}
public Father(T t) { super(); this.t = t; }
public T getT() { return t; }
public void setT(T t) { this.t = t; }
} //子類指定了具體的型別 class Son extends Father<String>{
} //子類也需要使用泛型 class Son3<T> extends Father<T>{
} //錯誤寫法,父類上定義有泛型需要進行處理 class Son2 extends Father<T>{
} |
1.3. 泛型介面
public class Demo8 { public static void main(String[] args) { MyInter<String> my = new MyInter<String>(); my.print("泛型");
MyInter2 my2 = new MyInter2(); my.print("只能傳字串"); } }
interface Inter<T> { void print(T t); }
// 實現不知為何型別時可以這樣定義 class MyInter<T> implements Inter<T> { public void print(T t) { System.out.println("myprint:" + t); } } //使用介面時明確具體型別。 class MyInter2 implements Inter<String> {
@Override public void print(String t) { System.out.println("myprint:" + t);
}
}
|
3 IO流
3.1 IO流的分類
|
輸入流 |
輸出流 |
說明 |
位元組流 |
InputStream |
OutputStream |
位元組流是處理位元組的(二進位制) |
字元流 |
Reader |
Writer |
字元流是處理字元的 |
注:這幾個類都是抽象類。
3.2 讀檔案的程式碼
3.3 拷貝檔案的程式碼
4 多執行緒
4.1 啟動執行緒方式
1, 自定義的類繼承Thread類。
使用程式碼為new MyThread().start()
2,自定義的類實現Runnable介面。
使用程式碼為new Thread(new MyRunnable()).start
4.2 程式碼
以下程式碼是分別用兩種方式啟動執行緒(還是用到了匿名內部類)
二、 Junit單元測試
1.1. Junit單元測試框架的基本使用
一、搭建環境:
匯入junit.jar包(junit4)
二、寫測試類:
0,一般一個類對應一個測試類。
1,測試類與被測試類最好是放到同一個包中(可以是不同的原始檔夾)
2,測試類的名字為被測試類的名字加Test字尾。
三:寫測試方法:
0,一般一個方法對應一個單元測試方法。
1,測試方法的名字為test字首加被測試方法的名字,如testAddPerson()。
2,單元測試方法上面要加上@Test註解(org.junit.Test)!
3,單元測試方法不能有引數,也不能有返回值(返回void)!測試的方法不能是靜態的方法。
四、測試方法的基本使用:
1,可以單獨執行一個測試方法,也可以一次執行所有的、一個包的、一個類中所有的測試方法。
2,執行完後,顯示綠色表示測試成功;顯示紅色表示測試失敗(拋異常後會測試失敗)。
1.2. Assert斷言工具類
其中有一些靜態的工具方法(不符合期望就拋異常):
assertTrue(...) 引數的值應是true
assertFalse(...) 引數的值應是false
assertNull(...) 應是null值
assertNotNull(...) 應是非null的值
assertSame(...) 使用==比較的結果為true(表示同一個物件)
AssertNotSame(...) 使用==比較的結果為false
assertEquals(...) 兩個物件equals()方法比較結果為true
1.3. 用於準備環境、清理環境的方法
@Test
表示單元測試方法。
@Before
所修飾的方法應是非static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的每個單元測試方法之前都執行一次。
@After
所修飾的方法應是非static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的每個單元測試方法之後都執行一次。
@BeforeClass
所修飾的方法應是static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的所有單元測試方法之前執行,只執行一次。
@AfterClass
所修飾的方法應是static的(且沒有引數,返回值為void)。
表示這個方法會在本類中的所有單元測試方法之後執行,只執行一次。
三、 內省(Introspector)
1 為什麼要學內省?
開發框架時,經常需要使用java物件的屬性來封裝程式的資料,每次都使用反射技術完成此類操作過於麻煩,所以sun公司開發了一套API,專門用於操作java物件的屬性。
內省是用於操作java物件的屬性的,那麼以下問題我們必須要清楚。
問題一: 什麼是Java物件的屬性和屬性的讀寫方法?
問題二: 如何通過內省訪問到javaBean的屬性 ?
1. 通過PropertyDescriptor類操作Bean的屬性.
public static void testPropertyDescriptor() throws Exception{ Person p = new Person(); PropertyDescriptor propertyDescriptor = new PropertyDescriptor("id",Person.class); //獲取屬性的寫的方法。 Method writeMethod = propertyDescriptor.getWriteMethod(); Method readMethod = propertyDescriptor.getReadMethod(); propertyDescriptor.getReadMethod(); writeMethod.invoke(p, 12); System.out.println(readMethod.invoke(p, null)); } |
2. 通過Introspector類獲得Bean物件的 BeanInfo,然後通過 BeanInfo 來獲取屬性的描述器( PropertyDescriptor ),通過這個屬性描述器就可以獲取某個屬性對應的 getter/setter 方法,然後通過反射機制來呼叫這些方法。
public static void testIntrospector() throws Exception{ BeanInfo beanInfo = Introspector.getBeanInfo(Person.class); PropertyDescriptor[] descriptor = beanInfo.getPropertyDescriptors(); for(PropertyDescriptor itemProperty : descriptor){ System.out.println(itemProperty.getReadMethod().getName()); } } |
存在的問題: sun公司的內省API過於繁瑣,所以Apache組織結合很多實際開發中的應用場景開發了一套簡單、易用的API操作Bean的屬性——BeanUtils。
public static void main(String[] args) throws Exception { Person p = new Person(); ConvertUtils.register(new Converter() {
@Override public Object convert(Class type, Object value) { try { if(value!=null){
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MM dd"); Date d = dateFormat.parse((String) value); return d; } } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); }
return null; } }, Date.class);
BeanUtils.setProperty(p,"id","110"); BeanUtils.setProperty(p,"name","狗娃"); BeanUtils.setProperty(p, "birthDay","1992 12 12"); System.out.println(p.getId() +"=="+ p.getName()+"======"+p.getBirthDay()); } |
四、 Properties類與配置檔案
4.3 Properties配置檔案說明
Properties類對應.properties檔案。檔案內容是鍵值對,鍵值對之間使用"="或空格隔開。開頭是"#"的表示註釋
Properties類在載入.properties檔案時使用的iso8859-1的編碼。所以這個檔案中的中文要特殊處理:如果這個配置檔案中有中文就必須要進行轉義,使用native2ascii.exe命令操作:
native2ascii d:/my.properties d:/my2.properties
使用Properties類中的load(InputStream) 方法可以載入配置檔案,使用其中的store(OutputStream) 方法可以儲存配置到指定檔案。
更多的資訊可以看Properties類的API文件。
4.4 載入配置檔案
4.5 寫配置檔案
作業:使用properties讀取配置檔案,讀取資料庫的使用者名稱、密碼。並且打包成jar包。
4.6 使用Properties類
public class DBUtil {
static Properties properties = new Properties();
static{ try { Class clazz = DBUtil.class; InputStreamReader fileReader = new InputStreamReader(clazz.getResourceAsStream("/db.properties")); properties.load(fileReader); } catch (IOException e) { e.printStackTrace(); } } public static String getUserName(){ String userName =properties.getProperty("userName"); return userName; }
public static String getPassword(){ return properties.getProperty("password"); } public static void main(String[] args) { System.out.println("使用者名稱:"+ getUserName()); System.out.println("密碼: "+ getPassword()); } } |
五、 檔案路徑
1.1. 絕對路徑
以根目錄或某碟符開頭的路徑(或者說完整的路徑)
例如:
l c:/a.txt (Windows作業系統中)
l c:/xxx/a.txt (Windows作業系統中)
l /var/xx/aa.txt (Linux作業系統中)
絕對路徑的問題: 比如C:\abc\a.properties檔案路徑,該路徑在windows上執行沒有 問題,但是如果把該專案移動到linux上面執行 ,該路徑就會出現問題了,因為在linux上面沒有c盤的,只有根目錄\。
1.2. 相對路徑
相對於當前路徑的一個路徑。例如當前資料夾為c:/abc時:相對路徑a.txt表示c:/abc/a.txt,相對路徑xx/a.txt = c:/abc/xx/a.txt
l . 表示當前資料夾
l .. 表示上級資料夾
相對路徑存在的問題:相對路徑是相對於目前執行class檔案的時候,控制檯所在的路徑,這樣子也會導致出現問題。
1.3. Java程式中的相對路徑
在Java程式中使用File時寫相對路徑,是指相對於執行java命令時當前所在的資料夾。
測試程式碼:
在命令列中使用cd命令切換到不同的路徑下試試,可以看到以上所說的效果。
在Eclipse中,當前路徑是工程的根目錄。
1.4. classpath路徑
1.4.1. classpath路徑說明
在Java程式中,一般情況下使用絕對路徑還是相對路徑都不太合適,因為Java程式的jar包所放的位置不確定,執行java程式時當前的路徑也不確定,所以不合適。一般在Java程式中我們會把資源放到classpath中,然後使用classpath路徑查詢資源。
Classpath路徑:就是使用classpath目前的路徑。
1.4.2. 獲取classpath中的資源(InputStream)