1. 程式人生 > 其它 >【陣列】順時針列印矩陣

【陣列】順時針列印矩陣

技術標籤:JavaSEjavajavase

基本介紹

為什麼要有泛型?

所有的資料型別都可以用Object表示,也就是說可以用這個類儲存資料,那為什麼要有泛型呢?我們假設有這麼一個情況,中藥櫃子有很多的抽屜,每個抽屜就相當於一個Object,這個時候抓藥怎麼辦,每次抓藥都把抽屜開啟看一眼嗎?顯然不可能,所以就有了每個櫃子標一個標籤用來記錄這個櫃子是放啥的。這個櫃子加標籤就可以理解為這裡的泛型。所以為什麼要有泛型

  • 解決元素儲存的安全性問題。(好比商品、藥品標籤,不會弄錯)
  • 解決獲取資料元素時,需要型別強制轉換的問題(好比不用每回拿商品、藥品都要辨別)

在這裡插入圖片描述

在這裡插入圖片描述

Java泛型可以保證如果程式在編譯時沒有發出警告,執行時就不會產生 ClassCastException異常。同時,程式碼更加簡潔、健壯。

泛型的概念:所謂泛型,就是允許在定義類、介面時通過一個標識表示類中某個屬性的型別或者是某個方法的返
回值及引數型別。這個型別引數將在使用時(例如,繼承或實現這個介面,用這個型別宣告變數、建立物件時確定(即傳入實際的型別引數,也稱為型別實參)。

泛型的引入背景:

集合容器類在設計階段/宣告階段不能確定這個容器到底實際存的是什麼型別的物件,所以在JDK1.5之前只能把元素型別設計為Object,JDK1.5之後使用泛型來解決。因為這個時候除了元素的型別不確定,其他的部分是確定的,例如關於這個元素如何儲存,如何管理等是確定的,因此此時把元素的型別設計成一個引數,這個型別引數叫做泛型。Collection,List,ArrayList 這個就是型別引數,即泛型。

自定義泛型結構

  1. 泛型宣告

    interface List 和 class GenTest 其中,T,K,V不代表值,而是表示型別。這裡使用任意字母都可以。常用T表示,是Type的縮寫。

  2. 泛型例項化

    一定要在類名後面指定型別引數的值(型別)。如:

    List<String> strList = new ArrayList<String>(); 
    Iteratorr<Customer> iterator = customers.iterator();
    

    T只能是類,不能用基本資料型別填充。但可以使用包裝類填充。把一個集合中的內容限制為一個特定的資料型別,這就是generics背後的核心思想

體會:使用泛型的主要優點是能夠在編譯時而不是在執行時檢測錯誤。

例如:

public class Order<T> {
    String orderName;
    //類的內部結構就可以使用類的泛型
    T orderT;
    
    //可以指定為方法的返回值
    public T getOrderT(){
        return orderT;
    }

    //方法的引數
    public void setOrderT(T orderT){
        this.orderT = orderT;
    }
    
    //泛型方法:在方法中出現了泛型的結構,泛型引數與類的泛型引數沒任何關係。
    //換句話說,泛型方法所屬的類是不是泛型類都沒關係。
    //泛型方法,可以宣告為靜態的。原因:泛型引數是在呼叫方法時確定的。並非在例項化類時確定。
    public static <E>  List<E> copyFromArrayToList(E[] arr){
        ArrayList<E> list = new ArrayList<>();
        for(E e : arr){
            list.add(e);
        }
        return list;
    }
}

//建立這個物件的時候指定我們需要的資料型別即可
Order<String> o = new Order<String>();

注意:靜態方法不能使用類中的泛型(泛型只有例項化的時候由我們制定,而static類載入就載入,所以靜態方法不能使用泛型)。catch中也不能使用泛型

例如:以下兩種情況會報錯!!!

//編譯不通過
public static void show(T orderT){
    System.out.println(orderT);
}

try{
}catch(T t){
}

泛型在繼承上的體現

如果B是A的一個子型別(子類或者子介面),而G是具有泛型宣告的類或介面,G並不是G的子型別! 比如:String是Object的子類,但是List並不是List 的子類。

public void testGenericAndSubClass() {
    Person[] persons = null;
    Man[] mans = null;
    // 而 Person[] 是 Man[] 的父類.
    persons = mans;
    Person p = mans[0];
    // 在泛型的集合上
    List<Person> personList = null;
    List<Man> manList = null;
    // personList = manList;(報錯)
}
  • 在類/介面上宣告的泛型,在本類或本介面中即代表某種型別,可以作為非靜態屬性的型別、非靜態方法的引數型別、非靜態方法的返回值型別。但在靜態方法中不能使用類的泛型。
  • 異常類不能是泛型的
  • 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
  • 父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型型別:
    • 子類不保留父類的泛型:按需實現
    • 子類保留父類的泛型:泛型子類
class Father<T1, T2> {
}
// 子類不保留父類的泛型
// 1)沒有型別 擦除
class Son1 extends Father {// 等價於class Son extends Father<Object,Object>{
}
// 2)具體型別
class Son2 extends Father<Integer, String> {
}
// 子類保留父類的泛型
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2> extends Father<Integer, T2> {
}
====================================
class Father<T1, T2> {
}
// 子類不保留父類的泛型
// 1)沒有型別 擦除
class Son<A, B> extends Father{//等價於class Son extends Father<Object,Object>{
}
// 2)具體型別
class Son2<A, B> extends Father<Integer, String> {
}
// 子類保留父類的泛型
// 1)全部保留
class Son3<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2, A, B> extends Father<Integer, T2> {
}

結論:子類必須是“富二代”,子類除了指定或保留父類的泛型,還可以增加自己的泛型

方法,也可以被泛型化,不管此時定義在其中的類是不是泛型類。在泛型方法中可以定義泛型引數,此時,引數的型別就是傳入資料的型別。

泛型方法的格式: [訪問許可權] <泛型> 返回型別 方法名([泛型標識 引數名稱]) 丟擲的異常

例如:

public class DAO {
    public <E> E get(int id, E e) {
    	E result = null;
    	return result;
    }
}

泛型在集合中的使用

public void test1(){
    ArrayList list = new ArrayList();
    //需求:存放學生的成績
    list.add(78);
    list.add(76);
    list.add(89);
    list.add(88);
    //問題一:型別不安全
    //list.add("Tom");

    for(Object score : list){
        //問題二:強轉時,可能出現ClassCastException
        int stuScore = (Integer) score;
        System.out.println(stuScore);
    }
}

上面這兩問題就是不使用泛型造成的,使用泛型後可寫為:

public void test2(){
    ArrayList<Integer> list =  new ArrayList<Integer>();

    list.add(78);
    list.add(87);
    list.add(99);
    list.add(65);
    //編譯時,就會進行型別檢查,保證資料的安全
    //list.add("Tom");

    //方式一:
    for(Integer score : list){
          //避免了強轉操作
          int stuScore = score;
	      System.out.println(stuScore);
    }
    //方式二:
    Iterator<Integer> iterator = list.iterator();
    while(iterator.hasNext()){
        int stuScore = iterator.next();
        System.out.println(stuScore);
    }
}

集合中使用泛型總結:

  • 集合介面或集合類在jdk5.0時都修改為帶泛型的結構。
  • 在例項化集合類時,可以指明具體的泛型型別
  • 指明完以後,在集合類或介面中凡是定義類或介面時,內部結構(比如:方法、構造器、屬性等)使用到類的泛型的位置,都指定為例項化的泛型型別。
  • 泛型的型別必須是類,不能是基本資料型別。需要用到基本資料型別的位置,拿包裝類替換
  • 如果例項化時,沒指明泛型的型別。預設型別為java.lang.Object型別。

萬用字元( ? )

類A是類B的父類,G<A>和G<B>是沒關係的,二者共同的父類是:G<?>

涉及萬用字元的集合的資料的寫入和讀取:

  • 允許讀取資料,讀取的資料型別為Object。
  • 新增(寫入):不能向其內部新增資料。除了新增null之外。
public void test3(){
    List<Object> list1 = null;
    List<String> list2 = null;

    List<?> list = null;

    //編譯通過
    list = list1;
    list = list2;

    List<String> list3 = new ArrayList<>();
    list3.add("AA");
    list3.add("BB");
    list3.add("CC");
    list = list3;
    //新增(寫入):對於List<?>就不能向其內部新增資料。
    //除了新增null之外。
    // list.add("DD");
    // list.add('?');
    list.add(null);

    //獲取(讀取):允許讀取資料,讀取的資料型別為Object。
    Object o = list.get(0);
    System.out.println(o);
}

public void print(List<?> list){
    Iterator<?> iterator = list.iterator();
    while(iterator.hasNext()){
        Object obj = iterator.next();
        System.out.println(obj);
    }
}

有限制條件的萬用字元的使用:

/*
    限制條件的萬用字元的使用。
    ? extends A:
    上限extends:使用時指定的型別必須是繼承某個類,或者實現某個介面,即<=

    ? super A:
    下限super:使用時指定的型別不能小於操作的類,即>=
*/
@Test
public void test4(){

    //限定這個集合只可以放Person類或者Person的子類的資料
    List<? extends Person> list1 = null;
    限定這個集合只可以放Person類huo'zh的資料
    List<? super Person> list2 = null;

    List<Student> list3 = new ArrayList<Student>();
    List<Person> list4 = new ArrayList<Person>();
    List<Object> list5 = new ArrayList<Object>();

    list1 = list3;
    list1 = list4;
    //list1 = list5;

    //list2 = list3;
    list2 = list4;
    list2 = list5;

    //讀取資料:
    list1 = list3;
    Person p = list1.get(0);
    //編譯不通過
    //Student s = list1.get(0);

    list2 = list4;
    Object obj = list2.get(0);
    //編譯不通過
    //Person obj = list2.get(0);

    //寫入資料:
    //編譯不通過
    //list1.add(new Student());

    //編譯通過
    list2.add(new Person());
    list2.add(new Student());
}