Java基礎之:OOP——內部類
在一個類A中又完整的宣告了另一個類B,那么類A我們就稱為外部類,類B就稱為內部類,
內部類最大的特點就是可以直接訪問私有屬性,并且可以體現類與類之間的包含關系,
內部類的分類:
-
創建在外部類的方法內部:
-
區域內部類
-
匿名內部類(實際也可以看作是,匿名區域內部類)
-
-
創建在外部類的屬性位置:
-
成員內部類
-
靜態內部類(實際也可以看作是,靜態成員內部類)
-
區域內部類
區域內部類是定義在外部類的方法體中的類,具體使用方法與使用細節如下:
-
1.可以直接訪問外部類的所有成員,包含私有的
-
2.不能添加訪問修飾符,因為它的地位就是一個區域變數,區域變數是不能使用修飾符的,但是可以使用final 修飾,因為區域變數也可以使用final
-
3.作用域僅僅在定義它的方法或代碼塊中,并且遵循前向參考原則
-
4.外部類---訪問------>區域內部類,創建物件呼叫
-
5.外部其他類---不能訪問----->區域內部類 (因為 區域內部類地位是一個區域變數),這點實際上是可以訪問的,不過要用到多型的性質,看下面思考題,
-
6.如果外部類和內部類的成員重名時,內部類訪問的話,默認遵循就近原則,如果想訪問外部類的成員,則可以使用 (外部類名.this.成員)去訪問
package class_inner;
/**
*
* 1.可以直接訪問外部類的所有成員,包含私有的
* 2.不能添加訪問修飾符,因為它的地位就是一個區域變數,區域變數是不能使用修飾符的,
* 但是可以使用final 修飾,因為區域變數也可以使用final
* 3.作用域僅僅在定義它的方法或代碼塊中,并且遵循前向參考原則
* 4.外部類---訪問------>區域內部類,創建物件呼叫
* 5.外部其他類---不能訪問----->區域內部類 (因為 區域內部類地位是一個區域變數)
* 這點實際上是可以訪問的,不過要用到多型的性質,看下面思考題,
* 6.如果外部類和內部類的成員重名時,內部類訪問的話,默認遵循就近原則
* 如果想訪問外部類的成員,則可以使用 (外部類名.this.成員)去訪問
*/
public class LocalInnerDetail {
?
public static void main(String[] args) {
//細節4:創建Outer物件,呼叫say()方法,即呼叫了Inner區域內部類
new Outer().say();
}
}
?
?
class Outer{
private int a;
public void say() {
//細節3:此時Inner類還沒有執行到,不滿足向前參考原則,所以不能創建物件
// Inner inner = new Inner();
class Inner{
private int b;
public void innerShow() {
//細節1:可以直接訪問外部類的所有成員,包含私有的
System.out.println(a);
}
}
//細節2:可以使用fanal修飾,因為區域內部類,地位就相當于區域變數
final class InnerA{
private int a;
public void innerShow() {
//細節6:就近原則 ,訪問到內部類的屬性a
System.out.println(a);
//細節6:訪問外部成員屬性,外部類名.this.成員
System.out.println(Outer.this.a);
}
}
Inner inner = new Inner();
inner.innerShow();
}
//細節3:
// Inner i = new Inner(); //報錯:Inner cannot be resolved to a type
}
關于細節2的補充說明:
區域內部類中只能訪問使用final修飾的區域變數(并且在同一個作用域內):
jdk7 final必須手動添加! jdk8 final是隱式加入,不用手動添加,
原因:防止區域變數訪問范圍擴大!
匿名內部類(重要!!!)
匿名內部類也可以看作是匿名區域內部類,這種內部類在實際開發中經常用到,所以比較重要,
匿名內部類的使用方式以及使用理解如下:
package class_inner.Anonymous;
?
public class AnonymousInner {
?
public static void main(String[] args) {
new XX().m1(); //輸出:AnonymousInner......
}
}
?
class XX{
?
public void m1() {
/*
* 對于a而言,編譯型別為A
* 運行型別為:new 后面一直到 ";" 前面這一部分代碼
* 就像是一個類的宣告 但這個類并沒有名字,宣告出來之后直接通過new產生一個物件回傳給A介面的參考a
* a的運行型別就是一個匿名內部類
*/
A a = new A() {
@Override
public void show() {
System.out.println("AnonymousInner......");
}
};
/*
* 對于show方法呼叫而言,它會先看編譯型別A介面中有沒有show方法,如果有編譯器通過,
* 然后在運行時,通過動態系結找到a的運行型別即匿名內部類中實作了的show方法,再執行,
*/
a.show();
//還可以使用匿名物件來創建匿名內部類,我們之前會使用匿名物件呼叫方法,例如: new A().show
new A() {
@Override
public void show() {
System.out.println("Other_AnonymousInner......");
}
}.show();
}
}
?
//不僅可以使用介面,也可以使用父類創建匿名內部類
interface A{
public void show();
}
匿名內部類的使用細節與區域內部類,基本相同,但由于匿名內部類的使用方式讓我們看起來很"奇特",所以在這里把兩種呼叫方式列出來做一個對比:
package class_inner.Anonymous;
?
public class AnonymousInner {
?
public static void main(String[] args) {
//可以使用匿名內部類,作為引數傳入方法中,呼叫順序于區域內部類相同
say(new B(){
public void show() {
System.out.println("interface_B.....");
}
});
//列印匿名內部類的運行型別,可以看到是沒有名字的,
System.out.println(new B() {
@Override
public void show() {
}
}.getClass());
System.out.println(new Father("jack").getClass()); //匿名物件
System.out.println(new Father("jack") {}.getClass());//匿名內部類
//使用匿名內部類列印輸出資訊
Father f = new Father("jack") {
@Override
public void show() {
System.out.println("AnonymousExtends_Class_Father_name:" + getName());
}
};
f.show();
}
public static void say(B b) {
b.show();
}
}
?
?
interface B{
public void show();
}
?
class Father{
private String name;
?
public String getName() {
return name;
}
?
public void setName(String name) {
this.name = name;
}
?
public Father(String name) {
super();
this.name = name;
}
public void show() {
System.out.println("Class_Father:" + name);
}
}
程式輸出:
interface_B.....
class class_inner.Anonymous.AnonymousInnerDetail$2
class class_inner.Anonymous.Father
class class_inner.Anonymous.AnonymousInnerDetail$3
AnonymousExtends_Class_Father_name:jack
匿名內部類的應用案例
有一個鈴聲介面Bell,里面有個ring方法, 有一個手機類Cellphone,具有鬧鐘功能alarmclock,引數是Bell型別 測驗手機類的鬧鐘功能,通過匿名內部類(物件)作為引數,列印:懶豬起床了 再傳入另一個匿名內部類(物件),列印:小伙伴上課了
package class_inner.Anonymous;
public class AnonymousInnerClassWork {
?
public static void main(String[] args) {
//方式一:匿名物件呼叫方法
new Cellphone().alarmclock(new Bell(){
@Override
public void ring() {
System.out.println("懶豬起床了");
}
});
//方式二:實名物件呼叫方法
Cellphone cellphone = new Cellphone();
cellphone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上課了");
}
});
}
}
?
class Cellphone {
public void alarmclock(Bell b) {
b.ring();
}
}
?
interface Bell{
public void ring();
}
?
成員內部類
成員內部類是宣告在外部類的成員屬性位置上的類,具體使用如下:
package class_inner;
?
import class_inner.OuterM.InnerM;
?
public class MemberInner {
?
public static void main(String[] args) {
//外部其他類---訪問---->成員內部類:
//方式一:匿名物件,訪問成員內部類,呼叫方法
new OuterM().new InnerM().show2();
//方式二:
OuterM outerM = new OuterM();
//如果想要接收InnerM物件,必須要引包
//import class_inner.OuterM.InnerM;
InnerM i = outerM.gerInnnerM();
i.show();
//方式三:
InnerM i2 = new OuterM().gerInnnerM();
i2.show2();
}
?
}
class OuterM{
private String name = "小范";
private int age = 20;
//可以使用所有的訪問修飾符來修飾成員內部類
public class InnerM{
private String name = "小黃";
public void show() {
//可以訪問外部類所有屬性與方法,因為成員內部類本質地位就是成員屬性
//如果外部類和內部類的成員重名時,如果想訪問外部類的成員,則可以使用(外部類名.this.成員)
System.out.println("name:" + OuterM.this.name + " age: " + age);
}
public void show2() {
System.out.println("name:" + name + " age: " + age);
}
}
public InnerM gerInnnerM() {
return new InnerM();
}
}
程式輸出:
name:小黃 age: 20
name:小范 age: 20
name:小黃 age: 20
靜態內部類
靜態內部類又可能看作是靜態成員內部類,因為它就是成員內部類加上了static修飾,
具體使用如下:
package class_inner;
?
import class_inner.OuterM.InnerM;
?
public class MemberInner {
?
public static void main(String[] args) {
//外部其他類---訪問---->成員內部類:
//方式一:匿名物件,訪問成員內部類,呼叫方法
new OuterM().new InnerM().show2();
//方式二:
OuterM outerM = new OuterM();
//如果想要接收InnerM物件,必須要引包
//import class_inner.OuterM.InnerM;
InnerM i = outerM.gerInnnerM();
i.show();
//方式三:
InnerM i2 = new OuterM().gerInnnerM();
i2.show2();
}
?
}
class OuterM{
private String name = "小范";
private int age = 20;
//可以使用所有的訪問修飾符來修飾成員內部類
public class InnerM{
private String name = "小黃";
public void show() {
//可以訪問外部類所有屬性與方法,因為成員內部類本質地位就是成員屬性
//如果外部類和內部類的成員重名時,如果想訪問外部類的成員,則可以使用(外部類名.this.成員)
System.out.println("name:" + OuterM.this.name + " age: " + age);
}
public void show2() {
System.out.println("name:" + name + " age: " + age);
}
}
public InnerM gerInnnerM() {
return new InnerM();
}
}
程式輸出:
name:小范
OuterS_phone:123
InnerS_Sphone:321
特別注意:
-
使用靜態內部類,只需要使用類名呼叫即可,就不會生成OuterS外部類的物件
-
出現重名的屬性時,只需要 (外部類名.屬性)就可以訪問
-
靜態內部類只能訪問外部靜態的內容
思考題
在區域內部類的細節5中提到外部其他類不能訪問區域內部類,但可以通過多型的特性將其實作,
具體方式是:
-
定義一個區域內部類可以訪問到的介面或父類
-
讓區域內部類繼承此介面或父類
-
在外部其他可以使用介面或父類的類中,就可以通過多型的方式使用到區域內部類,
-
這種方式主要就是使用了多型的動態系結機制
package test;
?
public class Test {
public static void main(String[] args) {
A a = new A();
System.out.println("a--hashCode= " + a.hashCode());
A a2 = a.say();
a2.show();
// X x = a.say();
// x.show();
}
}
?
interface X{
public void show();
}
?
class A{
public void show() {
}
private int n1 = 10;
private int n3 = 330;
?
public A /* X */ say() {
int n2 = 20;
class InnerA extends A /* implements X */{
int n3 = 30;
public void show() {
System.out.println("A--n3=" + A.this.n3 + "\tInnerA--n3= " + n3);
System.out.println("A--hashCode=" + A.this.hashCode());
}
}
InnerA innerA = new InnerA();
innerA.show();
return innerA;
}
}
程式輸出:
a--hashCode= 366712642
A--n3=330 InnerA--n3= 30
A--hashCode=366712642
A--n3=330 InnerA--n3= 30
A--hashCode=366712642
說明
在程式中可以使用介面 X 將區域內部類"接出來",也可以使用父類 A 將區域內部類"接出來",
這種思路需要靈活應用動態系結機制,考慮區域內部類的運行型別問題,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/235864.html
標籤:Java
