Java嵌套类

时间:2020-01-09 10:36:52  来源:igfitidea点击:

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

嵌套类的目的是将嵌套类与其周围的类明确组合在一起,表示这两个类将一起使用。或者,也许只能在其封闭的(拥有的)类内部使用嵌套的类。

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

在Java中,嵌套类被视为其封闭类的成员。因此,嵌套类可以声明为" public"," package"(无访问修饰符)," protected"和" private"(更多信息,请参见访问修饰符)。因此,Java嵌套类也可以由子类继承,如我有关Java继承的教程中所述。

我们可以在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 {
  }

}

这是创建"内部"类的实例的方法:

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);
        }
    }
}

注意内部类的printText()方法如何引用外部类的私有文本字段。这是完全可能的。这是我们调用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);
        }
    }
}

在上面的示例中,"外部"类和"内部"类都包含一个名为" text"的字段。当"内部"类引用"文本"时,它引用其自己的字段。当"外部"指代"文本"时,它也指代其自身的字段。

但是,Java使"内部"类可以引用"外部"类的"文本"字段成为可能。为此,它必须在text字段引用前加上Outer.this.(外部类名+.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.printText()方法将同时打印Inner.text和Outer.text字段。

本地类

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

class Outer {

    public void printText() {

        class Local {

        }

        Local local = new Local();
    }

}

只能从定义它们的方法或者作用域块内部访问它们。

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

局部类还可以访问同一方法或者作用域块中的局部变量,前提是这些变量被声明为" final"。

从Java 8开始,局部类还可以访问局部变量和声明了局部类的方法的参数。必须将参数声明为" final"或者实际上是final。实际上,final表示变量在初始化后再也不会更改。方法参数通常实际上是最终的。

局部类也可以在静态方法中声明。在这种情况下,本地类只能访问封闭类的静态部分。局部类不能包含所有类型的静态声明(允许将常量声明为" static final"),因为即使在静态方法中声明了局部类,其本质上也是非静态的。

相同的阴影规则适用于本地类,也适用于内部类。

匿名类

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

匿名类在实例化时定义。这是一个声明超类(称为"超类")的匿名子类的示例:

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代码将导致将"匿名类doIt()"打印到" System.out"中。匿名类继承(扩展)了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;

    //static initializer
    {  this.text = textToPrint;  }

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

instance.doIt();

相同的屏蔽规则适用于匿名类和内部类。

嵌套类的优点

Java嵌套类的好处是我们可以将属于同一类的类组合在一起。我们可以通过将它们放在同一个程序包中来完成此操作,但是将一个类放在另一个程序包中则可以进行更强的分组。

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

一个例子是" Cache"类。在Cache类中,我们可以声明CacheEntry类,其中可以包含有关特定缓存条目的信息(缓存值,插入时间,访问次数等)。如果Cache类的用户不需要获取有关CacheEntry本身的信息,而只需要获取缓存的值,则可能永远不会看到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"类公开它。