java四种内部类

内部类介绍

内部类定义在外部类的局部位置(方法中/代码块)

(1) 局部内部类(有类名) (2) 匿名内部类(无类名)

内部类定义在外部类的成员位置 (和成员变量/方法同一位置)

(1) 成员内部类(无static修饰符) (2) 静态内部类(有static修饰符)

一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类

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

基本语法

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

类的五大成员

package com.lian.pojo.f.demo16;

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

    }
}

//外部类
class Outer {

    //属性
    private int n1 = 100;

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

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

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

    //内部类
    class Inner{

    }
}

一、局部内部类

说明:局部内部类是定义在外部类的局部位置,比如方法或代码块中,并且有类名
1、可以直接访问外部类的包含私有访问权限的所有成员
2、不能添加访问修饰符,因为他的地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final修饰
3、作用域:仅仅在定义它的方法或代码块中
4、局部内部类访问外部类的成员,直接可访问
5、外部类访问局部内部类的成员,必须在作用域内(方法或代码块),创建对象再访问
6、外部其他类不能访问局部内部类,因为局部内部类的地位和局部变量一样
7、如果外部类和局部内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问

System.out.println("外部类的n= "+ 外部类名.this.成员)
package com.lian.pojo.f.demo16;

/**
 * 演示局部内部类的使用
 */
public class LocalInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.m1();
        System.out.println("outer的hashcode= " + outer);
    }
}

//外部类
class Outer {
    private int n1 = 100;
    //私有方法
    private void m2() {
        System.out.println("Outer m2()");
    }

    public void m1() { //方法
        //1.局部内部类是定义在外部类的局部位置,通常在方法
        //3.不能添加访问修饰符,但是可以使用final 修饰,final代表不可以被其他类继承
        //4.作用域 : 仅仅在定义它的方法或代码块中
        final class Inner { //局部内部类(本质仍然是一个类)
            //2.可以直接访问外部类的所有成员,包含私有的,类似于外部类的灵魂,共享身体所有权利
            private int n1 = 800;
            public void f1() {
                //5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
                //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
                //   使用 外部类名.this.成员)去访问
                //   解读 Outer.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer.this就是哪个对象
                System.out.println("n1= " + n1 + " 外部类的n1= " + Outer.this.n1);
                System.out.println("Outer.this hashcode=" + Outer.this);
                m2(); //内部类可直接调用外部类的属性和方法
            }
        }
        //6. 外部类在方法中,可以创建Inner对象,然后调用方法即可
        Inner inner = new Inner();
        inner.f1();
    }

}

打印结果

n1=800 外部类的n1=100
Outer.this hashcode=com.lian.pojo.f.demo16.Outer@7cef4e59
Outer m2()
outer的hashcode=  com.lian.pojo.f.demo16.Outer@7cef4e59

二、匿名内部类

1、语法

new/接口/抽象类(参数列表){
	类体
}

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

Person p = new Person();   //对象

Person p = new Person(){};  //匿名内部类

3、可以访问外部类包括私有访问权限的成员
4、不能添加访问修饰符,因为它的地位和局部变量一样
5、作用域:仅仅再定义它的方法或代码块中
6、匿名内部类访问外部类成员,可直接访问
7、外部其他类不能访问匿名内部类
8、如果外部类和局部内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问

案例1

package com.lian.pojo.f.demo16;

/**
 * 演示匿名内部类的使用
 */
public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
    }
}

class Outer { //外部类
    private int n1 = 10;//属性
    public void method() {//方法
        //基于接口的匿名内部类
        //解读
        //1.需求: 想使用IA接口,并创建对象
        //2.传统方式,是写一个类,实现该接口,并创建对象
        //3.需求是 Tiger/Dog 类只是使用一次,后面再不使用
        //4. 可以使用匿名内部类来简化开发
        //5. tiger的编译类型 ? IA
        //6. tiger的运行类型 ? 就是匿名内部类  Outer$1,系统自动分配
        /*
            我们看底层 会分配 类名 Outer$1
            class Outer$1 implements IA {
                @Override
                public void cry() {
                    System.out.println("老虎叫唤...");
                }
            }
         */
        //7. jdk底层在创建匿名内部类 Outer$1,立即马上就创建了 Outer$1实例,并且把地址返回给 tiger
        //8. 匿名内部类使用一次,就不能再使用

        /**
         * 接口、类、抽象类 都可以作为 匿名内部类
         * 传统方法:新建子类继承 接口、抽象类 重新父类的方法,缺点 增加了多余的鸡肋的类
         * 匿名内部类方法: 直接语法就是 new class/interface/abstractClass(){}
         * 匿名内部类 系统其实内部给隐式的创建了 子类继承 class/interface/abstractClass
         * 例如 父类编译类型是 IA,子类运行类型是 外部类Outer$n
         */
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger的运行类型=" + tiger.getClass()); //子类是系统隐式创建 Outer$1
        tiger.cry();
        tiger.cry();
        tiger.cry();

        //传统编码方式
//        IA tiger = new Tiger();
//        tiger.cry();

        //演示基于类的匿名内部类
        //分析
        //1. father编译类型 Father
        //2. father运行类型 Outer$2
        //3. 底层会创建匿名内部类
        /*
            class Outer$2 extends Father{
                @Override
                public void test() {
                    System.out.println("匿名内部类重写了test方法");
                }
            }
         */
        //4. 同时也直接返回了 匿名内部类 Outer$2的对象
        //5. 注意("jack") 参数列表会传递给 构造器
        Father father = new Father("jack"){
            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father对象的运行类型=" + father.getClass());//系统隐式创建继承父类的子类名 Outer$2
        father.test();

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


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("小狗汪汪...");
//    }
//}

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

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

案例2

package com.lian.pojo.f.demo16;

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.f1();
        //外部其他类---不能访问----->匿名内部类,因为匿名内部类的作用域只局限于外部类的局部方法或代码块中
        System.out.println("main outer hashcode=" + outer);
    }
}

class Outer {
    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=" + Outer.this.n1 );
                //inner n1= 88 outer n1= 99
                System.out.println("inner n1= " + n1 + " outer n1= " + Outer.this.n1 );
                //Outer0.this 就是调用 f1的 对象
                System.out.println("Outer.this hashcode=" + Outer.this);
            }
        };
        p.hi();//动态绑定, 运行类型是 Outer0$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);
    }
}
//抽象类/接口 和以上类案例同理...

匿名内部类做参数使用

package com.lian.pojo.f.demo16;

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

        //方法2: 接口直接当做实参传递,简洁高效
        f(new A() {
            @Override
            public void show() {
                System.out.println("interface : this is a good picture");
            }
        });

        //方法1:传统方法,类实现接口,类当作参数传递
        f(new Picture());

    }

    //静态方法,形参是接口类型
    public static void f(A a) {
        // a.show(); = new A().show();
        a.show();
    }
}

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

//类->实现IL => 编程领域 (硬编码)
class Picture implements A {
    @Override
    public void show() {
        System.out.println("class : this is a good picture");
    }
}

练习

需求:
1、有一个铃声接口Bell,里面有个ring方法
2、有一个手机类CellPhone,具有闹钟功能 alarmClock,参数是Bell类型
3、测试手机类的闹钟功能,通过匿名内部类对象作为参数
4、再传入另一个匿名内部类对象

package com.lian.pojo.f.demo16;

public class InnerClassExercise {
    public static void main(String[] args) {
        /*
        1.有一个铃声接口Bell,里面有个ring方法
        2.有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型
        3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
        4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
         */
        CellPhone cellPhone = new CellPhone();
        //解读
        //1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise$1
        //2. 重写了 ring
        // 编译类型:父类Bell , 运行类型:系统隐式自动生成 子类 InnerClassExercise$1
        // 可以理解为:Bell bell = new InnerClassExercise$1(); 
        // 子类InnerClassExercise$1继承父类的ring()方法,遵循 动态绑定机制
        // 动态绑定机制:bell可访问父类所有属性和方法(需遵守访问控制规则),可访问子类继承父类的方法
        //3. Bell bell = new Bell() {
        //            @Override
        //            public void ring() {
        //                System.out.println("懒猪起床了");
        //            }
        //        }

        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("lazy pig get up");
            }
        });

        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("honey begin class");
            }
        });
    }
}

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

class CellPhone{ //类
    public void alarmClock(Bell bell){ //形参是Bell接口类型
        System.out.println(bell.getClass()); //运行类型,系统自动为接口隐式生成,名称:外部类$n
        // new Bell().ring()
        bell.ring();//动态绑定
    }
}

三、成员内部类

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

class Outer{  //外部类
private int n = 10;
public String name = "dan";

class Inner{
	public void say(){
		System.out.println("outer n"+n+"outer name"+name);
	}
}

3、作用域和外部类的其他成员一样是整个类
4、成员内部类访问外部类成员,直接访问
5、外部类访问成员内部类,创建对象再访问
6、外部其他类访问成员内部类
7、如果外部类和成员内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问

案例1

package com.lian.pojo.f.demo16;

public class MemberInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.t1();

        //外部其他类,使用成员内部类的三种方式
        //解读
        // 第一种方式
        // outer.new Inner(); 相当于把 new Inner()当做是outer成员
        // 成员内部类的地位和成员变量、方法是一样的,类可直接调用,也有用修饰符的权利
        Outer.Inner inner = outer.new Inner();
        inner.say();
        // 第二方式 在外部类中,编写一个方法,可以返回 Inner对象
        Outer.Inner innerInstance = outer.getInnerInstance();
        innerInstance.say();
    }
}

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

    //方法,返回一个Inner实例
    public Inner getInnerInstance(){
        return new Inner();
    }

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

    //1.注意: 成员内部类,是定义在外部内的成员位置上
    //2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    public class Inner {//成员内部类
        private double sal = 99.8;
        private int n1 = 66;
        public void say() {
            //可以直接访问外部类的所有成员,包含私有的
            //如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
            //,可以通过  外部类名.this.属性 来访问外部类的成员
            System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer.this.n1);
            hi(); //成员内部类可以调用父类外部类的所有属性和方法,灵魂伴侣,拥有和母体一样的权利
        }
    }

    //写方法
    public void t1() {
        //使用成员内部类
        //创建成员内部类的对象,然后使用相关的方法
        Inner inner = new Inner();
        inner.say();
        System.out.println(inner.sal);
    }
}

案例2

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

    }
}

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;
    }

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

四、静态内部类

1、静态内部类是定义在外部类的成员位置,并且有static修饰
2、可以直接访问外部类包括私有访问权限的所有成员,但不能直接访问外部类的非静态成员
3、作用域和外部类的其他成员一样是整个类
4、静态内部类访问外部类成员,直接访问所有静态成员
5、外部类访问静态内部类,创建对象再访问
6、外部其他类访问静态内部类
7、如果外部类和成员内部类的成员重名时,默认遵守就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员 访问

package com.lian.pojo.f.demo16;

public class StaticInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.m1();

        //外部其他类 使用静态内部类
        //方式1
        //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer.Inner inner = new Outer.Inner();
        inner.say();
        //方式2
        //编写一个方法,可以返回静态内部类的对象实例.
        Outer.Inner inner1 = outer.getInner();
        System.out.println("============");
        inner1.say();

//        Outer.Inner inner2 = Outer.getInner();
//        System.out.println("************");
//        inner2.say();

    }
}

class Outer { //外部类
    private int n1 = 10;
    private static String name = "张三";
    private static void cry() {}
    //Inner10就是静态内部类
    //1. 放在外部类的成员位置
    //2. 使用static 修饰
    //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
    //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    //5. 作用域 :同其他的成员,为整个类体
    static class Inner {
        private static String name = "教育";
        public void say() {
            //如果外部类和静态内部类的成员重名时,静态内部类访问的时,
            //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
            System.out.println(name + " 外部类name= " + Outer.name);
            cry(); //静态内部类可调用父类的所有属性和方法
        }
    }

    public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
        Inner inner = new Inner();
        inner.say();
    }

    public Inner getInner() {
        return new Inner();
    }

//    public static Inner getInner() {
//        return new Inner();
//    }

}

版权声明:本文为weixin_40964170原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。