1. 程式人生 > >面向物件(下)

面向物件(下)

目錄

1. 繼承

1.1 概述

2. 多型

3. 抽象類

4. 介面

4.1 概述

5.4 導包

7. 內部類

7.1 概述

1. 繼承

1.1 概述

多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,只要繼承那個類即可。

單獨的這個類稱為父類,基類或者超類;這多個類可以稱為子類或者派生類。 有了繼承以後,我們定義一個類的時候,可以在一個已經存在的類的基礎上,還可以定義自己的新成員。

通過extends關鍵字可以實現類與類的繼承

格式:class 子類名 extends 父類名 {}  

案例1:用繼承改進學生老師案例

//多個類同屬性同行為的一般寫法
//學生類
class Student{
	public void eat(){
		System.out.println("吃飯");
	}
	
	public void sleep(){
		System.out.println("睡覺");
	}
}

//老師類
class Teacher{
	public void eat(){
		System.out.println("吃飯");
	}
	
	public void sleep(){
		System.out.println("睡覺");
	}
}

//測試類
class ExtendsDemo{
	public static void main(String[] args){
		//建立學生類物件
		Student s = new Student();
		//通過物件呼叫學生類中的方法
		s.eat();
		s.sleep();
		System.out.println("-----");
		
		//建立老師類物件
		Teacher t = new Teacher();
		t.eat();
		t.sleep();
	}
}


//用繼承改進
//父類
class Person{
	public void eat(){
		System.out.println("吃飯");
	}
	
	public void sleep(){
		System.out.println("睡覺");
	}
}

//子類
class Student extends Person{}

class Teacher extends Person{}


//測試類
class ExtendsDemo{
	public static void main(String[] args){
		//建立學生類物件
		Student s = new Student();
		//通過物件呼叫學生類中的方法
		s.eat();
		s.sleep();
		System.out.println("-----");
		
		//建立老師類物件
		Teacher t = new Teacher();
		t.eat();
		t.sleep();
	}
}

1.2 繼承的優缺點

1.2.1 優點

  1. 提高了程式碼的複用性——多個類相同的成員可以放到同一個類中
  2. 提高了程式碼的維護性——如果功能的程式碼需要修改,修改一處即可
  3. 讓類與類之間產生了關係,是多型的前提。

1.2.2 缺點

類與類之間產生了關係,這也是繼承的一個弊端:類的耦合性很強

開發原則:低耦合,高內聚

解釋:耦合——類與類之間的關係;內聚——自己完成某件事情的能力

1.3 Java中繼承的特點

1. Java只支援單繼承,不支援多繼承

即,一個類只能繼承一個父類,不能有多個父類。有的語言是可以多繼承的,格式為:extends 父類1,父類2,……

2. Java支援多層繼承(繼承體系)

//格式:
class A{}
class B extends A{}
class C extends B{}

//在C類中建立物件,可以通過物件直接呼叫B類和A類中的方法

1.4 Java中繼承的注意事項

  1. 子類只能繼承父類所有非私有的成員(成員方法和成員變數)——也體現了另一個弊端:打破了封裝性
  2. 子類不能繼承父類的構造方法,但是可以通過super(後面講)關鍵字去訪問父類構造方法。
  3. 不要為了部分功能而去繼承 ,繼承中類之間體現的是:”is a”的關係,即子類是父類的一種

1.5 繼承中類的各組成部分關係

1.5.1 繼承中成員變數之間的關係

  • 子類中的成員變數的名稱和父類中成員變數不一樣
  • 子類中的成員變數的名稱和父類中成員變數一樣時,其值採用就近原則

名稱一樣時,在子類方法中訪問一個變數的查詢順序:

  1. 首先在子類方法的區域性範圍找,有就使用;
  2. 然後在子類的成員範圍找 ,有就使用;
  3. 最後在父類成員範圍找(不能訪問到父類區域性範圍,因為方法不呼叫不執行) ,有就使用;
  4. 如果還是沒有就報錯

super關鍵字和this關鍵字的區別

案例2:this和super的應用和輸出區別

//this和super的區別

//父類
class Father{
	int num = 10;
}

//子類
class Son extends Father{
	int num = 20;
	
	//show方法
	public void show(){
		int num = 30;
		System.out.println(num);//輸出30
		System.out.println(this.num);//輸出20
		System.out.println(super.num);//輸出10
	}
}

class ExtendsDemo1{
	public static void main(String[] args){
		int num = 30;
		//建立子類物件
		Son s = new Son();
		//呼叫show方法
		s.show();
	}
}

this:代表本類對應的引用

super:代表父類儲存空間的標識(可以理解為父類引用)

this  和  super  的使用
this 解釋 super 解釋
訪問成員變數 this.成員變數 呼叫本類的成員方法 super.成員變數 呼叫父類的成員變數
訪問構造方法 this(...) 呼叫本類的構造方法,()裡為引數 super(...) 呼叫父類的構造方法,()裡為引數
訪問成員方法 this.成員方法() 呼叫本類的成員方法 super.成員方法() 呼叫父類的成員方法

1.5.2 繼承中構造方法之間的關係

  • 子類中所有的構造方法預設都會訪問父類中空引數的構造方法

案例3:子類構造方法和父類構造方法的關係

//子類構造方法和父類構造方法的關係
//父類
class Father{
	//父類無參構造方法,構造方法的方法名與該類的類名相同
	public Father(){
		System.out.println("這是父類的無參構造方法");
	}
	
	//父類帶參構造方法
	public Father(String name){
		System.out.println("這是父類的帶參構造方法");
	}
}

//子類
class Son extends Father{
	//子類無參構造方法,構造方法的方法名與該類的類名相同
	public Son(){
                //super();//預設呼叫父類的無參構造方法
		System.out.println("這是子類的無參構造方法");
	}
	
	//子類帶參構造方法
	public Son(String name){
                //super();//預設呼叫父類的無參構造方法
		System.out.println("這是子類的帶參構造方法");
	}
}

class ExtendsDemo2{
	public static void main(String[] args){
		//建立子類物件
		Son s = new Son();
		
		System.out.println("-----------");
		
		//建立帶參的子類物件
		Son s1 = new Son("卡卡西");
	}
}輸出:
這是父類的無參構造方法
這是子類的無參構造方法
-----------
這是父類的無參構造方法
這是子類的帶參構造方法

原因:

因為子類會繼承父類中的資料,可能還會使用父類的資料。所以,子類初始化之前,一定要先完成父類資料的初始化

注意:

子類每一個構造方法的第一條語句預設都是:super();

注意事項:

1. 子類的構造方法預設會去訪問父類的無參構造方法                 是為了子類訪問父類資料的初始化

2. 父類中如果沒有無參構造方法(只給了帶參構造方法),怎麼辦?                 a. 子類通過super去明確呼叫帶參構造                 b. 子類通過this呼叫本身的其他構造,但是一定會有一個去訪問了父類的構造,讓父類提供無參構造

練習題1:

//以下程式碼的輸出結果是?
class Fu{
	public int num = 10;
	public Fu(){
		System.out.println("fu");
	}
}
class Zi extends Fu{
	public int num = 20;
	public Zi(){
		System.out.println("zi");
	}
	public void show(){
		int num = 30;
		System.out.println(num);
		System.out.println(this.num);
		System.out.println(super.num);
	}
}
class Test {
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show();
	}
}
//輸出結果:
fu
zi
30
20
10

練習題2:

要點:

  1. 一個類的靜態程式碼塊、構造程式碼塊、構造方法執行流程——靜態程式碼塊 > 構造程式碼塊 > 構造方法
  2. 靜態的內容是隨著類的載入而載入;
  3. 子類初始化之前會進行父類的初始化。
class Fu {
	static {
		System.out.println("靜態程式碼塊Fu");
	}

	{
		System.out.println("構造程式碼塊Fu");
	}

	public Fu() {
		System.out.println("構造方法Fu");
	}
}

class Zi extends Fu {
	static {
		System.out.println("靜態程式碼塊Zi");
	}
	
	{
		System.out.println("構造程式碼塊Zi");
	}

	public Zi() {
		System.out.println("構造方法Zi");
	}
}

class Test1 {
	public static void main(String[] args) {
		Zi z = new Zi();
	}
}
//輸出結果
靜態程式碼塊Fu
靜態程式碼塊Zi
構造程式碼塊Fu
構造方法Fu
構造程式碼塊Zi
構造方法Zi

練習題3:

要點:

  1. 成員變數型別(基本型和引用型)
  2. 一個類的初始化過程
    1. 成員變數的初始化(預設初始化→顯示初始化→構造方法初始化)
  3. 子父類的初始化(又稱分層初始化)
    1. 先進行父類初始化,再進行子類初始化
class X {
	Y b = new Y();
	X() {
		System.out.print("X");
	}
}

class Y {
	Y() {
		System.out.print("Y");
	}
}

public class Z extends X {
	Y y = new Y();
	Z() {
		System.out.print("Z");
	}
	public static void main(String[] args) {
		new Z(); 
	}
}
//輸出結果:
YXYZ

1.5.3 繼承中成員方法之間的關係

  • 子類中的方法和父類中的方法宣告不一樣
  • 子類中的方法和父類中的方法宣告一樣時,先看子類中有無,有就使用;沒有就在父類中找

案例4:

class Father{
	public void show(){
		System.out.println("Show Father");
	}
}
class Son extends Father{
	public void method(){
		System.out.println("method Son");
	}
	public void show(){
		System.out.println("Show Son");
	}
}
class ExtendsDemo3{
	public static void main(String[] args){
		Son s = new Son();
		s.show();
		s.method();
	}
}
輸出結果:
Show Son
method Son

1.6 方法重寫

1.6.1 概述

子類中出現了和父類中一模一樣的方法宣告,也被稱為方法覆蓋,方法複寫 

1.6.2 使用特點

  1. 如果方法名不同,就呼叫對應的方法
  2. 如果方法名相同,最終使用的是子類自己的

1.6.3 注意事項

  1. 父類中私有方法不能被重寫
  2. 子類重寫父類方法時,訪問許可權不能更低
  3. 父類靜態方法,子類也必須通過靜態方法進行重寫(這個算不上方法重寫)

兩個思考題: 1. Override和Overload的區別?Overload是否可以改變返回值型別?

答:

  • 方法重寫——在子類中出現和父類一模一樣的方法宣告的現象
  • 方法過載——同一個類中出現的方法名相同,引數列表不同的現象,能改變返回值,因為與返回值型別無關

2. this和super的區別和各自的作用?

  • this——代表當前類的物件的引用
  • super——代表父類儲存空間的標識

作用見1.5.3節的表格

1.7 final關鍵字

1.7.1 概述

final關鍵字是最終的意思,可以修飾類,成員變數,成員方法

1.7.2 特點

  • 修飾類——被修飾的類為最終類,最終類不能被繼承(不能有子類)
  • 修飾方法——被final修飾的方法不能被重寫(覆蓋)
  • 修飾變數——被修飾的變數不能被重新賦值,其實就是常量(為自定義常量,之前學的為字面值常量)

1.7.3 兩個問題

1. final修飾區域性變數的問題

  • 在方法內部,該變數不可以被改變
  • 在方法宣告上,引數型別
    • 為基本型別,值不能被改變,
    • 為引用型別,地址值不能被改變 

2. final修飾變數的初始化時機

  • final修飾的變數只能賦值一次
  • 在物件構造完畢前即可

2. 多型

2.1 多型的概述

某一個物件(事物),在不同時刻表現出來的不同狀態

舉例:水的不同狀態

2.2 多型的前提和體現

前提:

  1. 要有繼承關係
  2. 要有方法重寫
  3. 要有父類引用指向子類物件( 父 f = new 子(); )

2.3 多型中的成員訪問特點

2.3.1 成員變數

編譯看左邊,執行看左邊

2.3.2 構造方法

建立子類物件的時候,訪問父類的構造方法,對父類的資料進行初始化

2.3.3 成員方法

編譯看左邊,執行看右邊(方法重寫覆蓋了)

2.3.4 靜態方法

編譯看左邊,執行看左邊(靜態隨著類的載入而載入)

2.4 多型的利弊

利:

  1. 提高了程式碼的維護性(由繼承保證)
  2. 提高了程式碼的擴充套件性(由多型保證)

弊:

  1. 不能使用子類中的特有功能

2.5 多型中的轉型問題

要想使用子類中的特有功能,有兩種方法:

  1. 建立子類物件呼叫方法即可(佔記憶體)
  2. 將父類的引用強制轉換為子類的引用(向下轉型)——Zi z = (Zi) f;

2.5.1 向上轉型

Fu f = new Zi();//左邊為大類,右邊為小類

2.5.2 向下轉型

Zi z = (Zi) f;——要求該f必須是能轉換為Zi的

2.6 多型的練習

案例:貓狗案例

//貓狗案例
class Animal{
	public void eat(){
		System.out.println("吃飯");
	}
}

//狗類繼承動物類
class Dog extends Animal{
	public void eat(){
		System.out.println("狗吃肉");
	}
	public void lookDoor(){
		System.out.println("狗看門");
	}
}

//貓繼承動物類
class Cat extends Animal{
	public void eat(){
		System.out.println("貓吃魚");
	}
	public void playGame(){
		System.out.println("貓玩捉迷藏");
	}
}

//測試類
class DuoTaiTest{
	public static void main(String[] args){
		//Animal類中的引用a指向Dog類,定義為狗
		Animal a = new Dog();
		a.eat();
		System.out.println("-------------");
		
		//建立Dog中的物件d,將a強制轉換使用Dog類中的特有功能,還原成狗
		Dog d = (Dog) a;
		d.eat();
		d.lookDoor();
		System.out.println("-------------");
		
		//定義為貓
		a = new Cat();
		a.eat();
		System.out.println("-------------");
		
		//還原為貓
		Cat c = (Cat)a;
		c.eat();
		c.playGame();
	}
}
輸出:
狗吃肉
-------------
狗吃肉
狗看門
-------------
貓吃魚
-------------
貓吃魚
貓玩捉迷藏

3. 抽象類

3.1 抽象類的概述

把多個共性的東西提取到一個類中,這是繼承的做法。但是,這多個共性的東西,在有些時候,方法宣告一樣,但是方法體。也就是說,方法宣告一樣,但是每個具體的物件在具體實現的時候內容不一樣。所以,我們在定義這些共性的方法的時候,就不能給出具體的方法體。        而一個沒有具體的方法體的方法是抽象的方法(注意沒有方法體和空方法體的區別)。        在一個類中如果有抽象方法,該類必須定義為抽象類。

3.2 抽象類的特點

  • 抽象類和抽象方法必須用abstract關鍵字修飾
格式:
abstract class 類名 {}
public abstract void eat(); 
  • 抽象類不一定有抽象方法,有抽象方法的類必須是抽象類
  • 抽象類不能例項化,因為它不是具體的。按照多型的方式,由具體的子類例項化。其實這也是多型的一種,抽象類多型 
  • 抽象類的子類,要麼是抽象類(不重寫抽象方法),要麼重寫抽象類中的所有抽象方法(此時子類為具體類)

3.3 抽象類的成員特點

成員變數

既可以是變數,又可以是常量

構造方法

有構造方法,用於子類訪問父類資料的初始化

成員方法

既可以是抽象方法,也可以是非抽象方法

抽象方法——強制要求子類做的事

非抽象方法——子類繼承的事情,提高程式碼複用性

4. 介面

4.1 概述

為了體現事物功能的擴充套件性,Java中就提供了介面來定義這些額外功能,並不給出具體實現

4.2 介面的特點

  • 介面用關鍵字interface修飾
格式:interface 介面名 {}
  • 類實現介面用implements修飾
格式:class 類名 implements 介面名 {}
  • 介面不能例項化(用多型的方式來實現例項化)

多型的三種方式:

  1. 具體類多型(幾乎沒有)
  2. 抽象類多型
  3. 介面多型(最常見)
  • 介面的子類
    • 是一個抽象類,意義不大
    • 是一個具體類,這個類必須重寫介面中的所有抽象方法

案例1:

/*
介面特點:
	介面用關鍵字interface表示
		格式:interface 介面名 {}
	類實現介面用implements表示
		格式:class 類名 implements 介面名 {}

*/
//貓狗案例
//定義動物培訓介面
interface AnimalTrain {
	//定義抽象類,無方法體
	public abstract void jump();
	
}

//抽象類實現介面,可以,但是意義不大
abstract class Dog implements AnimalTrain {
	
} 

//具體類實現介面
class Cat implements AnimalTrain {
	//重寫方法
	public void jump(){
		System.out.println("小貓釣魚");
	}
}
class InterfaceDemo {
	public static void main(String[] args){
		/*
		在類AnimalTrain中建立物件at報錯,因為AnimalTrain是抽象類,無法例項化
		AnimalTrain at = new AnimalTrain();
		at.jump();
		*/
		AnimalTrain at = new Cat();
		at.jump();
	}
}

4.3 介面的成員特點

成員變數

  1. 介面中的變數預設為常量,且是靜態的
  2. 有預設修飾符:public static final
  3. 預設修飾符建議自己手動給出

構造方法

  1. 介面沒有構造方法,因為介面主要功能為擴充套件,沒具體存在
  2. 其實現介面的具體類有構造方法,預設繼承於Object類
  • 所有的類都預設繼承自一個類:Object

成員方法

  1. 只能是抽象方法
  2. 預設修飾符:public abstract
  3. 預設修飾符建議自己手動給出

案例2:

//介面的成員特點
//定義Inter介面
interface Inter {
	//定義成員變數
	public int num = 10;
	public final int num1 = 20;
	
	//給介面寫構造方法,編譯報錯
	//因為介面沒有構造方法
	//public Inter(){}//錯誤: 需要<識別符號>
	
	//寫成員方法
	//public void show(){}//錯誤: 介面抽象方法不能帶有主體
	abstract void show();//預設許可權為public
}

/*
//具體類InterImpl實現介面
//開發中,介面名+Impl這種格式是介面的實現類格式
class InterImpl implements Inter {
	//給實現類寫構造方法,編譯不報錯。因為所有的類都預設繼承自一個類:Object
	public InterImpl(){
		//訪問父類構造方法
		super();
	}
}
*/

//具體類InterImpl實現介面(預設繼承於Object類)
class InterImpl extends Object implements Inter {
	//給實現類寫構造方法,編譯不報錯
	//因為所有的類都預設繼承自一個類:Object
	public InterImpl(){
		//訪問父類構造方法
		super();
	}
	//具體類中的成員方法
	public void show(){}//不寫public報錯:正在嘗試分配更低的訪問許可權; 以前為public
}

//測試類
class InterfaceDemo1 {
	public static void main(String[] args) {
		//建立物件i
		Inter i = new InterImpl();
		
		//多型中訪問成員變數是訪問的左邊(只有成員方法是訪問的右邊,因為方法重寫)
		System.out.println(i.num);//輸出10
		System.out.println(i.num1);//輸出20
		
		//i.num = 100;// 錯誤: 無法為最終變數num分配值
		//i.num1 = 200;// 錯誤: 無法為最終變數num1分配值
		
		System.out.println(Inter.num);//通過介面名字訪問變數,輸出10
		System.out.println(Inter.num1);//通過介面名字訪問變數,輸出20
		
	}
}

4.2 類與類、類與介面、介面與介面的關係

4.2.1 類與類的關係

繼承關係(extends):只能單繼承,可以多層繼承

4.2.2 類與介面的關係

實現關係(interface定義介面,implements實現介面):

可以單實現,也可以多實現(介面之間用逗號隔開即可,呼叫的時候注意方法與介面對應)

還可以在繼承一個類的同時實現多個介面(聯想預設繼承類Object)

4.2.3 介面與介面的關係

繼承關係,可以單繼承,也可以多繼承

抽象類和介面的區別:

A:成員區別
    抽象類: 
        成員變數:可以是常量,也可以是變數
        構造方法:有
        成員方法:可以使抽象,也可以是非抽象

    介面:
        成員變數:只能是常量
        成員方法:只能是抽象的

B:關係區別
    類與類:
        繼承,只能單繼承,不能多繼承

    類與介面:
        實現,可以單實現,也可以多實現

    介面與介面:
        繼承,可以單繼承,也可以多繼承

C:設計理念區別
    抽象類:
        被繼承體現的是is a的關係
        抽象類中定義的是該繼承體系中的共性功能
    介面:
        被繼承體現的是like a的關係
        介面中定義的是該繼承體系中的擴充套件功能

4.3 形式引數和返回值的問題

形式引數:

  1. 基本型別
  2. 引用型別(類、抽象類、介面)

案例1:類名作為形參,需要的是該類的物件

class Student{
	public void study(){
		System.out.println("好好學習,天天向上");
	}
}

class StudentDemo{
	public void method(Student s){
		s.study();
	}
}
//測試類
class StudentTest{
	public static void main(String[] args){
		//測試Student中的study方法,在Student類中建立物件s
		Student s = new Student();
		//通過該類中的物件s呼叫study方法
		s.study();
		
		//測試StudentDemo中的method方法,在StudentDemo類中建立物件ss
		StudentDemo sd = new StudentDemo();
		//在Student類中建立物件ss作為呼叫method方法時的引數
		Student ss = new Student();
		//呼叫該類中的method方法,不要忘記傳參
		sd.method(ss);

                //匿名物件用法
                new StudentDemo().method(new Student());
	}
}

案例2:抽象類作為形參,需要的是該抽象類的子類物件

//抽象類作為形參
//抽象類(抽象類的格式)
abstract class Person{
	public abstract void study();//抽象方法沒有具體的方法體
}

class PersonDemo{
	public void method(Person p){
		p.study();
	}
}

//定義一個具體的學生類繼承自抽象類Person
class Student extends Person{
	//重寫抽象類的study方法
	public void study(){
		System.out.println("好好學習,天天向上");
	}
}

//測試類
class PersonTest{
	public static void main(String[] args){
		//使用PersonDemo類中的method方法,在該類中建立物件pd
		PersonDemo pd = new PersonDemo();
		//抽象類Person例項化
		//Person p = new Person();//錯誤: Person是抽象的; 無法例項化
		//間接例項化
		Person p = new Student();
		pd.method(p);
	}
}

案例3:介面作為形參,需要的是該介面的實現類物件

//介面作為形參
//定義一個興趣的介面
interface Hobby{
	//介面的成員方法只能是抽象方法
	public abstract void hobby();
}

class HobbyDemo{
	public void method(Hobby h){
		h.hobby();
	}
}

//定義一個具體類來實現介面
class Teacher implements Hobby{
	//方法重寫
	public void hobby(){
		System.out.println("卡卡西愛親熱天堂,貓咪老師愛燒酒");
	}
}

//測試類
class HobbyTest{
	public static void main(String[] args){
		//測試HobbyDemo類中的method方法,先建立物件
		HobbyDemo ht = new HobbyDemo();
		Hobby h = new Teacher();
		ht.method(h);
	}
}

返回值型別:

  1. 基本型別
  2. 引用型別(類、抽象類、介面)

案例1:返回值型別為類的時候,返回的其實是該類的物件

class Student {
	public void study() {
		System.out.println("Good Good Study,Day Day Up");
	}
}

class StudentDemo {
	//返回一個Student型別的值,其實是返回Student類的物件
	public Student getStudent() {
		//建立Student類的物件s並返回s
		//Student s = new Student();
		//return s;
		//用匿名物件簡化
		return new Student();
	}
}

class StudentTest2 {
	public static void main(String[] args) {
		//呼叫Student類中的study()方法,不要直接建立Student的物件
		//使用StudentDemo建立物件sd
		StudentDemo sd = new StudentDemo();
		Student s = sd.getStudent(); 
		s.study();
	}
}

案例2:返回值為抽象類的時候,返回的其實是該抽象類的具體類的物件

//定義一個抽象類
abstract class Person {
	public abstract void study();
}

class PersonDemo {
        //返回的是抽象類Person的子類Student的物件
	public Person getPerson() {
		//Person p = new Student();
		//return p;
		//匿名物件簡化
		return new Student();
	}
}
//定義抽象類的子類
class Student extends Person {
	//方法重寫
	public void study() {
		System.out.println("Good Good Study,Day Day Up");
	}
}

class PersonTest2 {
	public static void main(String[] args) {
		//測試Person類中的study()方法
		PersonDemo pd = new PersonDemo();
		Person p = pd.getPerson(); 
		p.study();
	}
}

案例3:返回值位介面的時候,返回的其實是該介面的實現類的物件

鏈式程式設計         物件.方法1().方法2().......方法n();                  這種用法:其實在方法1()呼叫完畢後,應該返回一個物件;                   方法2()呼叫完畢後,應該返回一個物件                   方法n()呼叫完畢後,可能是物件,也可以不是物件

匿名物件中,物件是作為引數傳遞

5. 包和導包

5.1 包的概述

5.1.1 概念

其實就是資料夾

5.1.2 作用

  1. 區分同名的類
  2. 對類進行分類管理
    1. 按照功能分(增、刪、查、改)
    2. 按照模組分

5.2 包的定義格式及注意事項

5.2.1 包的定義格式

package 包名;
多級包用.分開即可
舉例:
package cn.itcast;

5.2.2 注意事項

  1. package語句必須是程式的第一條可執行的程式碼
  2. package語句在一個java檔案中只能有一個
  3. 如果沒有package,預設表示無包名

5.3 帶包的類的編譯和執行

手動式

  1. javac編譯當前類檔案。
  2. 手動建立包對應的資料夾。
  3. 把1步驟的class檔案放到2步驟的最終資料夾下。
  4. 通過java命令執行    (eg:需要帶包名稱的執行 java cn.itcast.HelloWorld )

自動式

  1. javac編譯的時候帶上-d即可 javac -d . HelloWorld.java
  2. 通過java命令執行,和手動式步驟4一樣

5.4 導包

5.4.1 概述

每次使用不同包下的類的時候,都需要加包的全路徑。java就提供了導包的功能

5.4.2 導包格式

import 包名;

這種方式匯入是到類的名稱。 雖然可以最後寫*,但是不建議,建議用誰導誰

思考題:

package,import,class有沒有順序關係?

答:有,package>import>class

  • package只能有一個
  • import能有很多個
  • class能有多個,以後建議只有一個

6. 許可權修飾符

6.1 四種許可權修飾符

public protected 預設 private
同一類中訪問
同一包中訪問子類和其他類
不同包中訪問子類
不同包中訪問其他類

6.2 類及其組成常用的修飾符

修飾符的分類:

  1. 許可權修飾符:public、protected、預設許可權修飾符、private
  2. 狀態修飾符:static、final
  3. 抽象修飾符:abstract

類 的常用修飾符

  1. 許可權修飾符:public(最常用)、預設許可權修飾符,protected和private不能修飾類
  2. 狀態修飾符:final
  3. 抽象修飾符:abstract

成員變數的常用修飾符:

  1. 許可權修飾符:private(最常用),預設的,protected,public
  2. 狀態修飾符:static,final

構造方法的常用修飾符:

  1. 許可權修飾符:private,預設的,protected,public(最常用)

成員方法的常用修飾符:

  1. 許可權修飾符:private,預設的,protected,public(最常用)
  2. 狀態修飾符:static,final
  3. 抽象修飾符:abstract

除此以外的組合規則:

  1. 成員變數:public static final
  2. 成員方法:
    1. public static 
    2. public abstract
    3. public final

7. 內部類

7.1 概述

把類定義在其他類的內部,這個類就被稱為內部類

7.2 內部類的訪問特點

  1. 內部類可以直接訪問外部類的成員,包括私有
  2. 外部類要訪問內部類的成員,必須建立物件

7.3 內部類的分類

根據內部類在類中定義的位置不同,可以分為兩大類:

  1. 成員內部類(內部類在成員位置)
  2. 區域性內部類(內部類在成員方法內部,即區域性位置)

7.4 成員內部類

案例1:成員內部類訪問

要點:

建立物件格式:外部類名.內部類名 物件名 = 外部類物件.內部類物件; eg: Outer.Inner oi = new Outer().new Inner();

class Outer {
	private int num = 10;
	class Inner {
		public void show(){
			System.out.println(num);
		}
	}
}

class InnerClassDemo {
	public static void main(String[] args){
		//訪問成員內部類Inner的show()方法
                //建立物件格式:外部類名.內部類名 物件名 = 外部類物件.內部類物件;
		Outer.Inner oi = new Outer().new Inner();
		oi.show();
	}
}

成員內部類的常用修飾符

private 為了保證資料的安全性

static 為了讓資料訪問更方便 (內部類用靜態修飾,此時內部類可以看成外部類的成員)

  1. 靜態的內部類訪問的外部類資料必須是靜態修飾的
  2. 內部類被靜態修飾後的方法 可以是:
    1. 靜態方法 
    2.  非靜態方法

7.5 區域性內部類

案例:區域性內部類訪問

  1. 可以直接訪問外部類的成員
  2. 可以建立內部類物件,通過物件呼叫內部類方法,來使用區域性內部類功能
class Outer {
	//定義成員變數
	private int num = 10;
	//定義成員方法
	public void method(){
		//定義區域性內部類
		class Inner {
			public void show(){
				System.out.println(num);
			}
		}
		//在區域性位置建立內部類物件i呼叫內部類show方法
		Inner i = new Inner();
		i.show();
	}
}

class InnerClassDemo {
	public static void main(String[] args){
		//建立外部類物件o,通過o訪問外部類成員方法method
		Outer o = new Outer();
		o.method();
	}
}

區域性內部類訪問區域性變數 必須用final修飾

原因:

因為區域性變數是隨著方法的呼叫二呼叫,隨著方法呼叫完畢而消失

這時,區域性物件並沒有立馬從堆記憶體中消失,還要使用那個變數

為了讓資料還能繼續被使用,就用fianl修飾,這樣,在堆記憶體裡面儲存的其實是一個常量值

7.6 匿名內部類

匿名內部類其實就是內部類的簡化寫法

前提:存在一個類或者介面

  • 這裡的類可以是具體類也可以是抽象類
格式:
new 類名或者介面名() {
    重寫方法;
}

本質: 是一個繼承了該類或者實現了該介面的子類匿名物件