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有關。
接口中的泛型有且隻能用于成員方法!
}
使用注意事項:
-
接口聲明的泛型,有且隻能用于類内的成員方法。
問題: default修飾的方法是否可以使用接口聲明的自定義泛型?
可以,因為有方法體,有對象。
- 遵從帶有自定義泛型的接口的類,一種可以遵從過程中明确告知泛型具體類型,一種是在建立類對象是明确泛型具體資料類型。
-
class TypeA<T> implements A<String>
正确
接口的泛型明确接口中的方法,使用泛型對應的具體資料類型
類聲明的泛型用于自定義限制自己類内的方法
-
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));
}
}
複制
很抱歉在這裡斷掉,集合還有很多沒總結,我會盡快補上第四周學習總結,或者單獨開一章集合。