当前位置: 首页 > news >正文

30、根据官方教程详解嵌套类、内部类、静态嵌套类、局部类、匿名类 ...

文章目录

  • 一、嵌套类(Nested Classes)
    • (1) 嵌套类、内部类、静态嵌套类概念
    • (2) 嵌套类的特点
    • (3) 何时使用嵌套类?
  • 二、内部类(Inner Classes)
  • 三、静态嵌套类
  • 四、内部类、静态嵌套类官方案例
  • 五、匿名类和局部类
    • (1) 局部类(Local Classes)
    • (2) 匿名类(Anonymous Classes)
      • ① 基于接口的匿名类
      • ② 基于类的匿名类
    • (3) 匿名类细节
    • (4) 匿名类最佳实践

🌻 嵌套类、内部类、局部类等是 Java 面向对象中的难点,但好好研究清楚感觉也不是特别难。博主再次学习这部分内容,依次巩固知识。
🌻 博主其他介绍相关内容的文章:嵌套类、内部类、静态嵌套类…
🌻 博主以官方教程为基础开始学习,其中的概念都是以官方教程为主,这样学习起来比较放心。

一、嵌套类(Nested Classes)

(1) 嵌套类、内部类、静态嵌套类概念

✏️ The Java programming language allows you to define a class within another class. Such a class is called a nested class.
📜 Java 语言允许您在一个类中定义其他的类,这样的类叫做嵌套类
📜 嵌套类:定义在其他类中的类

public class OuterClass {
    class NestedClass {

    }

    static class StaticNestedClass {
        
    }
}

🌻 上面示例中的 NestedClass 类和 StaticNestedClass 类都是被定义在 OuterClass 类中的类
🌻 NestedClass 和 StaticNestedClass 是嵌套类


✏️ Nested classes are divided into two categories: non-static and static. ① Non-static nested classes are called inner classes. ② Nested classes that are declared static are called static nested classes.
📜 嵌套类被分为两种:静态的和非静态的
📜 【非静态嵌套类】非静态嵌套类没有被static关键字修饰,也叫做内部类
📜 【静态嵌套类】静态嵌套类被static关键字修饰

public class OuterClass {
    class InnerClass { // 内部类(非静态嵌套类)

    }

    static class StaticNestedClass { // 静态嵌套类

    }
}

📜 嵌套类外层的类叫做外部类(outer class)
📜 最外层的外部类叫做顶级类(Top-level Class)

public class A {
    class B {
        class C {

        }
    }
}

🌻 B 的外部类是 A
🌻 C 的外部类是 B 和 A
🌻 A 是 B 和 C 的顶级类


(2) 嵌套类的特点

✏️ A nested class is a member of its enclosing class(外部类). ① Non-static nested classes (inner classes) have access to other members of the enclosing class, even if they are declared private. ❓ ② Static nested classes do not have access to other members of the enclosing class.❓ As a member of the OuterClass, a nested class can be declared private, public, protected, or package private. 【Recall(回想) that outer classes can only be declared public or package private.】
📜 嵌套类(内部类或静态嵌套类)属于它的外部类的成员。
📜 非静态嵌套类(内部类)可以访问它的外部类的所有成员(静态成员和实例成员),即使是private成员
📜 静态嵌套类无法直接访问外部类的实例成员(若想访问,需创建外部类实例)
📜 作为外部类的一个成员,嵌套类可以被 private、public、protected、package-private(没有修饰符) 等修饰

🌻 顶级类只能被 public 修饰,或没有访问修饰符

public class TopLevel {
    private static int six = 6;
    private int five = 5;

    static class StaticNested {
        class Hello {
            void test() {
                // 访问外部类(此处是顶级类)的静态成员
                System.out.println(six); // 6

                // 创建外部类实例后, 才可访问外部类的实例成员
                TopLevel topLevel = new TopLevel();
                System.out.println(topLevel.five); // 5
            }
        }
    }
}

class TestMain {
    public static void main(String[] args) {
        // 创建一个静态嵌套类
        TopLevel.StaticNested staticNested = new TopLevel.StaticNested();
        // 通过静态嵌套类实例创建静态嵌套类里面的内部类实例
        TopLevel.StaticNested.Hello hello = staticNested.new Hello();
        // 6
        // 5
        hello.test();
    }
}

(3) 何时使用嵌套类?

博主其他嵌套类的文章
✏️ ① It is a way of logically grouping classes that are only used in one place: If a class is useful to only one other class, then it is logical to embed(嵌套) it in that class and keep the two together. Nesting such “helper classes” makes their package more streamlined.
📜 这是一种对仅在一个地方使用的类进行逻辑分组的方法: 如果一个类(如:A)仅仅在另一个类(如:B)中被使用,那么把它嵌套到另一个类中(把 A 嵌套到 B 中)是合乎逻辑的。嵌套这样的帮助类可以让程序包呈现流线型,更加高效。
在这里插入图片描述
在这里插入图片描述


✏️ ② It increases encapsulation: Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A’s members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.
📜 嵌套类增加了封装性: ① 考虑顶级类 A 和顶级类 B;② B 中需要访问 A 中被声明为private的成员;③ 通过把 B 隐藏在类 A 中(把 B 弄成 A 的嵌套类),A 的成员可以被声明为private,并且 B 可以访问到 A 中的私有成员。另外,B 类本身也可以不向外界暴露。
在这里插入图片描述

🌻 上图:A 类中定义的私有属性和私有方法无法被类 C 访问和调用;B 被嵌套到类 A 中,B 可以访问或调用类 A 中的私有属性和私有方法。

在这里插入图片描述


✏️ ③ It can lead to more readable and maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.
📜 嵌套类可以使代码更具可读性和可维护性: 将类嵌套在顶级类中,能使代码更接近它被使用的位置。

二、内部类(Inner Classes)

✏️ As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object’s methods and fields. Also, because an inner class is associated with an instance, it cannot define any static members itself.
📜 与实例方法和实例变量一样,内部类与其外部类的实例相关联,并且可以直接访问该对象的方法和字段。此外,由于内部类与实例相关联,它本身不能定义任何静态成员。


public class OuterClass {
    class InnerClass {
         
    }
}

✏️ An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance.
📜 InnerClass 的实例仅存在于 OuterClass 的实例中,InnerClass 的实例可以直接访问其外部类的方法和属性。

✏️ To instantiate an inner class, you must first instantiate the outer class.
📜 要创建内部类对象必须先创建外部类对象

下面是创建一个 InnerClass 对象的语法:

public class OuterClass {
    class InnerClass {
        void print() {
            System.out.println("print something");
        }
    }
}

class TestDemo {
    public static void main(String[] args) {
        // 先有外部类实例后, 才能创建内部类实例
        OuterClass outer = new OuterClass();

        // 通过外部类对象, 创建内部类对象
        OuterClass.InnerClass inner = outer.new InnerClass();
        // output: print something
        inner.print();
    }
}

✏️ There are two special kinds of inner classes: local classes and anonymous classes.
📜 局部类和匿名类是两种特殊的内部类。

三、静态嵌套类

✏️ As with class methods and variables, a static nested class is associated with its outer class. And like static class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class: it can use them only through an object reference.
📜 与类方法和类变量一样,静态嵌套类与其外部类相关联。和类方法一样,静态嵌套类不能直接引用其外部类中定义的实例变量或方法(只能通过对象引用使用外部类的实例成员)


✏️ A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.
📜 静态嵌套类与其外部类的实例成员交互,就像与任何其他顶级类交互一样。实际上,静态嵌套类在行为上就是一个顶级类。只是为了方便打包,把它嵌套在另一个顶级类中而已。

✏️ You instantiate a static nested class the same way as a top-level class.
📜 就像实例化一个顶级类一样实例化静态嵌套类。

public class OuterClass {
    private static final int sixFive = 65;

    static class StaticNestedClass {
        void hundred() {
            System.out.println(sixFive + 35);
        }
    }
}

class TestDemo {
    public static void main(String[] args) {
        OuterClass.StaticNestedClass snc = new OuterClass.StaticNestedClass();
        // 100
        snc.hundred();
    }
}

在这里插入图片描述

四、内部类、静态嵌套类官方案例

一个顶级类:

public class TopLevelClass {
    void accessMembers(OuterClass outer) {
        System.out.println(outer.outerField);
        System.out.println(OuterClass.staticOuterField);
    }
}
public class OuterClass {
    String outerField = "1.Outer field";
    static String staticOuterField = "2.Static outer field";

    class InnerClass {
        void accessMembers() {
            System.out.println(outerField);
            System.out.println(staticOuterField);
        }
    }

    static class StaticNestedClass {
        void accessMembers(OuterClass outer) {
            System.out.println(outer.outerField);
            System.out.println(staticOuterField);
        }
    }

    public static void main(String[] args) {
        System.out.println("Inner class:");
        System.out.println("------------");
        OuterClass outerObject = new OuterClass();
        // 通过外部类实例创建内部类实例
        OuterClass.InnerClass innerObject = outerObject.new InnerClass();
        innerObject.accessMembers();

        System.out.println("\nStatic nested class:");
        System.out.println("--------------------");
        // 创建静态嵌套类实例
        StaticNestedClass staticNestedObject = new StaticNestedClass();
        staticNestedObject.accessMembers(outerObject);

        System.out.println("\nTop-level class:");
        System.out.println("--------------------");
        // 创建外部类实例
        TopLevelClass topLevelObject = new TopLevelClass();
        topLevelObject.accessMembers(outerObject);
    }
}

五、匿名类和局部类

① 📖 类的五大成员:① 属性;② 方法;③ 构造器;④ 代码块;⑤ 嵌套类


② 📖 嵌套类的分类
在这里插入图片描述

✏️ There are two special kinds of inner classes: local classes and anonymous classes.
📜 局部类和匿名类是两种特殊的内部类。

(1) 局部类(Local Classes)

✏️ Local classes are classes that are defined in a block, which is a group of zero or more statements between balanced braces. You typically find local classes defined in the body of a method.
📜 代码块(block):在花括号中的零条或多条语句
📜 局部类是定义在代码块中的类
📜 通常您看到的局部类都是定义在方法体中的


✏️ You can define a local class inside any block. For example, you can define a local class in a method body, a for loop, or an if clause.
📜 您可以把局部类定义在任何代码块中(如方法体、for 循环、if 子句)


✏️ A local class has access to the members of its enclosing class.
📜 局部类可以直接访问它的外部类的所有成员(即使是 private 成员)


📜 局部类不能被访问修饰符(public、protected、private)修饰。局部类的地位就是一种局部变量。
📜 局部类可以被final关键字修饰(因为局部变量也可以用final修饰,被final修饰的局部类不能被其他局部类继承)


📜 局部类的作用域仅仅在定义它的代码块中

局部类例子:

public class OuterClass {
    private int five = 5;

    private void m() {
        System.out.println("OuterClass_m()");
    }

    public void test() {
        /* LocalClass 是一个局部类(被定义在方法体中) */
        final class LocalClass {
            private void test() {
                System.out.println("LocalClass_five = " + five);
                m();
            }
        }

        // 创建一个局部类对象
        LocalClass localClass = new LocalClass();
        localClass.test();
    }
}

class TestMain {
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        
        // LocalClass_five = 5
        // OuterClass_m()
        outerClass.test();
    }
}

📜 外部类和局部类的成员重名的时候遵循就近原则(若想访问外部类的成员:外部类类名.this.成员

public class OuterClass {
    private int num = 5;

    private void m() {
        System.out.println("OuterClass_m()");
    }

    public void test() {
        class LocalClass {
            int num = 6;

            private void test() {
                System.out.println(num); // 6
                System.out.println(OuterClass.this.num); // 5
                m();
                OuterClass.this.m();
                // OuterClass.this 是调用方法的外部类的对象
            }

            private void m() {
                System.out.println("LocalClass_m()");
            }
        }

        LocalClass localClass = new LocalClass();
        localClass.m();
        localClass.test();
    }
}

class TestMain {
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();

        // 1. LocalClass_m()
        // 2. 6
        // 3. 5
        // 4. LocalClass_m()
        // 5. OuterClass_m()
        outerClass.test();
    }
}

✏️ a local class has access to local variables. However, a local class can only access local variables that are declared final. When a local class accesses a local variable or parameter of the enclosing block, it captures that variable or parameter.
📜 局部类可以访问局部变量。然而,局部类只能访问被声明为final的局部变量。当局部类访问了局部变量或参数的时候,局部类捕获了该局部变量或参数。

✏️ Starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final.
📜 从 jdk8 开始,局部类可以访问 final有效 final 的局部变量或参数。
📜 有效 final:假如一个变量或参数的值在被初始化后从来也没有被改变过,那么这个变量或参数就是有效 final

public class OuterClass {
    public void test(int v) {
        int n = 5; // n 是有效 final 的变量
        // n = 1; // n 不能第二次赋值, 否则就不是有效final

        // v = 2; // v 不能第二次赋值, 否则就不是有效final

        class LocalClass {
            private void m() {
                System.out.println(n + 6); // 11
                System.out.println(v + v); // 16
            }
        }

        LocalClass localClass = new LocalClass();
        localClass.m();
    }
}

class TestMain {
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();

        // 11
        // 16
        outerClass.test(8);
    }
}

📜 局部类中不能定义任何的static成员(编译时常量除外)
在这里插入图片描述

(2) 匿名类(Anonymous Classes)

匿名类相关文章

✏️ Anonymous classes enable you to make your code more concise(简洁的). They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.
📜 匿名类可以让您的代码更加简洁。匿名类可以让您在声明类的同时创建该类的一个实例。匿名类类似局部类,只是匿名类没有类名。如果您只需要使用一次局部类,您可以考虑使用匿名类。

📜 当接口或抽象类的实现类在整个项目中只使用过一次的时候,可以考虑使用匿名类(Anonymous Class)


① 基于接口的匿名类

看下面的代码,引出匿名类:

public class DemoMain {
    public static void main(String[] args) {

    }
}

interface InterPhone {
    void playGame(String name);
}

🌻 在上面的代码中,有一个接口 InterPhone
🌻 若需要使用该接口,需要进行以下几步:① 写一个类实现(implements)该接口,并给予该接口中的抽象方法以具体实现;② 创建该类的实例;③ 通过该类的实例调用 playGame 方法【代码如下】

public class DemoMain {
    public static void main(String[] args) {
        // (2) 创建该类(ApplePhone)的实例
        ApplePhone applePhone = new ApplePhone();

        // (3) 通过该类的实例调用 playGame 方法
        // output: ApplePhone_playGame(String name)_王者荣耀
        applePhone.playGame("王者荣耀");
    }
}

interface InterPhone {
    void playGame(String name);
}

/* (1) 写一个类实现(implements)该接口,并给予该接口中的抽象方法以具体实现 */
class ApplePhone implements InterPhone {
    @Override
    public void playGame(String name) {
        System.out.println("ApplePhone_playGame(String name)_" + name);
    }
}

🌻 假如 ApplePhone 在整个项目过程中,只使用了一次,以后永远不会使用:可以通过匿名类完成 InterPhone 接口的使用,无需创建 ApplePhone 类【代码如下】

public class DemoMain {
    public static void main(String[] args) {
        /*
            applePhone 的编译类型是:InterPhone
            applePhone 的运行类型是:匿名类
            
            匿名类类名是:外部类类名$数字(如:DemoMain$1)
            数字是匿名类对象的编号
            
            new: 创建匿名类对象
         */
        InterPhone applePhone = new InterPhone() {
            @Override
            public void playGame(String name) {
                System.out.println("AnonymousClass_" + name);
            }
        };

        // 匿名类类名:class com.gq.DemoMain$1
        System.out.println(applePhone.getClass());

        // output: AnonymousClass_王者荣耀
        applePhone.playGame("王者荣耀");
    }
}

interface InterPhone {
    void playGame(String name);
}

② 基于类的匿名类

非抽象类:

public class DemoTest {
    public static void main(String[] args) {
        /*
            c1 的运行类型是:class com.gq.Computer
         */
        Computer c1 = new Computer();
        System.out.println(c1.getClass());
        // Computer_open()
        c1.open();

        /*
            c2 的运行类型是:class com.gq.DemoTest$1
        */
        Computer c2 = new Computer() {
            /**
             * 匿名类中重写 Computer 类中的 open 方法
             */
            @Override
            public void open() {
                System.out.println("AnonymousClass_open()");
            }
        };
        System.out.println(c2.getClass());
        // AnonymousClass_open()
        c2.open();
    }
}

class Computer {
    public void open() {
        System.out.println(getClass().getSimpleName() + "_open()");
    }
}

抽象类:

public class DemoTest {
    public static void main(String[] args) {
        Computer c = new Computer() {
            @Override
            public void open() {
                System.out.println("Anonymous_open()❀");
            }
        };

        // Anonymous_open()❀
        c.open();
    }
}

/**
 * 抽象类
 */
abstract class Computer {
    public abstract void open();
}

基于非抽象类的匿名类的另一个例子:

public class DemoTest {
    public static void main(String[] args) {
        // output: 庆医吃苹果, 花费68888.0元
        // 庆医和68888会传给 Person 的构造器
        new Person("庆医", 68888) {
            @Override
            public void eat(String food) {
                super.eat(food);
            }
        }.eat("苹果");
    }
}

class Person {
    private String name;
    private double money;

    public Person(String name, double money) {
        this.name = name;
        this.money = money;
    }

    public void eat(String food) {
        System.out.println(name + "吃" + food + ", 花费" + money + "元");
    }
}

(3) 匿名类细节

📖 匿名类既是一个类的定义,同时它本身也是一个对象。从语法上看,它既有定义类的特征,也有创建对象的特征
在这里插入图片描述

📖 匿名类中可以直接访问外部类中的所有成员(即使是 private)【但是如果匿名类是在静态方法中,则只能访问外部类中的静态成员】
📖 匿名类不能被访问修饰符修饰
📖 匿名类只有在实例相关的代码块中使用的时候才可直接访问外部类中的实例成员
📖 外部类和匿名类的成员重名的时候遵循就近原则(若想访问外部类的成员:外部类类名.this.成员

(4) 匿名类最佳实践

📖 可把匿名类当做参数传递 (感觉上是把方法作为参传递)

public class DemoTest {
    public static void main(String[] args) {
        int v = test(new ITest() {
            @Override
            public int math(int v) {
                return v + 666;
            }
        }, 1);
        // v = 667
        System.out.println("v = " + v);
    }

    private static int test(ITest iTest, int val) {
        return iTest.math(val);
    }
}

interface ITest {
    int math(int v);
}

结束,如有错误请不吝赐教!

相关文章:

  • 【Java牛客刷题】入门篇(05)
  • docker入门
  • 【NodeJs-5天学习】第四天存储篇① ——安装使用mysql 8.0
  • 【Verilog 流水线设计】以全加器为例阐述流水线设计的影响
  • spring boot 使用Mybatis-plus的查询方法
  • nginx中root和alias的区别
  • pytorch深度学习训练模板
  • 【Qt+FFMPEG】 - 封装 解码音视频 线程
  • Arduino框架下最便宜的开发芯片-CH552初探
  • Java高并发编程实战5,异步注解@Async自定义线程池
  • 前端进阶——ES6
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • 【Python+大数据】第一天:安装VMware及Centos,配置虚拟机网络,学习Linux命令。研究生开学10天的感受。
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • JAVA代码操作HDFS
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • Git初体验
  • interface和setter,getter
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • MySQL QA
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • PHP那些事儿
  • React-Native - 收藏集 - 掘金
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • Sass Day-01
  • Vim Clutch | 面向脚踏板编程……
  • vuex 学习笔记 01
  • 记一次删除Git记录中的大文件的过程
  • 使用agvtool更改app version/build
  • 数据仓库的几种建模方法
  • 一起参Ember.js讨论、问答社区。
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • - 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
  • 责任链模式的两种实现
  • k8s使用glusterfs实现动态持久化存储
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • ​批处理文件中的errorlevel用法
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • (06)金属布线——为半导体注入生命的连接
  • (3)nginx 配置(nginx.conf)
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (TOJ2804)Even? Odd?
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (转)大道至简,职场上做人做事做管理
  • (转载)Linux 多线程条件变量同步
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET Framework 服务实现监控可观测性最佳实践
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET 简介:跨平台、开源、高性能的开发平台