關於java內部類(Inner Class)

本文摘自:http://blog.csdn.net/coolwzjcool/archive/2007/04/19/1570821.aspx

java內部類(Inner Class)

Inner Class 即嵌套類,也即C++和C#中的Nested Class。但Java 的Inner Class 與 C++和C#最大的不同之處在於,嵌套類包含一個指向其容器類的引用,可以訪問容器類的成員。以下代碼演示了這一點:


public class Container {
String Name;
class InnerClass
{
InnerClass(){};
public void func()
{
System.out.println(Name);
}
}
public Container(String name){ Name=name;
InnerClass a=new InnerClass();
}
public static void main(String [] arg)
{
Container a=new Container(“ContainerA");
InnerClass b=a.new InnerClass(); //注意此處
b.func();
InnerClass c=(new Container(“ContainerB")).new InnerClass();
c.func();
}
}


注意其中獨特的new語法,在靜態函數要創建一個Inner Class,必須有一個其容器類的實例。如果直接創建
InnerClass b=new InnerClass();則會導致編譯出錯。而在Container的構造函數中,創建InnerClass時,自動將this作爲InnerClass的引用。在Inner Class 中使用容器類的成員,不需指定實例,自動指向創建它的容器類。 這是一個很有用的語法特徵,編譯器替我們省了許多事。 本例的輸出是: ContainerA ContainerB 還可以看到,Inner Class 可以訪問容器類的任何成員,不管是public、private或protected的成員全是透明的。反之則不然,容器類只能訪問Inner Class的public成員。

===================

匿名的內部類是沒有名字的內部類。不能extends(繼承) 其它類,但一個內部類可以作爲一個接口,由另一個內部類實現。

===================

3類inner class
1、一般的inner class

class aaaa{
class InnerClass {
// Write Codes Here
}
}


2、Method local Inner Class
local class 是定義在method內的class,其 scope 在該method 內

3、Anonymous Inner Class
一種形式:return new Destination{ //inner class };
另一種形式:someMethod(new SomeClass( ) { //code } );
[注意]
(1)anonymous nested class 必須implement 某個 interface 或是 extend 某個 class,但是不使用 implements 或 extends 關鍵字
(2)anonymous class 內不能宣告 constructor
(3)可宣告 instance initializer 做初值設定

class Out{

class Inner1{}

static class Inner2{}

}


Inner1 需要通過 Out 的實例構造。

Out out = new Out();
Out.Inner1 oi1 = out.new Inner1();

Inner2 可以通過 Out 的類名構造。

Out.Inner2 oi2 = new Out.Inner2();

例如上面的實例:添加靜態內部類

static class Inner2{
public void f2(){System.out.println("pppp");}
}
main函數的調用時用Container類:
Container.Inner2 i2 = new Container.Inner2();
i2.f2();


總結:當前的外部類中,內部類的public或者private屬性可以被訪問

class InnerClass
{
private int x = 5;
public int y = 10;
InnerClass(){};
public void func()
{
System.out.println(Name+"-x-"+x+"-y-"+y);
}
}
class Inner1{
public void f1(){
InnerClass ic = new InnerClass();
System.out.println(ic.y+"|||"+ic.x);
System.out.println(Name);
}
}


1. 嵌套類

首先必須要注意,內部類和嵌套類的區別。嵌套類擁有訪問宿主類任意成員的權限。嵌套類除了訪問權限外,並不會獲得宿主類實例的引用,也不會隨宿主類自動產生實例。


class Class1
{
private int x = 13;

public void Test()
{
new Class1Sub(this).Test();
}

public class Class1Sub
{
private Class1 o;

public Class1Sub(Class1 o)
{
this.o = o;
}

public void Test()
{
Console.WriteLine(o.x);
}
}
}

class Program
{
static void Main()
{
// 1.
new Class1().Test();

// 2.
Class1 o = new Class1();
Class1.Class1Sub s = new Class1.Class1Sub(o);
s.Test();
}
}



class Class1 {
private int x = 13;

public void test() {
new Class1Sub(this).test();
}

public static class Class1Sub {
private Class1 o;

public Class1Sub(Class1 o){
this.o = o;
}

public void test() {
System.out.println(o.x);
}
}
}

public class program {
public static void main(String[] args) {
// 1.
new Class1().test();

// 2.
Class1 o = new Class1();
Class1.Class1Sub s = new Class1.Class1Sub(o);
s.test();
}
}


Java 的例子和 C# 基本類似,不過要注意的是 Java Class1.Class1Sub 被申明爲 "static class"。
另外,C# 嵌套類缺省是 private 方式的,也就是說不能被外界訪問。可以被賦予 private/internal/protected/internal protected/public中的任一種權限。Java 的嵌套類缺省是 friendly 方式,可以被賦予 private/protected/public/friendly中的一種。

2. 內部類

內部類和嵌套類比較相似,但沒有static修飾,不能像嵌套類那樣在外部創建內部類的實例對象。它雖然不會自動隨宿主類生成實例,但卻能自動獲取宿主類的引用,從而訪問宿主類的實例成員。

我們修改上面的例子,將其改成內部類,我們發現Class1Sub可以直接訪問宿主實例的任意字段,並不需要使用宿主引用。這其實是Java編譯器做的一些“魔法”,當在宿主實例內生成內部類時會自動將宿主引用隱性傳遞給內部類實例。當然,在內部類也可以使用"宿主名.this"獲取宿主引用。


class Class1 {
private int x = 13;

public void test() {
new Class1Sub().test();
}

class Class1Sub {
public void test() {
System.out.println(Class1.this.toString());
System.out.println(x);
}
}
}

public class program {
public static void main(String[] args) {
new Class1().test();
}
}


內部類可以跨越多重嵌套,訪問任意級別的嵌套類成員。

class Class1 {
void doSomething(){};

class Class1Sub {
class class1Sub2 {
void test() { doSomething(); }
}
}
}


3. 本地內部類

我們還可以在方法內部創建內部類,這種內部類被稱之爲“本地內部類(local inner class)”。本地內部類不能有權限修飾符,和下面的匿名內部類非常相似。如果本地內部類需要使用方法參數,則必須爲該參數添加final關鍵字。

class Class1 {
private int x = 13;

void test() {
class Sub {
Sub() { System.out.println(x); }
}

new Sub();
}

void test2(final String s) {
class Sub {
Sub() { System.out.println(s); }
}

new Sub();
}
}

public class program {
public static void main(String[] args) {
new Class1().test();
new Class1().test2("abc");
}
}


3. 匿名內部類

我們可以在方法內部創建一個實現某個接口或者繼承某個類的匿名類,它具備內部類的全部功能,只不過因爲沒有“名字”,我們不能在外面實例化它,也不能創建構造方法,而只能使用實例初始化代碼段。如果匿名類需要使用外部方法參數,必須爲該參數加上final關鍵字。


abstract class Base {
abstract void test();
}

interface IBase {
void test();
}

class Class1 {
private int x = 13;

public Base makeBase() {
return new Base(){
{
System.out.println("new Base...");
}

public void test() {
System.out.println(x);
}
};
}

public IBase makeIBase(final String s) {
return new IBase(){
public void test() {
System.out.println(s);
}
};
}
}

public class program {
public static void main(String[] args) {
new Class1().makeBase().test();
new Class1().makeIBase("abc").test();
}
}


一般情況下,匿名內部類和本地內部類都可以實現相同的功能。選擇本地內部類的額外理由是:

1. 需要或者覆寫構造方法。
2. 創建多個本地內部類實例。
3. 沒有可用的基類或接口。(匿名內部類必須實現某個接口和繼承自某個基類。)

匿名內部類和本地內部類還可以放在任意的代碼範疇(scope)內。

interface I1 {}

class ClassX
{
void print(final int i) {
if (i > 0) {
class Sub {
Sub() { System.out.println(i); }
}

new Sub();
}
else {
new I1() {
{
System.out.println("i <= 0");
}
};
}
}
}

public class program {
public static void main(String[] args) {
new ClassX().print(1);
new ClassX().print(0);
}
}


4. 繼承內部類

在宿主類內部繼承內部類,和正常情況下沒什麼區別。

class Class1 {

private int x = 10;
public Class1() { new Sub2(); }

class Sub {
Sub(){ System.out.println("Sub:" + x); }
}

class Sub2 extends Sub {
Sub2(){ System.out.println("Sub2:" + x); }
}
}


而爲了在外部繼承內部類,就必須:
1. 包含有宿主類引用參數的構造方法。
2. 在構造方法內部調用 super() 方法。

class Class1 {
class Class1Sub {
}
}

class Class1SubX extends Class1.Class1Sub {
public Class1SubX(Class1 o) {
o.super();
}
}

public class program {
public static void main(String[] args) {
new Class1SubX(new Class1());
}
}


雖然在外部繼承內部類以後,我們可以在外部創建內部類實例,但同時也失去了隱性訪問宿主類成員的權利。

5. 覆寫內部類

繼承含有內部類的宿主類,並不會同時繼承其內部類,即便在繼承類內部有相同名稱的內部類,也完全是兩個不同的類型。

class Class1 {
void test() { new Sub(); }

class Sub {
Sub() { System.out.println("Class1.Sub"); }
}
}

class Class2 extends Class1 {

void test() { new Sub(); }

class Sub {
Sub() { System.out.println("Class2.Sub"); }
}
}

public class program {
public static void main(String[] args) {
Class1 o = new Class2();
o.test();
}
}


輸出
Class2.Sub


由於編譯器並不會因爲 Class2.Sub 是 Class1.Sub 的繼承類,因此並沒有自動調用 Class1.Sub 構造方法。我們修改一下,使得 Class2.Sub extends Class1.Sub,OK!這回對了。

class Class1 {
void test() { new Sub(); }

class Sub {
Sub() { System.out.println("Class1.Sub"); }
}
}

class Class2 extends Class1 {

void test() { new Sub(); }

class Sub extends Class1.Sub {
Sub() { System.out.println("Class2.Sub"); }
}
}

public class program {
public static void main(String[] args) {
Class1 o = new Class2();
o.test();
}
}


輸出:
Class1.Sub
Class2.Sub
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章