首頁 > 軟體

Java基本語法之內部類範例詳解

2022-03-18 19:00:46

1.內部類概念及分類

將一個類定義在另一個類的內部或者介面內部或者方法體內部,這個類就被稱為內部類,我們不妨將內部類所在的類稱為外圍類,除了定義在類,介面,方法中的內部類,還有一種特殊的內部類,那就是使用關鍵字new建立一個匿名類的物件,而這個匿名類其實就是一個內部類,具體說是一個匿名內部類,經常用於傳入構造器實參構造物件,例如PriorityQueue物件的建立。

一個類定義在另一個類的內部或者介面內部,並且沒有static修飾時,這個內部類就稱為範例內部類,使用static修飾時,這個內部類被稱為靜態內部類(巢狀類),將一個類定義在程式碼塊內部,特別是方法體內部,這個內部類被稱為本地內部類或者區域性內部類,還有一種就是上面所說的匿名內部類。

2.範例內部類

2.1範例內部類的建立

範例內部類的定義很簡單,就直接定義在外圍類的裡面就可以了。問題是如何建立一個內部類物件,首先,需要明白內部類與外圍類中的成員變數與方法是平起平坐的,都是屬於外圍類物件的(無static修飾),所以想要內部類物件先得有外圍類物件,第一種建立內部類物件的方式就是使用一個方法返回一個內部類物件,並且這個內部類物件的型別為OutClassName.InnerClassName,即外圍類名.內部類名。

public class Outer {

    class Inner1 {
        private String str;
        public Inner1(String s) {
            this.str = s;
        }
        public String readStr() {
            return this.str;
        }
    }

    class Inner2 {
        private int val;
        public Inner2(int i) {
            this.val = i;
        }
        public int readVal() {
            return this.val;
        }
    }
    //建立內部類
    public Inner1 creatInner1(String s) {
        return new Inner1(s);
    }
    public Inner2 creatInner2(int i) {
        return new Inner2(i);
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner1 inner1 =  outer.creatInner1("Inner1");
        Outer.Inner2 inner2 = outer.creatInner2(2);

        System.out.println(inner1.readStr());
        System.out.println(inner2.readVal());
    }
}
//output:
Inner1
2

Process finished with exit code 0

2.2使用.this和.new

當然,建立一個內部類還有其他的方法,就是使用外圍類的物件.new來建立一個內部類物件,語法為:

外部類物件.new 內部類構造方法;

public class Outer {

    class Inner1 {
        private String str;
        public Inner1(String s) {
            this.str = s;
        }
        public String readStr() {
            return this.str;
        }
    }

    class Inner2 {
        private int val;
        public Inner2(int i) {
            this.val = i;
        }
        public int readVal() {
            return this.val;
        }
    }


    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner1 inner1 =  outer.new Inner1("Inner1");
        Outer.Inner2 inner2 = outer.new Inner2(2);

        System.out.println(inner1.readStr());
        System.out.println(inner2.readVal());
    }
}
//output:
Inner1
2

Process finished with exit code 0

內部類的特性不止如此,內部類還能與外圍類連結,首先範例內部類物件的建立是依賴外圍類物件的參照,範例內部類與外圍類的成員變數地位一樣,如果想要通過內部類物件存取外圍類的成員變數與方法(特別是同名變數)。在內部類中,可以使用外圍類名.this獲取外圍類物件,然後使用獲得的這個外圍類物件來使用外圍類的變數與方法。

public class Outer {
    private String outStr;
    public Outer(String str) {
        this.outStr = str;
    }

    public void printOuter() {
        System.out.println(this.outStr);
    }

    class Inner {
        private String str;
        public Inner(String s) {
            this.str = s;
        }
        public String readStr() {
            return this.str;
        }
        //使用.this獲取父類別物件參照
        public Outer outer() {
            return Outer.this;
        }
    }



    public static void main(String[] args) {
        Outer outer = new Outer("outerString");
        Outer.Inner inner = outer.new Inner("innerString");

        Outer out = inner.outer();
        out.printOuter();
    }
}
//output:
outerString

Process finished with exit code 0

其實內部類物件中有兩個this,直接使用this.成員獲取的是內部類物件自己的成員,而像上面使用外圍類名.this.成員獲取的是外圍類的成員,也就是說在內部類中this指向內部類物件自己的參照,外部類名.this指向外圍類物件的參照。

當出現內部類與外圍類變數名相同時,這兩個this就有很大的用處了。

public class Outer {
    public int a = 12;
    public int b = 16;
    public int c = 20;

    class Inner{
        public int a = 8;
        public int c = 48;
        public int d = 2;

        public void printVal() {
            System.out.println("外圍類變數a=" + Outer.this.a);
            System.out.println("外圍類變數b=" + Outer.this.b);
            System.out.println("外圍類變數c=" + Outer.this.c);
            System.out.println("內部類變數a=" + this.a);
            System.out.println("內部類變數c=" + this.c);
            System.out.println("內部類變數d=" + this.d);
        }
    }

    public Inner creatInner() {
        return new Inner();
    }

    public static void main(String[] args) {
        Outer outer = new Outer();

        Outer.Inner inner = outer.creatInner();
        inner.printVal();
    }
}
//output:
外圍類變數a=12
外圍類變數b=16
外圍類變數c=20
內部類變數a=8
內部類變數c=48
內部類變數d=2

Process finished with exit code 0

如果沒有出現同名,直接獲取的成員即可,可以不用使用上面所說的this,出現同名,直接存取的成員預設是內部類物件的。

2.3內部類實現迭代列印

內部類可以與外圍類的所有元素的存取許可權,所以我們可以嘗試使用內部類來遍歷輸出外圍類中的元素,雖然內部類與外圍類的成員地位是平等,但內部類畢竟也是類,它也可以像外圍類一樣繼承類和實現介面。

import java.util.Arrays;

interface Selector{
    //判斷是否迭代完全部元素
    public boolean end();
    //獲取當前元素
    public Object current();
    //迭代下一個元素
    public void next();
}

public class SequeArray {
    private Object[] items;
    private int usedSize;

    public SequeArray(int capacity) {
        items = new Object[capacity];
    }
    public void add(Object val) {
        if (usedSize == items.length) items = Arrays.copyOf(items, 2 * usedSize);

        items[usedSize++] = val;
    }

    private class SequeSelector implements Selector{
        private int index;

        public boolean end() {
            return index == usedSize;
        }
        public Object current() {
            return items[index];
        }
        public void next() {
            if (index < usedSize) index++;
        }
    }
    public SequeSelector sequeSelector() {
        return new SequeSelector();
    }

    public static void main(String[] args) {
        SequeArray array = new SequeArray(10);

        for (int i = 0; i < 10; i++) {
            array.add(i+1);
        }
        //發生了向上轉型
        Selector selector = array.sequeSelector();

        while (!selector.end()) {
            System.out.print(selector.current() + "   ");
            selector.next();
        }
    }
}
//output:
1   2   3   4   5   6   7   8   9   10   
Process finished with exit code 0

2.4內部類的繼承

內部類的繼承有一點麻煩,因為內部類的構造器必須依賴外圍類的參照,所以需要一段特殊的語法來說明內部類與外圍類的關聯:

外圍類參照.super();

具體怎麼使用看如下一段程式碼:

public class Outer {
    class Inner{
        
    }
}
class Person extends Outer.Inner {
    private String str;
    private int id;
    public Person(Outer out, String str, int id) {
        out.super();
        this.str = str;
        this.id = id;
    }
    public void readVal() {
        System.out.println(this.str + this.id);
        
    }
}

當內部類被繼承的時候,需要通過外圍類的參照傳入父類別的構造方法中並呼叫super()才能通過編譯。

如果內部類繼承外部類,直接繼承即可,不需要這麼麻煩。

程式生成一個java檔案的位元組碼檔案時,命名為公共外部類$內部類。

內部類可以無限巢狀,一般沒人這麼幹。

3.靜態內部類

靜態內部類也稱巢狀類,它就是在範例內部類的定義前加入一個static關鍵字,像下面這樣:

public class Outer {
    
    static class Inner{
        
    }
}

與範例內部類的區別是靜態內部類是屬於類的而不是屬於物件的,因此靜態內部類的建立不依賴與外部類物件,這是和範例內部類最大的區別之一。

public class Outer {
    static class Inner{
        private String str;
        public Inner(String s) {
            str = s;
        }
    }

    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner("我是靜態內部類!");
        System.out.println(inner.str);
    }
}
//output:
我是靜態內部類!

Process finished with exit code 0

4.匿名內部類

匿名內部類的用法我們已經見過了,就是建立PriorityQueue物件時,預設建立的是小堆,需要傳入比較器來建立大堆。

        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });

像上面這種在方法中使用new關鍵字建立一個匿名類的物件作為實參,裡面這個匿名類就是匿名內部類,建立過程中的Comparator匿名類物件是繼承所需傳參型別的,在這裡也就是Comparator類。

如果使用自定義型別傳入優先佇列,是一定要實現比較器的,實現比較器最常見的方式就是匿名內部類與Lambda表示式。

假設我有一個Person類陣列,需要對他們的name排序,如果不使用內部類,就需要在Person類中實現比較器。

import java.util.Arrays;
import java.util.Comparator;

class Preson {
    public String name;
    public Integer id;
    public Preson(String name, Integer id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public String toString() {
        return "Preson{" +
                "name='" + name + ''' +
                ", id=" + id +
                '}';
    }
}
class PersonComparator implements Comparator<Preson> {
    @Override
    public int compare(Preson o1, Preson o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class Main {
    public static void main(String[] args) {
        Preson preson1 = new Preson("aboluo", 24);
        Preson preson2 = new Preson("zhousi", 25);
        Preson preson3 = new Preson("syyfjy", 2);
        Preson[] presons = {preson1, preson2, preson3};
        Arrays.parallelSort(presons, new PersonComparator());
        System.out.println(Arrays.toString(presons));
    }
}
//output:
[Preson{name='aboluo', id=24}, Preson{name='syyfjy', id=2}, Preson{name='zhousi', id=25}]

Process finished with exit code 0

使用內部類上述程式碼就變為:

import java.util.Arrays;
import java.util.Comparator;

class Preson {
    public String name;
    public Integer id;
    public Preson(String name, Integer id) {
        this.name = name;
        this.id = id;
    }


    @Override
    public String toString() {
        return "Preson{" +
                "name='" + name + ''' +
                ", id=" + id +
                '}';
    }
}

public class Main {
    public static void main(String[] args) {
        Preson preson1 = new Preson("aboluo", 24);
        Preson preson2 = new Preson("zhousi", 25);
        Preson preson3 = new Preson("syyfjy", 2);
        Preson[] presons = {preson1, preson2, preson3};
        Arrays.parallelSort(presons, new Comparator<Preson>() {
            @Override
            public int compare(Preson o1, Preson o2) {
                return o1.name.compareTo(o2.name);
            }
        });
        System.out.println(Arrays.toString(presons));
    }
}
//output:
[Preson{name='aboluo', id=24}, Preson{name='syyfjy', id=2}, Preson{name='zhousi', id=25}]

Process finished with exit code 0

還有一個本地內部類,就是類定義在方法中,就不多說了,基本上不用,用法如下:

public class Outer {
    public void func() {
        class Inner{
            //
        }
    }
}

以上就是Java基本語法之內部類範例詳解的詳細內容,更多關於Java內部類的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com