韩顺平java教程个人笔记 内部类(局部内部类、匿名内部类、成员内部类、静态内部类)

内部类

一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类。内部类是类的第五大成员。

基本语法

class Outer{//外部类

class Inner {//内部类

}

}

没有嵌套和被嵌套关系的类称为 外部其他类

内部类的分类

  • 定义在外部类的局部位置上(比如某个方法中)
    • 局部内部类(有类名)
    • 匿名内部类(没有类名)
  • 定义在外部类的成员位置上
    • 成员内部类(没有static修饰的类)
    • 静态内部类(有static修饰的类)

局部内部类

局部内部类定义在外部类的局部位置上,通常在方法中,且有类名

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

  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。

局部变量不能使用修饰符,但是可以使用final修饰

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

  2. 局部内部类 访问 外部类的成员:直接访问

  3. 外部类 访问 局部内部类的成员:创建对象再访问(必须在作用域内)

  4. 外部其他类 不能访问 局部内部类(因为局部内部类地位和局部变量一样)

  5. 如果外部类 和局部内部类的成员重名时,遵循就近原则。

    • 如果想访问外部类的成员,可以使用 外部类名.this.成员 去访问
//localInnerClass.java

package com.learnJava.innerClass;

public class localInnerClass {
    public static void main(String[] args) {
        //创建外部类的对象并调用 m1方法
        Outer outer = new Outer();
        outer.m1();
        /** 调用m1后的运行过程
         *  创建 Inner 类 -> 发现内部需要创建 Inner对象 -> 创建对象
         *  -> 调用 f1()-> 调用 m2()
         */

        //用Hashcode 检验 outer 和 Outer.this是否为同一个对象
        System.out.println("outer对象 的Hashcode:" + outer);


    }

}

class Outer {
    private int n1 = 100;//私有属性
    private void m2(){
        System.out.println("Outer 的 m2()方法");
    };//私有方法
    public void m1(){//局部内部类通常定义在方法中

        final class Inner{//2. 不能添加修饰符但可以用final修饰
            private int n1 = 800;//内部类中定义一个和外部类属性同名的属性n1
            public void f1(){//1.  可以直接访问外部类的包括私有在内的所有成员
                System.out.println("n1=" + n1);//4. 访问外部类方式:直接访问。但是第7点内部类定义了一个重名的属性,此处遵循就近原则变为访问内部类的n1
                System.out.println("外部类的n1:" + Outer.this.n1);//外部类的n1改成用 外部类名.this.n1 来访问
                /** 解释为什么用Outer.this:
                 *  1. 存在访问冲突时,需要准确找到属性
                 *  2. Outer.this 本质上是Outer类的对象,即哪个对象调用了 m1, 哪个对象就是 Outer.this
                 *  3. 主函数创建了 Outer对象 outer, 并调用了m1, 所以outer就是这里的 Outer.this
                 */
                m2();
                System.out.println("Outer.this 的Hashcode:" + Outer.this);
            }
        }

        /** 如果用final修饰了class Inner,那么就不能在方法中再继承Inner类了
         *  class Inner02 extends Inner { }
         */

        //5. 外部类访问内部类方式:在作用域中,通过创建内部类对象来访问成员
        //创建在方法外则编译器找不到内部类在哪
        Inner inner = new Inner();
        inner.f1();
    }
    {//3. 作用域为定义它的方法或代码块中
        class Inner03 {}
    }

}


输出结果


n1=800
外部类的n1:100
Outerm2()方法
Outer.thisHashcode:com.learnJava.innerClass.Outer@6d03e736
outer对象 的Hashcode:com.learnJava.innerClass.Outer@6d03e736

匿名内部类

匿名内部类定义在外部类的局部位置中

  • 是一个类
  • 是一个内部类
  • 没有名字
  • 是一个对象

基本语法

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

类体

};

基本使用

下面给出了基于接口和类的使用方式。基于抽象类时记得实现内部的抽象方法。

//AnonymousInnerClass.java


package com.learnJava.innerClass;


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

    }
}
class Outer02 {
    private int n1 = 10;
    public void method(){//方法
        /** 一、基于接口的匿名内部类
         * 需求:使用IA接口创建对象
         * 1. 传统方式:写一个类实现该接口并创建对象
         * 2. 匿名方式: 如果一个类只使用一次,则可以选择用匿名方式
         */

        IA tiger1 = new Tiger();//传统方式:创建类再创建对象
        tiger1.cry();

        //匿名方式:不能直接创建接口对象,但是可以后面跟着实现该接口,而不用单独创建一个类
        IA tiger2 = new IA(){//编译类型:IA, 运行类型:匿名内部类
            @Override
            public void cry() {
                System.out.println("匿名-老虎叫");
            }
        };//记得加分号
        tiger2.cry();
        System.out.println("tiger2的运行类型:" + tiger2.getClass());//用来查看匿名内部类返回的类型
        /**
         * 匿名内部类底层实现方式也相当于创建了一个类来实现接口,创建完后把地址返回给tiger2
         * 但是对于开发者而言效率更高了
         * class Outer02$1 implements IA(){
         *     @Override
         *     public void cry() {
         *         System.out.println("匿名-老虎叫");
         *     }
         * }
         *
         */

        /*
            二、基于类的匿名内部类
         */
        Father father = new Father("jack"){//此处 father的编译类型是 Father, 但运行类型不是 Father,而是 匿名内部类

            @Override
            public void test() {
                System.out.println("匿名内部类重写了Father类中的test()");
            }
        };
        System.out.println("father 的运行类型" + father.getClass());

        /*  
         *   类的匿名内部类底层实现:
         * class Outer02$2 extends Father(){
         *      @Override
         *      public void test() {
         *         System.out.println("匿名内部类重写了Father类中的test()");
         *      }
         * }
         */
    }
}

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

}

class Tiger implements IA{//传统方式
    @Override
    public void cry() {
        System.out.println("对象-老虎叫");
    }
}
class Father{//类
    public Father(String name) {//构造器

    }
    public void test(){//方法

    }
}

输出结果

对象-老虎叫
匿名-老虎叫
tiger2的运行类型:class com.learnJava.innerClass.Outer02$1
father 的运行类型class com.learnJava.innerClass.Outer02$2

注意事项

和局部内部类相似处:

  1. 不能添加访问修饰符,因为它地位等同于局部变量
  2. 作用域:仅仅在定义它的方法或代码块中
  3. 匿名内部类 直接访问 包括私有在内 的外部类成员
  4. 外部其他类 不能访问 匿名内部类
  5. 如果外部类 和局部内部类的成员重名时,遵循就近原则。
    • 如果想访问外部类的成员,可以使用 外部类名.this.成员 去访问

特点:

​ 匿名内部类既是一个类的定义,同时本身也是一个对象。

//AnonymousInnerClassDetail.java

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        OuterDetail outerDetail = new OuterDetail();
        outerDetail.f1();
    }
}
class Person{
    public void hi(){
        System.out.println("Person 的 hi()");
    };
}

class OuterDetail{
    private int n1 = 99;
    public void f1(){
        Person p = new Person(){
            @Override
            public void hi(){
                System.out.println("匿名内部类 的 hi()");
            }
        };
        System.out.println(p.getClass());
        p.hi();//动态绑定,运行类型为OuterDetail$1

        //也可以这样直接调用方法
        new Person(){
            @Override
            public void hi(){
                System.out.println("匿名内部类 的 hi()");
            }
        }.hi();
    }
}

实践

  1. 匿名内部类可以当做实参直接传递。创建的对象只使用一次时,会更方便。(表现在代码上即:函数的括号中直接new对象出来)

  2. 起床案例:

    • 有一个铃声接口Bell,里面有ring方法
    • 有一个手机类Cellphone,闹钟功能alarmClock,参数是Bell类型
    • 测试手机类的闹钟功能,通过匿名内部类作为参数,打印:“起床了"
    • 再传入另一个匿名内部类,打印:“上课了"
//AnonymousInnerClassExercise.java


public class AnonymousInnerClassExercise {
    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() {//每次调用alarmClock(),参数会进行动态绑定
            @Override
            public void ring() {
                System.out.println("上课了");
            }
        });
    }
}

interface Bell {
    void ring();
}
class Cellphone {
    public void alarmClock(Bell bell) {
        bell.ring();
    }
}

成员内部类

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

  1. 可以直接访问外部类的所有成员
  2. 可以添加任意访问修饰符,也即地位和成员相同
  3. 作用域:整个外部类体中都可使用
  4. 成员内部类 直接访问 外部类
  5. 外部类 创建对象再访问 内部类
  6. 外部其他类 三种访问 内部类
  7. 如果外部类 和局部内部类的成员重名时,遵循就近原则。
    • 如果想访问外部类的成员,可以使用 外部类名.this.成员 去访问
//MemberInnerClass.java

package com.learnJava.innerClass;

public class MemberInnerClass {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        System.out.println("外部类调用结果:");
        outer08.t1();//外部类通过调用方法来使用成员内部类

        System.out.println("\n外部其他类调用结果:");
        //外部其他类如何创建内部类?(注意这里是外部其他类,不是外部类)
        //1. 通过创建对象来new一个
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();

        //2. 在外部类编写一个方法,用来返回内部类
        Outer08.Inner08 inner08instance = outer08.getInner08Instance();
        inner08instance.say();

        //3. 将1和2合起来,熟练之后可以用
        new Outer08().new Inner08().say();

    }
}

class Outer08{
    private int n1 = 10;
    public String name = "张三";
    //以下成员内部类定义在和属性、方法平级的位置上
    class Inner08 {
        public void say(){
            //可以直接访问外部类的所有成员
            System.out.println("n1=" + n1 + " name=" + name);
        }
    }

    //写一个方法用来返回Inner08实例,对应主函数中的第2点
    public Inner08 getInner08Instance(){
        return new Inner08();
    }

    //写一个方法来使用成员内部类
    public void t1(){
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}

输出结果

外部类调用结果:
n1=10 name=张三

外部其他类调用结果:
n1=10 name=张三
n1=10 name=张三
n1=10 name=张三

静态内部类

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

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

  2. 可以添加任意访问修饰符

  3. 作用域为整个类

  4. 访问方式和成员内部类相似,但要加上静态成员相关的限制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值