JavaSE——面向对象11:内部类(局部内部类、匿名内部类、成员内部类、静态内部类)

目录

一、内部类基本介绍

(一)内部类定义

(二)内部类基本语法

(三)内部类代码示例

(四)内部类的分类

二、局部内部类

三、匿名内部类(重要)

(一)基本介绍

(二)基于接口的匿名内部类

(三)基于类的匿名内部类

(四)注意事项与使用细节

(五)匿名内部类的最佳实践——当做实参直接传递

(六)小练习

四、成员内部类

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

2.成员内部类可以添加任意的访问修饰符,因为它的地位就是一个成员

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

4.作用域:成员内部类的作用域为整个类体

6.外部类和成员内部类的成员重名访问规则

五、静态内部类

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

2.可以添加任意访问修饰符,因为它的地位就是一个成员

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

4.作用域:同其他的成员一样,为整个类体

5.外部其他类访问静态内部类的三种方式

6.如果外部类和静态内部类的成员重名访问规则

7.小练习


一、内部类基本介绍

(一)内部类定义

        一个类的内部,又完整地嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。内部类最大的特点就是,可以直接访问私有属性,并且可以体现类与类之间的包含关系。内部类也是重点内容,因为底层源码中有大量内部类。

        类的五大成员:属性、方法、构造器、代码块、内部类。

(二)内部类基本语法

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

class Other{ // 外部其它类
}

(三)内部类代码示例

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

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

    // 方法
    public void hi() {}

    // 代码块
    {
        System.out.println("这是一个代码块...");
    }

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

// 外部其它类
class Other {}

(四)内部类的分类

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

二、局部内部类

1.局部内部类定义在外部类的局部位置,比如方法体,并且有类名。

class Outer02 {
    public void m1() {
        class Inner02 { // 局部内部类可以定义在方法中

        }
    }

    {   // 局部内部类也可以定义在代码块中
        class Inner03{}
    }
}

2.局部内部类的作用域:仅在定义它的方法或代码块中

3.局部内部类可以直接访问外部类的所有成员,包含私有的。

class Outer02 {
    private int n1 = 100;

    private void m2() {
        System.out.println("m2方法被执行...");
    }

    public void m1() {
        class Inner02 { // 局部内部类
            public void f1() {
                // 局部内部类可以直接访问外部类的私有属性
                System.out.println("n1=" + n1);
                // 局部内部类可以直接访问外部类的私有方法
                m2();
            }
        }
    }
}

4.外部类访问局部内部类的成员,必须先创建局部内部类对象,再调用方法

注意:必须在作用域中。

public class LocalInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
        // f2方法被执行...
        // n1=100
        // m2方法被执行...
    }
}

class Outer02 {
    private int n1 = 100;

    private void m2() {
        System.out.println("m2方法被执行...");
    }

    public void m1() {
        class Inner02 { // 局部内部类
            public void f1() {
                System.out.println("n1=" + n1);
                m2();
            }
        }
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }

    {
        class Inner03 {
            public void f2() {
                System.out.println("f2方法被执行...");
            }
        }
        Inner03 inner03 = new Inner03();
        inner03.f2();
    }
}

5.局部内部类不能添加访问修饰符,因为它本质上就是一个局部变量,且仍然是一个类。

局部变量是不能使用修饰符的,只有属性可以。但是可以使用final修饰,因为局部变量也可以使用final,当使用final修饰局部内部类后,表示该局部内部类不能被继承,是一个最终类。

6.外部其它类不能访问局部内部类,因为局部内部类的地位,是一个局部变量

7.如果外部类和局部内部类的成员重名时,默认遵循就近原则;

如果想访问外部类的成员,就要使用 外部类名.this.成员去访问

public class LocalInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        // Outer02.this 本质就是外部类的对象
        // 即:哪个对象调用了局部内部类所在的m1方法,Outer02.this就是哪个对象

        System.out.println("Outer02 hashCode=" + outer02); // 4554617c
        outer02.m1();
    }
}

class Outer02 {
    private int n1 = 100;

    private void m2() {
        System.out.println("m2方法被执行...");
    }

    public void m1() {
        class Inner02 { // 局部内部类
            private int n1 = 900;

            public void f1() {
                // Outer02.this 本质就是外部类的对象
                // 即:哪个对象调用了局部内部类所在的m1方法,Outer02.this就是哪个对象
                System.out.println("Outer02.this hashCode=" + Outer02.this);// 4554617c
                System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
                m2();
            }
        }
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }
}

三、匿名内部类(重要)

(一)基本介绍

定义:匿名内部类是定义在外部类的局部位置,比如方法、代码块中,并且没有类名。

匿名内部类特点:

  1. 本质是类
  2. 是一个内部类
  3. 该类没有名字(代码中虽然无法看到名字,但实际上JVM会给它分配一个名字)
  4. 同时还是一个对象

基本语法:

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

(二)基于接口的匿名内部类

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.传统方式:写一个类,实现该IA接口,并创建对象
        // 3.但是创建的类只是使用一次,后面不再使用,有些浪费和繁琐
        // 4.可以使用匿名内部类来简化开发
//        Tiger tiger = new Tiger();
//        tiger.cry();

        // 5.我们知道,接口是不能被new的,但是后面的{}里面的内容,相当与实现了接口中的方法
        // 6.tiger的编译类型:IA
        // 7.tiger的运行类型:就是匿名内部类,系统分配的Outer04$1
        /*
         * 底层是会创建一个类 Outer04$1 实现IA接口,即:
           class Outer04$1 implements IA {
               @Override
               public void cry() {
                   System.out.println("老虎叫唤...");
               }
           }
         */
        // 8.JDK底层在创建匿名内部类 Outer04$1,立即就创建了 Outer04$1对象
        //  并且把地址返回给tiger后,该匿名内部类Outer04$1 就立即消失了
        // 9.匿名内部类只能使用一次,但是tiger是一个对象,可以反复调用
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        // 使用getClass()获取对象的运行类型:
        System.out.println("tiger的运行类型="+tiger.getClass()); // Outer04$1
        tiger.cry();
        tiger.cry();
        tiger.cry();
    }
}

interface IA {
    void cry();
}

/*
传统方式:定义类实现IA接口:
class Tiger implements IA {
    @Override
    public void cry() {
        System.out.println("老虎叫唤...");
    }
}*/

(三)基于类的匿名内部类

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.father的编译类型:Father
        // 2.father的运行类型:系统按照顺序分配的 Outer04$2
        // 3.底层会创建匿名内部类:
        /*
            class Outer04$2 extends Father{
                @Override
                public void test() {
                    System.out.println("匿名内部类重写了test方法...");
                }
            }
         */
        // 4.同时也直接返回了 匿名内部类 Outer04$2的对象,即:father
        // 5.注意:("jack")参数列表会传递给Father构造器
        Father father = new Father("jack") {
            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法...");
            }
        };

        System.out.println("father对象的运行类型:" + father.getClass()); // Outer04$2
        father.test(); // 匿名内部类重写了test方法...
        father.test(); // 匿名内部类重写了test方法...
        father.test(); // 匿名内部类重写了test方法...

        // 6.注意:如果new Father("jack")后面没有{},那么father的运行类型就是Father
        Father father1 = new Father("jack");
        System.out.println("father1对象的运行类型:" + father1.getClass()); // Father


//-----------------------------------------------------------------------------------


        // 基于抽象类的匿名内部类
        // 1.animal编译类型:Animal
        // 2.animal的运行类型:系统按照顺序分配的 Outer04$3
        // 3.底层会创建匿名内部类:
        /*
         * 底层是会创建一个类 Outer04$3 实现IA接口,即:
           class Outer04$3 implements Animal {
               @Override
               public void eat() {
                   System.out.println("小狗吃骨头...");
               }
           }
         */
        Animal animal = new Animal() {
            // 这里要注意:因为Animal中的eat方法是抽象的,所以eat方法必须被实现
            @Override
            void eat() {
                System.out.println("小狗吃骨头...");
            }
        };
        System.out.println("animal的运行类型:" + animal.getClass()); // Outer04$3
        animal.eat(); // 小狗吃骨头...
        animal.eat(); // 小狗吃骨头...
        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();
}

(四)注意事项与使用细节

  1. 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象。因此从语法上看,它既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类方法。
  2. 匿名内部类可以直接访问外部类的所有成员,包含私有的。
  3. 匿名内部类不能添加访问修饰符,因为它的地位就是一个局部变量。
  4. 作用域:匿名内部类的作用域仅在定义它的方法或代码块中。
  5. 外部其它类不能访问匿名内部类,因为匿名内部类地位是一个局部变量。
  6. 如果外部类和匿名内部类成员重名时,匿名内部类访问的话,默认就近原则,如果想访问外部类成员,则可以使用(外部类名.this.成员)去访问。
public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
        //外部其他类---不能访问----->匿名内部类
        System.out.println("main outer05 hashcode=" + outer05); // 4554617c
    }
}

class Outer05 {
    private int n1 = 99;

    public void f1() {
        //创建一个基于类的匿名内部类
        //不能添加访问修饰符,因为它的地位就是一个局部变量
        //作用域 : 仅仅在定义它的方法或代码块中
        Person p = new Person() {
            private int n1 = 88;

            @Override
            public void hi() {
                //可以直接访问外部类的所有成员,包含私有的
                //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
                //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
                System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +
                        " 外部内的n1=" + Outer05.this.n1);
                // 匿名内部类重写了 hi方法 n1=88 外部内的n1=99

                //Outer05.this 就是调用 f1的 对象
                System.out.println("Outer05.this hashcode=" + Outer05.this); // 4554617c
            }
        };


        /**
         * 调用匿名内部类方式一:动态绑定
         */
        p.hi();//动态绑定, 运行类型是 Outer05$1


        /**
         * 调用匿名内部类方式二:直接调用
         */
        //也可以直接调用, 匿名内部类本身也是返回对象
        // class 匿名内部类 extends Person {}
        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);
    }
}

(五)匿名内部类的最佳实践——当做实参直接传递

将匿名内部类当做实参传递,简洁高效。

代码示例:

传统方式实现接口中的方法:

public class InnerClassExercise01 {
    public static void main(String[] args) {
        f1(new Picture());
        // 传统方式实现接口中的方法...
    }

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

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

// 定义类Picture实现IL接口
// 类->实现IL=>编程领域(硬编码)
class Picture implements IL {

    @Override
    public void show() {
        System.out.println("传统方式实现接口中的方法...");
    }
}

使用匿名内部类传参:

public class InnerClassExercise02 {
    public static void main(String[] args) {
        f1(new IL() {
            @Override
            public void show() {
                System.out.println("匿名内部类作为参数传递的方法...");
            }
        });
    }

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

interface IM {
    void show();
}

        相比与传统定义类实现接口中的方法的硬编码方式,将匿名内部类作为参数传递的方式,更加简洁高效,前提是该抽象方法只使用一次。

(六)小练习

需求:

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

代码实现:

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

        CellPhone cellPhone = new CellPhone();

        //1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise02$1
        //2. 重写了 ring
        //3. Bell bell = new Bell() {
        //            @Override
        //            public void ring() {
        //                System.out.println("懒猪起床了");
        //            }
        //        }
        cellPhone.alarmClock(new Bell01() {
            @Override
            public void ring() {
                System.out.println("懒猪起床了");
            }
        });

        cellPhone.alarmClock(new Bell01() {
            @Override
            public void ring() {
                System.out.println("小伙伴上课了");
            }
        });
    }
}

interface Bell { //接口
    void ring();//方法
}

class CellPhone {//类

    public void alarmClock(Bell01 bell01) {//形参是Bell接口类型
        System.out.println(bell01.getClass());
        bell01.ring();//动态绑定
    }
}

运行结果:

四、成员内部类

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

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

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    //1.注意: 成员内部类,是定义在外部内的成员位置上
    class Inner08 { // 成员内部类
    }
}

2.成员内部类可以添加任意的访问修饰符,因为它的地位就是一个成员

访问修饰符:public、protected、默认、private

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

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();
        // n1 = 10 name = 张三 外部类的n1=10
        // hi()方法...
    }
}

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    class Inner08 { // 成员内部类
        public void say() {
            // 4.成员内部类可以直接访问外部类的所有成员,包含私有的
            System.out.println("n1 = " + n1 + " name = " + name);
            hi();
        }
    }

    public void t1() {
        Inner08 inner08 = new Inner08();
        inner08.say();
    }
}

4.作用域:成员内部类的作用域为整个类体

        成员内部类的作用域和外部类的其他成员一样,为整个类体。

        外部类方位成员内部类:在外部类的成员方法中,创建成员内部类对象,再调用相关的方法。

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();
        // n1 = 10 name = 张三 外部类的n1=10
        // hi()方法...
        // 400.56
    }
}

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    class Inner08 { // 成员内部类
        private double salary = 400.56;

        private void say() {           
            System.out.println("n1 = " + n1 + " name = " + name);
            hi();
        }
    }

    // 3.外部类访问成员内部类:
    // 说明:在外部类的成员方法中创建成员内部类对象,再使用相关的方法
    public void t1() {
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(inner08.salary);
    }
}

5.外部其它类访问成员内部类的两种方式

  • 方式一:new 成员内部类(); 将成员内部类当做一个普通的成员来访问,即:对象.成员
  • 方式二:在外部类中定义一个方法,返回一个成员内部类实例
public class MemberInnerClass01 {
    public static void main(String[] args) {
        // 外部其它类访问成员内部类的两种方式
        // 方式一:new Inner08(); 将成员内部类当做一个普通的成员来访问,即:对象.成员
        Outer08 outer08 = new Outer08();
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();

        // 方式二:在外部类中定义一个方法,返回一个Inner08实例
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();
    }
}

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    public class Inner08 { // 成员内部类
        private double salary = 400.56;

        public void say() {
            System.out.println("n1 = " + n1 + " name = " + name);
            hi();
        }
    }

    // 在外部类中定义一个方法,返回一个Inner08实例
    public Inner08 getInner08Instance() {
        return new Inner08();
    }
}

运行结果:

6.外部类和成员内部类的成员重名访问规则

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

class Outer08 { // 外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    public class Inner08 { // 成员内部类
        private double salary = 400.56;
        private int n1 = 66;

        public void say() {
            // 6.如果外部类和成员内部类的成员重名时,成员内部类访问的时候,默认就近原则,
            // 如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
            System.out.println("n1 = " + n1 + " name = " + name +
                    " 外部类的n1=" + Outer08.this.n1);
            hi();
        }
    }
}

五、静态内部类

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

class Outer01{
    private int n1 = 10;
    private static String name = "张三";

    static class Inner10{ // 静态内部类

    }
}

2.可以添加任意访问修饰符,因为它的地位就是一个成员

访问修饰符:public、protected、默认、private

class Outer01{
    private int n1 = 10;
    private static String name = "张三";

    public static class Inner10{ // 静态内部类

    }
}

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

class Outer01 {
    private int n1 = 10;
    private static String name = "张三";

    private static void m1() {
        System.out.println("外部类的静态方法被执行...");
    }

    public static class Inner10 { // 静态内部类
        public void say() {
            System.out.println(name);
            m1();
        }
    }
}

4.作用域:同其他的成员一样,为整个类体

外部类访问静态内部类:要先创建对象,再访问

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.t1();
        // 张三
        // 外部类的静态方法被执行...
    }
}

class Outer10 {
    private int n1 = 10;
    private static String name = "张三";

    private static void m1() {
        System.out.println("外部类的静态方法被执行...");
    }

    public static class Inner10 { // 静态内部类
        public void say() {
            System.out.println(name);
            m1();
        }
    }

    public void t1(){
        Inner10 inner10 = new Inner10();
        inner10.say();
    }
}

5.外部其他类访问静态内部类的三种方式

public class StaticInnerClass01 {
    public static void main(String[] args) {
        // 外部其他类 使用静态内部类
        // 方式一:
        // 静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();
        System.out.println("-------------------");

        // 方式二:
        // 编写一个方法,可以返回静态内部类的对象实例.
        Outer10 outer10 = new Outer10();
        Outer10.Inner10 inner101 = outer10.getInner10();
        inner101.say();

        System.out.println("-------------------");

        // 方式三:
        // 外部类.静态方法
        Outer10.Inner10 inner10_ = Outer10.getInner10_();
        inner10_.say();
    }
}

class Outer10 {
    private int n1 = 10;
    private static String name = "张三";

    private static void m1() {
        System.out.println("外部类的静态方法被执行...");
    }

    public static class Inner10 { // 静态内部类
        public void say() {
            System.out.println(name);
            m1();
        }
    }

    public Inner10 getInner10() {
        return new Inner10();
    }

    public static Inner10 getInner10_() {
        return new Inner10();
    }
}

6.如果外部类和静态内部类的成员重名访问规则

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

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.t1();
        // 静态内部类 name=李四 外部类 name=张三
        // 静态内部类的静态方法被执行...
        // 外部类的静态方法被执行...
    }
}

class Outer10 {
    private int n1 = 10;
    private static String name = "张三";

    private static void m1() {
        System.out.println("外部类的静态方法被执行...");
    }

    public static class Inner10 { // 静态内部类
        private static String name = "李四";

        private static void m1() {
            System.out.println("静态内部类的静态方法被执行...");
        }

        public void say() {
            System.out.println("静态内部类 name=" + Inner10.name +
                    " 外部类 name=" + Outer10.name);
            m1(); // 或者是Inner10.m1();
            Outer10.m1();
        }
    }

    public void t1() {
        Inner10 inner10 = new Inner10();
        inner10.say();
    }
}

7.小练习

判断下面代码的输出结果:

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

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

    class Inner { // 成员内部类
        public int a = 5;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值