Skip to content

S01-12 JavaSE-接口与内部类

[TOC]

课堂练习 InterfaceExercise02.java

java
package com.hspedu.interface_;

public class InterfaceExercise02 {
    public static void main(String[] args) {
        new C().pX();
    }
}

interface A {
    // 等价于 public static final int x = 0;
    int x = 0;
}

class B {
    // 普通属性
    int x = 1;
}

class C extends B implements A {
    public void pX() {
        // System.out.println(x); // 错误,原因不明确x
        // 可以明确的指定x
        // 访问接口的x 就使用A.x
        // 访问父类的x 就使用super.x
        System.out.println(A.x + " " + super.x);
    }
}

内部类

基本介绍

嵌套其他类的类称为外部类(outer class),被嵌套的类称为内部类(inner class)

内部类是类的第五大成员【属性、方法、构造器、代码块、内部类】,最大特点:

  • 可以直接访问外部类的私有属性
  • 体现类与类之间的包含关系

注意:内部类是学习的难点,也是重点,底层源码中有大量内部类的应用。

基本语法

java
class Outer { // 外部类
    class Inner { // 内部类
    }
}

class Other { // 外部其他类
}

快速入门案例

java
package com.hspedu.innerclass;

public class InnerClass01 {
    public static void main(String[] args) {
    }
}

class Outer { // 外部类
    private int n1 = 100;

    public Outer(int n1) {
        this.n1 = n1;
    }

    public void m1() {
        System.out.println("m1()");
    }

    { // 代码块
        System.out.println("代码块...");
    }

    class Inner { // 内部类, 在Outer 类的内部
    }
}

内部类的分类

定义位置类型说明
成员位置成员内部类无static修饰
成员位置静态内部类有static修饰
局部位置(方法/代码块)局部内部类有类名
局部位置(方法/代码块)匿名内部类无类名(重点)

局部内部类的使用 LocalInnerClass.java

核心特点

  1. 可以直接访问外部类的所有成员(包含私有)
  2. 不能添加访问修饰符(地位等同于局部变量),但可以用final修饰
  3. 作用域:仅在定义它的方法或代码块中
  4. 访问外部类成员:直接访问
  5. 外部类访问局部内部类:创建对象(必须在作用域内)
  6. 外部其他类:不能访问局部内部类
  7. 成员重名时:默认就近原则,访问外部类成员用 外部类名.this.成员

代码示例

java
package com.hspedu.innerclass;

/**
 * 演示局部内部类的使用
 */
public class LocalInnerClass {
    public static void main(String[] args) {
        // 演示一遍
        Outer02 outer02 = new Outer02();
        outer02.m1();
        System.out.println("outer02 的hashcode=" + outer02);
    }
}

class Outer02 { // 外部类
    private int n1 = 100;

    // 私有方法
    private void m2() {
        System.out.println("Outer02 m2()");
    }

    public void m1() { // 方法
        // 局部内部类(本质仍然是一个类)
        final class Inner02 {
            private int n1 = 800;

            public void f1() {
                // 可以直接访问外部类的所有成员,包含私有的
                System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
                System.out.println("Outer02.this hashcode=" + Outer02.this);
                m2();
            }
        }

        // 外部类在方法中,可以创建Inner02 对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }
}

匿名内部类的使用(重要)

本质

  1. 是一个类
  2. 是内部类
  3. 没有类名
  4. 同时是一个对象

基本语法

java
new 类或接口(参数列表) {
    类体
};

代码示例(AnonymousInnerClass.java)

java
package com.hspedu.innerclass;

/**
 * 演示匿名内部类的使用
 */
public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}

class Outer04 { // 外部类
    private int n1 = 10; // 属性

    public void method() { // 方法
        // 基于接口的匿名内部类
        // 1. 需求:想使用IA接口,并创建对象
        // 2. 传统方式:写一个类实现接口,再创建对象
        // 3. 需求:该类只使用一次
        // 4. 简化开发:使用匿名内部类
        // 5. tiger的编译类型:IA
        // 6. tiger的运行类型:Outer04$1(底层自动分配类名)
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger 的运行类型=" + tiger.getClass());
        tiger.cry();

        // 基于类的匿名内部类
        // 1. father编译类型:Father
        // 2. father运行类型:Outer04$2
        // 3. 底层创建匿名内部类并返回对象
        Father father = new Father("jack") {
            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father 对象的运行类型=" + father.getClass());
        father.test();

        // 基于抽象类的匿名内部类
        Animal animal = new Animal() {
            @Override
            void eat() {
                System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();
    }
}

interface IA { // 接口
    void cry();
}

class Father { // 类
    public Father(String name) { // 构造器
        System.out.println("接收到name=" + name);
    }

    public void test() { // 方法
    }
}

abstract class Animal { // 抽象类
    abstract void eat();
}

匿名内部类细节(AnonymousInnerClassDetail.java)

  1. 语法奇特:既是类定义,也是对象
  2. 可以直接访问外部类的所有成员(包含私有)
  3. 不能添加访问修饰符(地位是局部变量)
  4. 作用域:仅在定义它的方法或代码块中
  5. 外部其他类:不能访问匿名内部类
  6. 成员重名时:默认就近原则,访问外部类成员用 外部类名.this.成员
java
package com.hspedu.innerclass;

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
        System.out.println("main outer05 hashcode=" + outer05);
    }
}

class Outer05 {
    private int n1 = 99;

    public void f1() {
        // 基于类的匿名内部类
        Person p = new Person() {
            private int n1 = 88;

            @Override
            public void hi() {
                // 直接访问外部类的私有成员
                System.out.println("匿名内部类重写了hi方法 n1=" + n1 +
                        " 外部类的n1=" + Outer05.this.n1);
                System.out.println("Outer05.this hashcode=" + Outer05.this);
            }
        };
        p.hi(); // 动态绑定,运行类型是Outer05$1

        // 直接调用匿名内部类的方法
        new Person() {
            @Override
            public void hi() {
                System.out.println("匿名内部类重写了hi方法,哈哈...");
            }

            @Override
            public void ok(String str) {
                super.ok(str);
            }
        }.ok("jack");
    }
}

class Person { // 类
    public void hi() {
        System.out.println("Person hi()");
    }

    public void ok(String str) {
        System.out.println("Person ok() " + str);
    }
}

匿名内部类的最佳实践

当做实参直接传递,简洁高效。

代码示例(InnerClassExercise01.java)

java
package com.hspedu.innerclass;

public class InnerClassExercise01 {
    public static void main(String[] args) {
        // 当做实参直接传递,简洁高效
        f1(new IL() {
            @Override
            public void show() {
                System.out.println("这是一副名画~~");
            }
        });

        // 传统方法
        f1(new Picture());
    }

    // 静态方法,形参是接口类型
    public static void f1(IL il) {
        il.show();
    }
}

// 接口
interface IL {
    void show();
}

// 实现类
class Picture implements IL {
    @Override
    public void show() {
        System.out.println("这是一副名画XX...");
    }
}

匿名内部类课堂练习

需求

  1. 定义铃声接口Bell,包含ring方法
  2. 手机类Cellphone,具有闹钟功能alarmClock(参数为Bell类型)
  3. 测试:通过匿名内部类作为参数,分别打印"懒猪起床了"和"小伙伴上课了"

代码实现

java
package com.hspedu.innerclass;

public class InnerClassExercise02 {
    public static void main(String[] args) {
        CellPhone cellPhone = new CellPhone();

        // 第一个匿名内部类:懒猪起床了
        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("懒猪起床了");
            }
        });

        // 第二个匿名内部类:小伙伴上课了
        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("小伙伴上课了");
            }
        });
    }
}

// 铃声接口
interface Bell {
    void ring();
}

// 手机类
class CellPhone {
    // 闹钟功能
    public void alarmClock(Bell bell) {
        System.out.println(bell.getClass());
        bell.ring(); // 动态绑定
    }
}

成员内部类的使用 MemberInnerClass01.java

核心特点

  1. 可以直接访问外部类的所有成员(包含私有)
  2. 可以添加任意访问修饰符(public、protected、默认、private)
  3. 作用域:整个外部类体
  4. 访问外部类成员:直接访问
  5. 外部类访问成员内部类:创建对象后访问
  6. 外部其他类访问方式:
    • 方式1:外部类对象.new 内部类()
    • 方式2:外部类提供方法返回内部类对象
  7. 成员重名时:默认就近原则,访问外部类成员用 外部类名.this.成员

代码示例

java
package com.hspedu.innerclass;

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();

        // 外部其他类使用成员内部类的三种方式
        // 方式1
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();

        // 方式2:通过外部类方法获取
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();
    }
}

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";
    private void hi() {
        System.out.println("hi()方法...");
    }

    // 成员内部类(无static修饰)
    public class Inner08 {
        private double sal = 99.8;
        private int n1 = 66; // 与外部类成员重名

        public void say() {
            // 直接访问外部类成员
            System.out.println("n1 = " + n1 + 
                    " name = " + name + 
                    " 外部类的n1=" + Outer08.this.n1);
            hi(); // 访问外部类私有方法
        }
    }

    // 方法:返回内部类对象
    public Inner08 getInner08Instance() {
        return new Inner08();
    }

    // 外部类使用内部类
    public void t1() {
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(inner08.sal);
    }
}

静态内部类的使用 StaticInnerClass01.java

核心特点

  1. 可以直接访问外部类的所有静态成员(包含私有),但不能直接访问非静态成员
  2. 可以添加任意访问修饰符
  3. 作用域:整个外部类体
  4. 访问外部类静态成员:直接访问
  5. 外部类访问静态内部类:创建对象后访问
  6. 外部其他类访问方式:
    • 方式1:外部类名.内部类名 对象名 = new 外部类名.内部类名()
    • 方式2:外部类提供静态方法返回内部类对象
  7. 成员重名时:默认就近原则,访问外部类成员用 外部类名.成员

代码示例

java
package com.hspedu.innerclass;

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.m1();

        // 外部其他类使用静态内部类
        // 方式1
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();

        // 方式2:通过外部类静态方法获取
        Outer10.Inner10 inner10_ = Outer10.getInner10_();
        inner10_.say();
    }
}

class Outer10 { // 外部类
    private int n1 = 10; // 非静态成员
    private static String name = "张三";
    private static void cry() {}

    // 静态内部类
    static class Inner10 {
        private static String name = "韩顺平教育";

        public void say() {
            // 访问外部类静态成员
            System.out.println(name + " 外部类name= " + Outer10.name);
            cry();
        }
    }

    // 外部类访问静态内部类
    public void m1() {
        Inner10 inner10 = new Inner10();
        inner10.say();
    }

    // 静态方法返回内部类对象
    public static Inner10 getInner10_() {
        return new Inner10();
    }
}

课堂测试题

java
public class Test { // 外部类
    public Test() { // 构造器
        Inner s1 = new Inner();
        s1.a = 10;
        Inner s2 = new Inner();
        System.out.println(s2.a); // 输出5
    }

    class Inner { // 成员内部类
        public int a = 5;
    }

    public static void main(String[] args) {
        Test t = new Test();
        Inner r = t.new Inner();
        System.out.println(r.a); // 输出5
    }
}