故鄉魂
阿新 • • 發佈:2020-07-11
https://bbs.pku.edu.cn/v2/post-read.php?bid=459&threadid=17733059
package com.example.nestedclassdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 巢狀類 Nested classes * - 靜態巢狀類: Static nested classes,即類前面有static修飾符 * - 非靜態巢狀類: Non-static nested classes,又名內部類,Inner classes * - 普通內部類(亦翻譯為:成員內部類) * - 區域性內部類(Local classes) * - 匿名內部類(Anonymous classes) * * 為什麼需要巢狀類 * - 不同的訪問許可權要求,更細粒度的訪問控制 * - 簡潔,避免過多的類定義 * - 語言設計過於複雜,較難學習和使用 * * 匿名內部類:Anonymous classes * - 沒有類名的內部類,必須繼承一個父類/實現一個父介面 * - 在例項化以後,迅速轉型為父類/父介面 * - 這種型別的物件,只能new一個物件,之後以物件名字操作 * - 可在普通語句和成員變數賦值時使用內部類 * * 區域性內部類:Local classes * - 定義在程式碼塊中的非靜態的類,如方法,for迴圈,if語句等 * - 定義後,即可建立物件使用 * - 只能活在這個程式碼塊中,程式碼塊結束後,外界無法使用該類 * * 瞭解匿名內部類和區域性內部類 * - 兩者幾乎相似 * - 區域性內部類可以重用,匿名內部類不能重用 * - 匿名內部類更簡潔 * * 普通內部類 * - 非static的類,定義在某個類的成員變數位置 * - 定義後,在類裡面均可以使用 * * 靜態巢狀類 * - 層級和包圍類(enclosing class)的成員變數/方法一樣 * - 第三方需要通過外部包圍類才可以訪問到靜態巢狀類 * * 瞭解普通內部類和靜態巢狀類 * - 兩者都定義在外圍類中的成員級別 * - 靜態巢狀類不依賴於外圍類物件,但是隻能訪問外圍類的靜態成員 * - 普通內部類必須依賴於外圍類物件,不能單獨存在,但是可以訪問外圍類的所有成員 * * 四種類對比(1) * Oracle官方文件比較 * - 匿名內部類:應用它,如果需要定義額外的變數和方法 * - 區域性內部類:在一個方法內,需要建立一個新的型別,並重復使用 * - 普通內部類:和區域性內部類相似,在一個類中定義,可重複使用,可以訪問外部類成員,但不需要訪問外部類方法的形參和內部變數 * - 靜態巢狀類:在一個類中定義,可重複使用,並需要訪問外部類的靜態成員 * * 外部訪問規則 * 外部訪問和修飾符關係 * - 普通內部類和靜態巢狀類可以被外部訪問 * - 外部訪問普通內部類和靜態巢狀類,和普通類之間訪問規則一樣 * * 變數遮蔽 Shadowing * - 巢狀類變數和外部包圍類的變數重名 * - 以離得近作為優先原則 * - 優先順序高的變數會遮蔽優先順序低的變數 * - 外部包圍類.this.變數名,可以訪問到外部包圍類的成員變數 * - 靜態巢狀類不能訪問非靜態變數 * - Java7及以前,匿名內部類和區域性內部類只能訪問外部包圍類的final成員變數 * - Java8及以後,匿名內部類和區域性內部類可訪問外部包圍類的final成員變數和事實意義上的final變數 * * * * * * * * */ @SpringBootApplication public class NestedclassDemoApplication { public static void main(String[] args) { SpringApplication.run(NestedclassDemoApplication.class, args); } }
package com.example.nestedclassdemo; /** * 匿名內部類 * - 沒有正式類名的內部類 * - 編譯器產生內部名字: 類名+$+數字編號 * - 沒有類名,沒有建構函式,能用父類/父介面的建構函式(可帶引數) * - 可以繼承,改寫,補充父類/父介面的方法 * - 內部不可以新定義靜態成員(變數+方法),常量除外 * - final static int a = 5; * - 可以訪問外部包圍類的成員變數和方法(包括private) * - 如果定義在靜態方法中,也只能訪問外部包圍類的靜態成員 * - 沒有類名,外部包圍類和其他類也無法訪問到匿名內部類 */ public class Outer1 { private String name = "abc"; public void f1(){ String name = "def"; Runnable r = new Runnable() { //匿名內部類不能定義靜態變數,除非是常量 public final static int a = 5; //public static int b = 3; String name = "ghi"; @Override public void run() { System.out.println("hello "+name); } //靜態方法不能在匿名內部類定義 //public static void f2(){} }; new Thread(r).start(); System.out.println(r.getClass().getName()); //編譯後會產生第二個class匿名內部類 Runnable r2 = new Runnable() { @Override public void run() { System.out.println("hello "+Outer1.this.name); } }; new Thread(r2).start(); System.out.println(r2.getClass().getName()); } }
package com.example.nestedclassdemo; import java.io.Serializable; /** * 區域性內部類: Local classes * - 編譯後名稱: 外部類名+$+序號+內部類名 * - 可以繼承其他類,或者實現其他介面 * - 非靜態的類,不能包含靜態成員(變數和方法),除了常量 * - 可以訪問外部包圍類的成員 * - 如果定義在靜態方法中,只能訪問包圍類的靜態成員 * - 區域性內部類不能是一個介面,即介面不能定義在程式碼塊中 */ public class Outer2 { String name = "abc"; //區域性內部類 public void f1(){ String name = "def"; class Inner2 extends Outer1 implements Runnable, Serializable { final static int a = 1; //不允許定義靜態普通類成員 //static int b = 2; String name = "ghi"; public void f2(){ System.out.println(name); System.out.println(Outer2.this.name); } @Override public void run() { System.out.println(name); } } Inner2 obj1 = new Inner2(); obj1.f2(); System.out.println(obj1.getClass().getName()); } //區域性內部類 public static void f2(){ final String name = "def"; class Inner2{ public String f2(){ return "this is f2()"; } } Inner2 inner2 = new Inner2(); String s = inner2.f2(); System.out.println(s); System.out.println(inner2.getClass().getName()); } }
package com.example.nestedclassdemo.shadow;
public class ShadowTest {
public int x = 0;
class FirstLevel{
public int x = 1;
void methodInFirstLevel(int x){
System.out.println("x = "+x); //line 10 x
System.out.println("this.x = "+this.x); //line 8 x
System.out.println("ShadowTest.this.x = "+ShadowTest.this.x); //line 5 x
}
}
public static void main(String... args){
ShadowTest st = new ShadowTest();
ShadowTest.FirstLevel f1 = st.new FirstLevel();
f1.methodInFirstLevel(20);
}
}
package com.example.nestedclassdemo.shadow;
public class ShadowTest2 {
public int x = 0;
public void f1(){
//區域性內部類無法訪問得到!
int x = 20;
class FirstLevel{
public int x = 1;
void methodInFirstLevel(int x){
System.out.println("x = "+x); //line 14 x
System.out.println("this.x = "+this.x); //line 12 x
System.out.println("ShadowTest.this.x = "+ShadowTest2.this.x); //line 5 x
}
}
FirstLevel obj = new FirstLevel();
obj.methodInFirstLevel(10);
}
public static void main(String... args) {
ShadowTest2 st = new ShadowTest2();
st.f1();
}
}
package com.example.nestedclassdemo.shadow;
public class ShadowTest3 {
public int x = 0;
public void f1(){
//可以訪問得到,沒有被遮蔽!
int x = 20;
class FirstLevel{
void methodInFirstLevel(){
System.out.println("x = "+x);//line 9 x
System.out.println("ShadowTest3.this.x = "+ShadowTest3.this.x); //line 5 x
}
}
//x=30;
FirstLevel obj = new FirstLevel();
obj.methodInFirstLevel();
}
public static void main(String... args) {
ShadowTest3 st = new ShadowTest3();
st.f1();
}
}
package com.example.nestedclassdemo.outer3;
public class Animal {
String name;
}
package com.example.nestedclassdemo.outer3;
public interface Flyable {
void fly();
}
package com.example.nestedclassdemo.outer3;
/**
* 普通內部類
* - 編譯後名稱: 外部類名+$+內部類名
* - 可以繼承其他類,或者實現其他介面
* - 可以用private/package private(不寫)/protected/public控制外界訪問
* - 非靜態的類,不能包含靜態變數/方法,除了常量
* - 和外部包圍類的例項相關,一個普通內部類例項肯定是在一個外部包圍類的例項中,
* 且可以訪問外部包圍類的所有成員
* - 在第三方類中,需要先建立外部包圍類例項,才能建立普通內部類的例項,
* 不允許單獨的普通內部類物件存在!!!
*/
public class Outer3 {
String name = "aaaaaa";
public class Bird extends Animal implements Flyable{
//常量OK
public static final int a = 3;
//不能定義非常量的static變數
//public static int b = 4;
@Override
public void fly() {
System.out.println(Outer3.this.name+" can fly");
}
}
public Bird b1 = new Bird();
public void f1(){
b1.fly();
System.out.println(b1.getClass().getName());
this.name = "bbbbbb";
b1.fly();
}
public Bird getBird(){
return this.b1;
}
}
package com.example.nestedclassdemo.outer3;
public class Outer3Test {
public static void main(String[] args) {
Outer3 foo1 = new Outer3();
foo1.f1();
Outer3.Bird foo2 = foo1.new Bird();
foo2.fly();
System.out.println(foo2 == foo1.getBird());
//foo1物件下,有2個內部類物件和它關聯
//不允許沒有關聯的單獨的普通內部類物件
//Outer3.Bird foo3 = new Outer3().Bird();
}
}
package com.example.nestedclassdemo.outer4;
/**
* 靜態巢狀類的語法總結
* - 需要加修飾符static
* - 可以定義靜態成員和非靜態成員
* - 不能直接訪問包圍類的非靜態成員,可直接訪問包圍類的靜態成員
* = 可通過包圍類的物件進行訪問非靜態成員
* - 外界可以通過靜態巢狀類名訪問其靜態成員,通過物件訪問其非靜態成員
* - 外界需要通過包圍類才可以訪問到靜態巢狀類,並建立其物件,不需要外部包圍類的例項
*/
public class Outer4 {
private String outField1 = "outer 111";
private static String outStaticField2 = "outer static 222";
/**
* 靜態巢狀類
*/
public static class Inner4{
//靜態巢狀類可以定義靜態和非靜態成員
private String innField1 = "inner 333";
static String innStaticField2 = "inner static 444";
public void innFun1(){
//靜態巢狀類可以直接訪問包圍類的靜態成員
System.out.println(innField1);
//System.out.println(Outer4.outField1);
System.out.println(Outer4.outStaticField2);
System.out.println(outStaticField2);
//靜態巢狀類可以通過物件訪問包圍類的非靜態成員
Outer4 outObj = new Outer4();
System.out.println(outObj.getOutField1());
}
public String getInnField1(){
return this.innField1;
}
public static String getInnStaticField2(){
hello();
return innStaticField2;
}
public static void hello(){
System.out.println("inner hello");
}
}
public String getOutField1(){
return this.outField1;
}
public static void outFun2(){
Inner4 obj1 = new Inner4();
Inner4 obj2 = new Inner4();
System.out.println(obj1==obj2);//false
System.out.println(obj1.getInnField1());
System.out.println(Inner4.getInnStaticField2());
}
public static void hello(){
System.out.println("outer hello");
}
}
package com.example.nestedclassdemo.outer4;
public class Outer4Test {
public static void main(String[] args) {
//第三方類訪問靜態巢狀類
Outer4.Inner4 obj1 = new Outer4.Inner4();
String innField1 = obj1.getInnField1();
System.out.println("innField1:"+innField1);
Outer4.Inner4 obj2 = new Outer4.Inner4();
System.out.println(obj1==obj2);
System.out.println("====================");
Outer4 obj3 = new Outer4();
Outer4.outFun2();
}
}