【Java面向对象】5-11 内部类

§5-11 内部类

本节内容将了解内部类的不同写法。

5-11.1 什么是内部类

内部类就是在一个类的内部定义一个类。

例如,在类 A 中定义类 B,那么,对于类 B 而言,A 是它的外部类;对于类 A 而言,B 是它的内部类。

内部类可简单地划分为以下几种:

  • 成员内部类;
  • 静态内部类;
  • 局部内部类;
  • 匿名内部类;

内部类特点

  1. 编译之后可生成独立的字节码文件;

    例如:类 Outer 中的内部类 Inner 会在编译后生成 Outer$Inner.class 文件;

  2. 内部类可直接访问外部类的私有成员,而不破坏其封装性;

  3. 可为外部类提供必要的内部功能组件;

下文将简单地介绍这些内部类。

5-11.2 成员内部类

成员内部类:在类的内部定义,与实例变量、实例方法同级别的类。

示例

package com.zebt.oop.internal;

public class Outer {
    //外部类的属性
    private String name = "张三";
    private int age = 20;
    
    //成员内部类。为保证可在外部访问、实例化,将访问限定符设置为public
    public class Inner {
        //内部类属性
		private String location = "北京";
        private int tel = 100;
        
        //内部类访问外部类的私有属性
        public void show() {
            System.out.println(name);
            System.out.println(age);
            System.out.println(location);
            System.out.println(tel);
        }
    }
}

成员内部类属于外部类的一个实例部分,要想创建内部类对象,必须要依赖外部类对象。

package com.zebt.oop;

import com.zebt.oop.internal.Outer;

public class Main {
    public static void main(String[] args) {
        //实例化外部类
        Outer outer = new Outer();
        //通过外部类实例化内部类
        Outer.Inner inner = outer.new Inner();
        //若还导入了com.zebt.oop.internal.Outer.Inner; 则可以写成 Inner inner = outer.new Inner();
        //调用内部类方法
        inner.show();
    }
}

运行,得到

内部类方法。
张三
20
北京
100

另外,还有一种 “一步到位” 的创建方法:

Outer.Inner inner = new Outer().new Inner();	//仅导入 Outer
Inner inner = new Outer().new Inner();			//导入 Inner

当内部类和外部类拥有重名变量时,优先访问内部类变量。若要访问外部类变量,应当使用 外部类类名.this.变量名

示例

package com.zebt.oop.internal;

public class Outer {
    private String name = "张三";
    
    public class Inner {
        private String name = "李四";
        
        public void show() {
            System.out.println(name);
            System.out.println(Outer.this.name);	//访问外部类的属性时,实际上就是通过 外部类类名.this.变量名访问
        }
    }
}

在测试类中调用 show()

import com.zebt.oop.internal.Outer;
import com.zebt.oop.internal.Outer.Inner;

public class Main {
    public static void main(String[] args) {
        Inner inner = new Outer().new Inner();
        inner.show();
    }
}

得到:

李四
张三
20
北京
100

注意:成员内部类不允许定义静态成员,但是可以包含静态常量

//内部类中
private static String country = "中国";	//不允许:不支持内部类中的静态声明
private static final String country = "中国";	//允许

5-11.3 静态内部类

在内部类 Inner 前使用 static 关键字修饰,即可将该成员类声明为静态内部类,其级别和外部类相同。

静态内部类不依赖外部类对象,可以直接创建或通过类名访问,可声明静态成员。

示例

package com.zebt.oop.internal;

public class Outer {
    private String name = "张三";
    private int age = 20;
    
    public static class Inner {
        private String location =  "上海";
        private int tel = 200;
        private static int count = 100;
        
        public void show() {
            //通过实例化外部类对象访问外部类属性
            Outer outer = new Outer();
            System.out.println(outer.name);
            System.out.println(outer.age);
            
            //直接访问内部类属性和方法
            System.out.println(this.location);
            System.out.println(this.tel);
            
            //访问静态内部类静态属性
            System.out.println(Inner.count);
        }
    }
}

在测试类中,可以直接创建静态内部类对象

package com.zebt.oop;

import com.zebt.oop.internal.Outer;

public class Main {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner();
        inner.show();
    }
}

得到:

张三
20
上海
200
10

注意

  • 只有成员内部类可以由 static 关键字修饰,变为静态内部类;
  • 静态内部类访问外部类的成员属性,需要通过外部类对象访问。

5-11.4 局部内部类

和局部变量的概念类似,定义在外部类的方法内部的类称为局部内部类,其作用范围和创建对象范围仅限于当前方法。

由于其范围限制在方法内部,局部内部类不可以由任何访问修饰符修饰。

package com.zebt.oop.internal;

public class Outer {
    private String name = "张三";
    private int age = 20;
    
    //外部类中的方法
    public void show() {
        String location = "深圳";

        //局部内部类
        class Inner {
            private String email = "Zhangsan@qq.com";
            private String qq = "10000000";

            public void show2() {
                //调用外部类属性
                System.out.println(Outer.this.name);
                System.out.println(Outer.this.age);

                //调用内部类属性
                System.out.println(this.email);
                System.out.println(this.qq);

                //调用局部变量
                //JDK 1.7 及以前,局部内部类访问局部变量,局部变量必须为常量;1.8 以后,自动为常量 final
                System.out.println(location);
            }
        }

        //在方法中创建局部内部类对象
        Inner inner = new PtInner();
        inner.show2();
    }
}

要想实例化局部内部类,其范围仅限其所在方法中。因此,在测试类中,必须通过外部类调用该方法实现。

package com.zebt.oop;

import com.zebt.oop.internal.Outer;

public class Main {
    public static void main(String[] args) {
        //创建外部类对象
        Outer outer = new outer();
        outer.show();
    }
}

运行,得到

张三
20
Zhangsan@qq.com
10000000
深圳

注意

  • 局部内部类访问外部类当前方法中的局部变量时,由于无法保证变量的生命周期与自身相同,变量必须修饰final
  • 局部内部类中不可包含静态变量,但可以包含静态常量static final);
  • 若外部类方法属于静态方法,局部内部类访问外部类属性时,应当通过实例化外部类对象访问;
  • 局部内部类的使用受限,仅在当前方法中可用。

5-11.5 匿名内部类

匿名内部类:没有类名的局部内部类,一切特征都与局部内部类相同。

匿名内部类必须继承一个父类或实现一个接口

示例

我们首先创建一个接口:

public interface Usb {
    //接口中的方法
    void service();
}

然后,编写一个实现类,实现 Usb,并重写方法:

public class Mouse implements Usb {
    @Override
    public void service() {
        System.out.println("连接成功,鼠标开始工作。");
    }
}

接着,在测试类中,利用多态,创建一个接口类对象:

public class UsbTest {
    public static void main(String[] args) {
        //使用传统方法:多态 + 局部内部类,创建一个接口类对象
        Usb usb = new Mouse();  //接口本身没有构造器,不允许创建接口类对象;但可以利用多态创建实现类对象
        usb.service();          //调用实现类中的重写方法
        System.out.println("==================");
    }
}

运行,得到

连接成功,鼠标开始工作。
==================

改变实现方法,使用局部内部类,在 Main() 方法中使用局部内部类实现 Usb 接口,并重写方法:

public class UsbTest {
    public static void main(String[] args) {
        //使用局部内部类(非匿名)
        class Fan implements Usb {
            //重写方法
            @Override
            public void service() {
                System.out.println("连接成功,风扇开始工作。");
            }
        }

        Usb usb2 = new Fan();
        usb2.service();         //调用实现类中的重写方法
        System.out.println("==================");
    }
}

运行,得到

连接成功,风扇开始工作。
==================

如果该局部内部类只使用一次,可以使用匿名内部类优化

public class UsbTest {
    public static void main(String[] args) {
        //使用匿名内部类优化
        Usb usb3 = new Usb() {
            @Override
            public void service() {
                System.out.println("连接成功,USB接口开始工作。");
            }
        };
        usb3.service();
        System.out.println("==================");
    }
}

运行,得到:

连接成功,USB接口开始工作。
==================

注意

  • 匿名内部类没有名字,一切特征与局部内部类相同;

  • 匿名内部类必须继承一个父类,或者实现一个接口;

  • 定义类、实现类、创建对象的语法合并,只能创建一个该类对象;

  • 优点:减少代码量;缺点:可读性较差;

  • 实际上,该匿名内部类也是有名字的(由编译器命名);

    例如:上述的 usb3 所创建的匿名内部类,编译后产生字节码文件 UsbTest$1.class

posted @ 2023-07-13 22:02  Zebt  阅读(14)  评论(0)    收藏  举报