嵌套类

Java嵌套类是指在一个类中定义另一个类。

嵌套类的目的是将嵌套类与其周围的类进行明确的分组,表明这两个类将被一起使用。或者这个嵌套类只能在它的封闭(拥有)类中使用。

Java开发人员通常将嵌套类称为内部类,但内部类(非静态嵌套类)只是几种不同类型嵌套类中的一种。

嵌套类可以认为是其封闭类的成员。因此,嵌套类可以被声明为public,package(无访问修饰符),protected和private类型的。因此,Java中的嵌套类也可以由子类继承。

你可以在Java中创建几种不同类型的嵌套类。Java嵌套类类型包括:

  • 静态嵌套类
  • 非静态嵌套类
  • 本地嵌套类
  • 匿名嵌套类

静态嵌套类

静态嵌套类可以像如下示例这样声明:

public class Outer {

  public static class Nested {

  }

}

为了创建一个Nested类的实例,你必须通过在Outer类的名字前加上前缀来引用它,如下所示:

Outer.Nested instance = new Outer.Nested();

在Java中,静态嵌套类本质上是一个正常的类,它嵌套在另一个类中。作为静态的,静态嵌套类只能通过对包含类实例的引用来访问封闭类的实例变量。

非静态嵌套类(内部类)

Java中的非静态嵌套类也称为内部类。内部类关联外部类的实例。因此,你必须首先创建一个封闭类的实例来创建一个内部类的实例。示例如下:

public class Outer {

  public class Inner {
  }

}

下面是创建了一个Inner的实例

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

请注意,为了创建内部类的实例,需要在外部类的实例后面加上new关键字。

非静态嵌套类(内部类)可以访问封闭类的字段,即使它们被声明为私有。示例如下:

public class Outer {

    private String text = "I am private!";

    public class Inner {

        public void printText() {
            System.out.println(text);
        }
    }
}

注意Inner类的printText()方法引用了Outer类的私有text字段。这是可以的。下面示例调用了printText()方法:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.printText();

内部类影子

如果Java内部类中声明了与其封闭类字段或方法名称相同的字段或方法,则内部字段或方法被称为在外部字段或方法上影子。示例如下:

public class Outer {

    private String text = "I am Outer private!";

    public class Inner {

        private String text = "I am Inner private";

        public void printText() {
            System.out.println(text);
        }
    }
}

在上面的例子中,Outer和Inner类都包含一个名为text的字段。当Inner类引用text时,它指的是它自己的字段。当Outer引用text时,它也指它自己的字段。

Inner类也可以引用Outer类的text字段。此时,需要在text字段前加上Outer.this。示例如下:

public class Outer {

    private String text = "I am Outer private!";

    public class Inner {

        private String text = "I am Inner private";

        public void printText() {
            System.out.println(text);
            System.out.println(Outer.this.text);
        }
    }
}

上例中会打印出Inner.text和Outer.text字段。

本地类

Java中的本地类就像内部类(非静态嵌套类)一样,它们是在方法内或作用域块({...})内定义。下面是一个例子:

class Outer {

    public void printText() {

        class Local {

        }

        Local local = new Local();
    }

}

本地类只能从定义它们的方法或作用域块中访问。

本地类可以像普通的内部类一样访问其封闭类的成员(字段和方法)。

本地类也可以访问在同一个方法或作用域块内的局部变量,只要这些变量被声明为final。

从Java 8开始本地类也可以访问声明本地类的方法的局部变量和参数。参数必须声明为final的或effectually final。effectually final意味着变量在初始化后永远不会被改变。方法参数通常就是这样的。

本地类也可以在静态方法中声明。在这种情况下,本地类只能访问封闭类的静态部分。本地类不能包含各种静态声明(允许使用常量 - 变量声明为effectually final),因为本质类本质上是非静态的,即使在静态方法中声明也是如此。

同样的影子规则适用于本地类和内部类。

匿名类

Java中的匿名类是没有类名的嵌套类。它们通常被声明为现有类的子类,或者作为某个接口的实现。

匿名类在实例化时被定义。下面的例子声明了一个匿名子类,其超类的名SuperClass:

public class SuperClass {

  public void doIt() {
    System.out.println("SuperClass doIt()");
  }

}
SuperClass instance = new SuperClass() {

    public void doIt() {
        System.out.println("Anonymous class doIt()");
    }
};

instance.doIt();

运行这个Java代码会打印出“Anonymous class doIt()”。匿名类的子类(扩展)SuperClass并重写doIt()方法。

Java匿名类也可以实现一个接口。示例如下:

public interface MyInterface {

  public void doIt();

}
MyInterface instance = new MyInterface() {

    public void doIt() {
        System.out.println("Anonymous class doIt()");
    }
};

instance.doIt();

正如你所看到的,实现接口的匿名类非常类似于扩展一个类的匿名类。

匿名类可以访问封闭类的成员。它也可以访问声明为final或有效的final变量(Java 8版本后)。

你可以在匿名类中声明字段和方法,但不能声明构造方法。不过,你可以给匿名类声明静态初始化程序。示例如下:

final Strint textToPrint = "Text...";

MyInterface instance = new MyInterface() {

    private String text;

    //静态初始程序
    {  this.text = textToPrint;  }

    public void doIt() {
        System.out.println(this.text);
    }
};

instance.doIt();

匿名类同样适用影子规则。

嵌套类的好处

Java嵌套类的优点是可以将属于一起的类组合在一起。你可以把它们放在同一个包里,但是把一个类放到另一个类里面就可以实现更强大的分组。

一个嵌套的类通常只被其封闭的类使用。有时嵌套类只对封闭类可见,只在内部使用,因此在封闭类之外永远不可见。其他时候,嵌套类在其封闭类之外是可见的,但只能与封闭类一起使用。

一个例子是一个Cache类。在Cache类中,你可以声明一个CacheEntry类,它可以包含关于特定缓存条目(缓存值,插入时间,访问次数等)的信息。如果Cache类的用户不需要获取有关CacheEntry本身的信息,而只能获取缓存的值,则Cache类的用户可能永远不会看到CacheEntry类。但是,Cache类可能会选择使CacheEntry类对外部可见,因此它们可以访问的不仅仅是缓存的值(例如有关值最后刷新时间的信息等)。

以下是两个Cache实现框架:

public class Cache {

    private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();

    private class CacheEntry {
        public long   timeInserted = 0;
        public object value        = null;
    }

    public void store(String key, Object value){
        CacheEntry entry = new CacheEntry();
        entry.value = value;
        entry.timeInserted = System.currentTimeMillis();
        this.cacheMap.put(key, entry);
    }

    public Object get(String key) {
        CacheEntry entry = this.cacheMap.get(key);
        if(entry == null) return null;
        return entry.value;
    }

}
public class Cache {

    private Map<String, CacheEntry> cacheMap = new HashMap<String, CacheEntry>();

    public class CacheEntry {
        public long   timeInserted = 0;
        public object value        = null;
    }

    public void store(String key, Object value){
        CacheEntry entry = new CacheEntry();
        entry.value = value;
        entry.timeInserted = System.currentTimeMillis();
        this.cacheMap.put(key, entry);
    }

    public Object get(String key) {
        CacheEntry entry = this.cacheMap.get(key);
        if(entry == null) return null;
        return entry.value;
    }

    public CacheEntry getCacheEntry(String key) {
        return this.cacheMap.get(key);
        }

}

第一个Cache类隐藏了内部嵌套类CacheEntry,而第二个Cache则开放了CacheEntry访问权限。

下一篇:抽象类

results matching ""

    No results matching ""