Back

Java内部类

内部类的介绍及分类

Java内部类


  1. 内部类的基本介绍
  2. 局部内部类
  3. 匿名内部类
  4. 成员内部类
  5. 静态内部类

内部类基本介绍

  • 一个类的内部又完整的嵌套了另一个类结构。被嵌套的类又称为内部类,嵌套其他类的类称为外部类。是类的五大成员之一(属性、方法、构造器、代码块、内部类)。

  • 内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

  • 基本语法

    class Outer{//外部类
        class Inner{//内部类
    
        }
    }
    class Other{//外部其他类
    
    }
    
  • 内部类的分类

    • 定义在外部类的局部位置上(比如方法内):
      1. 局部内部类(有类名)
      2. 匿名内部类(没有类名,重点!)
    • 定义在外部类的成员位置上:
      1. 成员内部类(没用static修饰)
      2. 静态内部类(使用static修饰)

局部内部类

说明:局部内部类是定义在外部类的局部位置上的,通常在方法中,并且有类名。

  1. 可以直接访问外部类的所有成员,包含私有的

  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final。(使用了final说明该内部类不能被继承!)

  3. 作用域:仅仅在定义它的方法或代码块中。

  4. 局部内部类访问外部类的成员(访问方式:直接访问)

  5. 外部类访问局部内部类的成员(访问方式:创建对象,再访问[注意,必须在作用域内!])

    代码演示

    package com.innerclass;
    
    public class LocalInnerClass {
        public static void main(String[] args) {
            Outer01 outer01 = new Outer01();
            outer01.m2();
        }
    }
    class Outer01{
        private int n = 100;
        private void m1(){
            System.out.println("m1");
        }
        public void m2(){
            //定义为final就不能被继承
            //作用域:定义它的方法或代码块中
            final class Inner01{//局部内部类
                public void f1(){
                    //访问外部类的成员属性n与m1
                    System.out.println("n = "+ n);
                    m1();
                }
            }
            //外部类调用必须在内部类的作用域内!
            Inner01 inner01 = new Inner01();
            inner01.f1();
        }
    
    
    }
    
  • 外部其他类不能访问局部内部类!因为局部内部类是一个局部变量

  • 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想要访问外部类的成员,则可以使用(外部类目.this.成员)去访问。

    System.out.println("OuterClass的n2 = "+ OuterClass.this.n2);
    

    OuterClass.this本质就是外部类的对象,即哪个对象调用了内部类作用域的方法,OuterClass.this就是哪个对象

匿名内部类

说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名(系统的底层有给他分配名字)。

  1. 匿名内部类的基本语法

    new 类或接口(参数列表){
        //类体
    };
    
  2. 匿名内部类的本质(代码演示)

    有一个需求,想要使用AnonInterface接口,并创建对象。

    传统的方式是写一个类,实现该接口,并创建其对象。

    AnonmousInterface接口

    interface AnonInterface{
        public void cry();
    }
    

    Tiger类

    class Tiger implements AnonInterface{
        public void cry(){
            @Override
            System.out.println("Titge 在叫");
        }
    }
    

    然后在main函数创建其对象即可

    package com.innerclass;
    
    public class AnonymousInnerClass {
        public static void main(String[] args) {
            AnonInterface tiger = new Tiger();
            tiger.cry();
        }
    }
    

    如果我们现在的需求改为Tiger类只使用一次,后面不再使用时。我们可以使用匿名内部类来简化开发。

    AnonInterface接口

    interface AnonInterface{
        public void cry();
    }
    

    外部类Outer02

    class   Outer02{
        private int n = 10;
        public void method(){
            //使用匿名内部类
            AnonInterface tiger = new AnonInterface(){
            @Override
            public void cry() {
                System.out.println("tiger 在叫");
            }
        };
        tiger.cry();
        }
    }
    

    main

    package com.innerclass;
    
    public class AnonymousInnerClass {
        public static void main(String[] args) {
            Outer02 outer02 = new Outer02();
            outer02.method();
        }
    }
    

    上述代码与传统方式均实现了需求,而且上面的代码,Tiger类只使用了一次。这就是匿名内部类的奇妙之处!

    思考:上述外部类Outer02中,Tiger的编译类型及运行类型分别是什么?

    • Tiger的编译类型为接口AnonInterface

    • Tiger的运行类型为匿名内部类 Outer02$1

      其实在使用匿名内部类时,底层是给该内部类分配了名字的

      Class Outer02$1 implements AnonInterface{
          @Override
          pubilc void cry(){
              System.out.println("Tiger 在叫");
          }
      }
      

      我们通过getclass()方法可以验证上述代码。

      System.out.println(tiger.getClass());
      

      image07

      而且,在外部类Outer02中,AnonInterface tiger = new AnonInterface()这条语句的执行,jdk底层会在创建匿名内部类Outer02$1时,立刻创建Outer02$1实例,并且把地址返回给tiger

      image08

    需要注意的是:匿名内部类Outer02$1是只使用一次,就不能再使用。而对象Tiger能重复使用!

    基于类构建的匿名内部类也与上述大体一致。

  3. 匿名内部类的细节

    • 匿名内部类的语法比较奇特,需要注意的是,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征。因此可以直接调用匿名内部类的方法。

      代码演示:

      创建Person类

      class Person{
          public void hi(){
              System.out.println("hi");
          }
          public void sayHi(String name){
              System.out.println("Hi "+name);
          }
      }
      

      创建外部类Outer03

      class Outer03{
          private int i = 10;
          public void f1(){
              Person person = new Person(){
                  @Override
                  public void hi() {
                      System.out.println("匿名内部类重写了Hi方法");
                  }
              };
              //动态绑定,person的运行类型是Outer03$1
              person.hi();
      
              //因为匿名内部类返回的是一个对象(new),所以可以直接调用方法
              //在底层 class 匿名内部类 extends Person
              new Person(){
                  @Override
                  public void sayHi(String name) {
                      super.sayHi(name);
                  }
              }.sayHi("jack");
      
          }
      }
      

      创建AnonymousInnerClassDetail

      package com.innerclass;
      
      public class AnonymousInnerClassDetail {
          public static void main(String[] args) {
              Outer03 outer03 = new Outer03();
              outer03.f1();
          }
      }
      

      输出结果:

      image09

    • 外部其他类不能访问匿名内部类

  4. 匿名内部类的实践

    匿名内部类可以当做实参直接传递,简洁高效,避免硬编码

    创建接口InterfaceExercise

    interface InterfaceExercise{
        public void show();
    }
    

    创建InnerClassExercise01

    package com.innerclass;
    
    public class InnerClassExercise01 {
        public static void main(String[] args) {
            //匿名内部类做形参,简洁高效。
            f1(new InterfaceExercise(){
                @Override
                public void show() {
                    System.out.println("这是一幅名画...");
                }
            });
        }
        //静态方法,参数是接口类型
        public static void f1(InterfaceExercise iE){
            iE.show();
        }
    }
    

    如果使用实现类实现该接口,那修改一个实现类的对象时会影响到其他的实现类对象。而使用匿名内部类,可以避免这个问题,并且这个类只使用一次即可。

    image10

成员内部类

说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。

  1. 可以直接访问外部类的所有成员,包含私有的

  2. 可以添加任意访问修饰符(public,protected,默认,private),因为它的地位就是一个成员。

    代码演示:

    创建外部类Outer05

    class Outer05{
        private int i = 10;
        //定义在成员位置上,可以添加修饰符!
        public class Inner04{
            public void say(){
                //可以访问外部类的私有属性
                System.out.println("成员内部类 i = "+i);
            }
        }
        //因为作用域是方法或代码块内,所以需要写一个方法来使用内部类
        public void t1(){
            Inner04 inner04 = new Inner04();
            inner04.say();
        }
    }
    

    创建测试类MemberInnerClass

    package com.innerclass;
    
    public class MemberInnerClass {
        public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.t1();
        }
    }
    

    运行结果:

    image11

  3. 作用域与外部类的其他成员一样,为整个类体。比如上述案例,在外部类的成员方法中创建成员内部类对象,再调用方法。

  4. 外部其他类可以访问成员内部类

    外部其他类使用成员内部类的两种方式

    第一种方式

    //第一种方式
    //outer05.new Inner04();相当于把 new Inner04()当做是outer05的成员
    Outer05.Inner04 inner04 = outer05.new Inner04();
    

    第二种方式

    //在外部类中编写一个方法,可以返回一个Inner04的值
    Outer05.Inner04 getInner04 = outer05.getInner04();
    

    外部类Outer05中

    //该方法返回一个成员内部类
    Inner04 getInner04(){
        return new Inner04();
    }
    

静态内部类

说明:静态内部类定义在外部类的成员位置,并且有static修饰

  1. 可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员

    image12

  2. 可以添加任意访问修饰符(public,protected,默认,private),因为它的地位就是一个成员。

  3. 作用域:同其他成员,为整个类体

    代码演示

    创建Outer06类

    class Outer06{
        private int n1 = 10;
        private static int n2 = 100;
        static class Inner06{
            void say(){
                System.out.println("hi n = " + n2);
            }
        }
        void t1(){
            Inner06 inner06 = new Inner06();
            inner06.say();
        }
    }
    

    创建测试类StaticInnerClass

    package com.innerclass;
    
    public class StaticInnerClass {
        public static void main(String[] args) {
            Outer06 outer06 = new Outer06();
            outer06.t1();
        }
    }
    

    运行结果:

    image13

  4. 静态内部类要访问外部类的话,可以访问其全部静态成员。

  5. 外部类访问静态内部类,需要先创建对象,再访问。

  6. 外部其他类使用静态内部类的方式有两种,与成员内部类的一样。

Built with Hugo
Theme Stack designed by Jimmy