Java 内部类

213 阅读5分钟

内部类存在的意义

内部类可以访问该类定义所处作用域范围内的数据

内部类Inner 可以访问外部类Outer中的private变量num

    public class Outer {
      private int num = 10;
      
      public class Inner {
        void print() {
          System.out.println(num);// 10
        }
      }
    }

为什么内部类可以访问外部类的成员呢?

执行javac Outer.java 生成两个class文件, Outer.class和Outer$Inner.class

当外部类的对象创建了一个内部类的对象时,内部类对象必定会秘密捕获一个指向外部类对象的引用,然后访问外部类的成员时,就是用那个引用来选择外围类的成员的

内部类可以对同一包中的其他类隐藏起来

我们都知道普通的类是不能使用privateprotected来修饰的,而内部类是可以用privateprotected来修饰

    public interface Incrementable {
      void increment();
    }
    
    public class Example {
    	private class InnerClass implements Incrementable {
    	  @Override
    	  public void increment() {
    	    System.out.println("InnerClass increment");
    	  }
    	}
    	
    	public Incrementable getInnerClass() {
    	  return new InnerClass();
    	}
    }

ExamplegetInnerClass返回了一个InnerClass的实例,但我们并不知道是如何实现的,而且InnerClassprivate的,所以很好的实现了隐藏。

内部类可以实现 java 单继承的缺陷

Java是单根继承的,无法继承多个类,最多可以实现多个接口,但使用接口有时候有很多不方便的地方。比如我们实现一个接口就必须实现它里面的所有方法。而有了内部类就不一样了。它可以使我们的类继承多个具体类或抽象类。

    public class ClassA {
      public String name() {
        return "Dsying";
      }
      
      public String doSomeThing() {
        return "do something";
      }
    }
    
    public class ClassB {
      public int age() {
        return 25;
      }
    }
    
    public class ClassC {
      private class Test1 extends ClassA {
        @Override
        public String name() {
          return super.name();
        }
      }
      
      private class Test2 extends ClassB {
        @Override
        public int age() {
          return super.age();
        }
      }
      
      public String getName() {
        return new Test1().name();
      }
      
      public int getAge() {
        return new Test2().age();
      }
      
      public static void main(String[] args) {
        ClassC c = new ClassC();
        System.out.println(c.getName());
        System.out.println(c.getAge());
      }
    }

从上面的例子可以看出,ClassC通过内部类Test1 继承 ClassA, 内部类Test2继承ClassB, 从而继承了 Test1name()Test2age()方法,并且不需要关注 ClassAdoSomeThing(), 这就是内部类实现多继承比实现多个接口的优点。

通过匿名内部类优化接口的实现

    public class User {
      int age;
      String name;
      
      public int getAge() {
        return age;
      }
      
      public void setAge(int age) {
        this.age = age;
      }
      
      public String getName() {
        return name;
      }
      
      public void setName(String name) {
        this.name = name;
      }
      
      public User(int age, String name) {
        this.age = age;
        this.name = name;
      }
      
      @Override
      public String toString() {
        return "User{" +
          "age=" + age +
          ", name='" + name + '\'' +
          '}';
      }
    }

提供一个方法过滤出符合条件的User

    public static List<User> filterUser(List<User> users, Predicate<User> predicate) {
      return users.stream().filter(predicate).collect(Collectors.toList());
    }

准备一个List<User>

    List<User> users = Stream.of(new User(27, "张三"), new User(18, "李四")).collect(Collectors.toList());

通过匿名内部类实现

    filterUser(users, new Predicate<User>() {
      @Override
      public boolean test(User user) {
        return user.getName().startsWith("张");
      }
    })
    // [User{age=27, name='张三'}]

通过lambda表达式实现

    filterUser(users, user -> user.getName().startsWith("李"))
    // [User{age=27, name='张三'}]

内部类的分类

内部类分为两种类型-

  • 非静态内部类 -这些是的非静态成员。
  • 静态内部类 -这些是的静态成员。

非静态内部类

类的内部类

创建内部类非常简单。您只需要在一个类中编写一个类。与类不同,内部类可以是私有的,并且一旦将内部类声明为私有,就不能从该类外部的对象访问它

    class Outer_Demo {
       int num;
       
       // inner class
       private class Inner_Demo {
          public void print() {
             System.out.println("This is an inner class");
          }
       }
       
       // Accessing he inner class from the method within
       void display_Inner() {
          Inner_Demo inner = new Inner_Demo();
          inner.print();
       }
    }
       
    public class My_class {
    
       public static void main(String args[]) {
          // Instantiating the outer class 
          Outer_Demo outer = new Outer_Demo();
          
          // Accessing the display_Inner() method.
          outer.display_Inner();
       }
    }

在这里,您可以观察到Outer_Demo是外部类,Inner_Demo是内部类,display_Inner()是在其中实例化内部类的方法,并且该方法是从main方法调用的

方法的内部类

在Java中,我们可以在方法中编写类,而这将是局部类型。与局部变量一样,内部类的范围也受方法限制。只能在定义内部类的方法内实例化方法本地内部类

    public class Outerclass {
       // instance method of the outer class 
       void my_Method() {
          int num = 23;
    
          // method-local inner class
          class MethodInner_Demo {
             public void print() {
                System.out.println("This is method inner class "+num);	   
             }   
          } 
    	   
          // Accessing the inner class
          MethodInner_Demo inner = new MethodInner_Demo();
          inner.print();
       }
       
       public static void main(String args[]) {
          Outerclass outer = new Outerclass();
          outer.my_Method();	   	   
       }
    }

匿名内部类

没有类名声明的内部类称为匿名内部类。在匿名内部类的情况下,我们同时声明和实例化它们。通常,它们在需要覆盖类或接口的方法时使用。匿名内部类的语法如下

    AnonymousInner an_inner = new AnonymousInner() {
       public void my_method() {
          // ........
          // ........
       }   
    };

以下程序显示了如何使用匿名内部类重写类的方法。

    abstract class AnonymousInner {
       public abstract void mymethod();
    }
    
    public class Outer_class {
    
       public static void main(String args[]) {
          AnonymousInner inner = new AnonymousInner() {
             public void mymethod() {
                System.out.println("This is an example of anonymous inner class");
             }
          };
          inner.mymethod();	
       }
    }

匿名内部类作为参数

通常,如果方法接受接口,抽象类或具体类的对象,则我们可以实现接口,扩展抽象类并将对象传递给方法。如果它是一个类,那么我们可以直接将其传递给方法

但是在这三种情况下,都可以将匿名内部类传递给该方法。这是将匿名内部类作为方法参数传递的语法:

    obj.my_Method(new My_Class() {
       public void Do() {
          // .....
          // .....
       }
    });

例如filterUser的参数中有一个Predicate参数为接口, 调用filterUser方法时,我可以传入一个该接口的匿名内部类

    public static List<User> filterUser(List<User> users, Predicate<User> predicate) {
      return users.stream().filter(predicate).collect(Collectors.toList());
    }
    
    filterUser(users, new Predicate<User>() {
      @Override
      public boolean test(User user) {
        return user.getName().startsWith("张");
      }
    })

静态内部类

静态内部类是外部类的静态成员。使用其他静态成员,可以在不实例化外部类的情况下对其进行访问。就像静态成员一样,静态内部类无法访问外部类的实例变量和方法。静态嵌套类的语法如下-

    class MyOuter {
       static class Nested_Demo {
       }
    }

实例化静态嵌套类与实例化内部类有点不同。以下程序显示了如何使用静态嵌套类。

    public class Outer {
       static class Nested_Demo {
          public void my_method() {
             System.out.println("This is my nested class");
          }
       }
       
       public static void main(String args[]) {
          Outer.Nested_Demo nested = new Outer.Nested_Demo();	 
          nested.my_method();
       }
    }