Java内部类一口气讲完!( •̀ ω •́ )✧

Java 内部类

Java面向对象的设计 - Java 内部类

什么是内部类?

作为包的成员的类被称为顶级类。

一个类可以在另一个类中声明。这种类型的类称为内部类。

如果在另一个类中声明的类被显式或隐式声明为static,它被称为嵌套类,而不是内部类。

包含内部类的类称为封闭类或外部类。

例子

下面的代码声明一个内部类。

复制代码
class Outer {
  public class Inner {
    // Members of the Inner class go here
  }
  // Other members of the Outer class go here
}

Outer类是一个顶级类。

Inner类是一个内部类。它是外类的成员。

外层类是Inner类的封闭(外部)类。

内部类可以是另一个内部类的封闭类。内部类的嵌套层次没有限制。

内部类的实例只能存在于其封闭类的实例中。

使用内部类的优点

以下是内部类的一些优点。

  • 在将使用它们的其他类附近定义类。
  • 提供了一个额外的命名空间来管理类结构。
  • 一些设计模式使用内部类更容易实现。
  • 实现回调机制使用内部类是优雅和方便的。它有助于在Java中实现闭包。

访问局部变量的限制

下面的代码演示了访问局部内部类中的局部变量的规则。

main()方法声明两个局部变量x和y。这两个变量都是最终的。

变量x在被初始化之后从不改变,变量y不能被改变,因为它被声明为final。

复制代码
public class Main {
  public static void main(String... args) {
    int x = 1;
    final int y = 2;

    class LocalInner {
      void print() {
        System.out.println("x = " + x);
        System.out.println("y = " + y);
      }
    }
    /*
     * Uncomment the following statement will make the variable x no longer
     * an effectively final variable and the LocalIneer class will not compile.
     */
    // x = 100;

    LocalInner li = new LocalInner();
    li.print();
  }
}

上面的代码生成以下结果。

内部类和继承

内部类可以继承另一个内部类,顶级类或其封闭类。

复制代码
class A {
  public class B {
  }

  public class C extends B {
  }

  public class D extends A {
  }
}

class E extends A {
  public class F extends B {
  }
}

内部类中没有静态成员

Java中的关键字static使一个构造成为一个顶层结构。

因此,我们不能为内部类声明任何静态成员(字段,方法或初始化器)。

允许在内部类中有作为编译时常量的静态字段。

复制代码
class A {
  public class B {
    public final static int DAYS_IN_A_WEEK = 7; // OK
    public final String str = new String("Hello");
  }
}

生成的内部类的类文件

每个内部类都被编译成一个单独的类文件。

成员内部类和静态内部类的类文件名格式如下:

复制代码
<outer-class-name>$<member-or-static-inner-class-name>

局部内部类的类文件名的格式如下:

复制代码
<outer-class-name>$<a-number><local-inner-class-name>

匿名类的类文件名的格式如下:

复制代码
<outer-class-name>$<a-number>

类文件名中的<a-number>是从1开始顺序生成的数字,以避免任何名称冲突。

静态上下文中的内类

我们可以在静态上下文中定义一个内部类,例如静态方法或静态初始化器。

所有静态字段成员都可以访问这样的内部类。

复制代码
class Outer {
  static int k = 1;
  int m = 2;

  public static void staticMethod() {
    // Class Inner is defined in a static context
    class Inner {
      int j = k; // OK. Referencing static field k
      // int n = m; // An error. Referencing non-static field m
    }
  }
}

Java 内部类类型

Java面向对象设计 - Java内部类类型

您可以在类中的任何位置定义内部类,您可以在其中编写Java语句。

有三种类型的内部类。内部类的类型取决于位置和声明的方式。

  • 成员内部类
  • 局部内部类
  • 匿名内部类

成员内部类

成员内部类在类中声明的方式与声明成员字段或成员方法相同。

它可以声明为public,private,protected或package-level。

成员内部类的实例可以仅存在于其封闭类的实例内。

以下代码创建了一个成员内部类。

复制代码
class Car {
  private int year;

  // A member inner class named Tire public
  class Tire {
    private double radius;
    public Tire(double radius) {
      this.radius = radius;
    }
    public double getRadius() {
      return radius;
    }
  } // Member inner class declaration ends here

  // A constructor for the Car class
  public Car(int year) {
    this.year = year;
  }
  public int getYear() {
    return year;
  }
}

局部内部类

一个局部内部类在块中声明。其范围仅限于声明它的块。

由于其范围限于其封闭块,因此其声明不能使用任何访问修饰符,例如public,private或protected。

通常,在方法内定义局部内部类。但是,它也可以在静态初始化器,非静态初始化器和构造器中定义。

下面的代码显示了一个局部内部类的例子。

复制代码
import java.util.ArrayList;
import java.util.Iterator;

public class Main {
  public static void main(String[] args) {
    StringList tl = new StringList();
    tl.addTitle("A");
    tl.addTitle("B");

    Iterator iterator = tl.titleIterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
class StringList {
  private ArrayList<String> titleList = new ArrayList<>();

  public void addTitle(String title) {
    titleList.add(title);
  }

  public void removeTitle(String title) {
    titleList.remove(title);
  }

  public Iterator<String> titleIterator() {
    // A local inner class - TitleIterator
    class TitleIterator implements Iterator<String> {
      int count = 0;
      @Override
      public boolean hasNext() {
        return (count < titleList.size());
      }

      @Override
      public String next() {
        return titleList.get(count++);
      }
    }

    TitleIterator titleIterator = new TitleIterator();
    return titleIterator;
  }
}

上面的代码生成以下结果。

例子

下面的代码有一个局部内部类继承自另一个公共类。

复制代码
import java.util.Random;

abstract class IntGenerator {
  public abstract int getValue() ;
}
class LocalGen {
  public IntGenerator getRandomInteger() {
    class RandomIntegerLocal extends IntGenerator {
      @Override
      public int getValue() {
        Random rand = new Random();
        long n1 = rand.nextInt();
        long n2 = rand.nextInt();
        int value = (int) ((n1 + n2) / 2);
        return value;
      }
    }

    return new RandomIntegerLocal();
  } // End of getRandomInteger() method
}

public class Main {
  public static void main(String[] args) {
    LocalGen local = new LocalGen();
    IntGenerator rLocal = local.getRandomInteger();
    System.out.println(rLocal.getValue());
    System.out.println(rLocal.getValue());
  }
}

上面的代码生成以下结果。

匿名内部类

匿名内部类没有名称。因为它没有名称,它不能有一个构造函数。

匿名类是一次性类。您定义一个匿名类并同时创建它的对象。

创建匿名类及其对象的一般语法如下:

复制代码
new Interface()  {
// Anonymous  class body  goes  here
}

复制代码
new Superclass(<argument-list-for-a-superclass-constructor>)  {
// Anonymous  class body  goes  here
}

new运算符用于创建匿名类的实例。

它后面是现有的接口名称或现有的类名称。

接口名称或类名称不是新创建的匿名类的名称。

如果使用接口名称,则匿名类实现接口。

如果使用类名,则匿名类继承自类。

仅当新运算符后面跟有类名时,才使用<argument-list>。如果新运算符后跟接口名称,则它为空。

如果<argument-list>存在,它包含要调用的现有类的构造函数的实际参数列表。

匿名类主体像往常一样在大括号中。

匿名类主体应该简短,以便更好的可读性。

下面的代码包含一个简单的匿名类,它在标准输出上打印一条消息。

复制代码
public class Main {
  public static void main(String[] args) {
    new Object() {
      // An instance initializer
      {
        System.out.println("Hello from  an  anonymous class.");
      }
    }; // A semi-colon is necessary to end the statement
  }
}

上面的代码生成以下结果。

例2

以下代码使用匿名类来创建Iterator。

复制代码
import java.util.ArrayList;
import java.util.Iterator;

public class Main {
  private ArrayList<String> titleList = new ArrayList<>();

  public void addTitle(String title) {
    titleList.add(title);
  }

  public void removeTitle(String title) {
    titleList.remove(title);
  }

  public Iterator<String> titleIterator() {
    // An anonymous class
    Iterator<String> iterator = new Iterator<String>() {
      int count = 0;

      @Override
      public boolean hasNext() {
        return (count < titleList.size());
      }

      @Override
      public String next() {
        return titleList.get(count++);
      }
    }; // Anonymous inner class ends here

    return iterator;
  }
}

Java 静态对象类

Java面向对象设计 - Java静态内部类

静态成员类不是内部类

在另一个类的主体中定义的成员类可以声明为静态。

例子

以下代码声明了顶级类A和静态成员类B:

复制代码
class A {
  // Static member class
  public static class B {
    // Body for class B goes here
  }
}

注意

静态成员类不是内部类。它被认为是一个顶级类。

静态成员类也称为嵌套顶级类。

A类的实例和B类的实例可以独立存在,因为它们都是顶级类。

静态成员类可以声明为public,protected,package-level或private,以限制其在其封闭类之外的可访问性。

使用静态成员类有两个好处:

  • 静态成员类可以访问其包含类的静态成员,包括私有静态成员。
  • 一个包通过提供一个命名空间,就像一个顶级类的容器。具有静态成员类的顶级类提供了额外的命名空间层。

静态成员类是其封闭顶级类的直接成员,而不是包的成员。

静态成员类的对象的创建方式与使用new运算符创建顶级类的对象的方式相同。要创建一个B类的对象,你写

复制代码
A.B bReference = new A.B();

由于类B的简单名称在类A中的范围内,因此我们可以使用其简单名称在类A中创建其对象

复制代码
B  bReference2 = new B(); // This  statement appears inside class  A  code

我们还可以通过导入com.java2s.innerclasses.A.B类,使用A类之外的简单名称B.

例2

下面的代码显示了如何使用静态内部类。

复制代码
public class Main {
  public static void main(String[] args) {
    Car.Tire m = new Car.Tire(17);
    Car.Tire m2 = new Car.Tire(19);

    Car.Keyboard k = new Car.Keyboard(122);
    Car.Keyboard k1 = new Car.Keyboard(142);

    System.out.println(m);
    System.out.println(m2);
    System.out.println(k);
    System.out.println(k1);
  }
}
class Car {
  // Static member class - Monitor
  public static class Tire {
    private int size;

    public Tire(int size) {
      this.size = size;
    }

    public String toString() {
      return "Monitor   - Size:" + this.size + "  inch";
    }
  }

  // Static member class - Keyboard
  public static class Keyboard {
    private int keys;

    public Keyboard(int keys) {
      this.keys = keys;
    }

    public String toString() {
      return "Keyboard  - Keys:" + this.keys;
    }
  }
}

上面的代码生成以下结果。

Java 内部类对象

Java面向对象设计 - Java内部类对象

局部内部类的对象是使用块中的新运算符创建的,它声明了类。

在声明类的同时创建一个匿名类的对象。

静态成员类是另一种类型的顶级类。

您可以按照创建顶级类的对象的方式创建静态成员类的对象。

成员内部类的实例始终存在于其封闭类的实例中。

语法

创建成员内部类的实例的一般语法如下:

复制代码
OuterClassReference.new MemberInnerClassConstructor()

OuterClassReference是包围类的引用,后跟一个后跟新运算符的点。

例子

成员内部类的构造函数调用遵循new运算符。

复制代码
class Outer  {
    public class  Inner {
    }
}

要创建内部成员内部类的实例,必须首先创建其封闭类Outer的实例。

复制代码
Outer  out  = new Outer();

现在,您需要在out参考变量上使用new运算符来创建Inner类的对象。

复制代码
out.new Inner();

为了将内部成员内部类的实例的引用存储在引用变量中,我们可以写下面的语句:

复制代码
Outer.Inner in = out.new   Inner();

以下代码显示了如何创建成员内部类的对象

复制代码
public class Main {
  public static void main(String[] args) {
    Car c = new Car();

    Car.Tire t = c.new Tire(9);
    
  }
}
class Car {
  public class Tire {
    private int size;

    public Tire(int size) {
      this.size = size;
    }

    public String toString() {
      return "Monitor   - Size:" + this.size + "  inch";
    }
  }

}

Java 内部类成员

Java面向对象设计 - Java内部类成员

内部类可以访问其所有实例成员,实例字段和其封闭类的实例方法。

复制代码
class Outer {
  private int value = 2014;
  public class Inner {
    public void printValue() {
      System.out.println("Inner: Value  = " + value);
    }
  } // Inner class ends here

  public void printValue() {
    System.out.println("Outer: Value  = " + value);
  }
  public void setValue(int newValue) {
    this.value = newValue;
  }
}

public class Main {
  public static void main(String[] args) {
    Outer out = new Outer();
    Outer.Inner in = out.new Inner();

    out.printValue();
    in.printValue();

    out.setValue(2015);

    out.printValue();
    in.printValue();
  }
}

上面的代码生成以下结果。

例子

以下代码显示如何访问内部类的内部变量。

复制代码
public class Main {
  public static void main(String[] args) {
    Outer out = new Outer();
    Outer.Inner in = out.new Inner();

    out.printValue();
    in.printValue();

    out.setValue(3);

    out.printValue();
    in.printValue();
  }
}
class Outer {
  private int value = 1;
  public class Inner {
    private int value = 2;
    public void printValue() {
      System.out.println("Inner: Value  = " + value);
    }
  } // Inner class ends here
  public void printValue() {
    System.out.println("Outer: Value  = " + value);
  }
  public void setValue(int newValue) {
    this.value = newValue;
  }
}

上面的代码生成以下结果。

在内部类中使用关键字this

以下代码显示如何在内部类中使用关键字this。

复制代码
class Outer {
  private int value = 1;
  class QualifiedThis {
    private int value = 2;

    public void printValue() {
      System.out.println("value=" + value);
      System.out.println("this.value=" + this.value);
      System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);
    }

    public void printHiddenValue() {
      int value = 2;
      System.out.println("value=" + value);
      System.out.println("this.value=" + this.value);

      System.out.println("QualifiedThis.this.value=" + QualifiedThis.this.value);
    }
  }
  public void printValue() {
    System.out.println("value=" + value);
    System.out.println("this.value=" + this.value);
  }  
}

public class Main {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Outer.QualifiedThis qt = outer.new QualifiedThis();
    System.out.println("printValue():");
    qt.printValue();

    System.out.println("printHiddenValue():");
    qt.printHiddenValue();
    outer.printValue();
  }
}

上面的代码生成以下结果。

隐藏变量

如果实例变量名称被隐藏,您必须使用关键字this或类名称以及关键字this限定其名称。

复制代码
class TopLevelOuter {
  private int v1 = 100;

  // Here, only v1 is in scope
  public class InnerLevelOne {
    private int v2 = 200;

    // Here, only v1 and v2 are in scope
    public class InnerLevelTwo {
      private int v3 = 300;

      // Here, only v1, v2, and v3 are in scope
      public class InnerLevelThree {
        private int v4 = 400;

        // Here, all v1, v2, v3, and v4 are in scope

      }
    }
  }
}

从外部类引用变量

以下代码显示如何从外部类引用变量。

复制代码
public class Test{
  private int value = 1;
  public class Inner {
    private int value = 2;

    public void printValue() {
      System.out.println("Inner: Value  = " + value);
      System.out.println("Outer: Value  = " + Test.this.value);
    }
  } // Inner class ends here

  public void printValue() {
    System.out.println("\nOuter - printValue()...");
    System.out.println("Outer: Value  = " + value);
  }

  public void setValue(int newValue) {
    System.out.println("\nSetting  Outer"s value to " + newValue);
    this.value = newValue;
  }

}
相关推荐
The Future is mine29 分钟前
Python计算经纬度两点之间距离
开发语言·python
Enti7c30 分钟前
HTML5和CSS3的一些特性
开发语言·css3
腥臭腐朽的日子熠熠生辉35 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
爱吃巧克力的程序媛37 分钟前
在 Qt 创建项目时,Qt Quick Application (Compat) 和 Qt Quick Application
开发语言·qt
ejinxian37 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之42 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
独好紫罗兰1 小时前
洛谷题单3-P5719 【深基4.例3】分类平均-python-流程图重构
开发语言·python·算法
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿