Java 内部类
Java 内部类是在另一个类的主体内定义的。Java 内部类可以声明为私有、公共、受保护或具有默认访问权限,而外部类只能具有公共或默认访问权限。Java 嵌套类分为两种类型。
-
静态嵌套类
如果嵌套类是静态的,则称为静态嵌套类。静态嵌套类只能访问外部类的静态成员。静态嵌套类与任何其他顶级类相同,嵌套只是为了方便打包。可以使用以下语句创建静态类对象。
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
-
Java 内部类
任何非静态嵌套类在 Java 中都称为内部类。Java 内部类与类的对象相关联,它们可以访问外部类的所有变量和方法。由于内部类与实例相关联,因此我们不能在其中包含任何静态变量。Java 内部类的对象是外部类对象的一部分,要创建内部类的实例,我们首先需要创建外部类的实例。Java 内部类可以像这样实例化;
OuterClass outerObject = new OuterClass(); OuterClass.InnerClass innerObject = outerObject.new InnerClass();
Java 有两种特殊的内部类。
-
本地内部类
如果在方法体中定义类,则该类称为本地内部类。由于本地内部类与 Object 无关,因此我们不能对其使用 private、public 或 protected 访问修饰符。唯一允许的修饰符是 abstract 或 final。本地内部类可以访问其定义范围内的封闭类的所有成员和本地 final 变量。此外,它还可以访问定义它的方法的非 final 局部变量,但不能修改它们。因此,如果您尝试打印非 final 局部变量的值,这是允许的,但如果您尝试从方法本地内部类内部更改其值,则会收到编译时错误。本地内部类可以定义为:
package com.journaldev.innerclasses; public class MainClass { private String s_main_class; public void print() { String s_print_method = ""; // local inner class inside the method class Logger { // able to access enclosing class variables String name = s_main_class; // able to access non-final method variables String name1 = s_print_method; public void foo() { String name1 = s_print_method; // Below code will throw compile time error: // Local variable s_print_method defined in an enclosing scope must be final or effectively final // s_print_method= ":"; } } // instantiate local inner class in the method to use 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(); } // Below will throw error because of the scope of the class //Test t = new Test(); //Foo f = new Foo(); } }
-
匿名内部类
没有名称的本地内部类称为匿名内部类。匿名类在单个语句中定义和实例化。匿名内部类总是扩展一个类或实现一个接口。由于匿名类没有名称,因此无法为匿名类定义构造函数。匿名内部类仅在定义它的地方可访问。定义如何创建匿名内部类有点困难,我们将在下面的测试程序中看到它的实际用法。
这是一个 Java 类,展示了如何定义 Java 内部类、静态嵌套类、本地内部类和匿名内部类。OuterClass.java
package com.journaldev.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 constructor
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;
}
//static nested class, can access OuterClass static variables/methods
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;
}
}
//inner class, non-static and can access all the variables/methods of the outer class
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;
}
}
//local inner class
public void print(String initial) {
//local inner class inside the method
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);
}
//anonymous inner class
public String[] getFilesInDir(String dir, final String ext) {
File file = new File(dir);
//anonymous inner class implementing FilenameFilter interface
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.journaldev.nested;
import java.util.Arrays;
//nested classes can be used in import for easy instantiation
import com.journaldev.nested.OuterClass.InnerClass;
import com.journaldev.nested.OuterClass.StaticNestedClass;
public class InnerClassTest {
public static void main(String[] args) {
OuterClass outer = new OuterClass(1,2,3,4);
//static nested classes example
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);
//inner class example
InnerClass innerClass = outer.new InnerClass();
System.out.println(innerClass.getName());
System.out.println(innerClass);
innerClass.setValues();
System.out.println(innerClass);
//calling method using local inner class
outer.print("Outer");
//calling method using anonymous inner class
System.out.println(Arrays.toString(outer.getFilesInDir("src/com/journaldev/nested", ".java")));
System.out.println(Arrays.toString(outer.getFilesInDir("bin/com/journaldev/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内部类的全部内容。