java内部类

一、基本介绍

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

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

内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

基本语法:

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

示意图:

基本代码演示:

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

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

    }
}

二、内部类的四大分类

基本说明:

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

1、局部内部类(有类名)

2、匿名内部类(没有类名,开发中用的很多)

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

1、成员内部类(没有static修饰)

2、静态内部类(使用static修饰)

1、局部内部类

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

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

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

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

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

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

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

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

原因:

示例代码:代码中的注释就是解释

public class LocalInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
    }
}

class Outer02 {
    //属性
    private int n1 = 100;
    private int n2 = 1;
    //外部类的私有方法
    private void m2(){
        System.out.println("我是外部类的私有方法");
    }
    //方法
    public void m1(){
        //1、局部内部类是定义在外部类的局部位置,通常是在方法
        //3、不能添加访问修饰符(public、private等),但是可以用final修饰,但是修饰过后的内部类不允许被继承
        class Inner02{ //局部内部类(本质仍然是一个类)
            //内部类的成员变量,与外部类成员变量同名
            private int n1 = 20; //这个成员变量会最先被访问到,遵循就近原则
            //2、可以直接访问外部类的所有成员,包含私有
            public void f1(){ //属于局部内部类的方法
                System.out.println("内部类的n1 = " + n1); //输出的是内部类的成员变量的值,因为外部类和内部类的成员变量同名
                //由于同名的缘故,需要(外部类.this.属性)进行访问
                //那个类调用了m1这个方法,那么内部类的this指向就是指向调用了m1方法的类
                //这里是Outer02调用了m1方法,所以this指向的就是Outer02
                System.out.println("外部类的n1 = " + Outer02.this.n1);
                //直接访问外部类的私有成员
                System.out.println("外部类的n2 = " + n2);
                m2();//调用外部类的私有方法
            }
        }
        //final关键字修饰过后的内部类是不可以被继承的
        //内部类可继承其他内部类,但仅限于在它定义的方法和代码块中
        class Inner03 extends Inner02{}

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



    public void m3(){
        //代码块
        {
            //内部类中仍然可以定义内部类
            class Inner04{
                class Inner06{
                    class Inner07{}
                }
            }
            //代码块中的内部类可以继承其他存在代码块的内部类
            class Inner05 extends Inner04{}
        }
    }
}

记住:

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

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

3、内部类的本质其实还是一个类,内部类中仍然可以定义内部类

2、匿名内部类(重点)

说明:

(1)本质还是一个类

(2)定义在一个类的内部,本质还是一个内部类

(3)该类没有名字,名字是由系统起的

(4)匿名内部类同时还是一个对象

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

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

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

2、匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类方法

演示:

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

class Outer05{
    private int n = 99;
    // Outer05 方法
    public void f1(){
        //第一种调用方法
        //创建一个基于类的匿名内部类
        Person p = new Person() {
            //重写 hi 方法
            @Override
            public void hi() {
                System.out.println("匿名内部类重写了hi方法");
            }
        };
        p.hi(); //动态绑定

        //第二种调用方法
        new Person(){
            @Override
            public void hi() {
                System.out.println("我是第二种调用方法");
            }
        }.hi();

        //匿名内部类传参
        new Person(){
            @Override
            public void ok(String str) {
                System.out.println("重写ok方法,接收的参数是 " + str);
            }
        }.ok("outer05穿传参");
    }
}

class Person{
    public void hi(){
        System.out.println("person say hi~");
    }

    public void ok(String str){
        System.out.println("person接收参数 " + str);
    }
}

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

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

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

6、匿名内部类----访问----->外部类成员 [直接访问]

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

8、如果外部类和匿名内部类的成员重名是,内部类访问的话,遵循就近原则,如果想访问外部类其他成员,则可以使用(外部类名.this.成员)访问

使用匿名内部类的原因:

为了简化开发,当开发中遇到有一个类指向使用一次,后面不再使用,就是用匿名内部类的方式

基于接口的匿名内部类示例代码:

public class AnonymousInnerClass {
    public static void main(String[] args) {
        //基于接口的匿名内部类,简化开发
        Outer04 outer04 = new Outer04();
        outer04.method();

    }
}
class Outer04{ //外部类
    private int n1 = 10; //属性
    public void method(){ //方法

        //问题:tiger的编译类型 :接口类型IA
        //问题:tiger的运行类型:匿名内部类 Outer04$1

        /*
            匿名内部类底层:
            xxx 是底层分配的名: Outer04$1 系统分配的名字
            class xxx implements IA{
                @Override
                public void cry() {
                    System.out.println("老虎叫。。。");
                }
            }
         */
        // jdk底层在创建匿名内部类 Outer04$1 ,立即马上创建了 Outer04$1 实例,并且把地址返回给tiger
        // 匿名内部类使用一次就不能再使用了,但是对象tiger可以使用
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫。。。");
            }
        };
        System.out.println("tiger的运行类型---> " + tiger.getClass());
        tiger.cry();
    }
}
//接口
interface IA{
    public void cry();
}

基于类的匿名内部类示例代码:

public class AnonymousInnerClass {
    public static void main(String[] args) {
        //基于接口的匿名内部类,简化开发
        Outer04 outer04 = new Outer04();
        outer04.method();

    }
}
class Outer04{ //外部类
    private int n1 = 10; //属性
    public void method(){ //方法

        //基于类的匿名内部类
        //father的编译类型:Father
        //father的运行类型:Outer04$2 匿名内部类
        //底层会创建一个匿名内部类
        /*
            底层:
            class Outer04$2 extends Father{}
         */
        //同时也直接返回了匿名内部类 Outer04$2 的对象
        //参数列表会传递给Father的构造器
        Father father = new Father("tom") {
            

            //匿名内部类重写test方法
            @Override
            public void test() {
                System.out.println("我重写了test方法");
            }
        };
        System.out.println("father对象的运行类型---> " + father.getClass());
        father.test();
    }
}

//类
class Father{
    public Father(String name){//构造器
        System.out.println("Father的--name ==> " + name);
    }
    public void test(){ //方法

    }
}

基于抽象类的匿名内部类:

public class AnonymousInnerClass {
    public static void main(String[] args) {
        //基于接口的匿名内部类,简化开发
        Outer04 outer04 = new Outer04();
        outer04.method();

    }
}
class Outer04{ //外部类
    private int n1 = 10; //属性
    public void method(){ //方法
        //基于抽象类的匿名内部类
        Animal animal = new Animal() {
            //必须要实现抽象类中的抽象方法
            @Override
            void eat() {
                System.out.println("我是抽象类中的抽象方法");
            }
        };
        animal.eat();
    }
}
//抽象类
abstract class Animal{
    abstract void eat();
}

练习一:

public class InnerClassExercise01 {
    public static void main(String[] args) {
        //接口当做实参进行传递,通过匿名内部类重写方法
        f1(new IL() {
            //这里重写了接口中的show方法,实现自己的逻辑
            @Override
            public void show() {
                System.out.println("我是接口show方法的重写");
            }
        });

        //传统的写法
        //首先有一个实现了IL的实现类,再将这个类传入 f1 这个方法
        Pictrue pictrue = new Pictrue();
        f1(pictrue);
    }

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

//实现 IL的类
class Pictrue implements IL{

    @Override
    public void show() {
        System.out.println("这是一幅名画。。。");
    }
}

练习二:

要求:

1、有一个铃声接口 Bell,里面有一个 ring 方法

2、有一个手机类 CellPhone,具有闹钟功能 alarmclock,参数是 Blee类型

3、测试手机类闹钟功能,通过匿名内部类(对象)作为参数,打印:闹钟响了

4、再传入另一个匿名内部类(对象),打印:上课了

实现代码:

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{
    // ring 方法
    void ring();
}

//手机类
class CellPhone{
    // 闹钟功能,参数是 Bell 类型
    public void alarmclock(Bell bell){
        //调用接口的 ring 方法
        bell.ring();
    }
}

3、成员内部类

基本介绍:

1、成员内部类是定义在外部类的成员位置,并且没有static修饰,可以直接访问外部类的所有成员,包含私有的

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

示例代码:

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

        Outer08 outer08 = new Outer08();
        outer08.m1();
    }
}
class Outer08{
    private int n1 = 100;
    public String name = "tom";

    //可以用修饰符进行修饰
    /*public*/ /*protected*/ /*private*/ class Inner08{
        public void say(){
            System.out.println("Outer08的 n1 = " + n1 +
                    " Outer08的 name = " + name);
        }
    }
    //在Outer08的 m1 方法中,实例化成员内部类 Inner08
    public void m1(){
        Inner08 inner08 = new Inner08();
        //调用成员内部类的 say 方法
        inner08.say();
    }
}

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

4、成员内部类----访问---->外部类(比如属性)[访问方式:直接访问]

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

        Outer08 outer08 = new Outer08();
        outer08.m1();
    }
}
class Outer08{

    //私有方法
    private void hi(){
        System.out.println("我是外部类的私有方法  hi()");
    }

	class Inner08{
		//内部类方法
        public void say(){
            //调用外部类的私有方法,直接调用
            hi();
        }
    }
    //在Outer08的 m1 方法中,实例化成员内部类 Inner08
    public void m1(){
        Inner08 inner08 = new Inner08();
        //调用成员内部类的 say 方法
        inner08.say();
    }
}

5、外部类-----访问---->内部类(说明)[访问方式:创建对象,再访问]

6、外部其他类---->访问----成员内部类

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

        Outer08 outer08 = new Outer08();
       // outer08.m1();

        //外部其他类调用成员内部类的三种方式
        //第一种方式
        Outer08.Inner08 inner08 = outer08.new Inner08(); //new Outer08().new Inner08();
        inner08.say();
        //第二种方式   在外部类中编写一个方法,返回Inner08对象实例
        Outer08.Inner08 getInner08 = outer08.getInner08();
        getInner08.say();
    }
}
class Outer08{
    //私有方法
    private void hi(){
        System.out.println("我是外部类的私有方法  hi()");
    }

	class Inner08{
        public void say(){
            //调用外部类的私有方法,直接调用
            hi();
        }
    }

    //方式二
    public Inner08 getInner08(){
        return new Inner08();
    }

    //在Outer08的 m1 方法中,实例化成员内部类 Inner08
    public void m1(){
        Inner08 inner08 = new Inner08();
        //调用成员内部类的 say 方法
        inner08.say();
    }
}

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

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

        Outer08 outer08 = new Outer08();
       	outer08.m1();
    }
}
class Outer08{
    private int n1 = 100;
    
    class Inner08{
        private int n1 = 6;
        public void say(){
            //就近原则
            System.out.println("n1的值是内部类的n1 = " + n1);
            //访问外部类重名的属性
            System.out.println("外部类n1的值 = " + Outer08.this.n1);
        }
    }

    //在Outer08的 m1 方法中,实例化成员内部类 Inner08
    public void m1(){
        Inner08 inner08 = new Inner08();
        //调用成员内部类的 say 方法
        inner08.say();
    }
}

4、静态内部类

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

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

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

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

示例代码:

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer09 outer09 = new Outer09();
        outer09.show();
    }
}
class Outer09{
    private int n1 = 10;
    private static String name = "张三";
    //1、放在外部类的成员位置
    //2、使用static修饰
    //4、可以添加任一访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
    /*public*/ /*private*/ /*protected*/ static class Inner02{
        public void say(){
            //3、可以直接访问外部类的所有静态成员,包含私有的,但是不能直接访问非静态成员
            System.out.println("访问外部类静态成员属性:" + name);
            //不能直接访问外部类的非静态成员
            //System.out.println("外部类非静态成员属性:" + n1);
        }
    }

    //5、作用域:同其他的成员一样,为整个类体
    //外部类方法
    public void show(){
        //外部类使用内部类
        new Inner02().say();
    }
}

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

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

6、外部其他类-----访问----->静态内部类

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer09 outer09 = new Outer09();
        outer09.show();

        //外部其他类访问静态内部类
        //方式一  因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer09.Inner02 getInner02 = new Outer09.Inner02();
        getInner02.say();

        //方式二 通过编写一个方法,返回静态内部类的对象实例
        Outer09.Inner02 inner021 = outer09.getInner02();
        inner021.say();
    }
}
class Outer09{
    private int n1 = 10;
    private static String name = "张三";

    //外部类静态方法
    private static void f1(){
        System.out.println("我是外部类静态方法 f1()");
    }

    //1、放在外部类的成员位置
    //2、使用static修饰
    //4、可以添加任一访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
    /*public*/ /*private*/ /*protected*/ static class Inner02{
        public void say(){
            //3、可以直接访问外部类的所有静态成员,包含私有的,但是不能直接访问非静态成员
            System.out.println("访问外部类静态成员属性:" + name);
            //不能直接访问外部类的非静态成员
            //System.out.println("外部类非静态成员属性:" + n1);

            //可以直接调用外部类静态方法
            f1();
        }
    }

    //5、作用域:同其他的成员一样,为整个类体
    //外部类方法
    public void show(){
        //外部类使用内部类
        new Inner02().say();
    }

    //方式二 返回静态内部类的对象实例
    public Inner02 getInner02(){
        return new Inner02();
    }
}

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

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer09 outer09 = new Outer09();
        outer09.show();
    }
}
class Outer09{
    private static String name = "张三";


    static class Inner02{
        //内部类的静态属性
        private static String name = "李四";
        public void say(){
            //就近原则
            System.out.println("我是内部类的静态成员变量:" + name);
            System.out.println("外部类的静态成员变量:" + Outer09.name);
        }
    }

    //5、作用域:同其他的成员一样,为整个类体
    //外部类方法
    public void show(){
        //外部类使用内部类
        new Inner02().say();
    }

}

三、小结

1、内部类有四种

局部内部类、匿名内部类(重要)、成员内部类、静态内部类

2、重点还是掌握匿名内部类的使用

语法:

new 类/接口(参数列表){
    //....
};

3、成员内部类、静态内部类 是放在外部类的成员位置,本质上还是一个成员

练习:

public class TestInnerClass01 {
    public static void main(String[] args) {
        TestInner testInner = new TestInner();
        TestInner.Inner inner = testInner.new Inner();
        System.out.println("main方法中的值:" + inner.a);
    }
}

class TestInner{

    public TestInner(){
        Inner inner01 = new Inner();
        inner01.a = 10;
        Inner inner02 = new Inner();
        System.out.println("外部类的test方法中的值:" + inner02.a);
    }

    class Inner{
        public int a = 5;
    }
}
posted @ 2022-08-28 11:39  花椒蛋炒饭  阅读(53)  评论(0编辑  收藏  举报