java自學第3期——繼承、多型、介面、抽象類、final關鍵字、許可權修飾符、內部類
一.繼承:
關鍵字extends
/*
定義一個父類:人類
定義父類格式:public class 父類名稱{
}
定義子類格式:public class 子類名稱 extends 父類名稱{
}
*/
- 程式碼示範:
*要點:
1.子類中在main方法中建立父類物件可呼叫父類方法;
2.不加關鍵字直接訪問本方法區域性變數(可重名時區分);
3.使用this關鍵字訪問本類成員變數(可重名時區分);
4.使用super關鍵字訪問父類成員變數(可重名時區分);
5.如果存在方法的重名:父類與子類有重名方法:
——物件new的誰則優先呼叫誰的重名方法,沒有則向上尋找;
前面介紹了成員變數、成員方法繼承的訪問特點,接下來是構造方法:
——子類構造方法(父類無參時)中有一個預設贈送的super()方法
——父類有參時,子類裡呼叫super的()裡傳參,過載時誰對的上呼叫誰;
6.如果要更新修改父類方法,本著設計原則儘量不去直接修改正在使用的類,
則可以進行覆蓋重寫:格式:通過使用super關鍵字繼承父類需要的方法。
@Override 方法外部相同(){ super.父類方法(); //這裡新增新的內容 } /* 小結super super關鍵字作用: 示例: public class demo06Super extends demo01people { public void method1(){ System.out.println(super.num); //1.在子類成員方法中訪問父類的成員變數 } public void method2(){ super.methodChongMing(); // 2.在子類成員方法中訪問父類的成員方法 } public demo06Super(){ super(); //3.在子類的構造方法中訪問父類的構造方法 } } */
7.this關鍵字的作用小結:
this關鍵字的作用:
1.在本類的成員方法中訪問本類的成員變數
2.在本類成員方法中訪問本類的另一個成員方法
3.在本類的構造方法中訪問本類的另一個構造方法:
(1)this(...)呼叫必須是構造方法的第一個語句,一個this;
(2)super和this兩種構造呼叫不能同時使用
- 程式碼示範:
String name = "python"; public void method1(){ String name = "python"; System.out.println(name);//無關鍵字時直接訪問本方法區域性變數 System.out.println(this.name);//訪問本類成員變數 System.out.println(super.num);//訪問父類成員變數 this.method2();//訪問本類另一個成員方法 }
8.最後,繼承三大特點:
1.單繼承:一個子類只有一個直接父類
2.多級繼承:父類、子類、子類也可作父類再向下延伸,最上級為java.lang.object類
3.一個父類可以有多個子類
二、多型
1.多型性:父類引用指向子類物件
多型的一個用處:無論右邊new的時候換成哪個子類物件,等號左邊呼叫方法都不會發生變化
格式:
父類名稱 物件名 = new 子類名稱();
或者:
介面名 物件名 = new 實現類名稱()。
2.成員方法:編譯看左,執行看右;
成員變數:編譯看左,執行也看左;例項變數不具備多型性.
程式碼示範:
//前面省略部分Zi類已經繼承Fu類
public static void main(String[] args) {
Fu one = new Zi();//多型的寫法
one.methodcommon();//重名時,成員方法優先使用子類(new誰先呼叫誰)。
one.fu1();//它的編譯時型別為Fu,可以呼叫Fu中的方法
// one.zi1(); 該呼叫編譯時會報錯,因為它的編譯時型別為Fu,無法呼叫zi1方法;
System.out.println(one.num);//優先看左邊Fu類
Zi two = (Zi) one;//將父類物件還原成子類物件。
System.out.println(one instanceof Fu);//輸出true,one可以做Fu類的例項物件
System.out.println(one instanceof Zi);//輸出true,one可以做Zi類的例項物件
}
3.轉型多型寫法左父右子是正常的向上轉型
4.向下轉型:為了讓物件呼叫子類方法(向上轉型只能呼叫左邊編譯型別的父類方法)
左子類右父類,是一個還原的過程,將父類物件還原成子類物件,且不能向下轉成別的子類。
格式: 子類名 物件名 = (子類名) 父類物件名; (後者也可以是介面)
Fu fu = new Fu();//向上轉型建立父類物件
Zi two = (Zi) one;//將父類物件還原成子類物件。
5.強制向下轉型時,判斷前面的物件是否是後面型別的例項,是否可以成功轉換,從而保證程式碼更加健壯。
格式: 物件 instanceof 型別
得到一個boolean值結果(true/false),判斷前面的物件能否作為後面型別的例項
Zi two = (Zi) one;//將父類物件還原成子類物件。
System.out.println(one instanceof Fu);//輸出true,one可以做Fu類的例項物件
System.out.println(one instanceof Zi);//輸出true,one可以做Zi類的例項物件
三、介面
1.介面定義了某一批類需要遵守的規範。這就意味著接口裡通常是定義一組公用方法。
2.介面是一種引用資料型別(類、介面、陣列),注意其中的抽象方法。
3.介面可以繼承介面,但不能繼承類。
4.Java9 裡可以有常量、抽象方法、預設方法、靜態方法、私有方法。
5.備註:介面編譯後生成的同樣是.class的位元組碼檔案
//格式
public interface 介面名稱(首字母大寫){
//抽象方法
}
- 接口裡的成員變數只能是常量,必須用public static final修飾,可省略
//final即為不可改變
public static final int MAX_NUMBER = 20;
- 接口裡的[普通方法]只能是抽象方法,public abstract可以省略
public abstract void out();
void getDate();
- 介面中的[預設方法]需要用default修飾
/*
當介面新新增方法時,新方法寫為預設方法,則可以不去動其實現類,預設方法自動被其繼承
預設方法同樣可以被覆寫。
*/
public default void print() {
foo();
System.out.println("預設方法呼叫3");
}
- 介面中定義[靜態方法],需要用static修飾
public static void staticTest() {
System.out.println("靜態方法!");
}
- 當倆預設方法中有重複內容時,抽取出來定義私有方法
//定義私有預設方法,給預設方法呼叫,但不應被實現類使用,所以許可權為私有
private void foo() {
System.out.println("預設方法呼叫2");
}
//定義私有靜態方法,給靜態方法呼叫,但不應被實現類使用,所以許可權為私有
private static void bar() {
System.out.println("bar私有靜態方法");
}
}
- 介面不能建立例項,但能用於宣告引用型別變數,且必須引用到實現類的物件;
- 一個實現類可以同時實現多個介面
//格式:
.public class 實現類名稱 impliments{
//必須覆寫介面中所有抽象方法;
},
- 在實現類(impliments)中進行介面的實現:
//
public class Demo01InterfaceImpl implements Demo01Interface,Demo02Interface {
//1,2介面都有out抽象方法,但只需覆寫一次
@Override
public void out() {
System.out.println("抽象方法覆寫!");
}
@Override
public void getDate() {
System.out.println("抽象方法覆寫!");
}
@Override
public void print(){
System.out.println("衝突的預設方法也需要覆寫!");//不衝突則不用覆寫
}
}
- 然後在main類裡建立實現類的物件,進行呼叫
public class Impliments {
public static void main(String[] args) {
//建立實現類的物件
Demo01InterfaceImpl ImplementationObject1 = new Demo01InterfaceImpl();
ImplementationObject1.getDate();
ImplementationObject1.out();
ImplementationObject1.print();//呼叫實現類裡繼承自介面的預設方法。
//接口裡的靜態方法只能通過介面名稱直接呼叫。
Demo01Interface.staticTest();
//通過介面名直接訪問常量。
System.out.println(Demo01Interface.MAX_NUMBER);
}
}
- 介面是多繼承,一個介面可以有多個父介面。
- 多個父介面中的抽象方法如果存在重名,正常覆寫;但預設方法重名需要帶有default關鍵字覆寫。
- 正常不衝突抽象方法不需要再覆寫(實現類多個介面時也是如此)。
//介面的多繼承,同樣使用extends關鍵字
public interface Demo03InterfaceExtends extends Demo01Interface,Demo02Interface {
@Override
default void print() {
//覆寫父介面的預設方法不能省略default關鍵字
}
}
四、抽象類
- 抽象方法:加上abstract關鍵字,去掉大括號,直接分號結束;
- 抽象類:抽象方法所在的類必須是抽象類,再class之前寫上abstract即可
- 不能直接new抽象類物件,必須用一個子類來繼承抽象父類
(抽象類物件呼叫的方法存在沒有具體內容的方法體,因此沒有意義) - 建立子類物件,不可建立抽象父類物件;
public abstract class demo01Abstract {
public abstract void method1();
public abstract void method2();
}
- 子類必須覆寫抽象類中的所有抽象方法(去掉abstrat,補上大括號)。
(假設不全部覆寫,物件呼叫的方法存在抽象方法,沒有意義)
//使用extends繼承抽象父類
public abstract class demo02Zi extends demo01Abstract {
//只覆寫了一個抽象方法,沒有將抽象方法全部覆寫完,也就是說本子類還存在著繼承下來的未覆寫的抽象方法
//所以該類也同樣是抽象類
@Override
public void method1() {
System.out.println("已經覆寫第method1方法!");
}
}
//在這個子子類中已經將所有抽象方法全部覆寫,所以該類不再是抽象類!
public class demo03Sun extends demo02Zi {
@Override
public void method2() {
System.out.println("已經覆寫method2方法!");
}
}
- 最後在main類中的main方法裡建立物件進行呼叫即可。
五、final關鍵字
- final表示它修飾的類、方法、成員變數、區域性變數不可改變。
- final修飾的類不可被繼承下去。無子類,不可被覆蓋重寫。
public final class 類名(){}
- final修飾的方法不可被覆蓋重寫。
public final void method(){}
- 類和方法不能同時使用final和abstract,二者矛盾。abstract表示抽象,是待定的;final表示最終的,是確定的。
- final修飾區域性變數,若是基本資料型別的數值,則不可改變;若是引用資料型別,則地址值不可改變,但地址指向的物件的內容可以改變。
- final修飾成員變數,也不可變,但必須要進行手動賦值,成員變數加final後不允許再擁有預設值。
public final class Demo01Final {
public static void main(String[] args) {
int NUM = 1;
System.out.println(NUM);
NUM = 2;//可以改變
System.out.println(NUM);
final int NUM1 = 2;
// NUM1 = 3; 錯誤,NUM是確定的值,不可被改變。
}
}
六、許可權修飾符
- 許可權大小:
四種許可權修飾符,訪問許可權從大到小:
public > protected > 空 > private
同一個類: yes yes yes yes
同一個包: yes yes yes no
不同包子類: yes yes no no
不同包非子類:yes no no no
七、內部類
一個外部類包含的一個巢狀的類,叫內部類。
分類:
1.成員內部類。
2.區域性內部類(包含匿名內部類)。
- 注意:內部類可以無限制地訪問外部類,外部類訪問內部類需要內部類物件。
- 各個類對應可以使用的許可權修飾符如下:
外部類:public/(default)
成員內部類:public.protect.(default).private
區域性內部類:什麼都沒有,注意並不是(default) - 內部類定義示例:
public class main {
private String name = "成員變數";
//定義一個成員內部類
public class innerClass {
private String innername = "內部成員變數";
}
// 定義一個成員內部類內的成員方法
public void innermethod1() {
System.out.println("內部類方法");
System.out.println(name);
}
}
使用內部類的兩種方式:
1.直接:在main方法中建立內部類物件:格式:
外.內 = new 外().內();
外部類名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱();
public static void main(String[] args) {
//直接建立內部類的物件。
InnerclassUsing.innerClass object = new InnerclassUsing().new innerClass();
object.innermethod1();
}
2.間接:在外部類的方法中,只用內部類,main只是呼叫外部類的方法,通過外部類方法訪問內部類。
public class InnerclassUsing {
private String name = "成員變數";
//定義一個成員內部類
public class innerClass {
private String innername = "內部成員變數";
// 定義一個成員內部類的成員方法
public void innermethod1() {
System.out.println("內部類方法");
System.out.println(name);
}
}
public void outMethod() {
System.out.println("外部類成員方法");
// System.out.println(innername); 錯誤。
//通過匿名物件訪問內部類變數和方法
System.out.println(new innerClass().innername);
//建立匿名物件並呼叫innermethod1方法。
new innerClass().innermethod1();
}
}
內部類的重名變數訪問格式:
- 本方法: 空
- 本類成員變數:this.
- 外部類成員變數:外部類的名稱.this.
public class Demo03CommonName {
//外部類成員變數
int NAME = 1;
public class innerClass{
//內部類成員變數
int NAME = 2;
public void method (){
//內部類區域性變數
int NAME = 3;
System.out.println(NAME);//3,區域性變數,就近原則。
System.out.println(this.NAME);//2,本類成員變數。
System.out.println(Demo03CommonName.this.NAME);//1,外部類的成員變數;
}
}
}
如果一個類定義在方法內部,那麼這個類叫區域性內部類。
區域性內部類只能被當前所屬的方法所使用,外部不可。
格式:
public class Localinnerclass {
String NAME = "外部成員變數";
public void method(){
//定義區域性內部類
class localLinnerClass{
String ONE = "區域性內部類成員變數";
public void nmethod2(){
System.out.println("區域性內部類的成員方法");
System.out.println(ONE);
}
}
}
}
區域性內部類,如果希望訪問所在方法的區域性變數,該變數應該滿足【有效final】的條件
public class Demo04Final {
public void method1(){
//這裡即使不寫final的話,不去修改 也算是有效final [從java8開始]
final String NAME = "bilibili";
class LocalInnerclass{
public void localInnerMethod(){
//區域性內部類內的方法訪問所在類外部的方法的區域性變數
System.out.println(NAME);
}
}
}
}
當介面是實現類只使用唯一一次時,可以使用匿名內部類
- 格式
介面名稱 物件名 = new 介面名稱(){
//這裡進行抽象方法的覆寫。
}; //別忘了結尾的分號
匿名內部類在建立物件的時候,只能使用唯一一次
匿名物件在使用方法時,只能使用唯一一次
匿名內部類是省略了(實現類/子類名稱),而匿名物件則是省略了物件名稱,二者不同。
//當實現類只使用一次時,可以使用匿名內部類的寫法!
Anonymous obj6 = new Anonymous() {
//覆寫匿名內部類裡所有抽象方法!
@Override
public void method1() {
System.out.println("bilibili?");
}
@Override
public void method2(){
System.out.println("覆寫method!");
}
};
obj6.method1();//列印內容
obj6.method2();
將類作為成員變數的寫法
//定義一個武器類
public class Weapon {
private String itsname;//武器名稱
private int attacknum;//武器攻擊力
//無參構造方法
public Weapon() {
}
//全參方法
public Weapon(String itsname, int attacknum) {
this.itsname = itsname;
this.attacknum = attacknum;
}
//get名字
public String getItsname() {
return itsname;
}
//set名字
public void setItsname(String itsname) {
this.itsname = itsname;
}
//get攻擊力
public int getAttacknum() {
return attacknum;
}
//set攻擊力
public void setAttacknum(int attacknum) {
this.attacknum = attacknum;
}
}
//定義一個女武神類
public class Valkyrie1 {
private String name;//女武神之名
private Weapon weapon;//將武器類變為成員變數 交給女武神類
//女武神攻擊方法
public void attack(){
System.out.println(name + "使用的" + weapon.getItsname() + "具有" + weapon.getAttacknum() +"點攻擊力");
}
//無參構造
public Valkyrie1() {};
//全參
public Valkyrie1(String name, Weapon weapon) {
this.name = name;
this.weapon = weapon;
}
//get女武神名字
public String getName() {
return name;
}
//set女武神名字
public void setName(String name) {
this.name = name;
}
//get女武神武器
public Weapon getWeapon() {
return weapon;
}
//set女武神武器
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
}
- 定義了女武神類和武器類後,在main方法中進行呼叫
Valkyrie1 valkyrie = new Valkyrie1();//建立一個女武神
valkyrie.setName("bronya"); //set女武神名字叫bronya
Weapon weapon = new Weapon("真理之鑰",1000);//建立一把武器,並同時賦予名稱和攻擊力
// 也可以分開寫: weapon.setItsname("真理之鑰");//武器名字
// 也可以分開寫: weapon.setAttacknum(1000);//武器攻擊力
valkyrie.setWeapon(weapon);//將定義好的武器交給女武神
valkyrie.attack();//最後,女武神使用這把武器進行攻擊