class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
这些类称为嵌套类。它们分为2种类型:
- 非静态嵌套类 - 非静态嵌套类。它们也以另一种方式称为内部类。
- 静态嵌套类 - 静态嵌套类。
- 本地类
- 匿名类
public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Go!");
}
public class HandleBar {
public void right() {
System.out.println("Steering wheel to the right!");
}
public void left() {
System.out.println("Steering wheel to the left!");
}
}
public class Seat {
public void up() {
System.out.println("The seat is up!");
}
public void down() {
System.out.println("The seat is down!");
}
}
}
这里我们有一门课Bicycle
——自行车。它有 2 个字段和 1 个方法 - start()
。 它与普通类的区别在于它有两个类,其代码写在里面——它们是(方向盘)Bicycle
类和(座椅)类。这些是成熟的类:如您所见,每个类都有自己的方法。此时,您可能会有一个问题:为什么我们要把一个类放在另一个类中?为什么要让它们成为内部的?好吧,假设我们需要在程序中为方向盘和座椅单独提供类。但你不必嵌套它们!你可以定期上课。例如,像这样: HandleBar
Seat
public class HandleBar {
public void right() {
System.out.println("Steering wheel to the right!");
}
public void left() {
System.out.println("Steering wheel left");
}
}
public class Seat {
public void up() {
System.out.println("The seat is up!");
}
public void down() {
System.out.println("The seat is down!");
}
}
非常好的问题!当然,我们没有技术限制——我们可以这样做。它更多的是从特定程序的角度和该程序的含义正确设计类。内部类是用于突出显示程序中与另一个实体密不可分的某个实体的类。方向盘、座椅、踏板是自行车的组成部分。脱离自行车,它们就没有意义了。如果我们将所有这些类设为单独的公共类,我们的程序可能具有例如以下代码:
public class Main {
public static void main(String[] args) {
HandleBar handleBar = new HandleBar();
handleBar.right();
}
}
嗯……这段代码的含义甚至很难解释。我们有一些奇怪的自行车车把(为什么需要它?说实话,不知道)。而且这个方向盘向右转……它自己,没有自行车……出于某种原因。通过将方向盘的本质与自行车的本质分开,我们就失去了程序的逻辑。使用内部类,代码看起来完全不同:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.HandleBar handleBar = peugeot.new HandleBar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handleBar.left();
handleBar.right();
}
}
控制台输出:
Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
突然发生的事情就有意义了!:) 我们创建了一个自行车对象。我们创建了它的两个“子对象”——方向盘和座椅。为了方便起见,我们将座椅调高了 - 然后我们就出发了:我们滚动并转向我们需要去的地方!:) 我们需要的方法在必要的对象上调用。一切都简单又方便。在此示例中,突出显示车把和座椅增强了封装性(我们将有关自行车部件的数据隐藏在相应的类中),并允许我们创建更详细的抽象。现在我们来看另一种情况。假设我们想要创建一个模拟自行车和零件商店的程序。 在这种情况下,我们之前的解决方案就会失败。在零件店的范围内,自行车的每个单独零件都具有意义,即使与自行车的本质无关。例如,我们需要“向买家出售踏板”、“购买新座椅”等方法。在这里使用内部类是错误的 - 我们的新程序中自行车的每个单独部分都有其自己的含义:它与自行车的本质分离,并且与自行车的本质没有任何联系。如果您想知道是否需要使用内部类,或者将所有实体分成单独的类,那么您应该注意这一点。面向对象编程很棒,因为它可以轻松地对现实世界的实体进行建模。在决定是否使用内部类时,可以将其用作指南。在真正的商店中,零件与自行车是分开的 - 这是正常的。这意味着在设计程序时这是正确的。好的,我们已经理清了“哲学”:) 现在让我们来熟悉一下内部类的重要“技术”特征。以下是您绝对需要记住和理解的内容:
-
如果没有“外部”类的对象,内部类的对象就不可能存在。
这是合乎逻辑的:这就是为什么我们将其设为
Seat
内部HandleBar
类,这样无主的方向盘和座椅就不会出现在我们的程序中。此代码将无法编译:
public static void main(String[] args) { HandleBar handleBar = new HandleBar(); }
由此得出以下重要特征:
-
内部类的对象可以访问“外部”类的变量。
例如,让我们向类中添加
Bicycle
一个变量int seatPostDiameter
- 座杆的直径。然后在内部类中
Seat
我们可以创建一个方法getSeatParam()
来告诉我们座位参数:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Go!"); } public class Seat { public void up() { System.out.println("The seat is up!"); } public void down() { System.out.println("The seat is down!"); } public void getSeatParam() { System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
现在我们可以在我们的程序中获取这些信息:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.getSeatParam(); } }
控制台输出:
Параметр сиденья: диаметр подседельного штыря = 40
注意:新变量是用最严格的修饰符 - 声明的
private
。并且内部类仍然可以访问! -
不能在“外部”类的静态方法中创建内部类对象。
这是通过内部类的设计特征来解释的。内部类可以有带参数的构造函数,也可以只有默认构造函数。但不管怎样,当我们创建内部类的对象时,对“外部”类的对象的引用会悄悄地传递给它。毕竟,这样一个物体的存在是前提。否则我们将无法创建内部类的对象。
但如果外部类方法是静态的,那么外部类对象可能根本不存在!这意味着内部类的逻辑将被打破。在这种情况下,编译器会抛出错误:
public static Seat createSeat() { //Bicycle.this cannot be referenced from a static context return new Seat(); }
-
内部类不能包含静态变量和方法。
这里的逻辑是一样的:静态方法和变量即使没有对象也可以存在并被调用。
但是如果没有“外部”类的对象,我们将无法访问内部类。
明显的矛盾啊!因此,内部类中禁止存在静态变量和方法。
编译器在尝试创建它们时会抛出错误:
public class Bicycle { private int weight; public class Seat { //inner class cannot have static declarations public static void getSeatParam() { System.out.println("Seat parameter: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
创建内部类对象时,其访问修饰符起着重要作用。
内部类可以用标准访问修饰符 -
public
、private
和protected
来表示package private
。它为什么如此重要?
这会影响我们程序中可以实例化内部类的位置。
如果我们的类
Seat
被声明为public
,我们可以在任何其他类中创建它的对象。唯一的要求是“外部”类的对象也必须存在。顺便说一下,我们已经在这里完成了这个:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.HandleBar handleBar = peugeot.new HandleBar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handleBar.left(); handleBar.right(); } }
我们可以轻松地
HandleBar
从Main
.如果我们将内部类声明为
private
,我们将只能访问在“外部”类中创建对象。Seat
我们将无法再从外部创建对象:private class Seat { //methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); //Bicycle.Seat has a private access in 'Bicycle' Bicycle.Seat seat = bicycle.new Seat(); } }
你可能已经明白其中的逻辑了:)
-
内部类的访问修饰符的工作方式与常规变量相同。
修饰符
protected
提供对其后代类和同一包中的类中的类变量的访问。protected
对于内部类也是如此。protected
可以创建内部类对象:- 在“外部”类内部;
- 在其后代阶级中;
- 在同一包中的那些类中。
如果内部类没有访问修饰符(
package private
),则可以创建内部类的对象- 在“外部”类内部;
- 在同一包中的类中。
你已经熟悉修饰符很长时间了,所以这里不会有任何问题。
GO TO FULL VERSION