1. 程式人生 > 其它 >3.陣列、面向物件和異常(狂神說)

3.陣列、面向物件和異常(狂神說)

Java陣列

陣列的定義

  • 陣列是相同型別資料的有序集合
  • 陣列描述的是相同型別的若干資料,按照一定先後次序排序組合而成
  • 其中,每一個數據稱作一個數組元素,每個陣列元素可以通過下標訪問它們

陣列的宣告建立

  • 首先必須宣告陣列變數,才能在程式中使用陣列。
dataType[] arrayRefVar; //首選
dataType arrayRefVar[]; //效果相同,但不是首選
  • Java語言使用new操作符來建立陣列,語法如下
dataType[] arrayRefVar = new dataType[arraySize]; //int[] nums=new int[10]
  • 陣列的元素是通過索引訪問的,陣列索引從0開始
  • 獲取陣列長度:arrays.length
int[] nums; //1.宣告一個數組
nums = new int[3]; //2.建立一個數組
//3.給陣列元素賦值
nums[0]=1;
nums[1]=2;
nums[2]=3;
for (int num : nums) { //列印陣列所有元素
    System.out.println(num);
}

記憶體分析

陣列的三種初始化

  • 靜態初始化
//靜態初始化:建立+賦值
int[] a={1,2,3};
Man[] mans={new Man(1,1),new Man(2,2)}
  • 動態初始化
//包含預設初始化
int[] a=new int[2]; //預設值為0
a[0]=1;
a[1]=2;

預設初始化

  • 陣列是引用型別,它的元素相當於類的例項變數,因此陣列一經分配空間,其中的每個元素也被按照例項變數同樣的方式被隱式初始化。

陣列的基本特點

  1. 其長度是確定的,陣列一旦被建立,它的大小就是不可改變的。

  2. 其元素必須是相同型別,不允許出現混合型別。

  3. 陣列中的元素可以是任何資料型別,包括基本型別和引用型別。

  4. 陣列變數屬於引用型別,陣列也可以看作物件,其中每個元素相當於該物件的成員變數。

    陣列本身就是物件,Java中物件是在堆中的,因此陣列無論儲存原始型別還是其他物件型別,

    陣列本身是在堆中的。

陣列的使用

  • For-Each迴圈
int[] arrays = {1,2,3,4,5};
//列印全部的陣列元素 JDK1.5 沒有下標
for (int array : arrays) {
    System.out.println(array);
}
  • 陣列作方法入參
//列印陣列元素
public static void printArray(int[] a){
    for (int i = 0; i < a.length; i++) {
        System.out.print(a[i]+" ");
    }
}
  • 陣列作返回值
//反轉陣列
public static int[] reverse(int[] arrays){
    int[] result = new int[arrays.length];
    //反轉的操作
    for (int i = 0; i < arrays.length; i++) {
        result[i] = arrays[arrays.length-i-1];
    }
    return result;
}

多維陣列

  • 多維陣列可以看成陣列的陣列,比如二維陣列就是一個特殊的陣列,其每一個元素都是一個一維陣列。
int arr[][] = new int[3][2]; //二維陣列,三行兩列

Arrays類

  • 陣列的工具類java.util.Arrays

  • 由於陣列物件本身並沒有什麼方法可以供我們使用,但API提供了一個工具類Arrays供我們使用。

  • Array類中的方法都是static修飾的靜態方法,使用時直接使用類名進行呼叫,可以不用物件呼叫。

  • 常用功能

    • 給陣列賦值:fill方法。
    • 排序:sort方法,升序。
    • 比較陣列:equals方法比較陣列中元素值是否相等。
    • 查詢陣列元素:binarySearch對排序好的陣列進行二分查詢法操作。
    int[] a = {1,2,3,4,9000,32145,451,21};
    System.out.println(a); // [I@28d93b30 (hashcode)
    
    //Arrays.toString 列印陣列元素
    System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 9000, 32145, 451, 21]
    
    //二分法查詢某值 返回下標
    System.out.println(Arrays.binarySearch(a, 9000)); // 4
    
    //填充
    Arrays.fill(a,2,4,0); //陣列[a[2]~a[4])之間填充0
    System.out.println(Arrays.toString(a)); //[1, 2, 0, 0, 9000, 32145, 451, 21]
    
    //升序排序
    Arrays.sort(a);
    
    

稀疏陣列

//建立一個二維陣列 11*11  0:沒有棋子,1:黑棋  2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
//輸出原始的陣列
System.out.println("原始的陣列:");
for (int[] array : array1) {
    for (int i : array) {
        System.out.print(i+"\t");
    }
    System.out.println();
}

//轉換為稀疏陣列儲存
//1.有效值的個數
int sum = 0; //有效值總數
for (int i = 0; i < 11; i++) {
    for (int j = 0; j < 11; j++) {
        if(array1[i][j]!=0){
            sum++;
        }
    }
}
//2.建立一個稀疏陣列
int[][] array2 = new int[sum+1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;

//3.遍歷二維陣列,將有效值存放到稀疏陣列
int count = 0;
for (int i = 0; i < array1.length; i++) {
    for (int j = 0; j < array1[i].length; j++) {
        if(array1[i][j]!=0){
            count++;
            array2[count][0] = i;
            array2[count][1] = j;
            array2[count][2] = array1[i][j];
        }
    }
}

//4.輸出稀疏陣列
System.out.println("稀疏陣列:");
for (int i = 0; i < array2.length; i++) {
    for (int j = 0; j < array2[i].length; j++) {
        System.out.print(array2[i][j]+"\t");
    }
    System.out.println();
}
/* 結果:
輸出原始的陣列
0	0	0	0	0	0	0	0	0	0	0	
0	0	1	0	0	0	0	0	0	0	0	
0	0	0	2	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
稀疏陣列
11	11	2	
1	2	1	
2	3	2	
*/

面向物件

  • 面向物件程式設計(Object-Oriented Programming, OOP)
  • 本質:以類的方式組織程式碼,以物件的組織(封裝)資料。
  • 抽象
  • 三大特性
    • 封裝
    • 繼承
    • 多型
  • 從認識論的角度考慮是先有物件後有類。物件是具體的事物,類是物件的抽象。
  • 從程式碼執行角度考慮是先有類後有物件。類是物件的模板

建立與初始化物件

  • 使用new來建立物件。
  • 使用new關鍵字建立的時候,除了分配記憶體之外,還會給建立好的物件進行預設的初始化,以及對類中構造器的呼叫。
  • 類中的構造器也被稱為構造方法,建立物件時必須要呼叫。有以下特點:
    • 必須和類的名字相同
    • 沒有返回型別,也不能寫void
  • 一個類即使什麼都不寫,也會存在一個預設的構造方法

構造器

public class Person {
    //一個類即使什麼都不寫,也會存在一個預設的無參構造方法
    //顯示地定義構造器
    String name;
    
    //作用:1. 使用new關鍵字,本質是在呼叫構造器
    //2. 用來初始化物件的值
    public Person(){} //無參構造
    
    //有參構造 3.一旦定義了有參構造,無參就必須顯示定義
    public Person(String name){
        this.name=name;
    }
	//Alt+insert 快捷鍵插入構造方法
}

記憶體分析

//定義一個寵物類
public class Pet {
    public String name; //預設 null
    public int age; 	//預設 0
    //無參構造

    public void shout(){
        System.out.println("叫了一聲");
    }
}
//應用類,建立呼叫物件
public class Application {
    public static void main(String[] args) {
        
        Pet dog = new Pet();

        dog.name = "旺財";
        dog.age = 3;
        dog.shout();
    }
}
  • 物件通過引用型別來操作:棧 - - ->堆

封裝

  • 該露的露,該藏的藏
    • 我們程式設計要追求“高內聚,低耦合”。高內聚就是類的內部資料細節由自己完成,不允許外部干涉;低耦合:僅暴露少量的方法給外部使用
  • 封裝(資料的隱藏)
    • 通常,應禁止直接訪問一個物件中資料的實際表示,而應通過操作介面來訪問,稱為資訊隱藏。
  • 作用
  1. 提高程式的安全性,保護資料
  2. 隱藏程式碼的實現細節
  3. 統一介面
  4. 系統可維護性增加了

繼承

  • 繼承的本質是對某一批類的抽象,從而實現對世界更好地建模。
  • extends的意思是”擴充套件“。子類是父類的擴充套件,使用關鍵字extends來表示。
  • Java中類只有單繼承,沒有多繼承!一個類只能繼承一個父類。
  • 繼承是類與類之間的一種關係,此外還有依賴、組合、聚合等。
  • 繼承關係的兩個類,一個為子類(派生類),一個為父類(基類)子類繼承父類。
  • 子類和父類之間,從意義上講應該具有”is a“的關係。
//學生類(子類)繼承 人類(父類)
public class Student extends Person{ /*Person extends Object*/
    ...
}
  • 子類繼承了父類,就會擁有父類的全部方法,而private私有屬性及方法無法繼承
  • 在Java中,所有類,都預設直接或間接繼承Object類 (Ctrl+H 可以檢視類關係)
  • 被final修飾的類,無法被繼承(斷子絕孫)。

super & this

  1. super()呼叫父類的構造方法,必須在構造方法的第一個
  2. super必須只能出現在子類的方法或構造方法中
  3. super()this()不能同時呼叫構造方法,因為this也必須寫在第一行
  • super與this的區別:super代表父類物件的引用,只能在繼承條件下使用;this呼叫自身物件,沒有繼承也可以使用。
super(); //隱藏程式碼,預設呼叫了父類的無參構造,要寫只能寫第一行

方法的重寫

  • 重寫:子類的方法必須與父類方法必須一致,方法體不同。
  • 重寫是方法的重寫,與屬性無關
  • 重寫方法只與非靜態方法有關,與靜態方法無關(靜態方法不能被重寫)
  • 靜態方法屬於類,非靜態方法屬於物件
  • 注意點:
  1. 方法名、引數列表必須相同
  2. 修飾符範圍可以擴大,不能縮小(public>protect>private)
  3. 丟擲的異常 範圍可以被縮小,不能擴大
  4. static(屬於類,不屬於例項),final(常量方法),private(私有)修飾的方法不能重寫

多型

  • 動態編譯:型別

  • 即同一方法可以根據傳送物件的不同而採用不同的行為方式

  • 一個物件的實際型別是確定的,但可以指向物件的引用可以有很多

  • 多型存在條件

    • 有繼承關係
    • 子類重寫父類方法
    • 父類引用指向子類物件

注意點:

  1. 多型是方法的多型,沒有屬性的多型
  2. 父類和子類,有聯絡 型別轉換異常: ClassCastException
  3. 存在條件:繼承關係,方法需要重寫,父類引用指向子類物件!

instanceof和型別轉換

  • instanceof 引用型別比較,判斷一個物件是什麼型別
public static void main(String[] args) {

    // Object > String
    // Objest > Person > Student
    // Objest > Person > Teacher
    Object object = new Student();
	// X instanceof Y,X引用指向的物件是不是Y的子類
    System.out.println(object instanceof Student); //true
    System.out.println(object instanceof Person); //true
    System.out.println(object instanceof Teacher); //false
    System.out.println(object instanceof Object); //true
    System.out.println(object instanceof String); //false
	
    //型別之間的轉化:父-子(高-低),低可以轉換為高
    Person obj = new Syudent(); //只能用Person方法(重寫了用子類重寫過的方法)
    (Student)obj.go(); //強轉之後可以用Student方法(Student->go())
}

型別轉換

  1. 父類引用指向子類的物件
  2. 把子類轉換為父類,向上轉型,會丟失自己原來的一些方法
  3. 把父類轉換為子類,向下轉型,強制轉換,才呼叫子類方法
  4. 方便方法的呼叫(轉型),減少重複的程式碼,簡潔。

Static

  • 靜態變數可以直接用類名訪問,也稱類變數。
  • 靜態變數(或方法)對於類,所有物件(例項)所共享。
  • 靜態區程式碼 載入類時一起被初始化,最早執行且只執行一次(第一次new)。
  • Math->隨機數:
//靜態匯入包
import static java.lang.Math.random;

public class Application {
    public static void main(String[] args) {

        //第一種隨機數,不用導包
        System.out.println(Math.random()); //0.7562202902634543

        //第二種隨機數,靜態匯入包
        System.out.println(random()); //0.5391606223844663
    }
}

在建立物件是,執行順序:靜態程式碼塊(最早,但只執行一次)、匿名程式碼塊、構造方法;

抽象類(abstract)

  • abstract修飾的類就是抽象類,修飾的方法就是抽象方法。
  • 抽象類中可以沒有抽象方法,但有抽象方法的類一定要宣告為抽象類。
  • 抽象類不能使用new來建立物件,它是用來讓子類繼承的。
  • 抽象方法只有方法的宣告,沒有實現,讓其子類實現。
  • 子類繼承抽象類,必須實現抽象類的所有方法,否則該子類也要宣告為抽象類。
//abstract 抽象類 類只能單繼承(介面可以多繼承)
public abstract class Action {

    //約束~有人幫我們實現~
    //抽象方法只有方法名,沒有方法的實現
    public abstract void doSth();

    //1.不能new抽象類,只能靠子類去實現它,僅作為一個約束
    //2.抽象方法只能出現在抽象類中,抽象類可以有普通方法
    //3.抽象類有構造器,可以派生子類
    //4.抽象類的意義:約束,提高開發效率。但是類只能單繼承,所以有侷限 用的不多
}

介面(interface)

  • 普通類:只有具體實現

  • 抽象類:具體實現和規範(抽象方法)都有

  • 介面:只有規範,沒有方法實現,專業的約束!約束與實現分離:面向介面程式設計~

  • 介面就是規範,定義的是一組規則,"你是什麼…必須做什麼…"的思想。

  • 介面的本質是約束,就像人間法律一樣,制定好大家都遵守。

//interface介面,介面都要有繼承類
//實現類(implements 可以繼承多個介面)
//多繼承,利用介面實現多繼承
public interface UserService {
    //定義的屬性都是常量,預設修飾 public static final
    public static final int AGE = 99; //一般不用
    //所有的定義的方法都是抽象的 預設public abstract
    public abstract void run();
    void add();
    void query();
    void delete();
}

注意點

  • 介面沒有構造方法,不能被例項化
  • 實現類必須要重寫介面中的方法
  • 實現類(implements) 可以實現多個介面

內部類

  • 內部類就是在一個類的內部再定義一個類,比如A類中定義了一個B類,那麼B就是A的內部類,而A相對B來說就是外部類
    1. 成員內部類:可以操作外部類的私有屬性及方法
    2. 靜態內部類:static修飾,不能訪問外部類私有屬性
    3. 區域性內部類:外部類的方法裡定義的類
    4. 匿名內部類:沒有名字初始化類

異常

  • 軟體程式在執行過程中,經常可能遇到異常問題,異常英文(Exception),意思是例外,這些例外情況需要我們寫程式做出合理的處理,而不至於讓程式崩潰。
  • 異常指程式執行中出現的不期而至的各種狀況:檔案找不到,網路連線錯誤,非法引數等。
  • 異常發生在程式執行期間,它影響了正常的執行流程。

簡單分類

  • 檢查型異常:最具代表性異常是使用者錯誤或問題引起的異常,這是程式設計師無法預見的。例如使用者要開啟一個不存在的檔案時引發的異常,這些異常在編譯時不能被簡單地忽略。
  • 執行時異常:是可能被程式設計師避免的異常,與檢查性異常相反,執行時異常可以在編譯時忽略。
  • 錯誤Error:錯誤不是異常,而是脫離程式設計師控制的問題。錯誤在程式碼經常被忽略。例如當棧溢位,一個異常就發生了,它們在編譯也檢查不到。

異常處理機制

  • 丟擲異常
  • 捕獲異常
  • 異常處理關鍵字:try、catch、finally、throw、throws
public static void main(String[] args) {
    int a = 1;
    int b = 0;

    try { //try監控區域
        System.out.println(a/b);
    }catch (ArithmeticException e){ //catch 捕獲異常
        System.out.println("程式出現異常,變數b不能為0");
    }catch (Exception e){
        e.printStackTrace();
    }finally { //一定會執行,處理善後工作,如關閉資源
        System.out.println("finally");
    }
    
    if(b==0){ //丟擲異常一般在方法中使用
        throw new ArithmeticException(); //主動丟擲異常
    }
}
//Ctrl+Alt+T 快捷鍵插入 try-catch

自定義異常