Java筆記:數組,異常,泛型

1、數組java

數組也是一種引用類型,其父類是Object,使用「數據類型[]」聲明,如「int[] array」表示聲明瞭一個元素類型爲int類型的數組array。程序員

數組初始化語法:算法

// 靜態初始化語法,即定義的時候就初始化好了全部的元素
int[] array1 = {100, 55, 30};
// 動態初始化語法,初始化時只定義好數組中元素的個數,new int[5]表示建立一個
// 有5個int類型元素的數組,可是並無初始化數組中元素的值,只是賦予了默認值,即
// 基本數據類型的默認值和引用類型的默認值null。
int[] array2 = new int[5];

 

使用數組時,應注意如下幾點:數組

  • 數組是一種容器,數組當中能夠存儲基本數據類型的數據,也能夠存儲引用數據類型的數據。對於基本數據類型,數組中存儲的是數據的值,而引用類型,數組當中存儲的是對象的引用,即內存地址。
  • 數組由於是引用類型,因此數組對象是存儲在堆內存當中的。
  • 數組一旦建立,其長度是不可變的。
  • 數組中的元素的數據類型是統一的,如int數組則表示此數組中的元素所有都是int類型的。
  • 全部數組對象都有length屬性(注意不是length方法),用來獲取數組中元素的個數。
  • 數組中在存儲時,數組中的元素的內存地址都是連續的。
  • 數組對象的內存地址是數組中第一個元素所在的內存地址。
  • 使用下標訪問數組時,若是下標超出了數組的長度,則會發生ArrayIndexOutOfBoundsException異常。

數組的訪問和賦值:直接經過下標進行訪問和賦值便可,如「array1[0]=22;」。工具

數組的優勢和缺點:測試

  • 優勢:根據下標去檢索元素時效率極高,由於數組中的元素在空間地址上是連續的,而且每一個元素佔用的內存空間是相同的,檢索某個元素時只須要根據數組內存地址的起始位置就能夠算出這個元素的內存地址,因此檢索第100個元素和第100萬個元素的地址的時間都是同樣的。
  • 缺點:一個是,爲了保證數組中每一個元素的內存地址連續性,因此在數組中間的某個位置刪除或增長元素時,會涉及到元素的向前或者向後位移的操做,此時的效率就會極低。另一個是,數組不能存儲大數據量,由於很難在內存空間上找到一塊特別大的連續的內存空間。

數組擴容:Java中數組擴容的原理或者說方法是將小容量的數組使用「System.arraycopy」方法拷貝到大容量的數組當中,而後刪除小容量的數組,Java中數組的擴容效率是較低的,因此在數組的使用當中,應該儘可能避免數組的拷貝和擴容。大數據

二維數組:二維數組,包括三位數組等多位數組,其實就是數組中的元素又是一個數組,多少維其實由這個數組的元素「套了多少層」決定的,二維就是數組中的元素是一個一維數組,同理,三位數組中的元素是一個二維數組,而後以此類推便可。spa

// 靜態初始化語法
int[][] a = {
    {1, 2, 3},
    {4, 5, 6},
    {9}
};

// 動態初始化語法
int[][] array = new int[3][4];

 

Arrays工具類:這個工具類最經常使用的就是sort和binarySearch這兩個方法,可是注意,二分查找方法的前提是數組已經排好序了。code

import java.util.Arrays;


public class ArrayToolsTest{
    public static void main(String[] args){
        int[] myArray = {3, 2, 6, 4};
        // sort方法能夠將一個數組排序,可是注意,sort方法並無返回值,
        // 即不是返回排好序的數組,而是直接排序傳入的數組
        Arrays.sort(myArray);

        // 二分查找算法的前提是須要數組已經排好序了
        // 返回值爲元素在數組中的下標,元素在數組中不存在則返回-1
        // 可是這個方法多用來判斷數組中有沒有這個元素,由於若是數組中該元素
        // 不僅一個的話,那麼返回的下標不必定是第一個元素的下標
        int indexd = Arrays.binarySearch(myArray, 6);
    }
}

 

 

2、異常對象

一、對異常的理解

異常也是類,每個異常類均可以建立異常對象。

異常繼承結構:經過幫助文檔中能夠看到,java.lang.Object --> java.lang.Throwable,而Throwable下有兩個子類Error和Exception,它們都是能夠拋出的,對於這兩個子類分支,Error分支下的子類(包括Error自身)稱之爲錯誤,錯誤一旦發生,一般是直接就退出程序了,沒有辦法及時去處理。而Exception分支下的子類(包括Exception自身)稱之爲異常,異常是能夠在代碼層面提早去處理的,Exception的子類又能夠分爲兩個分支,一個分支是RuntimeException及其子類,稱爲運行時異常,另外一個分支就是除RuntimeException外的其它Exception的直接子類,也稱爲編譯時異常。

編譯時異常:之因此稱之爲編譯時異常,是由於在編譯階段就能夠發現並提醒程序員提早處理這種異常,對於這類異常怎麼提早去處理,仍是得要實際使用中多練纔能有更深的體會。

運行時異常:這類異常在編譯時不會報錯,可是編譯經過以後在運行時又會出錯,因此叫運行時異常,好比對於表達式10/0,除數爲0確定是錯的,可是編譯器並不會識別並提醒,編譯經過以後運行的時候確定就會報錯了。

異常處理:處理異常的方式有兩種,一種是使用throws關鍵字和throw關鍵字,將異常拋出給上一級,讓上一級去處理(上一級此時必須處理這個異常);另外一種是使用「try...catch」語句把異常捕獲,可是注意,捕獲到了這個異常不必定要去處理它,讓它直接「過」也是容許的。

二、throws拋出異常

throws使用示例:

public class ThrowsTest{
    public static void main(String[] args){
        // 這裏在編譯時會發生錯誤,也就是編譯時異常,之因此有這個異常
        // 由於在func定義中有throws關鍵字,表示這個方法在執行過程當中可能會發生
        // 對應的異常(ClassNotFoundException),因此它的上一級必須去處理
        // 這個異常,不處理的話,編譯器就會不經過。
        func();
    }
    
    // 使用throws關鍵字拋出可能發生的異常
    public static void func() throws ClassNotFoundException{
        System.out.println("my func!!!");
        
        // 使用throw手動拋出一個異常
        throw new ClassNotFoundException("未找到類異常!");
    }
}

 

關於throws的使用,須要注意:

  • throws拋出的異常,一般有兩種處理方式,一種是繼續使用throws關鍵字向上一級拋出一樣的異常,即調用者自身不處理這個異常,讓再上一級去處理。另外一種是使用try...catch語句去捕獲拋出的異常。
  • Java內置類或者咱們本身定義的類若是有使用throws關鍵字,就表示它是編譯時異常,使用的時候必需要去處理它。
  • throws拋異常時,既能夠拋出具體的異常,也能夠拋出它的某個父類異常,最頂級的異常類能夠是Exception類,它包含了全部異常。
  • 使用throws的時機就是若是這個異常但願調用者來處理,那麼就是用throws拋出它,其餘狀況應該使用try捕獲的方式。

三、try捕獲異常

try使用示例:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            // 將可能會發生異常的語句放在try的語句塊中
            // try語句塊中的代碼必定會去執行,直到發生異常爲止
            fis = new FileInputStream("Z:\\Study\\temp.md");
            System.out.println(10 / 0);
        } catch (ArithmeticException e) {  // 捕獲可能會發生的異常
            // 捕獲到異常後,對異常進行處理
            // catch語句塊中的代碼只有捕獲到異常以後纔會執行
            // ArithmeticException e這個語句至關因而聲明一種引用類型的變量,相似於方法的形參,e保存的是異常對象的內存地址,而且能夠在catch語句塊中去使用這個對象引用e。
            System.out.println("發生算術異常!!!");
        } catch (FileNotFoundException e) {  // 可使用多個catch語句塊捕獲不一樣的異常
            // 多個catch時會從上到下依次捕獲,執行了一個catch以後,其餘的catch就不會執行了
            // 而且多個catch語句時,應該遵循從上到下的「從小到大」捕獲,即具體異常在前,它的父類型依次日後
            System.out.println("文件不存在!!!");
        } finally {
            // finally塊中的語句不管是否發生異常都會處理,哪怕try塊最後有個return語句
            // 執行到return語句時也會先執行finally中的語句,再執行return
            // 能夠用來處理一些不管是否發生異常都要處理的操做,如關閉文件流等
            if (fis != null){
                try{
                    fis.close();
                } catch(IOException e) {
                    e.printStackTrace();
                }
                
            }
            System.out.println("關閉文件等其餘操做。。。");
        }
    }
}

 

注意,catch捕獲的異常能夠是具體的異常,也能夠是具體異常的父類型異常,此時能夠理解爲多態。

異常對象中的經常使用方法:

  • getMessage():獲取異常的簡單描述信息。
  • printStackTrace():打印異常追蹤的堆棧信息。

四、自定義異常

自定義的異常類須要繼承Exception或者RuntimeException,而且須要定義無參和有參兩個構造方法。

public class MyException extends Exception{
    public MyException{
        
    }
    public MyException(String s){
        super(s);
    }
}

自定義異常中的方法重寫或者覆蓋時須要注意一個語法問題,重寫的方法拋出的異常不能比父類的方法更大或者說更寬泛,只能更小或者說更具體,好比父類異常方法拋出了IOException異常,那麼異常子類中重寫這個方法時就不能拋出Exception異常,可是能夠拋出IOException異常或者FileNotFoundException異常。

 

3、泛型

泛型在使用尖括號「<標識符1,標識符2,...>」來表示,標識符表明的是某種類型。

泛型的做用實際上是用它定義了一個模板,定義時並無寫死數據的類型,當真正使用的時候能夠根據須要傳入本身的數據類型。

自定義泛型:

 /*
   自定一個泛型只須要在類名以後使用<標識符>便可
   注意,此處的標識符是隨意定義,就像變量名同樣
  */
 public class MyGenericTest<T> {
     public static void main(String[] args) {
         // 定義的時候,傳入的類型是什麼,那麼建立的對象使用的泛型類型就是什麼類型
         MyGenericTest<String> mgt = new MyGenericTest<>();
         mgt.func("自定義泛型方法測試!");
     }
 
     /*
       若是想要使用泛型定義的類型,在方法參數中直接使用便可
      */
     public void func(T t){
         System.out.println(t);
     }
 }

 

集合中泛型的應用:

 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
 
 public class GenericTest {
     public static void main(String[] args) {
         // 指定集合中的元素類型爲Pet,不能存儲其它類型的元素
         // 使用new的時候能夠不用再傳入類型了,能夠自動推斷,此時的表達式<>也稱爲鑽石表達式
         // 若是不指定泛型,也是能夠的,默認就是Object類型
         List<Pet> petList = new ArrayList<>();
         Cat c = new Cat();
         Dog d = new Dog();
 
         petList.add(c);
         petList.add(d);
 
         // 迭代器的聲明也須要加上泛型的定義
         Iterator<Pet> it = petList.iterator();
         while (it.hasNext()) {
             // 本來next方法返回值的類型爲Object,使用泛型以後返回的類型直接就是指定
             // 的類型,不須要進行類型轉換了。
             Pet p = it.next();
             p.play();
             // 固然,若是要使用具體的子類對象的方法,仍是須要轉型以後才能調用
             if (p instanceof Cat){
                 Cat myCat = (Cat)p;
                 myCat.sleep();
             }
             if (p instanceof Dog){
                 Dog myDog = (Dog)p;
                 myDog.bark();
             }
         }
         /*
         輸出結果:
         寵物在玩耍!
         貓咪在睡覺!
         寵物在玩耍!
         狗子在嚎叫!
         */
     }
 }
 
 
 class Pet {
     public void play() {
         System.out.println("寵物在玩耍!");
     }
 }
 
 
 class Cat extends Pet {
     public void sleep() {
         System.out.println("貓咪在睡覺!");
     }
 }
 
 
 class Dog extends Pet {
     public void bark() {
         System.out.println("狗子在嚎叫!");
     }
 }
相關文章
相關標籤/搜索