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

JavaSE之面向物件(下)

1.繼承(關鍵字extends)

繼承是兩個類之間的關係。當類Son繼承了一個已存在的類Father後,類Son就擁有了類Father所有的非private屬性和方法,同時還可以在Son類中新增新的屬性和方法。原來已存在的類稱為父類或基類,也可以稱為超類。新派生的類稱為原來的子類或是派生類。

子類繼承父類語法:

訪問控制符 [修飾符] class 子類名 extends 父類名{
[屬性定義]
[構造方法定義]
[方法宣告]
}

語法說明:

  1. 訪問修飾符:訪問修飾符是public,那麼任意類均可以使用這個類,如果不寫,則只有與該類定義在同一包內才可以訪問這個類
  2. 修飾符:可選,有static final transient volatile
  3. 在宣告類時,使用extends關鍵字表明兩個類之間的繼承關係
  4. 子類可以定義自身的屬性和方法,如果子類定義的屬性或方法與父類相同,那麼父類的這一屬性或方法被隱藏或重寫。通過子類或子類的物件不能直接呼叫父類的這一屬性或方法,但是父類或父類的物件依然可以呼叫這一屬性或方法。當子類執行從父類繼承來的方法,若需使用同名的成員變數時,所使用的是父類的成員變數。
  5. 在類宣告時,若沒有使用extends關鍵字繼承父類,則自動繼承Object類,因此除了根類Object之外,所有的Java類都直接或間接的繼承java.lang.Object類

Java繼承的特點:

  1. 繼承關係可以傳遞,即子類向上相容繼承父類的所有屬性和方法,非私有的是顯式繼承,私有的屬性和方法是隱式繼承,私有的屬性和方法可通過getter、setter的方式訪問。
  2. 繼承簡化了人們對事物的認識和描述,能清晰的體現相關類間的層次結構關係
  3. 繼承減小了程式碼和資料的冗餘度,增加了程式的重用性。
  4. Java只支援單繼承(C++是多繼承的)即class A extends B,C的語法是錯誤的編譯不通過,但可以多層繼承即class B extends C{....} class A extends B

若子類和父類在同一個包內,子類不能直接訪問父類和private的屬性和方法,但可以呼叫非private的屬性和方法。若子類和父類不在同一個包內

,那麼子類可以訪問父類protected、public 的屬性和方法,但不能訪問私有、預設訪問級別的屬性和方法。

重寫父類的方法
在繼承中,不僅可以使用方法過載(在同一個類中實現兩個或兩個以上方法名相同,引數列表不同的方法)還可以實現方法的重寫。
子類中定義的方法使用的方法名、返回型別和引數列表與父類中的方法一樣,則稱為子類的方法重寫了父類的方法。
父類的構造方法不能被重寫,因為構造方法與所在類的類名相同,而子類和父類的類名不同。
當父類的方法被重寫後,子類或子類物件呼叫的是被重寫之後的方法,父類或父類的物件還是可以呼叫被重寫之前的方法。

使用方法重寫的約束

  1. 子類的方法名,返回值型別,引數列表必須與父類的方法名,返回值型別,引數列表完全一致。
  2. 子類不能縮小父類方法的訪問許可權。
  3. 父類的靜態方法不能被子類重寫為非靜態方法,父類的非靜態方法也不能被子類重寫為靜態方法,子類可以定義與父類的靜態方法同名的靜態方法,以便在子類中隱藏父類的靜態方法。
  4. 子類方法不能丟擲比父類方法更多的異常
  5. 父類中的私有方法不能被子類重寫
  6. 父類中的抽象方法可以被子類通過兩種途徑重寫:1.實現父類的抽象方法2.子類重新宣告父類的抽象方法
  7. 父類中的非抽象方法可以被子類重寫為抽象方法

構造方法與繼承

在繼承中,子類物件在例項化之前必須先呼叫父類的構造方法,再呼叫子類自身的構造方法,在子類中,通過關鍵字super呼叫 與super([引數列表])方法中引數列表相同的父類的構造方法,若子類中沒有特別指定super([引數列表])方法,則預設新增並呼叫無參的super()方法。
呼叫父類構造方法的super()方法要寫在子類構造方法的首行。
this()和super()呼叫構造方法時不能同時出現,因為都需要放在首行。
super關鍵字
在繼承中,使用super關鍵字可以引用父類的屬性、方法和構造方法。
如果在子類中隱藏了父類的屬性及重寫了父類的方法,那麼需要在子類中呼叫父類被覆蓋的屬性或方法就不能直接呼叫了,需要使用super關鍵字。

內部類

在一個類Outer的內部定義一個類Inner,此時Outter稱為外部類,Inner稱為內部類。類的定義是可以多層巢狀的。
內部類中定義的成員要比外部定義的成員具有更嚴格的隱藏資訊功能。

內部類的作用:

  1. 內部類的物件可以訪問外部類的所有屬性和方法,包括私有的,因為內部類被當作了外部類的成員,同一個類的成員可以相互訪問。但是外部類不能訪問內部類中的實現細節,這一特點彌補了繼承的缺憾。
class Outer{
	private String msg = "這是寫在外部類的私有屬性";
	
	//定義一個成員內部類
	class Inner{
	
		int num=2;

		public void print(){		
		//列印外部類的私有屬性	
		System.out.println(msg);		
		}
	}
	
	public void fun(){	
		Inner in =new Inner();
		in.print();		
	}
}

public class InnerTest{
	
	public static void main(String[] args){
		//建立外部類物件,呼叫內部類方法
		Outer ou =new Outer();
		ou.fun();	
	}
}

在這裡插入圖片描述
2. 內部類提供了更好的封裝,可以隱藏在外部類之中,不被同一個包的其他類所見。
3. 匿名內部類可以方便 的定義執行時回撥和用於僅需要一次使用的類
頂級類(最外層的類)只有兩種訪問控制權限public與預設;內部類有四種訪問控制權限:public protected 預設 private

在外部類中建立內部類內部類.內部物件名=new 內部類();
在外部類以外的其他類中訪問內部類外部類.內部類 內部類物件 = new 外部類().new 內部類();

class Outer{
//定義外部類的私有屬性
private String s="this is 外部類";

//宣告內部類
public class Inner{
	//定義內部類的方法	
	public void add(int x,int y){
		
		System.out.println(x+"+"+y+"="+(x+y));
		
	}
	public void getStr(){
		
		//內部類中直接訪問外部類成員屬性
		System.out.println(s);
	}
}

//從外部呼叫內部類的方法
public void getInofo(){
	
	//匿名內部類物件呼叫外部類方法
	new Inner().getStr();
	//宣告建立內部類例項物件
	Inner in = new Inner();
	//例項呼叫內部類方法
	in.add(7,8);
}
}

public class InnerTest2{
	
	
	public static void main(String[] args){
		//在其他類中建立外部類的例項物件
		Outer out=new Outer();
		//外部類例項呼叫外部類的方法
		out.getInofo();
		//在其他類中建立內部類的例項物件
		Outer.Inner in = new Outer().new Inner();
		//呼叫內部類的方法
		in.add(1,2);
		in.getStr();
	}
}

內部類的分類
根據定義在內部類在外部類定義的位置不同可分為:Ⅰ.區域性內部類,是指定義在外部類方法體內部的內部類。Ⅱ.成員內部類,是指定義在外部類方法體外部的內部類。
成員內部類是依附於外圍類的,只有先建立了外圍類才能建立內部類
(1)例項內部類
例項內部類是指宣告在外部的方法體外的,即與外部類的成員(屬性、方法)同級。沒有static修飾的內部類。
例項內部類的特點

  1. 在例項內部類中不能存在任何靜態的變數或方法。
  2. 外部類的靜態方法外部類以外的其他類,若需要訪問內部類,則必須通過外部類建立內部類的訪問例項訪問。外部類.內部類 內部類物件 = new 外部類().new 內部類();
  3. 例項內部類可以訪問外部類的所有成員屬性,在外部類中不能直接訪問內部類的成員,必須通過內部類的例項訪問。
  4. 多層巢狀中,Outer類包含Inner1,Inner1類又包含Inner2,則在Outer類中不能直接訪問Inner2,應通過Inner1的例項訪問Inner2類。
class Outer{
//宣告第一層內部類
public class Inner1{
	//宣告第二層內部類
	public class Inner2{
		
		//定義內部類的方法	
	public void add(int x,int y){		
		System.out.println(x+"+"+y+"="+(x+y));		
			}
		}
	}
	
	//外部類中定義普通方法想要訪問Inner2類
	public void getAdd(){
		//外部類通過 Inner1的例項訪問Inner2類
		Inner1.Inner2 in= new Inner1().new Inner2();
		in.add(3,4);
	}
}
  1. 如果在例項內部類Inner與外部類Outer有著同名的成員變數i,則在內部類中,i、this.i、Inner.this.i 都表示Inner類的成員,而Outer.this.i才表示外部類Outer的成員。
class Outer{
//外部類的成員屬性
int i=10;
public class Inner{
	
	//內部類的同名成員屬性
	int i=20;
	//內部類的方法
	public void getOuter(){
	System.out.println("i表示Inner的成員i:"+i);//20
	System.out.println(this.i);//20
	System.out.println(Inner.this.i);//20
	System.out.println(Outer.this.i);//10

	}
}
//外部類的同名方法
public void getOuter(){
	
	System.out.println(Outer.this.i);//10
	
	}
}
public class InnerTest3{
	
	public static void main(String[] args){
		
		//通過匿名內部類的物件呼叫內部類的方法
		new Outer().new Inner().getOuter();
		
		//通過匿名外部類的物件呼叫外部類的方法
		new Outer().getOuter();		
	}
}

(2)靜態內部類
靜態內部類是宣告在外部類中,作為外部類的靜態成員的,即使用static 關鍵字修飾的內部類。與例項內部類只能宣告非靜態成員不同,靜態內部類既可以宣告靜態成員,也可以宣告非靜態成員,靜態內部類不能訪問外部類的非靜態成員,只能訪問外部類的靜態成員(只能通過外部類的例項或物件訪問)。靜態內部類的物件例項可以獨立建立,靜態內部類類似於頂層類,只不過被定義在了一個類的內部。
靜態內部類建立物件例項:

外部類.靜態內部類 靜態內部類物件名 = new 外部類.靜態內部類();
class Outer{
	
	//定義外部類屬性
	private int a=4;
	//定義外部類靜態屬性
	private static int b=5;
	
	//定義靜態內部類
	public static class Inner{

		private int x=5;//可定義非靜態屬性
		private static int y=6;//可定義靜態屬性
		//可定義非靜態方法
		public void add(int x,int y){
			
			int temp=new Outer().a;
			//int temp=a;//error
			int x=b;//在靜態內部類中可直接訪問外部類的靜態屬性
			System.out.println(temp+"x"+x+"="+(temp+x));
		}
		//可定義靜態方法
		public static void reduce(int x,int y){
			System.out.println(x+"-"+y+"="+(x-y));
		}
	}
	
	//外部類的普通方法
	public void getInfo(){
		//通過靜態內部類的匿名物件呼叫內部類的方法
		new Inner().add(4,3);
		//可通過靜態內部類的類名直接呼叫靜態內部類的方法
		Inner.reduce(4,3);
	}
}
public class StaticInner{
	
	public static void main(String[] args){
		//建立外部類例項
		Outer out=new Outer();
		out.getInfo();
		//建立內部類例項
		Outer.Inner in =new Outer. Inner();
		in.add(4,3);
		in.reduce(4,3);
	}
}

(3)區域性內部類
區域性內部類是指定義在方法體的內部類,區域性內部類僅在該方法有效。
區域性內部類不能被外部類以及外部類以外的其他類訪問,因此區域性內部類是不需要(也不能被)訪問控制符和static修飾的,
區域性內部類不能定義static成員,
區域性內部類可以訪問外部類的靜態成員,
若需呼叫外部類的非靜態成員,可以通過外部類的例項。
區域性內部類只能訪問所在方法的final型別的引數和變數,若不宣告,系統自動將變數或形參轉為final型別的變數(JDK8形參變為隱式final宣告).

class Outer{
	//定義外部類的非靜態私有屬性
	private float f=0.1f;
	//定義外部類的靜態私有屬性
	private static int a=7;
	//定義外部類的普通方法
	public void fun(){
		
		//在普通方法裡定義內部類稱為區域性(方法內部類)
		class Inner{
			//方法內部類中定義私有屬性
			private float innerF=4.3f;
			public Inner(){
				System.out.println(f+"+"+innerF+"="+(f+innerF));
			}
		}
		//在外部類普通方法裡建立內部類匿名物件,JVM才能載入方法內部類
		new Inner();
	}
	//外部類中定義靜態方法
	public static void fun2(){
		//靜態方法中定義內部類,也不能加sattic 和 訪問修飾符
		class Inner2{
			private int i=2;
			public Inner2(){
				//在靜態方法內部類中直接呼叫外部類的靜態私有屬性a
				System.out.println(i+"+"+a+"="+(i+a));
				//在靜態方法內部類中直接通過匿名物件呼叫外部類的私有屬性
				float temp= new Outer().f;
				System.out.println(i+"+"+temp+"="+(i+temp));
			}
		}
		//在外部類靜態方法裡建立內部類匿名物件
		new Inner2();
	}
}
public class PartInner{
	
	public static void main(String[] args){
		Outer out = new Outer();
		out.fun();
		out.fun2();
	}
}

(4)匿名內部類
匿名內部類是指在定義時沒有名稱的類,必須在宣告時使用new語句 宣告類。匿名內部類是一種特殊的內部類,除了具有普通類的特點以外,還有自己的特點。匿名內部類一般只使用一次。
匿名內部類語法:

new <類或介面>([引數列表]){
.....
}

引數列表表示呼叫父類構造方法時傳入的引數,匿名內部類只在其定義的程式碼塊內使用一次,所有無法為其定義構造方法。匿名內部類總是使用父類的構造方法建立例項,如果匿名內部類實現的時介面,那麼匿名內部類的構造方法就是Object().

雖然匿名內部類沒有類名,匿名內部類必須擴充套件一個基類或實現一個介面,但不能明顯的使用extends 或 implements關鍵字。若匿名內部類繼承抽象類或實現介面時,還要實現父類及介面中的所有抽象方法。有名稱的類若沒有顯式的指定父類,系統會讓其自動繼承Object類,但匿名內部類不會自動繼承Object類,所有每個匿名內部類都要明確的指出它繼承的類或實現的介面。
匿名內部類繼承抽象類

abstract class AbstrClass{//定義抽象類
	
	public abstract void getInfo();//宣告抽象類
	
} 
class Outer{//定義類(匿名內部類的外部類)
	
	public void print(){
	//在print方法中呼叫show方法,show方法的形參中開闢了抽象類的匿名物件
	//實現了抽象類的抽象方法,使之成為匿名內部類
		show(new AbstrClass(){  
			public void getInfo(){ //實現了抽象類中的方法
			System.out.println("Java匿名內部類");
			}
		});//show方法的呼叫實現了Outer類繼承抽象類成為匿名內部類
	}
	public void show(AbstrClass a){//定義show 方法,形參為AbstrClass類的例項物件
		
		a.getInfo();
	}
}
public class AnonymityInner{
	public static void main(String[] args){
		new Outer().print();//建立外部部類的物件並呼叫匿名內部類的方法
	}
}

匿名內部類實現介面


interface Inter{//定義介面類Inter
	public abstract void getInfo();
}
class InterClass{//定義類InnerClass(匿名內部類的外部類)
	
	public void print(){//定義類InnerClass類中的普通方法
		show(new Inter(){//呼叫show方法
		
	//在show方法的引數中通過匿名物件實現介面類的抽象方法,使此類成為匿名內部類	
			public void getInfo(){
				System.out.println("Java匿名內部類,實現介面");
			}
		});
	}
	
	public void show(Inter i){
		i.getInfo();
	}
}
public class AnonymityInner{
	public static void main(String[] args){
		new InterClass().print();//建立外部類 InterClass的物件並呼叫方法
	}
}

匿名內部類可以訪問外部類的所有成員,但匿名內部類定義在方法之中,只能訪問方法中的final型別的引數和區域性變數。

abstract class AbstrClass{//定義抽象類
	
	public abstract void getInfo();//宣告抽象類
	
}
class InterClass2{
	public void print(int i,final int k){
		int x=10;
		final int y =20;
		show(new Inter(){
			public void getInfo(){
				System.out.println("print方法的final型別引數:"+k);
				System.out.println("print方法的final型別區域性變數:"+y);
				System.out.println("print方法的非final型別引數:"+i);
				System.out.println("print方法的非final型別區域性變數:"+x);
			}
		});
	}
	public void show(Inter i){
		i.getInfo();
	}
}
public class AnonymityInner{
	public static void main(String[] args){
		new InterClass2().print(3,5);
	}
}

在這裡插入圖片描述
匿名內部類定義在方法之中,能訪問方法中的非final型別的引數和區域性變數是因為JDK8會將區域性變數和形參變為隱式final宣告

匿名內部類允許非靜態程式碼塊對成員進行初始化:

class InterClass3{
	public void print(){
		show(new Inter(){
			int x;
			{
				x=10;
			}
			public void getInfo(){
				System.out.println("x="+x);
			}
		});
	}
	public void show(Inter i){
		i.getInfo();
	}
}
public class AnonymityInner{
	public static void main(String[] args){
		new InterClass3().print();//建立inner匿名內部類的物件並呼叫匿名內部類的方法
	}
}