阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

内部类

31次阅读
没有评论

共计 3093 个字符,预计需要花费 8 分钟才能阅读完成。

在 Java 程序中,通常情况下,我们把不同的类组织在不同的包下面,对于一个包下面的类来说,它们是在同一层次,没有父子关系:

java.lang
├── Math
├── Runnable
├── String
└── ...

还有一种类,它被定义在另一个类的内部,所以称为内部类(Nested Class)。Java 的内部类分为好几种,通常情况用得不多,但也需要了解它们是如何使用的。

Inner Class

如果一个类定义在另一个类的内部,这个类就是 Inner Class:

class Outer {class Inner {// 定义了一个 Inner Class
    }
}

上述定义的 Outer 是一个普通类,而 Inner 是一个 Inner Class,它与普通类有个最大的不同,就是 Inner Class 的实例不能单独存在,必须依附于一个 Outer Class 的实例。示例代码如下:

// inner class
public class Main {public static void main(String[] args) {Outer outer = new Outer("Nested"); // 实例化一个 Outer
        Outer.Inner inner = outer.new Inner(); // 实例化一个 Inner
        inner.hello();}
}

class Outer {private String name;

    Outer(String name) {this.name = name;
    }

    class Inner {void hello() {System.out.println("Hello," + Outer.this.name);
        }
    }
}

观察上述代码,要实例化一个 Inner,我们必须首先创建一个Outer 的实例,然后,调用 Outer 实例的 new 来创建 Inner 实例:

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

这是因为 Inner Class 除了有一个 this 指向它自己,还隐含地持有一个 Outer Class 实例,可以用 Outer.this 访问这个实例。所以,实例化一个 Inner Class 不能脱离 Outer 实例。

Inner Class 和普通 Class 相比,除了能引用 Outer 实例外,还有一个额外的“特权”,就是可以修改 Outer Class 的 private 字段,因为 Inner Class 的作用域在 Outer Class 内部,所以能访问 Outer Class 的 private 字段和方法。

观察 Java 编译器编译后的 .class 文件可以发现,Outer类被编译为 Outer.class,而Inner 类被编译为Outer$Inner.class

Anonymous Class

还有一种定义 Inner Class 的方法,它不需要在 Outer Class 中明确地定义这个 Class,而是在方法内部,通过匿名类(Anonymous Class)来定义。示例代码如下:

// Anonymous Class
public class Main {public static void main(String[] args) {Outer outer = new Outer("Nested");
        outer.asyncHello();}
}

class Outer {private String name;

    Outer(String name) {this.name = name;
    }

    void asyncHello() {Runnable r = new Runnable() {@Override
            public void run() {System.out.println("Hello," + Outer.this.name);
            }
        };
        new Thread(r).start();}
}

观察 asyncHello() 方法,我们在方法内部实例化了一个 RunnableRunnable 本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了 Runnable 接口的匿名类,并且通过 new 实例化该匿名类,然后转型为Runnable。在定义匿名类的时候就必须实例化它,定义匿名类的写法如下:

Runnable r = new Runnable() {// 实现必要的抽象方法...
};

匿名类和 Inner Class 一样,可以访问 Outer Class 的 private 字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心类名,比直接定义 Inner Class 可以少写很多代码。

观察 Java 编译器编译后的 .class 文件可以发现,Outer类被编译为Outer.class,而匿名类被编译为Outer$1.class。如果有多个匿名类,Java 编译器会将每个匿名类依次命名为Outer$1Outer$2Outer$3……

除了接口外,匿名类也完全可以继承自普通类。观察以下代码:

// Anonymous Class
import java.util.HashMap;

public class Main {public static void main(String[] args) {HashMap<String, String> map1 = new HashMap<>();
        HashMap<String, String> map2 = new HashMap<>() {}; // 匿名类!
        HashMap<String, String> map3 = new HashMap<>() {
            {put("A", "1");
                put("B", "2");
            }
        };
        System.out.println(map3.get("A"));
    }
}

map1是一个普通的 HashMap 实例,但 map2 是一个匿名类实例,只是该匿名类继承自 HashMapmap3 也是一个继承自 HashMap 的匿名类实例,并且添加了 static 代码块来初始化数据。观察编译输出可发现 Main$1.classMain$2.class两个匿名类文件。

Static Nested Class

最后一种内部类和 Inner Class 类似,但是使用 static 修饰,称为静态内部类(Static Nested Class):

// Static Nested Class
public class Main {public static void main(String[] args) {Outer.StaticNested sn = new Outer.StaticNested();
        sn.hello();}
}

class Outer {private static String NAME = "OUTER";

    private String name;

    Outer(String name) {this.name = name;
    }

    static class StaticNested {void hello() {System.out.println("Hello," + Outer.NAME);
        }
    }
}

static 修饰的内部类和 Inner Class 有很大的不同,它不再依附于 Outer 的实例,而是一个完全独立的类,因此无法引用 Outer.this,但它可以访问Outerprivate静态字段和静态方法。如果把 StaticNested 移到 Outer 之外,就失去了访问 private 的权限。

小结

Java 的内部类可分为 Inner Class、Anonymous Class 和 Static Nested Class 三种;

Inner Class 和 Anonymous Class 本质上是相同的,都必须依附于 Outer Class 的实例,即隐含地持有 Outer.this 实例,并拥有 Outer Class 的 private 访问权限;

Static Nested Class 是独立类,但拥有 Outer Class 的 private 访问权限。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计3093字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中