Java基础——内部类

本文详细介绍了Java中的内部类,包括局部内部类、匿名内部类、成员内部类和静态内部类的基本概念、语法、访问权限以及应用场景。通过实例展示了如何创建和使用这些内部类,强调了它们在访问外部类成员和简化代码结构方面的优势。
摘要由CSDN通过智能技术生成

内部类

基本介绍

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类 (inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员思考:类的五大成员是哪些?【属性、方法、构造器、代码块、内部类】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类.


package day04.innerClass;

public class InnerClass { // 外部其他类
    public static void main(String[] args) {

    }
}
class Outer{
    private int n = 10; // 1.属性

    public void hi(){ // 2.方法
        System.out.println("hi...");
    }

    public Outer(int n) { // 3.构造器
        this.n = n;
    }

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

    class Inner{ // 5.内部类

    }

}

基本语法


class Outer{ // 外部类
    class inner{ // 内部类
    
    }
}
class Other{ // 外部其他类

}

内部类的分类【4个】

定义在外部类局部位置上(比如方法内):

  1. 局部内部类(有类名)
  2. 匿名内部类(没有类名,重点!!!!!!)

定义在外部类的成员位置上:

  1. 成员内部类(没用 static 修饰)
  2. 静态内部类(使用 static 修饰)

局部内部类

  1. 可以直接访问外部类的所有成员,包含私有的
  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用 final 修饰,因为局部变量也可以使用 final
  3. 作用域:仅仅在定义它的方法或代码块中。
  4. 局部内部类 — 访问 — 外部类的成员【访问方式:直接访问】
  5. 外部类 — 访问 — 局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
  6. 外部其他类 — 不能访问 — 局部内部类(因为局部内部类地位是一个局部变量)
  7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问【演示】

注意:

  1. 局部内部类本质还是一个类,地位相当于方法的一个局部变量
  2. 定义在外部类的方法或代码块中
  3. 作用域:仅仅在定义它的方法或代码块中

package day04.innerClass;

public class InnerClass {
    public static void main(String[] args) {
        // 演示
        Outer outer = new Outer();
        outer.m2();
    }
}
class Outer{ // 外部类
    private int n = 10; // 外部类的属性
    private void m1(){ // 外部类的方法
        System.out.println("Outer m1()");
    }
    public void m2(){ // 外部类的方法
        // 【局部内部类本质还是一个类,可以有自己的属性、方法、构造器、代码块、内部类】
        // 1.局部内部类,定义在外部类的方法中
        // 2.不能添加访问修饰符,但是可以添加 final
        // 3.作用域:仅限于定义它的方法或代码块中
        final class Inner{
            public void m3(){
                int a = 10; // 内部类的局部变量
                // 4.局部内部类可以直接访问外部类的所有成员,包括私有成员
                System.out.println("n = " + n);
                m1();
            }
        }
        // 5.在定义内部类的方法中,可以创建内部类对象,并调用内部类的方法
        Inner inner = new Inner();
        inner.m3();
    }

}

输出结果:

在这里插入图片描述


package day04.innerClass;

public class InnerClass {
    public static void main(String[] args) {
        // 演示
        Outer outer = new Outer();
        outer.m2();
    }
}
class Outer{ // 外部类
    private int n = 10; // 外部类的属性
    private void m1(){ // 外部类的方法
        System.out.println("Outer m1()");
    }
    public void m2(){ // 外部类的方法
        // 【局部内部类本质还是一个类,可以有自己的属性、方法、构造器、代码块、内部类】
        // 1.局部内部类,定义在外部类的方法中
        // 2.不能添加访问修饰符,但是可以添加 final
        // 3.作用域:仅限于定义它的方法或代码块中
        final class Inner{
            private int n = 100;
            public void m3(){
                int a = 10; // 内部类的局部变量
                // 4.局部内部类可以直接访问外部类的所有成员,包括私有成员
                // 7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
                //   Outer.this 本质就是外部类的对象,即哪个对象调用了 m2(),Outer.this 就是哪个对象
                System.out.println("n = " + n + " 外部类的 n = " + Outer.this.n);
                m1();
            }
        }
        // 5.在定义内部类的方法中,可以创建内部类对象,并调用内部类的方法
        Inner inner = new Inner();
        inner.m3();
    }

}

输出结果:
在这里插入图片描述

匿名内部类

匿名内部类的使用
  1. 本质是类
  2. 同时还是一个对象
  3. 该类没有名字
  4. 内部类
  5. 匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名。
基本语法:

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

基于接口的内部类案例:

package day04.AnonymousInnerClass;

public class AnnoymousInner {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
    }
}
class Outer{
    private int n = 10;
    public void method(){
        // 1.需求:使用 IA 接口,并创建对象
        // 2.传统方法:写一个类,实现该接口,并创建对象
//        Tiger tiger = new Tiger();
//        tiger.cry();
//
//        Dog dog = new Dog();
//        dog.cry();


        // 3.需求更新:Tiger / Dog 类只使用一次,后面不再使用(即:Outer$1 不再能 new 对象,但是已经创建好的对象仍可以使用)
        // 4.可以使用匿名内部类来简化开发
        // 5.tiger 的编译类型?IA
        // 6.tiger 的运行类型?就是匿名内部类 ===> Outer$1
        /*
            我们看底层,底层代码如下:会分配类名 Outer$1
            public XXXX implements IA{ // 此处的 XXXX ===> Outer$1
                @Override
                public void cry() {
                    System.out.println("老虎叫唤...");
                }
            }
         */
        // 7.以下代码解读:JDK 底层在创建匿名内部类 Outer$1 时, 立马就创建了 Outer$1 的实例,并把地址返回给 tiger
        // 8.匿名内部类使用一次就不能在使用了,即 Outer$1 会消失,但是创建好实例对象依然可以使用。
        IA tiger = new IA(){
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        // 查看 tiger 的运行类型
        System.out.println("tiger 的运行类型:" + tiger.getClass());
        // 输出结果:tiger 的运行类型:class day04.AnonymousInnerClass.Outer$1
        tiger.cry();


        IA dog = new IA(){
            @Override
            public void cry() {
                System.out.println("小狗汪汪...");
            }
        };
        // 查看 dog 运行类型
        System.out.println("dog 的运行类型:" + dog.getClass());
        // 输出结果:dog 的运行类型:class day04.AnonymousInnerClass.Outer$2
        dog.cry();

        // 附:
        // 可以总结一点:如果一个对象的运行类型是 xxxx$x,则一定是使用了匿名内部类

    }
}

interface IA{
    public void cry();
}
// 传统方法
//class Tiger implements IA{
//    @Override
//    public void cry() {
//        System.out.println("老虎叫唤...");
//    }
//}
//class Dog implements IA{
//    @Override
//    public void cry() {
//        System.out.println("小狗汪汪...");
//    }
//}

基于类的内部类案例

package day04.AnonymousInnerClass;

public class AnnoymousInner {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
    }
}
class Outer{
    private int n = 10;
    public void method(){

        // 基于类的匿名内部类
        // 分析
        // 1. father 编译类型:Father
        // 2. father 运行类型:Outer$$3
        // 3. 底层会创建匿名内部类
        /*
            public Outer$3 extends Father{
                @Override
                public void test() {
                    System.out.println("匿名内部类重写了 test() 方法");
                }
            }
         */
        // 4. JDK 底层创建 Outer$3 匿名内部类时,立马就创建了 Outer$3 的实例,并返回给 father
        // 5. ("Gin") 参数列表会传递给构造器
        Father father = new Father("Gin"){
            @Override
            public void test() {
                System.out.println("匿名内部类重写了 test() 方法");
            }
        };
        // 查看 father 的运行类型
        System.out.println("father 的运行类型:" + father.getClass());
        // 输出结果:father 的运行类型:class day04.AnonymousInnerClass.Outer$3
        father.test();


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

    }
}


class Father{ // 类
    public Father(String name) { // 构造器
        System.out.println("接收到参数 " + name);
    }
    public void test(){ // 方法

    }
}
abstract class Animal{
    abstract void eat();
}

匿名内部类的细节
  1. 匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
  2. 可以直接访问外部类的所有成员,包含私有的
  3. 不能添加访问修饰符,因为它的地位就是一个局部变量。
  4. 作用域:仅仅在定义它的方法或代码块中。
  5. 匿名内部类 — 访问 — 外部类成员【访问方式:直接访问】
  6. 外部其他类 — 不能访问 — 匿名内部类(因为匿名内部类地位是个局部变量)
  7. 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

package day04.AnonymousInnerClass;

public class AnonymousInner2 {
    public static void main(String[] args) {
        Outer1 outer1 = new Outer1();
        outer1.m();

        // Outer1.this 就是调用 m() 的对象
        System.out.println("main Outer1 hashcode = " + outer1);
    }
}
class Outer1{
    private int n = 10;
    public void m(){
        // 1. 创建一个基于类的匿名内部类,将对象赋值给 p
        Person p = new Person(){
            private int n = 20;
            @Override
            public void hi() {
                // 可以直接访问外部类的所有成员,包括私有成员
                // 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则
                // 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
                System.out.println("匿名内部类重写了 hi() 方法 n = " + n);
                System.out.println("访问外部类的重名成员变量 Outer1.n = " + Outer1.this.n);

                // Outer1.this 就是调用 m() 的对象
                System.out.println("Outer1.this hashCode = " + Outer1.this);
            }
        };
        System.out.println("p 的运行类型是:" + p.getClass());
        // 动态绑定,运行类型是 Outer1$1
        p.hi();


        // 也可以直接调用,匿名内部类本身返回的就是对象
        new Person(){
            @Override
            public void ok(String name) {
                super.ok(name);
            }
        }.ok("Gin");

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

输出结果:

在这里插入图片描述

匿名内部类的应用

由于匿名内部类本身也是一个对象,所以当作为实参直接传递时,简洁高效

案例一:


package day04.AnonymousInnerClass;

public class AnonymousInner3 {
    public static void main(String[] args) {
        // 使用匿名内部类创建对象,作为实参传递,简洁高效
        m(new IL() {
            @Override
            public void show() {
                System.out.println("匿名内部类:这是一幅名画...");
            }
        });


        // 传统方法,new 一个 Mo 实例,作为参数传递
        m(new Picture());
    }

    public static void m(IL il){
        il.show();
    }

}
interface IL{
    void show();
}

// 传统方法:创建一个 类 实现 IL 接口   ===> 编程领域称这种风格为 “硬编码”
class Picture implements IL{
    @Override
    public void show() {
        System.out.println("传统方法:这是一幅名画...");
    }
}

在这里插入图片描述
案例二:

  1. 有一个铃声接口Bell,里面有个ring()方法。
  2. 有一个手机类Cellphone,具有闹钟功能alarmclock(),参数是Bell类型
  3. 测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了...
  4. 再传入另一个匿名内部类(对象),打印:小伙伴们上课了...

package day04.AnonymousInnerClass;

public class AnonymousInner {
    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){
        bell.ring();
    }
}


在这里插入图片描述

成员内部类

  1. 成员内部类是定义在外部类的成员位置,并且没有 static 修饰。
  2. 可以直接访问外部类的所有成员,包含私有的
  3. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
  4. 作用域:和外部类的其他成员一样,作用域是整个类体。比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法
  5. 成员内部类 — 访问 — 外部类(比如:属性)【访问方式:直接访问】
  6. 外部类 — 访问 — 内部类(说明)【访问方式:创建对象】,再访问

package day04.AnonymousInnerClass;

public class AnonymousInner5 {
    public static void main(String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.m();
    }
}
class Outer2{
    private int n = 10;
    public String name = "Gin";

    private void ok(){
        System.out.println("外部类私有方法 ok()");
    }

    // 1. 成员内部类是定义在外部类的成员位置上
    // 2. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
    public class Inner{ // 成员内部类
        private double rate = 99.4;
        public void say(){
            // 3. 可以直接访问外部类的所有成员,包括私有的
            System.out.println("n = " + n + ", name = " + name);
            ok();
        }
        private void hello(){
            System.out.println("内部类的私有方法:hello()");
        }
    }

    // 4. 在外部类中定义方法来调用内部类
    public void m(){
        // 5. 创建内部类对象后,就可以使用内部类的所有成员,包括私有的
        Inner inner = new Inner();
        inner.say();
        // 6. 可以访问内部类的私有成员
        System.out.println("内部类的私有属性:" + inner.rate);
        inner.hello();
    }

}

  1. 外部其他类 — 访问 — 成员内部类【2 种方式】
  2. 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。

package day04.AnonymousInnerClass;

public class AnonymousInner5 {
    public static void main(String[] args) {
        Outer2 outer2 = new Outer2();
        outer2.m();
        System.out.println("===============");

        // 外部其他类使用成员内部类的 2 中方式
        // 第 1 种方式:相当于把 new Inner() 当作是 outer2 的成员
        Outer2.Inner inner = outer2.new Inner();
        inner.say();
        System.out.println("===============");

        // 第 2 种方式:
        Outer2.Inner innerInstance = outer2.getInnerInstance();
        inner.say();
        
    }
}
class Outer2{
    private int n = 10;
    public String name = "Gin";

    private void ok(){
        System.out.println("外部类私有方法 ok()");
    }

    // 1. 成员内部类是定义在外部类的成员位置上
    // 2. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
    public class Inner{ // 成员内部类
        private double rate = 99.4;
        private int n = 30;
        public void say(){
            // 3. 可以直接访问外部类的所有成员,包括私有的
            System.out.println("内部类的 n = " + n + ", name = " + name);
            ok();
            System.out.println("外部类的 n = " + Outer2.this.n);
        }
        private void hello(){
            System.out.println("内部类的私有方法:hello()");
        }
    }

    // 4. 在外部类中定义方法来调用内部类
    public void m(){
        // 5. 创建内部类对象后,就可以使用内部类的所有成员,包括私有的
        Inner inner = new Inner();
        inner.say();
        // 6. 可以访问内部类的私有成员
        System.out.println("内部类的私有属性:" + inner.rate);
        inner.hello();
    }

    // 创建 Inner内部类 对象的方法
    public Inner getInnerInstance(){
        return new Inner();
    }

}

静态内部类

静态内部类的使用

静态内部类可以有静态成员,而非静态内部类则不能有静态成员

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

  1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
  2. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
  3. 作用域:同其他的成员,为整个类体。

package day04.AnonymousInnerClass;

public class AnonymousInner6 {
    public static void main(String[] args) {
        Outer3 outer3 = new Outer3();
        outer3.m();
    }
}
class Outer3{
    private int n = 10;
    private static String name = "Gin";

    // 静态内部类
    // 1. static 修饰
    // 2. 位置在外部类的成员位置
    // 3. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
    // 4. 作用域:同其他的成员,为整个类体。
    public static class Inner{
        public void say(){
            // 5. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
            System.out.println("name = " + name);
        }
    }

    public void m(){
        Inner inner = new Inner();
        inner.say();
    }
}

  1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
  2. 外部类访问内部类,先创建对象,再访问
  3. 外部其他类访问静态内部类:new 外部类名.静态内部类名(),前提,要满足静态内部类的访问权限

package day04.AnonymousInnerClass;

public class AnonymousInner6 {
    public static void main(String[] args) {
        Outer3 outer3 = new Outer3();
        outer3.m();
        System.out.println("========================");

        // 外部其他类访问静态内部类:
        // 方式1. new 外部类名.静态内部类名(),前提,要满足静态内部类的访问权限
        Outer3.Inner inner = new Outer3.Inner();
        inner.say();
        System.out.println("========================");

        // 方式2. 编写一个方法,可以返回静态内部类的对象实例
        // 调用非静态方法
        Outer3.Inner innerInstance = outer3.getInnerInstance();
        innerInstance.say();
        System.out.println("========================");

        // 调用静态方法
        Outer3.Inner instance_ = Outer3.getInstance_();
        instance_.say();
    }
}
class Outer3{
    private int n = 10;
    private static String name = "Gin";

    // 静态内部类
    // 1. static 修饰
    // 2. 位置在外部类的成员位置
    // 3. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
    // 4. 作用域:同其他的成员,为整个类体。
    public static class Inner{
       
        public void say(){
            // 5. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
            System.out.println("name = " + name);
        }
    }

    // 6. 外部类访问内部类,先创建对象,再访问
    public void m(){
        Inner inner = new Inner();
        inner.say();
    }

    // 获取内部类对象的非静态方法
    public Inner getInnerInstance(){
        return new Inner();
    }
    // 获取内部类对象的静态方法
    public static Inner getInstance_(){
        return new Inner();
    }


}

  1. 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问

package day04.AnonymousInnerClass;

public class AnonymousInner6 {
    public static void main(String[] args) {
        Outer3 outer3 = new Outer3();
        outer3.m();
        System.out.println("========================");

        // 外部其他类访问静态内部类:
        // 方式1. new 外部类名.静态内部类名(),前提,要满足静态内部类的访问权限
        Outer3.Inner inner = new Outer3.Inner();
        inner.say();
        System.out.println("========================");

        // 方式2. 编写一个方法,可以返回静态内部类的对象实例
        // 调用非静态方法
        Outer3.Inner innerInstance = outer3.getInnerInstance();
        innerInstance.say();
        System.out.println("========================");

        // 调用静态方法
        Outer3.Inner instance_ = Outer3.getInstance_();
        instance_.say();
    }
}
class Outer3{
    private int n = 10;
    private static String name = "Gin";

    // 静态内部类
    // 1. static 修饰
    // 2. 位置在外部类的成员位置
    // 3. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
    // 4. 作用域:同其他的成员,为整个类体。
    public static class Inner{

        // 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则.
        // 如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
        private String name = "Vodka";

        public void say(){
            // 5. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
            System.out.println("name = " + name);
            System.out.println("外部类的 name = " + Outer3.name);
        }
    }

    // 6. 外部类访问内部类,先创建对象,再访问
    public void m(){
        Inner inner = new Inner();
        inner.say();
    }

    // 获取内部类对象的非静态方法
    public Inner getInnerInstance(){
        return new Inner();
    }
    // 获取内部类对象的静态方法
    public static Inner getInstance_(){
        return new Inner();
    }


}

练习

判断输出结果


package day04.AnonymousInnerClass;

public class AnonymousInner7 {
    public static void main(String[] args) {
        Test test = new Test();
        Test.Inner inner = test.new Inner();
        System.out.println(inner.n);
    }
}
class Test{
    public Test() {
        Inner inner1 = new Inner();
        inner1.n = 10;
        Inner inner2 = new Inner();
        System.out.println(inner2.n);
    }

    class Inner{
        public int n = 5;
    }

}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值