java學習第三周總結

  • 2020 年 3 月 18 日
  • 筆記

1. 多態

多態在java中是一個比較難入門理解的內容,有點不著邊際。所以我們先用介面來初步解釋一下多態的基礎通俗含義,以後再來慢慢補充。

我們在之前的學習中知道了介面的含義,也基本了解了介面的使用方法,接下來我們就用介面來演示一下多態。

1.1 介面生活化演示

從生活中映射USB介面

interface USB 規定USB設備必須完成的方法 void connect();

滑鼠類 implements USB 滑鼠是一個USB設備,必須完成connect方法

鍵盤類 implements USB 鍵盤是一個USB設備,必須完成connect方法

USB介面在電腦上,我們需要使用USB介面

/*  從生活中映射USB介面  	interface USB  		規定USB設備必須完成的方法  		void connect();    滑鼠類 implements USB  	滑鼠是一個USB設備,必須完成connect方法  鍵盤類 implements USB  	鍵盤是一個USB設備,必須完成connect方法   */  /**   * USB介面   * @author Anonymous   *   */  interface USB {  	/**  	 * 要求所有的USB設備,必須完成的方法,告知USB介面連接之後完成的  	 * 功能是什麼  	 */  	void connect();  }    /**   * 滑鼠類,遵從USB介面,實現connect方法   *   * @author Anonymous   */  class Mouse implements USB {  	@Override  	public void connect() {  		System.out.println("滑鼠連接USB介面,控制游標");  	}  }    /**   * Logi類,繼承Mouse滑鼠類   * 		1. 滑鼠設備   * 		2. Logi間接遵從USB介面,是一個USB設備   * @author Anonymous   *   */  class Logi extends Mouse {  	@Override  	public void connect() {  		System.out.println("Logi Master 2S");  	}  }    /**   * 鍵盤類,遵從USB介面,實現connect方法   *   * @author Anonymous   */  class Keyboard implements USB {  	@Override  	public void connect() {  		System.out.println("鍵盤連接USB介面,輸入設備");  	}  }    /**   * IKBC繼承Keyboard類   * 		1. 鍵盤設備   * 		2. 間接遵從USB介面,也是一個USB設備   * @author Anonymous   *   */  class IKBC extends Keyboard {  	@Override  	public void connect() {  		System.out.println("IKBC C87 靜音紅軸");  	}  }    /**   * PC電腦類,使用USB介面,這裡需要通過USB介面連接一個USB設備   *   * @author Anonymous   */  class PC {  	/**  	 * 電腦類連接USB介面連接方法,這裡需要的是一個USB設備  	 *  	 * @param usb USB介面對應的設備  	 */  	public void usbConnect(USB usb) {  		// usb設備執行connect方法  		usb.connect();  	}  }    public class Demo1 {  	public static void main(String[] args) {    		// 電腦中有一個方法是使用USB介面連接USB設備  		PC pc = new PC();    		// 滑鼠和鍵盤都是USB設備  		Mouse mouse = new Mouse();  		Keyboard keyboard = new Keyboard();    		// 電腦需要通過USB介面連接USB設備,mouse滑鼠就是USB設備  		pc.usbConnect(mouse);  		// keyboard鍵盤也是USB設備  		pc.usbConnect(keyboard);    		// 傳入一個Logi類對象  		pc.usbConnect(new Logi());  		// 傳入一個IKBC類對象  		pc.usbConnect(new IKBC());  	}  }

1.2 繼承生活化演示

動物園: 所有的動物都可以看做是一個Animal類 狗類 Dog

熊貓類 Panda

老虎類 Tiger

方法: 餵食動物的方法 獲取動物的方法

/*  動物園:  	所有的動物都可以看做是一個Animal類  狗類  	Dog  熊貓類  	Panda  老虎類  	Tiger    方法:  	餵食動物的方法  	獲取動物的方法   */  /**   * 動物類   * @author Anonymous   *   */  class Animal {    }    /**   * 狗類,為Animal動物的子類   * @author Anonymous   *   */  class Dog extends Animal {    }    /**   * 熊貓類,為Animal動物的子類   * @author Anonymous   *   */  class Panda extends Animal {    }    /**   * 老虎類,為Animal動物的子類   * @author Anonymous   *   */  class Tiger extends Animal {    }    public class Demo2 {  	public static void main(String[] args) {  		Animal animal = new Animal();  		Dog dog = new Dog();  		Tiger tiger = new Tiger();  		Panda panda = new Panda();    		// 這裡傳入一個Animal類對象  		feed(animal);    		// 這裡可以傳入Dog類對象,因為Dog類對象是Animal類的子類對象  		feed(dog);  		// 這裡傳入的對象是Tiger類對象,Tiger類是Animal的子類  		feed(tiger);  		// 同上  		feed(panda);    		// 數據類型強轉,  		Dog dog2 = (Dog) getAnimal();  		System.out.println(dog2.getClass());  	}    	/**  	 * 該方法的是餵食【動物】的方法  	 *  	 * @param animal 需要的參數是Animal類對象  	 */  	public static void feed(Animal animal) {  		// 獲取當前對象的完整包名.類名  		System.out.println(animal.getClass() + "來吃飯了!!!");  	}    	/**  	 * 返回【動物】類對象的方法  	 *  	 * @return Animal類對象  	 */  	public static Animal getAnimal() {  		// return new Animal();  		return new Dog();  		// return new Tiger();  		// return new Panda();  	}  }

通過上邊兩個程式碼的實現,不難看出主要是介面的引用指向引用借口的類對象,還有父類的引用指向子類的對象。 這個就叫做多態!!!

作用: 1. 拓寬方法的參數範圍 例如: 方法參數為Animal類型 Animal類對象 可以傳入Animal類型本身,或者子類對象都可以 方法參數為USB介面類型 只要是直接或者間接遵從USB介面的類對象可以作為方法的參數傳入 2. 拓寬方法的返回值範圍。與參數同理。 3. 簡化程式碼開發,提高開發效率,整合數據類型。

2. 異常

這個我已經在之前的部落格中總結過了,可以去看一下我的「異常基礎」。 異常基礎

3. 泛型

可能我比較特殊,也可能是我學習深度,廣度都不太夠。在我看來,泛型的學習比多態還要更難一些…

3.1 什麼是泛型,為什麼引入泛型

在我們學習泛型之前,所有編寫的功能程式碼是要考慮之後的復用!!!

當前程式碼有且只支援一種類型,如果需要更換數據類型,按照目前的技術,只能重新完成對應數據類型的功能程式碼。有可能出現各式各樣的類型,工作是重複而且無意義的。

這裡不單單是對於功能模組的要求封裝。同時需要對數據類型進行統一的約束!!!根據用戶的要求對應數據類型。

Java的泛型就是來解決對應的問題

格式: <自定義泛型無意義大寫英文字母佔位符> 一般使用<T> <E> <K> <V>

泛型可以在方法中使用 泛型可以在類中使用 泛型可以在介面中使用

3.2 泛型在方法中使用

使用靜態方法舉例說明泛型在方法中的使用格式

格式: public static 返回值類型[自定義泛型] 方法名(必須存在一個對應泛型的參數) { 方法體內可以使用自定義泛型 }

使用注意事項: 1. 自定義泛型聲明在返回值之前,已保證方法的參數和返回值都可以使用對應的泛型 2. 方法聲明的泛型,在方法的參數中必須有一個參數是對應聲明的自定義泛型。當前參數是用於約束方法內所有使用到泛型的位置對應的具體數據類型是什麼。 3. 程式碼演示如下

public class Demo1 {  	public static void main(String[] args) {  		/*  		 * test方法使用了自定義泛型,傳入的實際參數是什麼類型  		 * 泛型T對應的具體數據類型是什麼類型  		 */  		 // 首先定義幾個變數  		Integer test = test(1234234);  		String test2 = test("武漢加油");  		Demo1 test3 = test(new Demo1());  		// 定義字元串類型數組  		String[] arr1 = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };  		// 調用方法  		printArray(arr1);    		System.out.println("-----------------------");    		// 定義Integer類型數組,Integer是int的包裝類,可以理解為int大哥  		Integer[] arr2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };  		printArray(arr2);    		System.out.println("-----------------------");    		// 定義Demo1類型數組  		Demo1[] arr3 = {new Demo1(), new Demo1(), new Demo1(), new Demo1(), new Demo1()};  		printArray(arr3);  	}    	/*  	 * 聲明一個帶有自定義泛型的方法  	 */  	/**  	 * 帶有自定義泛型聲明的方法  	 *  	 * @param <T> 自定義泛型無意義佔位符  	 * @param t 指定的泛型數據類型,由用戶約束  	 * @return 對應的T類型, 具體數據類型由用戶約束  	 */  	public static <T> T test(T t) {  		return t;  	}    	/*  	 * 展示任意類型數組中數據的方法  	 */  	/**  	 * 展示任意類型數組的方式  	 *  	 * @param <T> 自定義泛型無意義佔位符  	 * @param arr 用戶指定類型的數組,同時約束自定義泛型對應的具體數據類型  	 */  	public static <T> void printArray(T[] arr) {  		for (int i = 0; i < arr.length; i++) {  			System.out.println(arr[i]);  		}  	}  } 

3.3 泛型可以在類中使用

這部分有一些比較難理解的點

格式: class 類名<自定義泛型無意義大寫字母佔位符> { 在類內的成員變數和成員方法都可以使用自定義泛型 建議: 成員變數不建議使用自定義泛型 }

使用注意事項: 1. 類聲明自定義泛型,需要通過創建對象的方式來約束 TypeA typeA = new TypeA(); Eclipse標準寫法 TypeA typeA = new TypeA<>(); IDEA寫法 2. 類聲明泛型約束之後,在類內的所有成員方法中使用的泛型都是類約束的泛型具體數據類型 3. **如果沒有約束類聲明泛型,所有使用到泛型的位置都是Object,【不推薦】**Object是所有類的超類,可以理解為盤古爸爸 4. 類聲明的自定義泛型不能用於類內的靜態方法【沒有對象】 類泛型約束靠的是類對象,他是個快樂的單身狗,怎麼約束他?不理解靜態的,可以看一下我的另一篇部落格static關鍵字

下邊我們通過程式碼來驗證一下:

/*   * 定義一個帶有自定義泛型的類   */  class TypeA<T> {  	/**  	 * 使用和類名一致的自定義泛型,需要通過【類對象】來約束  	 *  	 * @param t 對應泛型具體數據類型的參數  	 * @return 對應泛型具體數據類型的返回值  	 */  	public T test1(T t) {  		return t;  	}    	/**  	 * 使用和類名一致的自定義泛型,需要通過【類對象】來約束  	 *  	 * @param t1 對應泛型具體數據類型的參數  	 * @param t2 對應泛型具體數據類型的參數  	 */  	public void test2(T t1, T t2) {  		System.out.println(t1);  		System.out.println(t2);  	}    	/*  	 * 為什麼類名自定義的泛型不能用於當前類內的靜態成員方法  	 * 		靜態成員方法是在類文件的載入階段準備完畢,已經可以明確的保證該方法的是  	 * 可以執行的。  	 * 		如果使用類聲明的自定義泛型,對應泛型的具體數據類型需要在創建類對象之後  	 * 才可以明確。  	 * 		當前靜態方法在類文件載入階段無法明確泛型數據類型是哪一個,也就無法保證  	 * 執行,【沒有對象】  	 *  	 * 如果方法想要使用自定義泛型,自娛自樂,自己定義自己用!!!  	 * 要求自定義的泛型佔位符和類聲明泛型佔位符不一致,避免沒有必要的歧義,提高閱讀性  	 */  	public static <E> E staticMethod(E e) {  		return e;  	}  }    public class Demo2 {  	public static void main(String[] args) {  		/*  		 * 創建帶有自定義泛型類的對象  		 * TypeA<String> typeA = new TypeA<String>(); Eclipse標準寫法  		 * TypeA<String> typeA = new TypeA<>(); IDEA寫法  		 */  		// 明確告知編譯器,這裡泛型對應的具體數據類型是String類型  		// 類內所有使用到泛型佔位符的位置都是的T類型  		TypeA<String> typeA = new TypeA<String>();    		String test1 = typeA.test1("北京烤鴨");  		typeA.test2("鹵煮火燒", "豆汁");    		// 明確告知編譯器這裡泛型對應的具體數據類型是Demo2類型  		TypeA<Demo2> typeA2 = new TypeA<Demo2>();    		Demo2 test12 = typeA2.test1(new Demo2());  		typeA2.test2(new Demo2(),new Demo2());    		/*  		 * 沒有告知編譯器泛型對應的具體數據類型,那麼在類內的所有非靜態  		 * 成員方法方法中使用到的泛型, 都是Object類型,雖然可以支援任意  		 * 類型傳入參數,但是缺少了數據類型一致化的約束。  		 *  		 * 不推薦!!!  		 */  		TypeA typeA3 = new TypeA();    	}  }

3.4 泛型可以在介面中使用

介面 interface 介面名 { 成員變數預設屬性: public static final 定義時必須初始化,並且初始化之後無法修改 成員方法預設屬性: public abstract 方法沒有方法體 有方法體如何使用: default修飾默認方法,非強制實現方法 }

泛型在介面中使用格式 interface 介面名<自定義泛型無意義佔位符> { 問題: 介面的泛型是否可以用於成員變數中? T t 定義時必須初始化,但是當前泛型的數據類型是不明確的,無法進行初 始化過程。和final有關。 介面中的泛型有且只能用於成員方法! }

使用注意事項:

  1. 介面聲明的泛型,有且只能用於類內的成員方法。 問題: default修飾的方法是否可以使用介面聲明的自定義泛型? 可以,因為有方法體,有對象。
  2. 遵從帶有自定義泛型的介面的類,一種可以遵從過程中明確告知泛型具體類型,一種是在創建類對象是明確泛型具體數據類型
  3. class TypeA<T> implements A<String> 正確 介面的泛型明確介面中的方法,使用泛型對應的具體數據類型 類聲明的泛型用於自定義約束自己類內的方法
  4. class TypeA<E> implements A<T> 錯誤 介面中的泛型沒有明確數據類型,也無法通過類創建對象的過程中明確 泛型對應的具體數據類型,無法編譯

程式碼實現:

interface A<T> {  	void test(T t);  }    /*   * 介面帶有泛型,遵從介面的實現類有兩種方案可以implements當前介面   * 		1. 自由   * 		2. 規矩   */    /**   * 自由方式,類名之後和介面同名自定義泛型,泛型對應的具體數據類型是   * 需要在創建當前類對象是進行約束使用。   *   * @author Anonymous   *   * @param <T> 遵從介面和介面一致的泛型   */  class TypeB<T> implements A<T> {    	@Override  	public void test(T t) {  		System.out.println("自由模式");  	}    }    /**   * 規矩模式,遵從介面時,介面使用泛型對應的具體數據類型已經明確   * 在類內使用介面中預設屬性為public abstract方法時,泛型已確認   *   * @author Anonymous   */  class TypeC<T> implements A<String> {    	@Override  	public void test(String t) {  		System.out.println("規矩模式");  	}    }    public class Demo3 {  	public static void main(String[] args) {  		/*  		 * 自由模式  		 */  		TypeB<Integer> typeB = new TypeB<Integer>();  		typeB.test(10);    		TypeB<Character> typeB2 = new TypeB<Character>();  		typeB2.test('A');    		TypeC typeC = new TypeC();    		typeC.test("比較無聊,但是很規矩");    	}  }

4. 集合

4.1 為什麼使用集合

開發中會使用大量相同數據類型的情況。如果使用數組來解決問題 1. 數組能夠使用的方法非常少,功能方法需要程式設計師自己完成。 2. 數據類型單一化,不支援多種情況。 3. 數組容量不可以更改。

集合為解決問題而生: 1. 方法多種多樣,基本功能完善 2. 數據類型支援多樣化,但是又不失數據類型一致要求 3. 容量可以變,並且不用開發者操心

4.2 集合架構

Java中集合的【總介面】Collection。Java中所有和集合有關的內容,都是Collection介面的子介面或者實現類

interface Collection –| interface List List介面,有序可重複 —-| class ArrayList 【重點】可變長數組結構 原碼實現,了解其中的特徵,性能… —-| class LinkedList 【重點】雙向鏈表結構 —-| class Vector 【遠古時代】JDK1.0 執行緒安全的ArrayList,如果不考慮執行緒安全問 題,建議使用ArrayList –| interface Set Set介面,無序不可重複 —-| HashSet 底層存儲數據的方式是採用哈希表方式 —-| TreeSet 底層存儲數據的方式一個平衡二叉樹方式

以上這些東西我們之後會一一講解,現在我們先來了解一下collection介面的常用方法

4.3 Collection<E>介面下的常用方法

增: boolean add(E e); 存入元素到當前集合對象中,這裡要求的數據類型是E類型,也就是泛型對於 的具體數據類型 boolean addAll(Collection<? extends E> c); class Dog extends Animal class Cat extends Animal class Tiger extends Animal ==> ? extends E 泛型的上限 要求存入的集合c中,存儲的元素要麼是E類型,要麼是E類的子類 刪: void clear(); 清空整個集合 boolean remove(Object obj); 刪除集合中的指定元素 boolean removeAll(Collection<?> c); 刪除兩個集合的交集 boolean retainAll(Collection<?> c); 保留兩個集合的交集 查: int size(); 返回集合中有效元素個數 boolean isEmpty(); 判斷當前集合是否為空 boolean contains(Object obj); 判斷指定元素在當前集合中是否存在 boolean containsAll(Collection<?> c); 判斷集合c是不是當前集合的子集合

以下是程式碼的實現: 注意導包,之前的部分程式碼也需要導包 就是這個: import java.util.ArrayList; import java.util.Collection;

public class Demo1 {  	public static void main(String[] args) {  		/*  		 * 因為Collection<E>是一個介面,介面沒有自己的類對象  		 * 這裡使用Collection介面的實現類來完成演示過程 ArrayList<E>  		 */  		Collection<String> c = new ArrayList<String>();    		c.add("82年的拉菲");  		c.add("82年的雪碧");  		c.add("82年的可樂");  		c.add("82年的老雪");    		System.out.println(c);    		Collection<String> c1 = new ArrayList<String>();    		c1.add("百威");  		c1.add("福佳白");  		c1.add("精釀啤酒");  		c1.add("修道院啤酒");    		c.addAll(c1);    		System.out.println(c);    		c.remove("82年的雪碧");  		System.out.println(c);    		//c.removeAll(c1);  		//System.out.println(c);    		//c.retainAll(c1);  		//System.out.println(c);    		System.out.println("size:" + c.size());  		System.out.println(c.isEmpty());    		System.out.println(c.contains("百威"));  		System.out.println(c.contains("哈爾濱"));    		System.out.println(c.containsAll(c1));    		c1.add("野格");  		System.out.println(c.containsAll(c1));  	}  }

很抱歉在這裡斷掉,集合還有很多沒總結,我會儘快補上第四周學習總結,或者單獨開一章集合。