关于Java里面的嵌套类,你了解多少?

前言

最近在看《Core Java for the Impatient》这本书,当然为了方便我看的是英文电子版的PDF格式(有需要的朋友,可以后台留言给我),期间又重新认识或升级了不少Java相关的知识,今天我们来聊一聊Java里面的内嵌类,又称嵌套类。

什么是嵌套类?

简单的说,就是把一个类定义在另外一个类里面,使两者拥有更亲密的关系。比如A类里面可以包含B,B类里面可以包含C,C类里面可以包含D,至于能嵌套多少层,在Java语言里面并没有明确的限制嵌套的层级,你可以无限的嵌套,但大多数情况下,嵌套的层级超过2层会是一个糟糕的设计。

嵌套类的意义

在Oracle官网文档里面,有如下的描述:

(1)是一种逻辑分组的方法,仅仅只在一个地方使用。也就是说这个嵌套类存在仅仅只为它的外部类服务。比如各种"Helper Class"

(2)它增加了封装性。我们都知道继承,封装,抽象,多态是Java语言最重要的四大特点。嵌套类对相对于其他外部的类是隐藏的。

(3)增加了可读性和可维护性。把相关的类定义在一个类文件里面在阅读和维护方法变得更加有利。

嵌套类的分类

(1)静态嵌套类(Static nested classes)

(2)非静态嵌套类(Non-static nested classes)又称内部类(Inner Class)

(3)本地类(Local classes)

(4)匿名类(Anonymous classes)

不同嵌套类的使用方法和特点

(1)静态嵌套类

特点:

1.1:作为静态的嵌套类,它属于它的外部类(enclosing class),但它不是外部类的实例

1.2: 类声明可以使用所有的访问修饰符

1.3:它仅仅可以访问外部类的静态字段和静态方法,私有的也可以

1.4:它可以定义静态和非静态的成员

示例如下:

public class Enclosing {

     

    private static int x = 1;

     

    public static class StaticNested {

 

        private void run() {

            // method implementation

        }

    }

     

    @Test

    public void test() {

        Enclosing.StaticNested nested = new Enclosing.StaticNested();

        nested.run();

    }

}

(2)非静态嵌套类

特点:

2.1: 它们通常也称为内部类(inner class)

2.2: 类声明可以使用所有的访问修饰符

2.3: 像外部类的成员变量和成员方法一样,内部类也可以认为是外部类的一个成员属性

2.4: 可以访问外部类所有的成员,包括静态和非静态的

2.5: 除了static final修饰的编译时常量成员外,内部只能定义非静态成员

示例如下:

public class Outer {

     

     static final int a=3; // 正确

     static int b=5; // 错误

     static final Object obj=null //错误,非编译时常量

     String c;//正确

     double d;//正确





    public class Inner {

        // ...

    }



    public static Inner getInner1(){



        return new Inner(); //错误,静态方法,不能访问非静态成员

    }



    public Inner getInner2(){



        return new Inner(); //正确 

    }



}

外部如何实例化:

Outer outer = new Outer();

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



或者



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





或者(如果内部类是私有的情况下)



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

(3)本地类(Local classes)

特点:

3.1: 可以定义在一个方法里面或者{}代码块里面。 (是的,你没听错,可以在方法里面定义类)

3.2: 类声明不能使用任何访问修饰符

3.3: 可以访问外部类所有的成员,包括静态和非静态的

3.4: 除了static final修饰的编译时常量成员外,内部只能定义非静态成员

3.5: 仅仅在所在的方法块或者代码内有效。

示例如下:

public class NewEnclosing {



    {

        class BlockLocal{



            void print(){

                //....

            }

        }



    }

     

    void run() {

        class Local {



            static final int a=3; // 正确

            static int b=5; // 错误

            static final Object obj=null //错误,非编译时常量

            String c;//正确

            double d;//正确

 

            void run() {

                // method implementation

            }

        }

        Local local = new Local();

        local.run();

    }

     

    @Test

    public void test() {

        NewEnclosing newEnclosing = new NewEnclosing();

        newEnclosing.run();

    }

}

(4)匿名类(Anonymous classes)

特点:

4.1: 匿名类是一次性使用的类,可以用于实现一个接口或者抽象类仅仅临时用一次,不能复用。

4.2: 可以访问外部类所有的成员,包括静态和非静态的

4.3: 除了static final修饰的编译时常量成员外,内部只能定义非静态成员

4.4: 是唯一一种不能定义构造方法不能使用继承功能不能使用实现接口功能的类。

示例如下:

先定义一个抽象类:

abstract class SimpleAbstractClass {

    abstract void run();

}

然后看如何定义匿名类:

public class AnonymousInnerTest {

     

    @Test

    public void whenRunAnonymousClass\_thenCorrect() {

        SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() {

            void run() {

                // method implementation

            }

        };

        simpleAbstractClass.run();

    }

}

关于this冲突

通过前面的介绍,我们知道一个类里面可以再次定义一个类,那么问题来了,如果在嵌套类里面使用this关键词

究竟引用的是谁?

答案是嵌套类的this是引用的本身,如果想在嵌套类里面使用外部类的this,那么必须使用外部类的类名类引用this:

示例如下:

public class NewOuter {

 

    int a = 1;

    static int b = 2;

 

    public class InnerClass {

        int a = 3;

        static final int b = 4;

 

        public void run() {

            //内部类引用this

            System.out.println("a = " + this.a);

            System.out.println("b = " + b);

            //外部类引用this

            System.out.println("NewOuterTest.this.a = " + NewOuter.this.a);

            System.out.println("NewOuterTest.b = " + NewOuter.b);

            System.out.println("NewOuterTest.this.b = " + NewOuter.this.b);

        }

    }

 

    @Test

    public void test() {

        NewOuter outer = new NewOuter();

        NewOuter.InnerClass inner = outer.new InnerClass();

        inner.run();

 

    }

}

关于序列化

为了避免内部类发生序列化异常:

java.io.NotSerializableException 

有如下两种方法避免:

(1)把内部类声明成静态类型,在Java里面静态变量是不会被序列化的,其属于类信息,保存在全局区域,在实例还没初始化前就已经存在了,所以不需要担心序列化的问题。

(2)确保每一个非静态嵌套类都要实现Serializable接口。

结论

本篇文章详细的介绍了Java里面有关嵌套类的知识,包括嵌套类的定义,意义,分类,特点和一些使用方法,关于嵌套类使用最多的场景当属于Java里面GUI开发的源码包里面包括AWT和SWING,当然Socket类,HashMap的源码中也都有嵌套类的影子,了解这些知识将更有助于我们开发中合理的使用它们。

最后,给大家留一个思考问题:

为什么非静态嵌套类里面(Inner Class)不能定义静态成员呢?

下篇文章中,我们再聊一聊。

如果觉得本文不错,欢迎转发,让更多的小伙伴可以看到和学习。

最后欢迎加入我的知识星球,一起学习。

image

参考链接:

https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

http://www.baeldung.com/java-nested-classes

https://stackoverflow.com/questions/1953530/why-does-java-prohibit-static-fields-in-inner-classes

https://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class

https://stackoverflow.com/questions/20252727/is-not-an-enclosing-class-java

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据结构与算法

1470 数列处理

个人博客:doubleq.win 1470 数列处理  时间限制: 1 s  空间限制: 1000 KB  题目等级 : 青铜 Bronze 题解 题目描述 D...

2715
来自专栏java一日一条

Java LinkedHashMap工作原理及实现

在理解了#7 介绍的HashMap后,我们来学习LinkedHashMap的工作原理及实现。首先还是类似的,我们写一个简单的LinkedHashMap的程序:

381
来自专栏学海无涯

22.Swift学习之泛型

如果此时我们想交换两个Double类型、或者是其他类型的值,就需要针对不同的类型写类似的方法,但是这些方法仅仅只是参数类型不同。如何解决?—— 泛型

631
来自专栏有趣的Python

慕课网-Linux C语言结构体-学习笔记

Linux C语言结构体 编译指令:预处理,宏定义, 建立自己的数据类型:结构体,联合体,动态数据结构 逻辑运算符:& | ^ ~ << >> 递归函...

4538
来自专栏Vamei实验室

Python深入01 特殊方法与多范式

Python一切皆对象,但同时,Python还是一个多范式语言(multi-paradigm),你不仅可以使用面向对象的方式来编写程序,还可以用面向过程的方式来...

2065
来自专栏chenjx85的技术专栏

leetcode-796-Rotate String

2706
来自专栏Java技术栈

Java中的6颗语法糖

来源:http://blog.csdn.net/danchu/article/details/54986442 ? 语法糖(Syntactic Sugar),也...

3428
来自专栏牛肉圆粉不加葱

[7] - trait

这是我以前在知乎上看到关于类继承作用的回答,虽不完全正确,却十分明确的表达出了好的代码应避免类继承而尽量使用类组合。Scala 显然也非常赞同这一点,以至于有了...

1012
来自专栏kevindroid

JNI所需的C语言知识小结

1575
来自专栏程序员互动联盟

【专业技术】你必须注意的11个C++要点

下面的这些要点是对所有的C++程序员都适用的。我之所以说它们是最重要的,是因为这些要点中提到的是你通常在C++书中或网站上无法找到的。如:指向成员的指针,这是许...

2735

扫码关注云+社区