Java学习笔记 --- 内部类

一、基本介绍

一个类的内部又完整的嵌套了另外一个类的结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。就是我们类的第五大成员(属性、方法、构造器、代码块、内部类),内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间包含的关系

基本语法 :

class Outer{   //外部类

        class Inner{ //内部类

        }

}

class Other{ //外部其他类

}

package com.javase.innerclass;

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

    }
}

class Outer {//外部类
    //属性
    private int n1 = 10;
    //构造器
    public Outer(int n1) {
        this.n1 = n1;
    }
    //代码块
    {
        System.out.println("代码块...");
    }
    //方法
    public void hi() {}
    //内部类
    class Dog {}
}

二、内部类的分类

1、局部内部类

局部内部类的使用细节

说明:局部内部类是定义在外部类的局部位置,比如方法或者代码块中,并且有类名

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

class Outer01 {//外部类
    private int n1 = 10;
    private void h1() {//私有方法
        System.out.println("外部类的h1()方法...");
    }
    public void m1() {//方法
        //局部内部类是定义在外部类的局部位置,通常是在方法中
        class Inner01 {
            //可以直接访问外部类的所有成员,包括私有的
            public void f1() {
                System.out.println("n1=" + n1);
                h1();
            }
        }
    }
}

2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用 final 修饰,因为局部变量也可以使用 final

Java学习笔记 --- 内部类_第1张图片

3、作用域仅仅在定义它的方法或者代码块中

4、局部内部类---访问--->外部类的成员,访问方式:直接访问

5、外部类---访问--->局部内部类的成员,访问方式:创建对象,再访问(必须在作用域内)

class Outer01 {//外部类
    private int n1 = 10;
    private void h1() {//私有方法
        System.out.println("外部类的h1()方法...");
    }
    public void m1() {//方法
        //局部内部类是定义在外部类的局部位置,通常是在方法中
        //不能添加访问修饰符,但是可以用final修饰
        //作用域仅仅在定义它的方法或者代码块中 
        class Inner01 {
            //可以直接访问外部类的所有成员,包括私有的
            public void f1() {
                System.out.println("n1=" + n1);
                h1();
            }
        }
        //外部类在方法中,可以创建Inner01对象,然后调用方法
        Inner01 inner01 = new Inner01();
        inner01.f1();
    }
}

6、外部其他类 ---不能访问--->局部内部类,因为局部内部类地位是一个局部变量

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

class Outer01 {//外部类
    private int n1 = 10;
    private void h1() {//私有方法
        System.out.println("外部类的h1()方法...");
    }
    public void m1() {//方法
        class Inner01 {
            //可以直接访问外部类的所有成员,包括私有的
            private int n1 = 20;
            public void f1() {
                //如果有重名的成员,遵循就近访问
                //访问外部类的成员,则可以使用(外部类名.this.成员)去访问
                System.out.println("n1=" + n1 + " 外部类的n1=" + Outer01.this.n1);
                h1();
            }
        }
    }
}

小结:

1、局部内部类定义在方法或者代码块中

2、作用域在方法体或者代码块中

3、局部内部类本质仍然是一个类

2、匿名内部类

匿名内部类的使用细节(重要!

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

           本质是一个类,内部类,该类没有名字,同时还是一个对象

1、匿名内部类的基本语法

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

演示基于接口的匿名内部类:

package com.javase.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() {//方法
        //基于接口的匿名内部类
        //1.需求:想使用IA接口,并创建对象
        //2.传统方法,是写一个类,实现该接口,并创建对象
        //3.需求创建的类只使用一次,后面不再使用
        //4.可以使用匿名内部类来简化开发
        //5.tiger的编译类型 ---> IA
        //6.tiger的运行类型 ---> 就是匿名内部类 Outer02$1
        /*
            看底层代码实现,系统会分配一个类名 Outer02$1
            class Outer02$1 implements IA() {
                @Override
                public void cry() {
                    System.out.println("老虎叫...");
                }
            }
         */
        //7.JDK底层在创建匿名内部类Outer02$1,马上就创建了Outer02$1实例对象,
        //  并且把地址返回给tiger
        //8.匿名内部类使用一次后就不能再使用
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫...");
            }
        };
        System.out.println("tiger的运行类型=" + tiger.getClass());
        tiger.cry();
    }
}

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

演示基于类的匿名内部类:

package com.javase.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() {//方法
        //演示基于类的匿名内部类
        //1.father 编译类型 ---> Father
        //2.father 运行类型 ---> Outer02$2
        //3.底层会创建匿名内部类
        /*
            class Outer02$2 extends Father {
                @Override
                public void test() {
                    System.out.println("匿名内部类重写test方法");
                }
            }
         */
        //4.同时也直接返回了 匿名内部类 Outer02$2的对象
        Father father = new Father("张三") {
            @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("小狗吃骨头...");
            }
        };
    }
}


class Father {//普通类
    public Father(String name) {

    }
    public void test() {

    }
}

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

2、匿名内部类的语法比较奇怪,请大家注意,因为匿名内部类即是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法

package com.javase.innerclass;

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        new Outer03().f1();
    }
}

class Outer03 {
    private int n1 = 10;
    public void f1() {
        //基于类的匿名内部类
        Person person = new Person() {
            @Override
            public void hi() {
                System.out.println("匿名内部类重写 hi()方法...");
            }
        };
        person.hi();//动态绑定 运行类型是Outer03$1

        //也可以直接调用,匿名内部类本身也是返回对象
        new Person() {
            @Override
            public void hi() {
                System.out.println("匿名内部类重写 hi()方法...直接调用");
            }
        }.hi();
    }
}

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

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

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

5、作用域:仅仅定义在它的方法或者代码块中

6、匿名内部类---访问--->外部类的成员,访问方式:直接访问

7、外部其他类 ---不能访问--->局部内部类,因为局部内部类地位是一个局部变量

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

匿名内部类的最佳实践:

1、当作实参直接传递,简洁高效

package com.javase.innerclass;

public class InnerClassExercise01 {
    public static void main(String[] args) {
        //当作实参直接传递,简洁高效
        f1(new IL() {
            @Override
            public void hi() {
                System.out.println("简洁高效...");
            }
        });
        //传统方法
        f1(new Car());
    }
    //静态方法,形参是接口类型
    public static void f1(IL il) {
        il.hi();
    }
}
//接口
interface IL {
    void hi();
}
//类->实现IL  编程领域(硬编码)
class Car implements IL {
    @Override
    public void hi() {
        System.out.println("传统方法...");
    }
}

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

package com.javase.innerclass;

import com.javase.interface_.Phone;

public class InnerClassExercise02 {
    public static void main(String[] args) {
        CellPhone phone = new CellPhone();
        phone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("懒猪起床了...");
            }
        });

        phone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("小伙伴上课了...");
            }
        });
    }
}
//接口
interface Bell{
    void ring();
}
//类
class CellPhone {
    public void alarmClock(Bell bell) {
        bell.ring();
    }
}

3、成员内部类

成员内部类的使用细节

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

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

2、可以添加任意的访问修饰符(public protected 默认 private),因为它的地位是一个成员

3、作用域和外部类的其他成员一样,为整个类体,比如在外部类的成员方法中创建成员内部类对象,再调用方法

4、成员内部类---访问--->外部类的成员,访问方式:直接访问

5、外部类 ---访问--->内部类,访问方式:创建对象,再访问

6、外部其他类 ---访问--->成员内部类,访问方式:有二种方式

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

package com.javase.innerclass;

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.h1();

        //外部其他类,使用成员内部类的二种方式
        //第一种方式
        Outer04.Inner04 inner04 = outer04.new Inner04();
        //第二种方式 在外部类中,编写一个方法,可以返回Inner04对象
        Outer04.Inner04 inner041 = outer04.getInner08();

    }
}

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

    public class Inner04 {//成员内部类
        private int n1 = 66;
        public void say() {
            //可以直接访问外部类的所有成员,包含私有的
            //如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则
            //果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
            System.out.println("n1=" + n1 + " 外部类的n1=" + Outer04.this.n1);
        }
    }
    //写方法
    public void h1() {
        //创建成员内部类对象,再调用方法
        new Inner04().say();
    }
    //可以返回Inner04对象实例
    public Inner04 getInner08() {
        return new Inner04();
    }

}

4、静态内部类

静态内部类的使用细节

说明:成员内部类是定义在外部类的成员位置,并且使用 static 修饰

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

2、可以添加任意的访问修饰符(public protected 默认 private),因为它的地位是一个成员

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

4、静态内部类---访问--->外部类(比如静态属性),访问方式:直接访问所有静态成员

5、外部类 ---访问--->静态内部类,访问方式:创建对象,再访问

6、外部其他类 ---访问--->静态内部类,访问方式:有二种方式

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

package com.javase.innerclass;

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

        //外部其他类访问静态内部类
        //方式1 因为是静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer05.Inner05 inner05 = new Outer05.Inner05();
        inner05.say();
        //方式2 编写一个方法可以返回静态内部类对象实例
        Outer05.Inner05 inner051 = new Outer05().getInner05();
        inner051.say();
        //静态方法返回静态内部类对象实例
        Outer05.Inner05 inner052 = Outer05.getInner05_();
        inner052.say();
    }
}

class Outer05 {//外部类
    private int n1 = 10;
    private static int n2 = 20;
    //放在外部类的成员位置,使用static修饰
    //可以添加任意的访问修饰符,因为它的地位是一个成员
    public static class Inner05 {//静态内部类
        private static int n2 = 20;
        public void say() {
            //可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
            //如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则
            //如果想访问外部类的成员,则可以使用(外部类名 . 成员)去访问
            System.out.println("n2=" + n2 + " 外部类n2=" + Outer05.n2);
        }
    }

    public void m1() {
        new Inner05().say();
    }

    public Inner05 getInner05() {
        return new Inner05();
    }

    public static Inner05 getInner05_() {
        return new Inner05();
    }
}

你可能感兴趣的:(java基础学习,java,学习,jvm)