Java内部类详解:类型、用法与实例

Java内部类详解

Java内部类是在另一个类的主体内定义的类。Java内部类可以声明为private、public、protected或默认访问级别,而外部类只能有public或默认访问级别。Java嵌套类分为两种主要类型。

1. 静态嵌套类

如果嵌套类是静态的,则称为静态嵌套类。静态嵌套类只能访问外部类的静态成员。静态嵌套类与其他顶级类相同,只是为了方便封装而嵌套。可以使用以下语句创建静态类对象:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

2. 内部类

任何非静态的嵌套类在Java中被称为内部类。Java内部类与类的实例相关联,它们可以访问外部类的所有变量和方法。由于内部类与实例相关联,因此它们不能有任何静态变量。Java内部类的对象是外部类对象的一部分,要创建内部类的实例,我们首先需要创建外部类的实例。可以像这样实例化Java内部类:

OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

有两种特殊类型的Java内部类:

2.1 局部内部类

如果一个类在方法体中定义,它被称为局部内部类。由于局部内部类与对象无关,我们不能在其上使用private、public或protected访问修饰符。唯一允许的修饰符是abstract或final。局部内部类可以访问封闭类的所有成员和在其定义范围内的局部final变量。此外,它还可以访问方法中的非final局部变量,但不能修改它们。因此,如果尝试打印非final局部变量的值将被允许,但如果尝试在方法局部内部类中更改其值,将会得到编译时错误。局部内部类的定义如下:

package com.Olivia.innerclasses;

public class MainClass {

    private String s_main_class;

    public void print() {
        String s_print_method = "";
        // 方法内部的局部内部类
        class Logger {
            // 可以访问封闭类的变量
            String name = s_main_class;
            // 可以访问非final方法变量
            String name1 = s_print_method;

            public void foo() {
                String name1 = s_print_method;
                // 以下代码将抛出编译时错误:
                // 在封闭范围中定义的局部变量s_print_method必须是final或有效final的
                // s_print_method = ":";
            }
        }
        // 在方法中实例化局部内部类以使用
        Logger logger = new Logger();
    }
}

我们也可以在任何代码块中定义一个局部内部类,例如静态代码块、if-else代码块等。然而,在这种情况下,类的范围将非常有限。

public class MainClass {

    static {
        class Foo {
        
        }
        Foo f = new Foo();
    }

    public void bar() {
        if(1 < 2) {
            class Test {
            
            }
            Test t1 = new Test();
        }
        // 以下代码将抛出错误,因为类的范围不同
        // Test t = new Test();
        // Foo f = new Foo();
    }
}

2.2 匿名内部类

没有名称的局部内部类称为匿名内部类。匿名类在一条语句中定义和实例化。匿名内部类总是继承一个类或实现一个接口。由于匿名类没有名称,因此无法为匿名类定义构造函数。匿名内部类只能在定义它的地方才能访问。如何创建匿名内部类有点难以定义,我们将在下面的测试程序中看到它的实时用法。

这是一个展示如何定义Java内部类、静态嵌套类、局部内部类和匿名内部类的Java类文件 - OuterClass.java:

package com.Olivia.nested;

import java.io.File;
import java.io.FilenameFilter;

public class OuterClass {
    
    private static String name = "OuterClass";
    private int i;
    protected int j;
    int k;
    public int l;

    // OuterClass构造函数
    public OuterClass(int i, int j, int k, int l) {
        this.i = i;
        this.j = j;
        this.k = k;
        this.l = l;
    }

    public int getI() {
        return this.i;
    }

    // 静态嵌套类,可以访问OuterClass的静态变量/方法
    static class StaticNestedClass {
        private int a;
        protected int b;
        int c;
        public int d;

        public int getA() {
            return this.a;
        }

        public String getName() {
            return name;
        }
    }

    // 内部类,非静态,可以访问外部类的所有变量/方法
    class InnerClass {
        private int w;
        protected int x;
        int y;
        public int z;

        public int getW() {
            return this.w;
        }

        public void setValues() {
            this.w = i;
            this.x = j;
            this.y = k;
            this.z = l;
        }

        @Override
        public String toString() {
            return "w=" + w + ":x=" + x + ":y=" + y + ":z=" + z;
        }

        public String getName() {
            return name;
        }
    }

    // 局部内部类
    public void print(String initial) {
        // 方法内的局部内部类
        class Logger {
            String name;

            public Logger(String name) {
                this.name = name;
            }

            public void log(String str) {
                System.out.println(this.name + ": " + str);
            }
        }

        Logger logger = new Logger(initial);
        logger.log(name);
        logger.log("" + this.i);
        logger.log("" + this.j);
        logger.log("" + this.k);
        logger.log("" + this.l);
    }

    // 匿名内部类
    public String[] getFilesInDir(String dir, final String ext) {
        File file = new File(dir);
        // 实现FilenameFilter接口的匿名内部类
        String[] filesList = file.list(new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(ext);
            }

        });
        return filesList;
    }
}

这是一个测试程序,展示了如何在Java中实例化和使用内部类。InnerClassTest.java:

package com.Olivia.nested;

import java.util.Arrays;
// 嵌套类可以在导入中使用,以便于实例化
import com.Olivia.nested.OuterClass.InnerClass;
import com.Olivia.nested.OuterClass.StaticNestedClass;

public class InnerClassTest {

    public static void main(String[] args) {
        OuterClass outer = new OuterClass(1,2,3,4);
        
        // 静态嵌套类示例
        StaticNestedClass staticNestedClass = new StaticNestedClass();
        StaticNestedClass staticNestedClass1 = new StaticNestedClass();
        
        System.out.println(staticNestedClass.getName());
        staticNestedClass.d=10;
        System.out.println(staticNestedClass.d);
        System.out.println(staticNestedClass1.d);
        
        // 内部类示例
        InnerClass innerClass = outer.new InnerClass();
        System.out.println(innerClass.getName());
        System.out.println(innerClass);
        innerClass.setValues();
        System.out.println(innerClass);
        
        // 使用局部内部类调用方法
        outer.print("Outer");
        
        // 使用匿名内部类调用方法
        System.out.println(Arrays.toString(outer.getFilesInDir("src/com/scdev/nested", ".java")));
        
        System.out.println(Arrays.toString(outer.getFilesInDir("bin/com/scdev/nested", ".class")));
    }
}

这是上述Java内部类示例程序的输出结果:

OuterClass
10
0
OuterClass
w=0:x=0:y=0:z=0
w=1:x=2:y=3:z=4
Outer: OuterClass
Outer: 1
Outer: 2
Outer: 3
Outer: 4
[NestedClassTest.java, OuterClass.java]
[NestedClassTest.class, OuterClass$1.class, OuterClass$1Logger.class, OuterClass$InnerClass.class, OuterClass$StaticNestedClass.class, OuterClass.class]

请注意,当编译OuterClass时,内部类、局部内部类和静态嵌套类会分别生成独立的类文件。

Java内部类的好处

  • 如果一个类只对另一个类有用,把它嵌套在一起是有意义的。这有助于类的封装。
  • Java的内部类实现了封装。需要注意的是,内部类可以访问外部类的私有成员,同时我们也可以将内部类对外部世界隐藏起来。
  • 将小的类保持在顶层类内部,使得代码更靠近使用它的地方,使得代码更可读和可维护。

以上就是关于Java内部类的全部内容。

bannerAds