CodeGym /Java 博客 /随机的 /嵌套内部类
John Squirrels
第 41 级
San Francisco

嵌套内部类

已在 随机的 群组中发布
你好!今天我们将讨论一个重要的话题——嵌套类在 Java 中是如何工作的。Java 允许您在另一个类中创建类:

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
这些内部类被称为嵌套。它们分为两种类型:
  1. 非静态嵌套类。这些也称为内部类。
  2. 静态嵌套类。
反过来,内部类有两个不同的子类别。内部类除了简单的内部类之外,还可以是:
  • 本地班级
  • 匿名类
使困惑?:) 没关系。为了清楚起见,这里有一个图表。如果您突然发现自己感到困惑,请在上课时回过头来看看! 嵌套内部类 - 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("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {
      
       public void up() {

           System.out.println("Seat up!");
       }
      
       public void down() {

           System.out.println("Seat down!");
       }
   }
}
我们在这里上课Bicycle。它有 2 个字段和 1 个方法:start(). 嵌套内部类 - 3它与普通类的不同之处在于它包含两个类:HandlebarSeat。他们的代码写在Bicycle类里面。这些是成熟的类:如您所见,每个类都有自己的方法。说到这里,您可能会有一个疑问:为什么我们要把一个类放在另一个类中?为什么要让它们成为内部类?好吧,假设我们需要单独的类来处理程序中的把手和座椅的概念。当然,我们没有必要让它们嵌套!我们可以做普通班。例如,像这样:

public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat 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 up! 
Let's go! 
Steer left! 
Steer right!
现在我们所看到的突然变得有道理了!:) 我们创建了一个自行车对象。我们创建了两个自行车“子对象”——一个把手和一个座椅。为了舒适起见,我们将座椅抬高,然后出发:根据需要踩踏板和转向!:) 我们需要的方法在适当的对象上被调用。一切都简单方便。在此示例中,将车把和座椅分开可增强封装(我们将有关自行车零件的数据隐藏在相关类中)并让我们创建更详细的抽象。现在让我们看看不同的情况。假设我们要创建一个模拟自行车商店和自行车备件的程序。 嵌套内部类 - 4在这种情况下,我们之前的解决方案将行不通。在自行车商店,每个单独的自行车零件即使与自行车分开也是有意义的。例如,我们将需要“向客户出售踏板”、“购买新座椅”等方法。在这里使用内部类是错误的——我们新程序中的每个自行车零件都具有代表意义其自身:可以脱离自行车的概念。如果您想知道是应该使用内部类还是将所有实体组织为单独的类,那么这正是您需要注意的。面向对象编程的好处在于它可以轻松地对真实世界的实体进行建模。在决定是否使用内部类时,这可能是您的指导原则。在实体店里,备件与自行车分开——这没关系。也就是说在设计程序的时候也是可以的。好的,我们已经了解了“哲学”:) 现在让我们熟悉内部类的重要“技术”特性。以下是您绝对需要记住和理解的内容:
  1. 没有外部类的对象,内部类的对象就不能存在。

    这是有道理的:这就是我们在程序中创建内部类SeatHandlebar内部类的原因——这样我们就不会得到孤立的车把和座椅。

    此代码无法编译:

    
    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }
    

    另一个重要特征由此而来:

  2. 内部类的对象可以访问外部类的变量。

    例如,让我们int seatPostDiameter在我们的类中添加一个变量(代表座杆的直径)Bicycle

    然后在Seat内部类中,我们可以创建一个displaySeatProperties()显示座位属性的方法:

    
    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("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: 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.displaySeatProperties();
       }
    }
    

    控制台输出:

    
    Seat properties: seatpost diameter = 40
    

    笔记:新变量使用最严格的访问修饰符 ( ) 声明private。而且内部类仍然可以访问!

  3. 不能在外部类的静态方法中创建内部类的对象。

    这可以通过内部类的组织方式的具体特征来解释。内部类可以有带参数的构造函数,或者只有默认构造函数。但无论如何,当我们创建一个内部类的对象时,外部类对象的引用无形中传递给了内部类创建的对象。毕竟,存在这样一个对象引用是绝对必要的。否则,我们将无法创建内部类的对象。

    但是如果外部类的方法是静态的,那么我们可能没有外部类的对象!这将违反内部类如何工作的逻辑。在这种情况下,编译器会产生一个错误:

    
    public static Seat createSeat() {
      
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
    
  4. 内部类不能包含静态变量和方法。

    逻辑是一样的:即使没有对象,静态方法和变量也可以存在并被调用或引用。

    但是如果没有外部类的对象,我们将无法访问内部类。

    明显的矛盾!这就是内部类中不允许使用静态变量和方法的原因。

    如果您尝试创建它们,编译器将生成错误:

    
    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
          
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    
  5. 创建内部类的对象时,其访问修饰符很重要。

    可以使用标准访问修饰符标记内部类:publicprivateprotectedpackage private

    为什么这很重要?

    这会影响我们可以在程序中创建内部类实例的位置。

    如果我们的Seat类声明为public,我们可以在任何其他类中创建Seat对象。唯一的要求是外部类的对象也必须存在。

    顺便说一句,我们已经在这里做了:

    
    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();
       }
    }
    

    我们很容易HandlebarMain类中获得了对内部类的访问权限。

    如果我们将内部类声明为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 private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }
    

    您可能已经理解其中的逻辑 :)

  6. 内部类的访问修饰符与普通变量的工作方式相同。

    修饰符protected提供对同一包中的子类和类中的实例变量的访问。

    protected也适用于内部类。我们可以创建protected内部类的对象:

    • 在外部类;
    • 在其子类中;
    • 在同一个包中的类中。

    如果内部类没有访问修饰符(package private),则可以创建内部类的对象:

    • 在外部类;
    • 在同一个包中的类中。

    您已经熟悉修饰符很长时间了,所以这里没有问题。

现在就这些了:) 但是不要懈怠!内部类是一个相当广泛的主题,我们将在下一课中继续探讨。现在你可以重温一下我们课程关于内部类的课了。下一次,让我们谈谈静态嵌套类。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION