面向物件編程(高級)
筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html)
類變數和類方法(static)
類變數
類變數-提出問題
提出問題的主要目的就是讓大家思考解決之道,從而引出我要講的知識點.說:有一群小孩在玩堆雪人,不時有新的小孩加入,請問如何知道現在共有多少人在玩?,撰寫程式解決,
傳統的方法來解決
使用我們現有的技術來解決這個問題,大家看看如何?
?思路
-
在main方法中定義一個變數count
-
當一個小孩加入游戲后count++,最后個count 就記錄有多少小孩玩游戲
public class ChildGame { public static void main(String[] args) { //定義一個變數count,統計有多少小孩加入了游戲 int count = 0; Child child1 = new Child("白精"); child1.join(); count++; Child child2 = new Child("老白"); child2.join(); count++; Child child3 = new Child("老白"); child3.join(); child3.count++; System.out.println("共有" + count + "小孩加入了游戲..."); } } class Child {//類 private String name; public Child(String name) { this.name = name; } public void join() { System.out.println(name + "加入了游戲.."); } }
?問題分析:
- count是一個獨立于物件,很尷尬
- 以后我們訪問count很麻煩,沒有使用到OOP
- 3.因此,我們引出類變數/靜態變數
類變數快速入門
思考:如果,設計一個int count表示總人數,我們在創建一個小孩時,就把count加1,并且 count是所有物件共享的就ok了!,我們使用類變數來解決
public class ChildGame {
public static void main(String[] args) {
Child child1 = new Child("白精");
child1.join();
child1.count++;
Child child2 = new Child("老白");
child2.join();
child2.count++;
Child child3 = new Child("老白");
child3.join();
child3.count++;
//......
System.out.println("共有" + child1.count + "小孩加入了游戲...");
System.out.println(child1.count);//3
System.out.println(child2.count);//3
System.out.println(child3.count);//3
System.out.println(Child.count);//3
}
}
class Child {//類
private String name;
//定義一個變數 count,是一個類變數(靜態變數)static靜態
//該變數最大的特點就是會被Child類的所有的物件實體共享
public static int count = 0;
public Child(String name) {
this.name = name;
}
public void join() {
System.out.println(name + "加入了游戲..");
}
}
其中child1,child2以及child3中的count指向相同空間,被共享,為同一值
jdk8以及之前,count(也就是靜態域)在方法區中,jdk8之后,jdk存放在堆中
有些書說在方法區... jdk版本有關系,記住一點: static變數是物件共享
不管static變數在哪里,共識:
- static變數是同一個類所有物件共享
- static類變數,在類加載的時候就生成了.
什么是類變數
類變數也叫靜態變數/靜態屬性,是該類的所有物件共享的變數,任何一個該類的物件去訪問它時,取到的都是相同的值,同樣任何一個該類的物件去修改它時,修改的也是同一個變數,這個從前面的圖也可看出來,
如何定義類變數
定義語法:
訪問修飾符 static 資料型別 變數名;[推薦]
static 訪問修飾符 資料型別 變數名;
class A{
public static string name = "abc";
static public int totalNum = 100;
}
如何訪問類變數
類名.類變數名
或者物件名.類變數名【靜態變數的訪問健飾符的訪問權限和范圍和普通屬性是一樣的,】
推薦使用:類名.類變數名;
public class Test {
public static void main(String[] args) {
//類名.類變數名
// 說明:類變數是隨著類的加載而創建,所以即使沒有創建物件實體也可以訪問
System.out.println(A.name);
A a = new A();
System.out.println("a.name=" +a.name);
}
}
class A {
//類變數
public static String name = "韓順平";
}
類變數使用注意事項和細節討論
-
什么時候需要用類變數
當我們需要讓某個類的所有物件都共享一個變數時,就可以考慮使用類變數(靜態變數):比如:定義學生類,統計所有學生共交多少錢,Student (name, fee) -
類變數與實體變數(普通屬性)區別
類變數是該類的所有物件共享的,而實體變數是每個物件獨享的,
-
加上static稱為類變數或靜態變數,否則稱為 實體變數/普通變數/非靜態變數
-
類變數可以通過類名.類變數名或者物件名.類變數名來訪問,但java設計者推薦我們使用 類名.類變數名 方式訪問,【前提是 滿足訪問修飾符的訪問權限和范圍】
-
實體變數不能通過類名.類變數名方式訪問,
-
類變數是在類加載時就初始化了,也就是說,即使你沒有助建家,只安尖加鄆,就可以使用類變數了,
-
類變數的生命周期是隨類的加載開始,隨著類消亡而銷毀,
類方法
類方法基本介紹
類方法也叫靜態方法,
形式如下:
訪問修飾符 static 資料回傳型別 方法名(){ }【推薦】
static 訪問修飾符 資料回傳型別 方法名(){ }
類方法的呼叫:
使用方式:類名.類方法名 或者 物件名.類方法名【前提是滿足訪問修飾符的訪問權限和范圍】
類方法應用案例
請大家看一個靜態方式小案例,(統計學費總和)
public class Test {
public static void main(String[] args) {
//創建2個學生物件,叫學費
Stu tom = new Stu("tom");
tom.payFee(100);
Stu mary = new Stu("mary");
mary.payFee(200);
//輸出當前收到的總學費
Stu.showFee();//300
}
}
class Stu {
private String name;//普通成員
// 定義一個靜態變數,來累積學生的學費
private static double fee = 0;
public Stu(String name) {
this.name = name;
}
//說明
//1.當方法使用了static修飾后,該方法就是靜態方法
//2.靜態方法就可以訪問靜態屬性/變數
public static void payFee(double fee) {
Stu.fee += fee;//累積到
}
public static void showFee() {
System.out.println("總學費有:" + Stu.fee);
}
}
類方法經典的使用場景
當方法中不涉及到任何和物件相關的成員,則可以將方法設計成靜態方法,提高開發效率,(不需要額外創建物件)
比如:工具類中的方法utils
Math類、Arrays類、Collections集合類看下原始碼:
小結
在程式員實際開發,往往會將一些通用的方法,設計成靜態方法,這樣我們不需要創建物件就可以使用了,比如列印一維陣列,冒泡排序,完成某個計算任務等..
舉例
public class Test {
public static void main(String[] args) {
System.out.println(MyTools.calSum(10,30));
}
}
class MyTools {
//開發自己的工具類時,可以將方法做成靜態的,方便呼叫class MyTools {
//求出兩個數的和
public static double calSum(double n1, double n2) {
return n1 + n2;
}
}
類方法使用注意事項和細節討論
- 類方法和普通方法都是隨著類的加載而加載,將結構資訊存盤在方法區:類方法中無this的引數
普通方法中隱含著this的引數 - 類方法可以通過類名呼叫,也可以通過物件名呼叫,
- 普通方法和物件有關,需要通過物件名呼叫,比如物件名.方法名(引數),不能通過類名調
用, - 類方法中不允許使用和物件有關的關鍵字,比如this和super,普通方法(成員方法)可以,[舉例]
- 類方法(靜態方法)中只能訪問靜態變數或靜態方法,【如何理解】
- 普通成員方法,既可以訪問普通變數(方法),也可以訪問靜態變數(方法),
小結:靜態方法,只能訪問靜態的成員,非靜態的方法,可以訪問靜態成員和非靜態成員(必須遵守訪問權限)
理解main方法語法
●深入理解main方法
解釋main方法的形式:public static void main(String[] args){}
-
main方法時虛擬機呼叫
-
java虛擬機需要呼叫類的main()方法,所以該方法的訪問權限必須是public
-
java虛擬機在執行main()方法時不必創建物件,所以該方法必須是static
-
該方法接收String型別的陣列引數,該陣列中保存執行java命令時傳遞給所運行的類的引數,案例演示,接收引數.
-
java 執行的程式 引數1 引數2 引數3
特別提示:
-
在main()方法中,我們可以直接呼叫main方法所在類的靜態方法或靜態屬性,
-
但是,不能直接訪問該類中的非靜態成員,必須創建該類的一個實體物件后,才能通過這個物件去訪問類中的非靜態成員
代碼塊
●基本介紹
代碼化塊又稱為初始化塊,屬于類中的成員[即 是類的一部分],類似于方法,將邏輯陳述句封裝在方法體中,通過{}包圍起來,
但和方法不同,沒有方法名,沒有回傳,沒有引數,只有方法體,而且不用通過物件或類顯式呼叫,而是加載類時,或創建物件時隱式呼叫,
●基本語法
[修飾符]{
? 代碼
};
注意:
- 修飾符可選,要寫的話,也只能寫static
- 代碼塊分為兩類,使用static修飾的叫靜態代碼塊,沒有static修飾的,叫普通代碼塊,
- 邏輯陳述句可以為任何邏輯陳述句(輸入、輸出、方法呼叫、回圈、判斷等)
- ;號可以寫上,也可以省略,
實體
package com.hspedu.p386;
public class codeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("你好,李煥英");
}
}
class Movie {
private String name;
private double price;
private String director;
//3個構造器-》多載
//老韓解讀
//(1)下面的三個構造器都有相同的陳述句
//(2)這樣代碼看起來比較冗余
//(3)這時我們可以把相同的陳述句,放入到一個代碼塊中,即可
//(4)這樣當我們不管呼叫哪個構造器,創建物件,都會先呼叫代碼塊的內容
//(5)代碼塊呼叫的順序優先于構造器,,
{
System.out.println("電影螢屏打開...");
System.out.println("廣告開始...");
System.out.println("電影正是開始...");
}
public Movie(String name) {
System.out.println("Hovie(Strihg name)被呼叫,.,");
this.name = name;
}
public Movie(String name, double price) {
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
this.name = name;
this.price = price;
this.director = director;
}
}
代碼塊使用注意事項和細節討論
-
static代碼塊也叫靜態代碼塊,作用就是對類進行初始化,而且它隨著類的加載而執行,并且只會執行一次,如果是普通代碼塊,每創建一個物件,就執行,
-
類什么時候被加載
①創建物件實體時(new)
②創建子類物件實體,父類也會被加載
③使用類的靜態成員時(靜態屬性,靜態方法)案例演示: A類 extends B類 的靜態塊
-
普通的代碼塊,在創建物件實體時,會被隱式的呼叫,被創建一次,就會呼叫一次,
如果只是使用類的靜態成員時,普通代碼塊并不會執行,
小結:
-
static代碼塊是類加載時,執行,只會執行一次
-
普通代碼塊是在創建物件時呼叫的,創建一次,呼叫一次
-
類加載的3種情況,需要記住.
public class Test { public static void main(String[] args) { // new A(); // new A(); // // System.out.println("------------------"); // new B(); // System.out.println("------------------"); // System.out.println(A.a); System.out.println(B.b); } } class A { public static int a=10; static {//只在加載類時運行 System.out.println("111111"); } {//普通代碼塊,在new物件時,被呼叫,而且是每創建一個物件,就呼叫一次 //可以這樣簡單的,理解普通代碼塊是構造器的補充 System.out.println("普通"); } } class B extends A{ static { System.out.println("2222222"); } }
- 創建一個物件時,在一個類呼叫順序是:(重點,難點)∶
①呼叫靜態代碼塊和靜態屬性初始化(注意:靜態代碼塊和靜態屬性初始化呼叫的優先級一樣,如果有多個靜態代碼塊和多個靜態變數初始化,則按他們定義的順序呼叫)
②呼叫普通代碼塊和普通屬性的初始化(注意:普通代碼塊和普通屬性初始化呼叫的優先級一樣,如果有多個普通代碼塊和多個普通屬性初始化,則按定義順序呼叫)
③呼叫構造方法,
-
構造器的最前面其實隱含了super()和呼叫普通代碼塊,新寫一個類,靜態相關的代碼塊,屬性初始化,在類加載時,就執行完畢,因此是優先于構造器和普通代碼塊執行的
class A2 { public void A() { //構造器 //這里有隱藏的執行要求 //(1) super():這個知識點,在前面講解繼承的時候,老師說 // (2)呼叫普通代碼塊的 System.out.println("ok"); } } -
我們看一下創建一個子類時(繼承關系),他們的靜態代碼塊,靜態屬性初始化,普通代碼塊,普通屬性初始化,構造方法的呼叫順序如下:
①父類的靜態代碼塊和靜態屬性(優先級一樣,按定義順序執行)
②子類的靜態代碼塊和靜態屬性(優先級一樣,按定義順序執行)
③父類的普通代碼塊和普通屬性初始化(優先級一樣,按定義順序執行)
④父類的構造方法
⑤子類的普通代碼塊和普通屬性初始化(優先級一樣,按定義順序執行)
⑥子類的構造方法//面試題
public class Test {
public static void main(String[] args) {
//老師說明
//(1)進行類的加載
//1.1 先加載父類A02 1.2 再加載Bo2//(2)創建物件
new B02();//物件
}
}
class A02 {//父類
private static int n1 = getVal01();
static {
System.out.println("A02的一個靜態代碼塊..");//2
}
{
System.out.println("A02的第一個普通代碼塊..");//5
}
public int n3 = getVal02();
public static int getVal01() {
System.out.println("getVal01");//1
return 10;
}
public int getVal02() {
System.out.println("getVal02");//6
return 10;
}
public A02() {
System.out.println("A02的構造器");//7
}
}
class B02 extends A02 { //
private static int n3 = getVal03();
static {
System.out.println("BO2的一個靜態代碼塊..");//4
}
public int n5 = getVal04();
{
System.out.println("B02的第一個普通代碼塊..");//9
}
public static int getVal03() {
System.out.println("getVal03");//3
return 10;
}
public int getVal04() {
System.out.println("getVal04");//8
return 10;
}
public B02() {
System.out.println("BO2的構造器");//10
}
}
- 靜態代碼塊只能直接呼叫靜態成員(靜態屬性和靜態方法),普通代碼塊可以呼叫任意成員,
單例設計模式
什么是單例模式
- 所謂類的單例設計模式,就是采取一定的方法保證在整個的軟體系統中,對某個類只能存在一個物件實體,并且該類只提供一個取得其物件實體的方法
- 單例模式有兩種方式: 1)餓漢式 2)懶漢式
單例模式應用實體
演示餓漢式和懶漢式單例模式的實作,步驟如下:
- 構造器私有化=》防止直接new
- 類的內部創建物件
- 向外暴露一個靜態的公共方法,getInstance
- 代碼實作
餓漢式
public class Test {
public static void main(String[] args) {
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
}
}
class GirlFriend {
private String name;
//為了能夠在靜態方法中,回傳gf物件,需要將其修飾為static
private static GirlFriend gf = new GirlFriend("小紅紅");
//如何保障我們只能創建一個 GirlFriend物件
//步驟
//步驟[單例模式-餓漢式]
//1,將構造器私有化
// 2.在類的內部直接創建
//3.提供一個公共的static方法,回傳 gf物件
private GirlFriend(String name) {
this.name = name;
}
public static GirlFriend getInstance() {
return gf;
}
}
懶漢式
package com.hspedu.test;
public class Test {
public static void main(String[] args) {
//new Cat("大黃");
Cat instance = Cat.getInstance();
System.out.println(instance);
}
}
class Cat {
private String name;
private static Cat cat;//步驟
//1.仍然構造器私有化
//2.定義一個static屬性物件
//3.提供一個public的static方法,可以回傳一個Cat物件
private Cat(String name) {
this.name = name;
}
public static Cat getInstance() {
if (cat == null) {//如果沒有創建cat封象
cat = new Cat("小可愛");
}
return cat;
}
}
餓漢式VS懶漢式
-
二者最主要的區別在于創建物件的時機不同:餓漢式是在類加載就創建了物件實體,而懶漢式是在使用時才創建,
-
餓漢式不存在執行緒安全問題,懶漢式存在執行緒安全問題,(后面學習執行緒后,會完善)
-
餓漢式存在浪費資源的可能,因為如果程式員一個物件實體都沒有使用,那么餓漢式創建的物件就浪費了,懶漢式是使用時才創建,就不存在這個問題,
在我們javaSE標準類中,java.lang.Runtime就是經典的單例模式,
final關鍵字
●基本介紹
final中文意思:最后的,最終的.
final 可以修飾類、屬性、方法和區域變數.
在某些情況下,程式員可能有以下需求,就會使用到final:
-
當不希望類被繼承時,可以用final修飾.
final class A{ } class B extends A {}//會報錯 -
當不希望父類的某個方法被子類覆寫/重寫(override)時,可以用final關鍵字修飾,【案例演示:訪問修飾符 final 回傳型別方法名】
class c { //如果我們要求hi不能被子類重寫 //可以使用final修飾hi方法 public final void hi() {} } class D extends C { @0verride public void hi {//報錯 System.out.println("重寫了C類的hi方法.."); } } -
當不希望類的的某個屬性的值被修改,可以用final修飾.【案例演示: public final double TAX_RATE=0.08】
-
當不希望某個區域變數被修改,可以使用final修飾【案例演示: final double TAX_RATE=0.08)
class F { public void cry() { //這時,NUM也稱為區域常量 final double NUM = 0.01; NUM= 0.9;//報錯 System.out.println("NUM=" + NUM); } }
final使用注意事項和細節討論
-
final修飾的屬性又叫常量,一般用XX_XX_XX 來命名
-
final修飾的屬性在定義時,必須賦初值,并且以后不能再修改,賦值可以在如下位置之一【選擇一個位置賦初值即可】:
①定義時:如public final double TAX_RATE=0.08;
②在構造器中
③在代碼塊中, -
如果final修飾的屬性是靜態的,則初始化的位置只能是
①定義時
②在靜態代碼塊賦值,不能在構造器中賦值, -
final類不能繼承,但是可以實體化物件,
-
如果類不是final類,但是含有final方法,則該方法雖然不能重寫,但是可以被繼承,
-
一般來說,如果一個類已經是final類了,就沒有必要再將方法修飾成final方法,(多此一舉)
-
final不能修飾構造方法(即構造器)
-
final和static往往搭配使用,效率更高,底層編譯器做了優化處理,
-
包裝類(Integer,Double,Float,Boolean等都是final),String也是final類,
抽象類
當父類的某些方法,需要宣告,但是又不確定如何實作時,可以將其宣告為抽象方法,那么這個類就是抽象類
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//思考:這里eat這里你實作了,其實沒有什么意義//即:父類方法不確定性的問題
//===>考慮將該方法設計為抽象(abstract)方法//===>所謂抽象方法就是沒有實作的方法
//===>所謂沒有實作就是指,沒有方法體
//===>當一個類中存在抽象方法時,需要將該類宣告為abstract類
// public void eat() {
// System.out.println("這是一個動物,但是不知道吃什么..");
// }
public abstract void eat() ;
}
抽象類的介紹
-
用abstract關鍵字來修飾一個類時,這個類就叫抽象類
訪問修飾符 abstract 類名{
} -
用abstract關鍵字來修飾一個方法時,這個方法就是抽象方法
訪問修飾符 abstract 回傳型別 方法名(引數串列);//沒有方法體 -
抽象類的價值更多作用是在于設計,是設計者設計好后,讓子類繼承并實作抽象類()
-
抽象類,是考官比較愛問的知識點,在框架和設計模式使用較多
抽象類使用的注意事項和細節討論
-
抽象類不能被實體化
public class AbstractDetail01 { public static void main(String[] args) { //抽象類,不能被實體化 new A();//報錯 } } abstract class A { } -
抽象類不一定要包含abstract方法,也就是說,抽象類可以沒有abstract方法
-
一旦類包含了abstract方法,則這個類必須宣告為abstract
class B {//報錯 public abstract void hi(); } -
abstract只能修飾類和方法,不能修飾屬性和其它的,
-
抽象類可以有任意成員【抽象類還是類】,比如:非抽象方法、構造器、靜態屬性等等
-
抽象方法不能有主體,即不能實作.如圖所示
abstract void aaa(){......}//報錯,不能存在“{}” -
如果一個類繼承了抽象類,則它必須實作抽象類的所有抽象方法,除非它自己也宣告為abstract類,[舉例 A類,B類,C類]
//如果一個類繼承了抽象類,則它必須實作抽象類的所有抽象方法,除非它自己也宣告為abstract類 abstract class E { public abstract void hi(); } abstract class F extends E { } class G extends E { @Override public void hi() { //這里相等于G子類實作了父類E的抽象方法,所謂實作方法,就是有方法體 } } -
抽象方法不能使用private、final和static來修飾,因為這些關鍵字都是和重寫相違背的,
抽象類最佳實踐-模板設計模式
最佳實踐
需求
- 有多個類,完成不同的任務job
- 要求統計得到各自完成任務的時間
- 請編程實作
感情的自然流露
- 先用最容易想到的方法
- 分析問題,提出使用模板設計模式
public class Test {
public static void main(String[] args) {
new A().job();
new B().job();
}
}
//如果一個類繼承了抽象類,則它必須實作抽象類的所有抽象方法,除非它自己也宣告為abstract類
class A {
public void job() {
//得到開始的時間
long start = System.currentTimeMillis();
long num = 0;
for (long i = 1; i <= 80000000; i++) {
num += i;
}
//得的結束的時間
long end = System.currentTimeMillis();
System.out.println("執行時間" + (end - start));
}
}
class B {
public void job() {
//得到開始的時間
long start = System.currentTimeMillis();
long num = 0;
for (long i = -10; i <= 800000; i++) {
num += i;
}
//得的結束的時間
long end = System.currentTimeMillis();
System.out.println("執行時間" + (end - start));
}
}
最佳實踐(使用動態系結機制)
設計一個抽象類(Template),能完成如下功能:
- 撰寫方法calculateTime( ,可以計算某段代碼的耗時時間
- 撰寫抽象方法job()
- 撰寫子類AA、BB,繼承抽象類Template,并實作job方法,
- 撰寫一個測驗類Test,看看是否好用,
public class Test {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
}
abstract class Template {//抽象類-模板設計模式
public abstract void job();//抽象方法
public void calculateTime() {//實作方法,呼叫job方法
//得到開始的時間
long start = System.currentTimeMillis();
job();
//得的結束的時間
long end = System.currentTimeMillis();
System.out.println("執行時間" + (end - start));
}
}
//如果一個類繼承了抽象類,則它必須實作抽象類的所有抽象方法,除非它自己也宣告為abstract類
class AA extends Template{
public void job() {
long num = 0;
for (long i = 1; i <= 80000000; i++) {
num += i;
}
}
}
class BB extends Template{
public void job() {
//得到開始的時間
long num = 0;
for (long i = -10; i <= 800000; i++) {
num += i;
}
}
}
介面
為什么有介面
先看一張圖:

usb插槽就是現實中的介面,
你可以把手機,相機,u盤都插在usb插槽上,而不用擔心那個插槽是專門插哪個的,原因是做usb插槽的廠家和做各種設備的廠家都遵守了統一的規定包括尺寸,排線等等,
介面快速入門
這樣的設計需求在java編程/php/.net/go中也是會大量存在的,我曾經說過,一個程式就是一個世界,在現實世界存在的情況,在程式中也會出現,我們用程式來模擬一下,
介面
public interface UsbInterface {
//規定介面的相關方法
public void start();
public void stop();
}
相機
public class Camera implements UsbInterface{//實作介面,就是把介面方法實作
//IDEA中alt+insert,選擇implement methods(或ctrl+i)快速創建相應的方法
@Override
public void start() {
System.out.println("相機開始作業");
}
@Override
public void stop() {
System.out.println("相機停止作業");
}
}
手機
//Phone 類 實作 UsbInterface
//解讀1.即 Phone類需要實作UsbInterface介面 規定/宣告的方法
public class Phone implements UsbInterface{
@Override
public void start() {
System.out.println("手機開始作業,..");
}
@Override
public void stop() {
System.out.println("手機停止作業..... ");
}
}
電腦
public class Computer {
//撰寫一個方法,計算機作業
public void work(UsbInterface usbInterface) {
usbInterface.start();
usbInterface.stop();
}
}
呼叫
public class Test {
public static void main(String[] args) {
//創建手機,相機物件
Camera camera = new Camera();
Phone phone = new Phone();
//創建計算機
Computer computer = new Computer();
computer.work(phone);//把手機接入到計算機
System.out.println("-----------------");
computer.work(camera);
}
}
●基本介紹
介面就是給出一些沒有實作的方法,封裝到一起,到某個類要使用的時候,在根據具體情況把這些方法寫出來,語法:
interface 介面名{
//屬性
//方法(1.抽象方法 2.默認實作方法 3.靜態方法)
}
class 類名 implements 介面{
自己屬性;
自己方法;
必須實作的介面的抽象方法
}
小結:
- 在Jdk7.0前介面里的所有方法都沒有方法體,即都是抽象方法,(已被淘汰,基本不用)
- Jdk8.0后介面可以有靜態方法,默認方法,也就是說介面中可以有方法的具體實作
interface AInterface {
//寫屬性
public int n1 = 10;//寫方法
//在介面中,抽象方法,可以省略abstract關鍵字
public void hi();
//在jdk8后,可以有默認實作方法,需要使用default關鍵字修飾
default public void ok() {
System.out.println("ok ...");
}
//在jdk8后,可以有靜態方法
public static void cry() {
System.out.println("cry........");
}
}
深入討
對初學者講,理解介面的概念不算太難,難的是不知道什么時候使用介面,下面我例舉幾個應用場景:
- 說現在要制造戰斗機,武裝直升機.專家只需把飛機需要的功能/規格定下來即可,然后讓別的人具體實作就可,
- 說現在有一個專案經理,管理三個程式員,功能開發一個軟體,為了控制和管理軟體,專案經理可以定義一些介面,然后由程式員具體實作,
注意事項和細節
-
介面不能被實體化
-
介面中所有的方法是public方法,介面中抽象方法,可以不用寫public和abstract,如下:
void aa();實際上是public abstract void aa();
-
一個普通類實作介面,就必須將該介面的所有方法都實作,
class Phone implements UsbInterface {//報錯 } -
抽象類實作介面,可以不用實作介面的方法,
abstract class Phone implements UsbInterface {//不報錯 } -
一個類同時可以實作多個介面
class Pig implements IB,IC { } -
介面中的屬性,只能是final的,而且是public static final修飾符,比如:
int a=1;實際上是public static final int a=1;(必須初始化) -
介面中屬性的訪問形式:介面名.屬性名
-
一個介面不能繼承其它的類,但是可以繼承多個別的介面
interface A extends B,C{} -
介面的修飾符只能是public和默認,這點和類的修飾符是一樣的,
實作介面Vs繼承類
?介面和繼承解決的問題不同
繼承的價值主要在于:解決代碼的復用性和可維護性,
介面的價值主要在于:設計,設計好各種規范(方法),讓其它類去實作這些方法,即更加的靈活..
?介面比繼承更加靈活
介面比繼承更加靈活,繼承是滿足is -a的關系(小明是學生),而介面只需滿足like - a的關系(飛機像鳥一樣飛)
?介面在一定程度上實作代碼解耦[即:介面規范性+動態系結]
介面的多型特性(和類的繼承中的多型類似)
-
多型引數(前面案例體現)
在前面的Usb介面案例,Usb usb,既可以接收手機物件,又可以接收相機物件,就體現了介面多型(介面參考可以指向實作了介面的類的物件)
-
多型陣列
演示一個案例:給Usb陣列中,存放 Phone 和相機物件,Phone類還有一個特有的方法call),請遍歷Usb陣列,如果是Phone物件,除了呼叫Usb介面定義的方法外,還需要呼叫Phone特有方法call.
Usb usbs[] = new Usb[2]; usbs[0]= new Phone(); usbs[1] = new Camera(); for (int i = 0; i< usbs.length; i++){ usbs[i].start(); usbs[i].stop(); if (usbs[i] instanceof Phone){ ((Phone)usbs[i]).call(); } } -
介面存在多型傳遞現象,
class InterfacePolyPass { public static void main(String[] args) { //介面型別的變數可以指向,實作了該介面的類的物件實體 IG ig = new Teacher(); //如果IG 繼承了IH 介面,而Teacher類實作了 IG介面 // 那么,實際上就相當于Teacher類也實作了IH介面, //這就是所謂的介面多型多型傳遞現象. IH ih = new Teacher(); } } interface IH { void hi(); } interface IG extends IH { } class Teacher implements IG { @Override public void hi() { } }
類定義的進一步完善

內部類
基本介紹
一個類的內部又完整的嵌套了另一個類結構,被嵌套的類稱為內部類(inner class),嵌套其他類的類稱為外部類(outer class),是我們類的第五大成員【思考:類的五大成員是哪些?屬性 方法 構造器 代碼塊 內部類】,內部類最大的特點就是可以直接訪問私有屬性,并且可以體現類與類之間的包含關系,(難點)
●基本語法
class Outer{//外部類
class Inner{//內部類
}
}
class Other{//外部其他類
}
內部類的分類
定義在外部類區域位置上(比如方法內):
- 區域內部類(有類名)
- 匿名內部類(沒有類名,重點!!!!!!)
定義在外部類的成員位置上:
- 成員內部類(沒用static修飾)
- 靜態內部類(使用static修飾)
區域內部類的使用
說明:區域內部類是定義在外部類的區域位置,比如方法中,并且有類名.
-
可以直接訪問外部類的所有成員,包含私有的
-
不能添加訪問修飾符,因為它的地位就是一個區域變數,區域變數是不能使用修飾符的,但是可以使用final修飾,因為區域變數也可以使用final
-
作用域:僅僅在定義它的方法或代碼塊中,
-
區域內部類---訪問---->外部類的成員[訪問方式:直接訪問]
-
外部類---訪問---->區域內部類的成員
訪問方式:創建物件,再訪問(注意:必須在作用域內) -
外部其他類---不能訪問----->區域內部類(因為區域內部類地位是一個區域變數)
-
如果外部類和區域內部類的成員重名時,默認遵循就近原則,如果想訪問外部類的成員,則可以使用(外部類名.this.成員)去訪問
System.out.println("外部類的n2=”+外部類名.this.n2);
記住:(1)區域內部類定義在方法中/代碼塊 (2)作用域在方法體或者代碼塊中 (3)本質仍然是一個類
public class Test {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02 {//外部類
private int n1 = 100;
private void m2() {
System.out.println("Outer02 m2()");
}//私有方法
public void m1() {//方法
//1.區域內部類是定義在外部類的區域位置,通常在方法
// 3.不能添加訪問修飾符,但是可以使用final修飾
// 4.作用域︰僅僅在定義它的方法或代碼塊中
String name = "xXx";
final class Inner02 {//區域內部類(本質仍然是一個類)
private int n1=800;
//2.可以直接訪問外部類的所有成員,包含私有的
public void f1() {
//5.區域內部類可以直接訪問外部類的成員,比如下面外部類n1 和 m2()
//7.如果外部類和區域內部類的成員重名時,默認遵循就近原則,如果想訪問外部類的成員,
// 使用 外部類名.this.成員)去訪問
System.out.println("n1=" + n1 +"外部類的n1="+Outer02.this.n1);
m2();
}
}
//6.外部類在方法中,可以創建Inner02物件,然后呼叫方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
// class Inner022 extends Inner02{
// }
}
{ //代碼塊
class Inner03 {
}
}
}
匿名內部類的使用(重要!!!)
//(1)本質是類 (2)內部類 (3)該類沒有名字 (4)同時還是一個物件
必須繼承—個抽象類或者實作一個介面
說明:匿名內部類是定義在外部類的區域位置,比如方法中,并且沒有類名
-
匿名內部類的基本語法
new 類或介面(引數串列){ 類體 }; -
匿名內部類的語法比較奇特,請大家注意,因為匿名內部類既是一個類的定義同時它本身也是一個物件,因此從語法上看,它既有定義類的特征,也有創建物件的特征,對前面代碼分析可以看出這個特點,因此可以呼叫匿名內部類方法,
-
可以直接訪問外部類的所有成員,包含私有的
-
不能添加訪問修飾符,因為它的地位就是一個區域變數,
-
作用域:僅僅在定義它的方法或代碼塊中,
-
匿名內部類---訪問---->外部類成員[訪問方式:直接訪問]
-
外部其他類---不能訪問----->匿名內部類(因為匿名內部類地位是一個區域變數)
-
如果外部類和匿名內部類的成員重名時,匿名內部類訪問的話,默認遵循就近原則如果想訪問外部類的成員,則可以使用(外部類名.this.成員)去訪問
例子(基于介面)
public class Test {
public static void main(String[] args) {
new Outer04().method();
}
}
class Outer04 {//外部類
private int n1 = 10;//屬性
public void method() {//方法
//基于介面的匿名內部類//老韓解讀
//1.需求:想使用IA介面,并創建物件
//2.傳統方式,是寫一個類,實作該介面,并創建物件
//3.老韓需求是 Tiger/Dog 類只是使用一次,后面再不使用
//4.可以使用匿名內部類來簡化開發
//5.tiger的編譯型別? IA
//6.tiger的運行型別?就是匿名內部類(物件名.getClass()可獲取名字) Outer04$1
/*我們看底層
class XXXX implements IA {
@override
public void cry(O {
System.out'.println("老虎叫喚...");
}
}
*/
//7.jdk底層在創建匿名內部類Outer04$1,立即馬上就創建了Outer04$1實體,并且把地址
// 回傳給tiger
//8.匿名內部類使用一次,就不能再使用(但此處的tiger物件可重復使用)
IA tiger = new IA(){
@Override
public void cry(){
System.out.println("老虎叫喚");
}
};
System.out.println("tiger的運行型別=" + tiger.getClass());
tiger.cry();
tiger.cry();
}
}
interface IA {//介面
public void cry();
}
例子(基于方法)
public class Test {
public static void main(String[] args) {
new Outer04().method();
}
}
class Outer04 {//外部類
private int n1 = 10;//屬性
public void method() {//方法
//演示基于類的匿名內部類
// 分析
//1.father編譯型別 Father
//2.father運行型別Outer04$2
//3.底層會創建匿名內部類
/*
class Outer04$1 extends Father{
override
public void test() {
System.out.println("匿名內部類重寫了test方法");
}
}
*/
//4.同時也直接回傳了匿名內部類Outer04$2的物件
Father tiger = new Father("jack") {
public void test() {
System.out.println("匿名內部類重寫了test方法");
}
};
System.out.println("tiger的運行型別=" + tiger.getClass());//Outer04$1
tiger.test();
//基于抽象類的匿名內部類
Animal animal = new Animal() {
@Override
void eat() {
System.out.println("小狗吃骨頭...");
}
};
animal.eat();
}
}
class Father {//類
public Father(String name) {//構造器
}
public void test() {//方法
}
}
abstract class Animal {//抽象類
abstract void eat();
}
成員內部類的使用
說明:成員內部類是定義在外部類的成員位置,并且沒有static修飾,
-
可以直接訪問外部類的所有成員,.包含私有的
-
可以添加任意訪問修飾符(public、protected、默認、private),因為它的地位就是一個成員,
-
作用域
和外部類的其他成員一樣,為整個類體比如前面案例,在外部類的成員方法中創建成員內部類物件,再呼叫方法.
-
成員內部類---訪問---->外部類(比如:屬性)[訪問方式:直接訪問](說明)
-
外部類---訪問------>內部類(說明)訪問方式:創建物件,再訪問
-
外部其他類---訪問---->成員內部類
-
如果外部類和內部類的成員重名時,內部類訪問的話,默認遵循就近原則,如果想訪問外部類的成員,則可以使用(外部類名.this.成員)去訪問
public class Test {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//6.外部其他類,使用成員內部類的兩種方式
// outer08.new Inner08();相當于把 new Inner08()當做是outer08成員
//這就是一個語法,不要特別的糾結.
Outer08.Innter08 innter08 = outer08.new Innter08();
//第二方式在外部類中,撰寫一個方法,可以回傳 Inner08物件
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
}
}
class Outer08 {//外部類
private int n1 = 10;
public String name = "張三";
private void hi() {
System.out.println("hi()方法...");
}
//1.注意:成員內部類,是定義在外部內的成員位置上
//2.可以添加任意訪問修飾符(public、protected 、默認、private),因為它的地位就是一個成員
private class Inner08 {//成員內部類
private double sal = 99.8;
public void say() {
//4.可以直接訪問外部類的所有成員,包含私有的
System.out.println("n1 = " + n1 + " name = " + name);
hi();//4
}
}
//方法,回傳一個Inner08實體
public Inner08 getInner08Instance(){
return new Inner08();
}
//寫方法
public void t1() {
//4.使用成員內部類
//創建成員內部類的物件,然后使用相關的方法
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(innerO8.sal);
}
}
靜態內部類的使用
說明:靜態內部類是定義在外部類的成員位置,并且有static修飾
- 可以直接訪問外部類的所有靜態成員,包含私有的,但不能直接訪問非靜態成員
- 可以添加任意訪問修飾符(public.protected、默認、private),因為它的地位就是一個成員,
- 作用域:同其他的成員,為整個類體
- 靜態內部類---訪問---->外部類(比如:靜態屬性)[訪問方式:直接訪問所有靜
態成員] - 外部類---訪問------>靜態內部類訪問方式:創建物件,再訪問
- 外部其他類---訪問--->靜態內部類
- 如果外部類和靜態內部類的成員重名時,靜態內部類訪問的時,默認遵循就近原則,如果想訪問外部類的成員,則可以使用(外部類名.成員)去訪問
public class Test {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他類使用靜態內部類
// 方式1
// 因為靜態內部類,是可以通過類名直接訪問(前提是滿足訪問權限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式2
//撰寫一個方法,可以回傳靜態內部類的物件實體,
Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
Outer10.Inner10 inner10_ = outer10.getInner10_();
System.out.println("============");
inner10_.say();
}
}
class Outer10 {//外部類
private int n1 = 10;
private static String name = "張三";
private static void cry(){}
// Inner10就是靜態內部類
//1.放在外部類的成員位置
//2.使用static修飾
//3,可以直接訪問外部類的所有靜態成員,包含私有的,但不能直接訪問非靜態成員
//4,可以添加任意訪問修飾符(public、 protected 、默認、private),因為它的地位就是一個成員//5,作用域:同其他的成員,為整個類體
//5.作用域:同其他的成員,為整個類體
static class Inner10 {
private static String name ="李四";
public void say() {
System.out.println(name);
System.out.println(Outer10.name);//7
cry();
}
}
public void m1() {
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10(){
return new Inner10();
}
public static Inner10 getInner10_(){
return new Inner10();
}
}
這里:小結
(1)內部類有四種區域內部類,匿名內部類成員內部類,靜態內部類
(2)重點還是掌握匿名內部類使用
new 類/介面(引數串列){
//...
};
(3)成員內部類,靜態內部類是放在外部類的成員位置,本質就是一個成員.
(4)其他細節看之前筆記...
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501485.html
標籤:其他
上一篇:Java常用類(一)
