< 筆記 > Java SE - 03 Java SE 面向對象

03 Java SE 面向對象

By Kevin Song

  • 03-01 類與對象
  • 03-02 封裝
  • 03-03 構造方法
  • 03-04 單例設計模式
  • 03-05 繼承
  • 03-06 接口
  • 03-07 多態
  • 03-08 內部類
  • 03-09 異常
  • 03-10 Object類
  • 03-11 包

面向對象的四個基本特徵
- 封裝
- 繼承
- 抽象
- 多態

03-01 類與對象

類:事物的描述

對象:該類事物的實例

描述汽車

  • 屬性
    • 輪胎數
    • 顏色
  • 行爲
    • 運行
class Car {
    int num;//成員變量
    String color; //成員變量
    void run() { //成員方法
        System.out.println(num+"..."+color);
    }
}

class CarDemo {
public static void main(String[ args[]) {
        Car c = new Car();//c是一個類類型的引用變量,指向了該類的對象。
        c.num = 4;
        c.color = "Red";
        c.run();
    }
}

成員變量和局部變量的區別

  • 定義位置不同
    • 成員變量定義在中,整個類中都可以訪問
    • 局部變量定義在方法,語句,局部代碼塊中,只在所屬的區域有效
  • 存放內存不同
    • 成員變量存在於堆內存的對象中
    • 局部變量存在於棧內存的方法中
  • 釋放時間不同
    • 成員變量隨着對象的創建而存在,隨着對象的消失而消失
    • 局部變量隨着所屬區域的執行而存在,隨着所屬區域的結束而釋放
  • 初始化不同
    • 成員變量都有默認初始化值
    • 局部變量沒有默認初始化值

類類型參數使用

//騎車改裝廠
public static void show(Car c) { //類類型的變量一定指向對象,要不就是null。
    c.num = 3;
    c.color = "black";
    System.out.println(c.num+“...”+c.color);
}

匿名對象:當對象對方法僅調用一次,就可以簡化成匿名對象

new Car();//定義對象的簡寫格式
new Car().run();

03-02 封裝

隱藏對象的屬性和實現細節,僅對外提供公共訪問方式

class Person {
    private int age; //私有:只在本類中有效,外部無法訪問

    public void setAge(int a) {
        if (a>0 && a<130)
            age = a;
        else
            System.out.println("數據錯誤");
    }
    void speak() {
        System.out.println("age="+age);
    }
}
clasee PersonDemo {
    public static void main(String[] args) {
        Person p = new Person();
        p.setAge(20);
        p.speak();
    }
}

03-03 構造方法

構造對象的時候用的方法

特點:

  • 方法名與類名相同
  • 不用定義返回值類型
  • 沒有具體的返回值

作用:

單一對象進行初始化

默認構造方法:

一個類中如果沒有定義過構造方法,那麼該類中會有一個默認的空參數構造方法。

class Person {
    private int age;
    private String name;//私有:只在本類中有效,外部無法訪問

    //定義一個Person類的構造方法
    Person() {
        Syetem.out.println("person run");
    }

    //初始化就有名字
    Person(String n) {
    name = n;
    }

    //初始化就有名字和年齡
    Person(String n, int a) {
    name = n;
    age = a;
    }

    void speak() {
        System.out.println("age="+age);
    }
}
clasee PersonDemo {
    public static void main(String[] args) {
        Person p = new Person();//構造方法:構建創造對象時用的方法
        Person p1= new Person("就很舒服");//重載構造方法
        Person p2= new Person("就很舒服",10)//重載構造方法
    }
}

構造方法和一般方法的區別:

    • 構造方法:對象創建時,就會被調用,進行對象初始化
    • 一般方法:對象創建後,需要方法的功能時才調用
    • 構造方法:對象創建時,只調用一次
    • 一般方法:對象創建後,可以被重複調用

this關鍵字

當成員變量和局部變量重名,可以用關鍵字this區分

class Person {
    private int age;
    private String name;//私有:只在本類中有效,外部無法訪問

    //定義一個Person類的構造方法

    Person(String name) {
    this.name = name;//this代表當前對象
    }

    void speak() {
        System.out.println("age="+age);
    }
}

this關鍵字細節:

  1. 一般方法不能調用構造方法,因爲構造方法是給對象初始化,被對象所調用。
  2. 構造方法之間互相調用
Person(String name) {
    this.name = name;
}

Person(String name, int age) {
    this(name);//必須放在構造方法第一行
    this.age = age;
}

static關鍵字

  • static是一個修飾符,用於修飾成員(成員變量和成員方法)
  • static修飾的成員被所有對象共享
  • static**優先於**對象存在,因爲static成員隨着類創建而存在
  • static修飾的變量可以被類名直接調用Person.country

成員變量和靜態變量的區別:

String name;
static String country = "CN";
  • 兩個變量的生命週期不同
    • 成員變量隨着對象的創建而存在,隨着對象的被回收而釋放。
    • 靜態變量隨着的加載而存在,隨着類的消失而消失。
  • 調用方式不同
    • 成員變量只能被對象調用
    • 靜態變量可以被對象類名調用
  • 別名不同
    • 成員變量也稱爲實例變量
    • 靜態變量稱爲類變量
  • 數據存儲位置不同
    • 成員變量存儲在堆內存的對象中,是對象的特有數據
    • 靜態變量數據存儲在方法區,是對象的共享數據

static注意事項

  • 靜態方法只能訪問靜態成員
  • 靜態方法中不可以使用this或者super關鍵字
  • 主方法是靜態的

主方法解析

public static void main(String[] args)
  • public:因爲權限必須是最大
  • static:不需要對象的,直接用主方法所屬類名調用即可
  • void:主方法沒有具體的返回值
  • main:方法名,JVM識別的固定的名字
  • String[] args:主方法的參數列表,是一個數組類型的參數,元素都是字符串類型

static關鍵字使用場合

  • 靜態對象: 如果是相同的數據,對象不需要做修改,只需要使用即可,不需要存儲在對象中,這個變量可以用static修飾
  • 靜態方法: 如果方法不需要訪問非靜態的成員變量,可以定義成static

靜態代碼塊

特點:隨着類的加載而執行。而且只執行一次

作用:給初始化

class StaticCode {
    static {
        System.out.println("haha");
    }
}

構造代碼塊

{//構造代碼塊。可以給所有對象進行初始化
    System.out.println("Person run");
}
  • 構造方法給對應的對象進行鍼對性的初始化
  • 構造代碼塊給所有對象初始化

數組工具類

class ArraryToolDemo {
    public static void main(String[] args) {
        int[] arr = {3,5,7,72,6};
        int max = ArrayTool.getIndex(arr,10);//靜態方法可以直接被類名調用,不需要創建對象,節約內存空間
        System.out.println("max="+max);
    }
}
/**
建立一個用於操作數組的工具類,期中包含着對數組常見的操作
@author Kevin Song
@version V1.0
*/
class ArrayTool {
    private ArrayTool() {}//該類中的方法都是靜態的,所以該類不需要創建對象,爲了保證不讓其他人創建該類對象可以將構造方法私有化
    /**
    獲取整形數組的最大值
    @param arr 接收一個元素爲int類型的數組
    @return 該數組的最大的元素值
    */
    public static int getMax(int[] arr) {
        int max = arr[0];
        for (int x =1; x<arr.length; x++) {
            if (arr[x]>max)
                max = arr[x];
        }
        return max;
    }
}

03-04 單例設計模式

作用:可以保證一個類在內存中的對象唯一

實現思想:

  1. 不允許其他程序用new創建該類對象。
  2. 在該類創建一個本類實例
  3. 對外提供一個方法讓其他程序可以獲取該對象

步驟:

  1. 私有化該類構造方法
  2. 通過new在本類中創建一個本類對象
  3. 定義一個公有的方法,將創建的對象返回
//通過在類中創建一個對象供其他程序使用
//不允許其他程序自己new對象
//對該類對象的修改都是對同一個對象的修改

//餓漢式
class Single {//類一加載,對象就已經建立
    private static Single s = new Single();
    private Single(){}//私有構造方法不允許其他程序new對象
    public static Single getInstance() {
        return s;
    }
}
//懶漢式
class Single2 {//只有調用了getInstance方法纔會建立對象

    private static Single2 s = null;
    private Single2() {}//私有構造方法不允許其他程序new對象
    public static Single2 getInstance() {
        if(s==null)
            s = new Single();
        return s;
    }
}
class SingleDemo {
    public static void main(String[] args) {
        Single s = Single.getInstance();
    }
}

03-05 繼承

從已有的類中派生出新的類,新的類能吸收已有類的數據屬性和行爲,並能擴展新的能力。

class Person {
    String name;
    int age;
}
//繼承Person類的數據
class Student extends Person {
    void study() {
        System.out.println(name+"..student study.."+age);
    }
}
//繼承Person類的數據
class Worker extends Person {
    void work() {
        System.out.println(name+"..worker work.."+age);
    }
}
class ExtendsDemo {
    public static void main(String[] args) {
        Student s = new Student();
        s.name = "Kevin";
        s.age = 24;
        s.study;
    }
}

繼承的優點

  • 提高代碼的複用性
  • 讓類與類之間產生了關係,給第三個特徵多態提供了前提

Java中繼承的特點

Java中支持單繼承。不直接支持多繼承,但對C++中的多繼承機制進行改良

  • 單繼承:一個子類只能有一個直接父類
  • 多繼承:一個子類可以有多個直接父類(Java中用接口進行改良)不直接支持,因爲多個父類中有相同成員,會產生調用的不確定性

java支持多層(多重)繼承

子父類中成員的特點體現:

1. 成員變量

  • 本類的成員和局部變量同名用this區分本類成員變量
  • 子父類中的成員變量同名用super區分父類

原理

  • this:代表一個本類對象的引用
  • super:代表一個父類空間
class Fu {
    private int num = 4;//子類不能直接訪問父類中的私有內容
    //子類可以通過父類提供的方法間接訪問父類中的私有內容
    public int getNum() {
        return num;
    }
}
class Zi extends Fu {
    private int num = 5;
    void show() {
        System.out.println(this.num+"....."+super.getNum());
    }
}
class ExtendsDemo2 {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

2. 成員方法

方法的兩個特性

  • 重載(Overload):同一個類中,方法名相同參數列表不同的方法使用叫重載
  • 重寫(Override):在子父類中,出現成員方法完全相同的情況,會運行子類方法

重寫注意事項:

  • 子類方法重寫父類方法時,子類權限必須大於等於父類權限
  • 靜態只能覆蓋靜態,或被靜態覆蓋
class Fu {
    void show() {
        System.out.println("fu show run");
    }
}
class Zi extends Fu {
    void show() { //重寫父類方法
        System.out.println("zi show run");
        super.show();
    }
}
class ExtendsDemo3 {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

3. 構造方法

  • 子類構造對象時,訪問子類構造方法時,父類構造方法也會運行。
    原因:在子類的構造方法中第一行有一個默認的隱式語句。super();

  • 子類的實例化過程:子類中所有的構造方法默認都會訪問父類中的空參數的構造方法
    原因:子類繼承父類,獲取了父類中內容,需要了解父類是如何對內容進行初始化的。

  • 如果父類中沒有定義空參數構造方法,子類的構造方法必須在第一行super明確要調用父類中哪個構造方法。同時子類構方法數中如果使用this調用了本類構造方法時,super就沒有了,因爲super和this都只能定義在第一行

class Fu {
    Fu() {
        System.out.println("A fu run");
    }
    Fu(int x) {
        System.out.println("B fu run");
    }
}
class Zi extends Fu {
    Zi() {
        //super();隱式語句,調用父類中空參數的構造方法
        System.out.println("C zi run");
    }
    Zi(int x) {
        //super();隱式語句,調用父類中空參數的構造方法
        System.out.println("D zi run");
    }
}
class ExtendsDemo4 {
    public static void main(String[] args) {
        new Zi(6);//輸出結果A D
    }
}

對象實例化和初始化過程

一個對象實例化過程

Person p = new Person();
  1. JVM讀取指定路徑下的Person.class文件,並加載進內存,並會先加載*Person的父類*
  2. 堆內存中開闢空間,分配地址
  3. 在對象空間中,對對象中的屬性進行默認初始化
  4. 調用對應的構造方法進行初始化
  5. 在構造方法中第一行調用父類中的構造方法進行初始化
  6. 父類初始化完畢後對子類的屬性進行顯式初始化
  7. 進行子類構造方法的特定初始化
  8. 初始化完畢後,將地址值複製給引用變量

對象的初始化過程:

  1. 加載子類構造方法
  2. super();加載父類構造方法
  3. 父類構造方法調用被子類重寫的方法
  4. 顯示初始化
  5. 構造代碼塊初始化
  6. 子類構造方法具體初始化
class Fu {
    Fu() {
        System.out,println("fu constructor run");
        show();
    }
    void show() {
        System.out.println("hehe");
    }
}
class Zi extends Fu {
    int num = 9; 
    { //第五步 構造代碼快初始化
        System.out.println("constructor code..."+num);
    }
    Zi() { //第一步
        super; //第二步
        //第四步 顯示初始化 
        System.out.println("zi constructor..."+ num); //第六步
    }
    void show() { //第三步
        System.out.println("show..."+num);
    }
}

final關鍵字

  • final是一個修飾符,可以修飾類,方法,變量
  • final修飾的類不可以繼承
  • final修飾的方法不可以重寫
  • final修飾的變量是一個常量,只能被賦值一次
  • 內部類只能訪問被final修飾的局部變量
class Fu { //final修飾的類不可以被繼承
    void method() { //final修飾的方法不可以被重寫
        //調用底層系統資源
    }
}
class Zi extends Fu {
    void method() {
        public static final double MY_PI = 3.14; //final修飾的變量是一個常量,只能被賦值一次
        System.out.println(MY_PI);
    }
}
class FinalDemo {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

常量命名規則

全部大寫,不同單詞之間用_分隔。例:MY_PI

全局變量:public static修飾的變量

public static int num = 9;

全局常量:public static final修飾的變量

public static final double MY_PI = 3.14;

抽象類

當一個類描述一個事物時,沒有足夠的信息描述事物,就是抽象類

abstract class Pet { //含有抽象方法的類是抽象類
    abstract void voice();//抽象修飾的方法
}
class Dog extends Pet{
    void voice() {
        System.out.println("woof")
    }
}
class Cat extends Pet{
    void voice() {
        System.out.println("meow")
    }
}
class AbstractDemo {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

抽象類特點

  1. 方法只有聲明沒有方法體 { } ,需要被abstract修飾,抽象方法必須定義在抽象類
  2. 抽象類不能被實例化
  3. 抽象類中的抽象方法必須被子類全部重寫後纔可以被實例化,否則這個子類也是抽象類

抽象類細節

  1. 抽象類中有構造方法,因爲需要給子類對象初始化
  2. 抽象類中可以不定義抽象方法,但是很少見,目的是不讓這個類創建對象
  3. abstract關鍵字不可以和這些關鍵字共存
    • private:因爲子類需要重寫抽象父類,private無法訪問
    • static:靜態成員可以直接被類名調用
    • final:final類無法被繼承而抽象類必須被繼承,final方法無法被重寫而抽象方法必須被重寫
  4. 抽象類和一般類的異同
    • 相同點:抽象類和一般類都用來描述事物
    • 不同點
      • 一般類有足夠的信息描述事物
        抽象類描述事物的信息有可能不足
      • 一般類中不能定義抽象方法
        抽象類中可以定義抽象和非抽象方法
      • 一般類可以被實例化
        抽象類不可以被實例化
  5. 抽象類一定是父類,因爲需要子類覆蓋其方法後纔可以對子類實例化。

示例:

/*
員工:
    屬性:姓名,工號,薪水
    行爲:工作。
程序員:
    屬性:姓名,工號,薪水。
    行爲:工作。
經理:
    屬性:姓名,工號,薪水,獎金。
    行爲:工作。
*/
abstract class Employee {
    private String name;
    private String id;
    private double pay;
    Employee(String name, String id, double pay) {
        this name = name;
        this id = id;
        this pay = pay;
    }
    public abstract void work(); //沒有方法體的抽象方法
}
class Programmer extends Employee { //描述程序員
    Programmer(String name, String id, double pay) {
        super(name, id, pay);
    }
    public void work() { //加入方法體,重寫父類方法
        System.out.println("programming");
    }
}
class Manager extends Employee { //描述經理
    Manager(String name, String id, double pay, int bonus) {
        super(name, id, pay);
        this.bonus = bonus;
    }
    public void work() { //加入方法體,重寫父類方法
        System.out.println("managing");
    }
}
class AbstractTest {
    public static void main(String[] args) {
        System.out.println("Hello World");
        Programmer Kevin = new Programmer(Kevin, 1110009945, 7330);
    }
}

03-06 接口

interface {}

抽象類中的方法都是抽象的抽象類就可以定義爲接口

接口中的成員都是公共權限:

  1. 全局常量:
public static final int NUM = 3;
  1. 抽象方法:
public abstract void show(); //返回值類型void可以改變
  • 類與類之間是繼承關係
  • 類與接口之間是實現關係
  • 接口與接口之間是繼承關係,而且接口之間可以多繼承

接口不可以實例化

只能由實現了接口的子類重寫了接口中所有的方法後,該子類纔可以實例化,否則這個子類是一個抽象類

interface Demo {
    public static final int NUM = 3; //全局常量
    public abstract void show1(); //抽象方法
}
class DemoImp1 implements Demo {
    public static final int NUM = 3; 
    public abstract void show1(){}
}
class InterfaceDemo {
    public static void main(String[] args) {
        DemoImp1 d = new DemoImp1();
        System.out.println(d.NUM);
        System.out.println(DemoImp1.NUM);
        System.out.println(Demo.NUM);
}

接口多實現

接口的方法前修飾符是固定的都是public abstract 返回值類型,因此只要兩個接口中的方法名相同,多實現不會存在不確定性

interface A {
    public void show();
}
interface B {
    public void show();
}
class Test implements A, Z {
    public void show(){} //接口都是抽象方法,不存在不確定性,因此可以多實現
}

一個類實現另一個類的同時,還可以實現多個接口,避免了單繼承的侷限性

class Test2 extends Q implements A,Z {}

接口和抽象類的異同點

相同點:都是向上抽取而來

不同點

與子類的關係:

  • 抽象類需要被繼承,而且只能多繼承
  • 接口需要被實現,而且可以多實現

方法的定義:

  • 抽象類中可以定義抽象與非抽象方法,子類繼承後可以直接使用非抽象方法
  • 接口中只能定義抽象方法,必須由子類去實現

性質

  • 抽象類中的繼承是is a關係,在定義該體系的基本共性內容
  • 接口的實現是 like a 關係,在定義體系額外功能

示例:

//Dog Classification: GuideDog, DetectDog
abstract class Dog {
    abstract void bark();
}
interface Guide {
    abstract void guide();
}
class GuideDog extends Dog implement Guide{
    public void bark() {}
    public void guide() {}
}

接口的應用

/*
Laptop Usage
爲了擴展筆記本的功能,定義一個規則,以後不論什麼設備只要符合這個規則就可以使用
*/
interface USB {
    public void open();
    public void close();
}
//一年後,出現了U盤和鼠標
//實現規則
class Udisk implement USB {
    public void open() {
        System.out.println("Udisk Open");
    }
    public void open() {
        System.out.println("Udisk Open");
    }
}
class UsbMouse implement USB {
    public void open() {
        System.out.println("UsbMouse Open");
    }
    public void open() {
        System.out.println("UsbMouse Open");
    }
}
class Laptop {
    public static void main(String[] args) {
        useUSB(new Udisk());
        useUSB(new UsbMouse());
    }
    //使用規則
    public static void useUSB(USB u) { //接口類型的引用,用於指向接口的子類對象
        u.open();
        u.close();
    }
}

03-07 多態

定義:某一類事物的多種存在形態

體現:父類或者接口的引用指向其子類的對象

Fu z = new Zi();

多態優缺點

  • 優點:提高了代碼的擴展性,前期定義的代碼可以使用後期的內容
  • 缺點:父類引用不能調用子類的特有內容

多態的前提

  • 必須有關係,繼承或者實現
  • 必須要重寫
/*
狗具備狗形態,也具備動物形態
貓具備貓形態,也具備動物形態
*/
abstract class Animal {
    abstract void eat();
}
class Dog extends Animal {
    void eat() {
        System.out.println("eat bone");
    }
    void housekeep() {
        System.out.println("house keeping");
    }
}
class Cat extends Animal {
    void eat() {
        System.out.println("eat fish");
    }
    void catchMouse() {
        System.out.println("catching mouse");
    }
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Animal c = new Cat();//父類引用指向子類對象
        Animal d = new Dog();//父類引用指向子類對象
        method(c);
        method(d);
    public static void method(Animal a) {
        a.eat(); //Animal父類特有內容可以直接調用
        if(a instanceof Cat) { //判斷對象是否爲貓類,如果是
            Cat c = (Cat)a; //轉換成貓類
            c.catchMouse(); //調用貓類特有方法
        } else if(a instanceof Dog) { //判斷對象是否爲狗類,如果是
            Dog d = (Dog)a; //轉換成狗類
            d.housekeep(); //調用狗類特有方法
        }
    }
}

轉型

  • 向上轉型:限制對特有功能的訪問
  • 向下轉型:可以使用特有功能

父類引用指向子類對象時,對象只能訪問子父類共有內容。

Animal c = new Cat();//向上轉型,貓對象提升到動物類型
c.eat();//只能訪問共有功能,不能訪問特有功能
Cat c = (Cat)a;//向下轉型,貓對象降低到貓類
c.catchMouse//可以訪問特有功能

類型判斷

instanceof:用於判斷對象的具體類型。只能用於引用數據類型判斷

public static void method(Animal a) {
    a.eat(); //Animal父類特有內容可以直接調用
    if(a instanceof Cat) { //判斷對象是否爲貓類,如果是
        Cat c = (Cat)a; //轉換成貓類
        c.catchMouse(); //調用貓類特有方法
    } else if(a instanceof Dog) {
        Dog d = (Dog)a;
        d.housekeep();
    }
}

多態中的成員特點

成員變量

  • 編譯時,參考引用型變量所屬的類中是否有調用的成員變量。有,編譯通過;沒有,編譯失敗。
  • 運行時,參考引用型變量所屬的類中是否有調用的成員變量。並運行該所屬類中的成員變量
  • 總結:參考引用型變量所屬的類中的成員變量

示例:

class Fu {
    int num = 3;//如果父類沒有num,則編譯失敗
}
class Zi extends Fu {
    int num = 5;
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);// 輸出3
    }
}

成員方法

  • 編譯時,參考引用型變量所屬的類中是否有調用的方法。有,編譯通過;沒有,編譯失敗。
  • 運行時,參考的是對象所屬的類中是否有調用的方法。並運行該所屬類中的成員變量
  • 總結:編譯看左邊,運行看右邊

寫程序時只能調用父類的方法,實際運行時調用的是子類重寫父類後的方法。

class Fu {
    void show() {
        System.put.println("fu show");
    }
}
class Zi extends Fu {
    void show() {
        System.put.println("zi show");
    }
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Fu f = new Zi();
        f.show();//輸出"zi show"
    }
}

靜態方法

  • 編譯時,參考引用型變量所屬的類中是否有調用的靜態方法
  • 運行時,參考引用型變量所屬的類中是否有調用的靜態方法
  • 總結:編譯運行都看左邊

其實對於靜態方法,是不需要對象的,可以直接被類名調用

class Fu {
    static void method() {
        System.put.println("fu static");
    }
}
class Zi extends Fu {
    static void method() {
        System.out.println("zi static");
    }
}
class DuoTaiDemo {
    public static void main(String[] args) {
        Fu f = new Zi();
        f.method();//輸出"fu static"
    }
}

03-08 內部類

定義在一個類內部的類,又稱爲內置類,嵌套類

內部類訪問特點

  • 內部類可以直接訪問外部類中的成員
  • 外部類需要建立內部類的對象才能訪問內部類成員
class Outer {
    private int num = 3; //內部類可以直接訪問私有內容
    static class Inner { //內部類
        void show() {
            System.out.println("show run.."+num);
        }
        //如果內部類中方法是靜態的,則內部類也是靜態的
        static void staticShow() { //靜態直接調用,不需要對象
            System.out.println("staticShow run.."+num);
        }
    }
    //外部類需要創建內部類對象才能訪問非靜態內部類內容
    public void method() { 
        Inner in = new Inner();
        in.show();
    }
}
class InnerClassDemo {
    public static void main(String[] args) {
        //直接訪問外部類的成員
        Outer out = new Outer();
        o.method;

        //直接訪問外部類中的內部類中的成員
        Outer.Inner in = new Outer().new Inner();
        in.show();

        //如果內部類是靜態的,相當於一個外部類
        Outer.Inner in = new Outer.Inner();
        in.show();

        //如果內部類是靜態的,成員也是靜態的,直接調用,不需要對象
        Outer.Inner.staticShow();
    }
}

內部類可以存放在局部位置上

內部類在局部位置上只能訪問局部中被final修飾的變量

class Outer {
    int num = 3;
    void method() {
        final int x = 9; //內部類中訪問的局部變量必須是final
        class Inner {
            void show() {
                System.out.println("show..."+x);
            }
        Inner in = new Inner();
        in.show();
        }
    }
}

匿名內部類

內部類的簡寫格式

new 父類or接口() {
    子類內容;
}

前提:內部類必須繼承一個外部類,或者實現一個接口

匿名內部類其實就是一個子類對象

abstract class Demo {
    abstract void show();
}
class Outer {
    int num = 4;

    //匿名內部類不能有名字,所以不能這樣定義
    //class Inner extends Demo {
    //    void show() {
    //        System.out.println("show..."+num);
    //    }
    //}

    public void method() { 
        //相當於 new Inner().show();
        new Demo() { //創建匿名內部類
            void show() { //重寫Demo抽象方法
                System.out.println("show..."+num);
            }
        }.show();
    }
}
class InnerClassDemo {
    public static void main(String[] args) {
        new Outer().method();
    }
}

匿名內部類應用

當方法參數是接口類型時,而且接口中的方法不超過三個,可以用匿名內部類作爲實際參數進行傳遞

interface Inter {
    void show1();
    void show2();
}
class Outer {
    //class Inner implements Inter {
    //    public void show1() {}
    //    public void show2() {}
    //}
    public void method() {
    //    Inner in = new Inner();
    //    in.show1();
    //    in.show2();
        Inter in = new Inter() { //重寫接口方法,創建匿名內部類對象
            public void show1() {}
            public void show2() {}
        }
        in.show1;
        in.show2;
    }
}

03-09 異常

異常:運行時發生的不正常情況,根據面向對象的思想被java封裝成了對象

異常類:在java中用類的形式對不正常的情況進行了描述和封裝對象,描述不正常的情況的類,就稱爲異常類。

class ExceptionDemo {
    public static void main(Stirng[] args) {
        int[] arr = new int[3];
        arr = null;
        System.out.println(arr[3]);
    }
    public static void sleep(int time) {
        if(time < 0) {
            //拋出一個對象
            new FuTime();//新建一個負time對象,這個對象中包含問題的名稱,信息,位置等信息
        }
        if (time > 100000) {
            //拋出一個對象
            new BigTime();
        }
    }
}

異常體系

不正常的情況分成兩類

Throwable(可拋出):可以被throwsthrow兩個關鍵字操作的類和對象都具備可拋性

  • Error:不可處理的問題
    特點:由JVM拋出的嚴重性的問題,這種問題一般不針對處理,直接修改代碼
  • Exception:可處理的問題

該體系的特點:子類的後綴名都是用其父類名作爲後綴

ArrayIndexOutOfBoundsException

異常處理方式

自動拋出異常

//自動拋出異常:
class Demo {
    public void method(int[] arr, int index) {  
        //JVM在這裏封裝了一個對象然後丟出:throw new ArrayIndexOutOfBoundsException(index)
        System.out.println(arr[index]);
    }
}
class ExceptionDemo {
    public static void main(Stirng[] args) {
        int[] arr = new int[3];
        Demo d = new Demo();
        d.method(arr,3);
    }
}

手動拋出異常:

自定義異常:java中沒有定義過的異常,按照java異常的創建思想和麪向對象的思想,對異常進行自定義描述,並封裝成對象。

自定義異常類:自定義異常類必須繼承異常體系,因爲只有稱爲異常體系的子類才具有可拋性,纔可以被throw,throws操作。要麼繼承Exception,要麼繼承RuntimeException

  • 繼承Exception或者其子類(編譯時異常):
    • 聲明:只聲明不捕捉,無法通過編譯,可以自定義報錯的信息
      • 自定義異常類,繼承Exception或者其子類
      • 通過throws 類名 在方法名後聲明自定義異常類
      • 通過throw new 類名(); 創建對象
      • 如果需要程序通過編譯正常運行,需要用Try-Catch對拋出的異常進行處理
    • 捕捉:try-catch捕捉處理異常,處理完輸出提示信息並且程序繼續運行
  • 繼承RuntimeException(運行時異常):繼承了RuntimeException的子類可以不用Try-Catch或者throws就可以通過編譯。

throw:異常拋出的關鍵字
throws:聲明自定義異常類的關鍵字

throws和throw的區別

  • throws使用在方法上
    throw使用在方法內
  • throws拋出的是異常類,可以拋出多個,用逗號隔開
    throw拋出的是對象

Exception類的所有子類中分爲兩種

  • 編譯時異常:其他子類
  • 運行時異常:RuntimeException和其子類

異常聲明

只聲明,不處理的異常,無法通過編譯,可以自定義報錯信息

//手動拋出異常
/*
角標爲負的異常在java中沒有定義過,需要自定義
*/
class FuShuIndexException extends Exception {
    FuShuIndexException() {}//空參數構造方法
    FuShuIndexException(String msg) {
        super(msg);//直接調用父類構造方法
    }
}
class Demooo {
    //創建自定義異常類對象的方法需要用throws聲明自定義異常
    public int method(int[] arr, int index) throws FuShuIndexException {
        if(index>=arr.length) {
            //手動拋出異常
            throw new ArrayIndexOutOfBoundsException("數組角標越界"+index);
        } else if(index<0) {
            throw new FuShuIndexException("數組角標不能是負數"+index);
        }
        return arr[index];
    }
}
class ExceptionDemo {
    //有自定義異常的主方法需要用throws聲明自定義異常
    public static void main(String[] args) throws FuShuIndexException {
        int[] arr = new int[3];
        Demooo d = new Demooo();
        d.method(arr,3);
    }
}

異常捕捉

try-catch處理完程序可以繼續運行

try {
    需要被檢測異常的代碼
}
catch(FuShuIndexException e) {
    處理異常的代碼
}
catch(Exception e) { //多個catch,父類catch一定要放在後面
    處理異常的代碼
}
finally {
    一定會被執行的代碼
}

try-catch-finally代碼塊特點

  • try catch finally
  • try catch:當沒有資源需要釋放時,可以不用finally
  • try finally:異常無法直接catch處理,但是資源需要關閉
class FuShuIndexException extends Exception {
    FuShuIndexException() {}//空參數構造方法
    FuShuIndexException(String msg) {
        super(msg);//直接調用父類構造方法
    }
}
class Demoo {
    public int method(int[] arr, int index) throws FuShuIndexException {
            if(index<0) {
               throw new FuShuIndexException("Index must be positive");
            }
        return arr[index];
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = new int[3];
        Demoo d = new Demoo();
        try {
            int num = d.method(arr, -3);
            System.out.println("num=" + num);
        } catch (FuShuIndexException e) { 
            //FuShuIndexException e = new FuShuIndexException("Index must be positive")
            //獲得自定義的異常提示信息
            System.out.println("Message:"+e.getMessage());
            //獲得字符串形式的自定義異常類信息
            System.out.println("String:"+e.toString());
            //獲得出現異常的代碼位置
            e.printStackTrace();
            System.out.println("負數角標異常");
        }
        System.out.println("over");
    }
}
/*
輸出:
Message:Index must be positive
FuShuIndexException: Index must be positive
String:FuShuIndexException: Index must be positive
    at Demoo.method(ExceptionDemo.java:10)
負數角標異常
    at ExceptionDemo.main(ExceptionDemo.java:20)
over
*/

異常處理的原則

  • 方法內如果拋出(throw)需要檢測的異常,那麼方法名後面必須要聲明(throws),否則必須在方法內部用try-catch捕捉,否則編譯失敗。
  • 如果調用到了聲明異常的方法,要麼try-catch,要麼throws,否則編譯失敗
  • 功能內容可以解決,就用try-catch。解決不了,用throws讓調用者解決。
  • 一個方法如果拋出多個異常,那麼調用時,必須有對應多個catch進行鍼對性處理。

finally代碼塊

通常用於關閉(釋放)資源,比如關閉數據庫連接

class Demo {
    public void show(int index) throws ArrayIndexOutOfBoundsException :
        if(index < 0)
            throw new ArrayIndexOutOfBoundsException("越界");
        int[] arr = new int[3];
        return arr[index];
    }
}
class ExceptionDemo {
    public static void main(String[] args) {
        Demo d = new Demo();
        try {
            int num = d.show(-3);
            System.out.println("num"+num);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.toString());
            //return; 如果加了return;則不會運行over
            //System.exit(o);如果加了這句,則不會運行finally代碼塊,直接退出JVM
        } finally { //一定會被執行,除非System.exit(0);退出JVM
            System.out.println("finally");
        }
        System.out.println("over");
    }
}

異常應用

//自定義藍屏異常類
class BlueScreenException extends Exception {
    BlueScreenException(String msg) {
        Super(msg);
    }
}
//自定義冒煙異常類
class MaoYanException extends Exception {
    MaoYanException(String msg) {
        Super(msg);
    }
}
//自定義無法處理異常類
class NoPlanException extends Exception {
    NoPlanException(String msg) {
        Super(msg);
    }
}
class Computer {
    private int state = 0;
    //定義電腦運行功能
    public void run() throws BlueScreenException, MaoYanException { 
        if(state == 1) { //狀態值爲1時電腦藍屏
            throw new BlueScreenException("藍屏");
        } else if(state == 2)
            throw new MaoYanException("冒煙");
        System.out.println("電腦運行");
    }
    //定義電腦重啓功能
    public void reboot() {
        state = 0; //重置電腦到正常狀態
        System.out.println("重啓");
    }
}
class Teacher {
    private String name;
    private Computer comp;
    Teacher(String name) {
        this.name = name;
    }
    public void teach() throws NoPlanException { 
        try {
            comp.run(); //電腦運行
            System.out.println("講課"); //正常講課
        } catch (BlueScreenException e) {
            System.out.println(e.toString());
            comp.reset();
            teach();
        } catch (MaoYanException e) {
            System.out.println(e.toString());
            test();
            //異常轉換:收到冒煙異常,轉換成NoPlan異常對外進行告知
            throw new NoPlanException("課時進度無法完成");
        }
    }
    public void test() {
        System.out.println("大家練習");
    }
}
class ExceptionTest {
    public static void main(String[] args) {
        Teacher t = new Teacher("Kevin");
        try (
            t.teach();
        ) catch(NoPlanException e) {
            System.out.println(e.toString()+"...");
            System.out.println("換老師");
        }
    }
}

異常的注意事項:

  • 子類在重寫父類方法時,父類的方法如果跑出了異常,那麼子類的方法只能拋出父類的異常或者該異常的子類
  • 如果父類拋出多個異常,那麼子類只能拋出父類異常的子集

03-10 Object類

Object類是所有類的根類,是不斷抽取而來,具備所有對象都具備的共性內容

Object類常用方法

equals方法

當兩個引用值指向同一個對象時才返回true

class Person extends Object {
    private int age;
    Person(int age) {
        this.age = age;
    }
}
Class EqualsDemo {
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();
        Person p3 = p1;

        System.out.println(p1 == p2);//false
        System.out.println(p1.equals(p2));//false
        System.out.println(p1.equals(p3));//true
    }
}

equals方法重寫

equals方法一般都會被重寫,根據對象的特有內容,建立判斷對象是否相同的依據

class Person extends Object {
    private int age;
    Person(int age) {
        this.age = age;
    }
    public boolean equals(Object obj) {//重寫Object類中的equals方法,進行對象年齡比較
        if(!(obj instanceof Person)) {
            return false;
            throw new ClassCastException("類型錯誤");
        }
        Person p = (Person)obj;
        return this.age == p.age;
    }
}
Class EqualsDemo {
    public static void main(String[] args) {
        Person p1 = new Person(20);
        Person p2 = new Person(20);

        System.out.println(p1 == p2);//false
        System.out.println(p1.equals(p2));//true
        System.out.println(p1.equals(new Demo()));//報錯,類型錯誤
    }
}

hashCode方法

判斷兩個對象是否完全相同

class Person extends Object {
    private int age;
    Person(int age) {
        this.age = age;
    }
    public int hashCode() { //重寫object類中的hashCode方法,輸出年齡
        return age;
    }
}
Class EqualsDemo {
    public static void main(String[] args) {
        Person p1 = new Person(20);
        Person p2 = new Person(20);

        System.out.println(p1);//輸出p1的哈希值
        System.out.println(p1.hashCode());//輸出p1的哈希值
        System.out.println(Integer.toHexString(p1.hashCode()));//輸出p1的十六進制哈希值

    }
}

getClass方法

獲取當前對象所屬字節碼對象

Class clazz1 = p1.getClass();
Class clazz1 = p2.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz1.getNma());//Person

toString方法

返回對象的字符串表示形式,它的值等於:

getClass().getNmae() + '@' + Integer.toHexString(hashCode())

建議重寫該方法

public String toString() {
    return "Person: " + age;
}

System.out.println(p1);//Person: 20
System.out.println(p1.getClass().getNmae() + '@' + Integer.toHexString(hashCode()));////Person@61de33

03-11 包

對類文件進行分類管理

package allpack.boypack.mypack;

class PackageDemo {
    public static void main(String[] args) {
        System.out.println("Hello Package!");
    }

}
//javac -d . PackageDemo.java
//java mypack.PackageDemo

包與包之間的訪問

包與包之間的類進行訪問,被訪問的包中的類必須是public的,被訪問的包中的類的方法也必須是public

package packa;

public class DemoA { //包與包之間訪問必須是public
    public void show() //包與包之間訪問必須是public{
        System.out.println("demoa show run");
    }
}
package packb;

public class DemoA { //包與包之間訪問必須是public
    protected void method() { //protected只能被繼承了父類的訪問
        System.out.println("demoa show run");
    }
}
package mypack;

class PackageDemo {
    public static void main(String[] args) {
        packa.DemoA d = new packa DemoA();
        d.show();
        System.out.println("Hello Package!");
    }
}
位置 public protected default private
同一類中 ok ok ok ok
同一包中 ok ok ok
子類中 ok ok
不同包中 ok

導入import

package packa;

public class DemoA { //包與包之間訪問必須是public
    public void show() //包與包之間訪問必須是public{
        System.out.println("demoa show run");
    }
}
package packb;

public class DemoA { //包與包之間訪問必須是public
    protected void method() {
        System.out.println("demoa show run");
    }
}
package mypack;
import packa.*//導入了packa包中所有的類
class PackageDemo {
    public static void main(String[] args) {
        packa.DemoA d = new packa DemoA();
        d.show();
        System.out.println("Hello Package!");
    }
}

Jar包
Java的壓縮包

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章