讓別人做網(wǎng)站是要每年續(xù)費嗎網(wǎng)絡(luò)營銷課程總結(jié)與心得體會
內(nèi)部類
- 引言
- 內(nèi)部類的共性
- 成員內(nèi)部類
- 靜態(tài)內(nèi)部類
- 非靜態(tài)內(nèi)部類
- 局部內(nèi)部類
- 匿名內(nèi)部類
- 內(nèi)部類的使用場景和好處
引言
Java不僅可以定義變量和方法,還可以定義類.
內(nèi)部類允許你把一些邏輯相關(guān)的類組織在一起,并可以控制內(nèi)部中類的可見性.
這么看來,內(nèi)部類就像是代碼一種隱藏機制:將類放在其他類的內(nèi)部,從而隱藏名字和組織代碼的模式.
根據(jù)定義方式的不同,分為四種類型: 靜態(tài)內(nèi)部類, 成員內(nèi)部類,局部內(nèi)部類,匿名內(nèi)部類
內(nèi)部類的共性
- 依然是一個獨立的類,在編輯之后內(nèi)部類會被編輯成獨立的.class文件,但是前面會添加外部類的類名和$符號
- 聲明為靜態(tài)的,就不能隨便訪問外部類的成員變量了,此時內(nèi)部類只能訪問外部類的靜態(tài)成員變量或方法
- 外部類不能直接訪問內(nèi)部類的成員,但可以通過內(nèi)部類對象來訪問
內(nèi)部類是外部類的一個成員,因此內(nèi)部類可以自由訪問外部類的成員變量,無論是否為private.
因為某個外圍類對象創(chuàng)建內(nèi)部類對象時,此內(nèi)部類會捕獲一個隱式引用,它引用了實例化內(nèi)部對象的外圍類對象,通過這個指針,可以訪問外圍類對象的全部狀態(tài).
實現(xiàn)原理
反編譯內(nèi)部類字節(jié)碼,分析主要通過以下幾步做到的:
- 編譯器為內(nèi)部類添加一個成員變量,它的類型和外部類的類型相同,這個成員變量就是指向外部類對象的引用
- 編譯器為內(nèi)部類的構(gòu)造方法添加一個參數(shù),參數(shù)類型是外部類類型,在構(gòu)造方法內(nèi)部使用這個參數(shù)為1中添加的成員變量賦值
- 在調(diào)用內(nèi)部類的構(gòu)造函數(shù)初始化內(nèi)部類對象時,會默認(rèn)傳入外部類的引用
下面我們舉個例子來看一下:
public class Outter {private Inner inner = null;public Outter() {}public Inner getInnerInstance() {if(inner == null)inner = new Inner();return inner;}protected class Inner {public Inner() {}}
}
反編譯Outter$Inner.class文件得到下面信息:
E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$Inner
Compiled from "Outter.java"
public class com.cxh.test2.Outter$Inner extends java.lang.ObjectSourceFile: "Outter.java"InnerClass:#24= #1 of #22; //Inner=class com/cxh/test2/Outter$Inner of class com/cxh/tes
t2/Outterminor version: 0major version: 50Constant pool:
const #1 = class #2; // com/cxh/test2/Outter$Inner
const #2 = Asciz com/cxh/test2/Outter$Inner;
const #3 = class #4; // java/lang/Object
const #4 = Asciz java/lang/Object;
const #5 = Asciz this$0;
const #6 = Asciz Lcom/cxh/test2/Outter;;
const #7 = Asciz <init>;
const #8 = Asciz (Lcom/cxh/test2/Outter;)V;
const #9 = Asciz Code;
const #10 = Field #1.#11; // com/cxh/test2/Outter$Inner.this$0:Lcom/cxh/t
est2/Outter;
const #11 = NameAndType #5:#6;// this$0:Lcom/cxh/test2/Outter;
const #12 = Method #3.#13; // java/lang/Object."<init>":()V
const #13 = NameAndType #7:#14;// "<init>":()V
const #14 = Asciz ()V;
const #15 = Asciz LineNumberTable;
const #16 = Asciz LocalVariableTable;
const #17 = Asciz this;
const #18 = Asciz Lcom/cxh/test2/Outter$Inner;;
const #19 = Asciz SourceFile;
const #20 = Asciz Outter.java;
const #21 = Asciz InnerClasses;
const #22 = class #23; // com/cxh/test2/Outter
const #23 = Asciz com/cxh/test2/Outter;
const #24 = Asciz Inner;{
/看中間的代碼
final com.cxh.test2.Outter this$0; //注意這一行代碼!!!
/看中間的代碼
public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);Code:Stack=2, Locals=2, Args_size=20: aload_01: aload_12: putfield #10; //Field this$0:Lcom/cxh/test2/Outter;5: aload_06: invokespecial #12; //Method java/lang/Object."<init>":()V9: returnLineNumberTable:line 16: 0line 18: 9LocalVariableTable:Start Length Slot Name Signature0 10 0 this Lcom/cxh/test2/Outter$Inner;
}
final com.cxh.test2.Outter this$0;
這是一個指向外部類對象的指針.
也就是說,編譯器會默認(rèn)成員內(nèi)部類添加了一個指向外部類對象的引用
那這個引用如何賦初值呢?
下面接著看一下內(nèi)部類的構(gòu)造器
public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);
雖然我們定義的內(nèi)部類的構(gòu)造器說無參構(gòu)造器,但是編譯器還是默認(rèn)添加一個參數(shù),該參數(shù)類型為指向外部類對象的一個引用.
所以成員內(nèi)部類中的Outter this&0
指針便指向類外部類對象,因此可以在成員內(nèi)部類中隨意訪問外部類的成員.
從這里也間接說明了成員內(nèi)部類是依賴外部類的,如果沒有創(chuàng)建外部類的對象,則無法對Outter this&0
引用進(jìn)行初始化賦值,也就無法創(chuàng)建成員內(nèi)部類的對象了.
成員內(nèi)部類
成員內(nèi)部類像是外部類的一個成員,它定義在另一個類的內(nèi)部
成員內(nèi)部類分為兩種:
- 靜態(tài)成員內(nèi)部類: 使用static修飾類
- 非靜態(tài)成員內(nèi)部類: 不實用static修飾類,在沒說明是靜態(tài)成員內(nèi)部類時,默認(rèn)成員內(nèi)部類指的是非靜態(tài)成員內(nèi)部類
靜態(tài)內(nèi)部類
定義在類內(nèi)部的靜態(tài)類,就是靜態(tài)內(nèi)部類.
靜態(tài)內(nèi)部類不需要依賴外部類,這點和靜態(tài)成員屬性類似,并且它不能使用外部類的非Static成員變量和方法.
因為沒有外部類對象的情況下,我們可以創(chuàng)建出靜態(tài)內(nèi)部類對象,這時候如果允許訪問外部類的非Static成員就會產(chǎn)生矛盾,因為外部類的非Static成員必須依附于具體的對象.
public class Out {private static int a;private int b;public static class Inner {public void print() {System.out.println(a);}}
}
訪問作用域: 可以訪問外部類所有的靜態(tài)變量和方法,即使是private也一樣可以訪問
與類不同點: 和一般類一致,可以定義靜態(tài)變量,方法,構(gòu)造方法等
使用的方法: 外部類.靜態(tài)內(nèi)部類。如:Out.Inner inner = new Out.Inner();inner.print();
非靜態(tài)內(nèi)部類
最普通的內(nèi)部類,定義位于另一類的內(nèi)部,如下面形式
class Circle {double radius = 0;public Circle(double radius) {this.radius = radius;}class Draw { //內(nèi)部類public void drawSahpe() {System.out.println("drawshape");}}
}
類Draw像是Circle的一個成員,Circle稱為外部類.
成員內(nèi)部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態(tài)成員)
但是外部類訪問內(nèi)部類成員,首先要創(chuàng)建一個成員內(nèi)部類的對象,再通過指向這個對象的引用來訪問
- 內(nèi)部類訪問外部類
class Circle {private double radius = 0;public static int count =1;public Circle(double radius) {this.radius = radius;}class Draw { //內(nèi)部類public void drawSahpe() {System.out.println(radius); //外部類的private成員System.out.println(count); //外部類的靜態(tài)成員}}
}
- 外部類訪問內(nèi)部類
class Circle {private double radius = 0;public Circle(double radius) {this.radius = radius;getDrawInstance().drawSahpe(); //必須先創(chuàng)建成員內(nèi)部類的對象,再進(jìn)行訪問}private Draw getDrawInstance() {return new Draw();}class Draw { //內(nèi)部類public void drawSahpe() {System.out.println(radius); //外部類的private成員}}
}
注意:
成員內(nèi)部類和外部類同名的成員變量或者方法時,會發(fā)生隱藏現(xiàn)象,即默認(rèn)情況下訪問的是成員內(nèi)部類的成員.
如果要訪問外部類的同名成員,需要以下面的形式進(jìn)行訪問:
外部類.this.成員變量
外部類.this.成員方法
局部內(nèi)部類
定義在一個方法或者一個作用域里面的內(nèi),它和成員內(nèi)部類的區(qū)別在于局部內(nèi)部類的訪問僅限于方法內(nèi)或者該作用域內(nèi).
class People{public People() {}
}class Man{public Man(){}public People getWoman(){class Woman extends People{ //局部內(nèi)部類int age =0;}return new Woman();}
}
注意: 局部內(nèi)部類就像是方法里面的一個局部變量一樣,是不能有public,protected,private以及static修飾符的.
匿名內(nèi)部類
這個應(yīng)該是我們編寫代碼時用的最多的,在編寫事件監(jiān)聽的代碼時使用匿名內(nèi)部類不但方便,而且使代碼更容易維護(hù).
public class AnonymousInnerClassExample { public static void main(String[] args) { // 創(chuàng)建一個訂單處理系統(tǒng)實例 OrderProcessingSystem system = new OrderProcessingSystem(); // 創(chuàng)建一個訂單實例 Order order = new Order("123", 100.0); // 使用匿名內(nèi)部類實現(xiàn)一個打印訂單詳情的處理器 system.process(order, new OrderProcessor() { @Override public void processOrder(Order order) { System.out.println("Processing order: " + order); } }); // 使用另一個匿名內(nèi)部類實現(xiàn)一個打折處理訂單的邏輯 system.process(order, new OrderProcessor() { @Override public void processOrder(Order order) { double discount = 0.1; // 假設(shè)打10%的折扣 double discountedAmount = order.getAmount() * (1 - discount); System.out.println("Processing discounted order: " + order + " with discounted amount: " + discountedAmount); } }); }
}
使用匿名內(nèi)部類的好處顯而易見
- 比較簡潔,只需要實現(xiàn)一個接口的簡單任務(wù),使用它可以避免創(chuàng)建額外命名類
- 可以訪問其外部類所有成員(包括私有成員),在某些場景下非常靈活
內(nèi)部類的使用場景和好處
為什么Java需要內(nèi)部類呢?總結(jié)有下面四點:
- 每個內(nèi)部類都能獨立的繼承一個接口的實現(xiàn),無論外部類是否已經(jīng)繼承了某個(接口)實現(xiàn),對于內(nèi)部類都沒有影響.
解釋一下:
Java本身不支持類的多繼承,但可以通過內(nèi)部類來實現(xiàn)接口的多繼承效果.我們可以在一個外部類中定義多個內(nèi)部類,每個內(nèi)部類都可以實現(xiàn)不同的接口,從而達(dá)到類類似多繼承的效果.
舉個簡單的代碼例子來說明這個概念:
// 定義一個接口A
interface InterfaceA { void methodA();
} // 定義另一個接口B
interface InterfaceB { void methodB();
} // 外部類
class OuterClass { // 外部類可以繼承自一個類,這里我們假設(shè)它繼承自O(shè)bject(實際上所有類都隱式繼承自O(shè)bject) // 內(nèi)部類1,實現(xiàn)接口A private class InnerClassA implements InterfaceA { @Override public void methodA() { System.out.println("Implementing methodA from InterfaceA in InnerClassA"); } } // 內(nèi)部類2,實現(xiàn)接口B private class InnerClassB implements InterfaceB { @Override public void methodB() { System.out.println("Implementing methodB from InterfaceB in InnerClassB"); } } // 可以提供獲取內(nèi)部類實例的方法 public InterfaceA getInnerClassA() { return new InnerClassA(); } public InterfaceB getInnerClassB() { return new InnerClassB(); }
} // 使用示例
public class Main { public static void main(String[] args) { OuterClass outer = new OuterClass(); // 獲取并調(diào)用內(nèi)部類A的方法 InterfaceA innerA = outer.getInnerClassA(); innerA.methodA(); // 獲取并調(diào)用內(nèi)部類B的方法 InterfaceB innerB = outer.getInnerClassB(); innerB.methodB(); }
}
- 方便將存在一定邏輯關(guān)系的類組織在一起,又可以對外界隱藏
- 方便編寫事件驅(qū)動程序
- 方便編寫線程代碼