Java-内部类

基本介绍

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

基本语法

class Outer{  //外部类

    class Inner{  //内部类
    
    }
}

class Other{  //外部其他类

}

内部类的分类

★定义在外部类的局部位置上(比如方法内或者代码块中)

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

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

②不能添加访问修饰符,因为是一个局部变量,局部变量时不能使用访问修饰符的。但是可以使用final修饰,局部变量可以定义为final(最终的);

③作用域:仅仅在定义它的方法中和代码块中;

④局部内部类访问外部类成员,直接访问;

⑤外部类访问局部内部类的成员,需要创建对象再访问(必须在作用域内);

⑥外部其他类不能访问局部内部类(因为局部内部类是一个局部变量)

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

package com.pero.innerclass;

/**
 * 局部内部类
 * 定义在外部类的局部位置,比如方法中
 *
 * @version 1.1
 * @author Pero
 */

public class LocalInnerClass {
    public static void main(String[] args) {
        new Outer02().hi();
        //new Inner02();  ⑥外部其他类不能访问局部内部类
    }
}
class Outer02{
    //属性
    private String name = "jake";

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

    //方法
    public void hi(){

        String name = "lucy";

        //方法中定义局部内部类
        //②不能添加访问修饰符,因为是一个局部变量,局部变量时不能使用访问修饰符的。
        //  但是可以使用final修饰,局部变量可以定义为final(最终的);
        //③作用域:仅仅在定义它的方法中和代码块中;
        final class Inner02{  //本质还是一个类
            //①可以直接访问外部类的所有成员,包含私有的;
            public void ok(){
                //④局部内部类访问外部类成员,直接访问;
                //⑦如果外部类和局部内部类的成员重名时,默认遵循就近原则,
                //  如果想访问外部类的成员,可以使用(外部类名.this.成员名)来访问
                //  外部类名.this本质就是外部类的对象,即那个对象调用hi()方法,外部类名.this就代表那个对象
                System.out.println("内部类name ="+name); //name = lucy
                System.out.println("外部类name = "+Outer02.this.name);  //name = jake
                say();
            }
        }

        //⑤外部类访问局部内部类的成员,需要创建对象在访问(必须在作用域内)。
        new Inner02().ok();

        class Inner03 { /* extends Inner02{*/  //Cannot inherit from final 'null'
        }
    }

}

2)匿名内部类(没有类名,重点)

匿名内部类是定义在外部类的局部位置,并且没有类名,同时还是一个对象

基本语法:

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

        类体;

};(;号不能少)

匿名内部类的本质:分别是基于接口的、基于普通类的、基于抽象类的;

package com.pero.innerclass;

/**
 * 匿名内部类的演示(基于接口、基于普通类、基于抽象类)
 *
 * @version 1.2
 * @author Pero
 */

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

        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}

class Outer04{  //外部类
    private int n = 10;  //属性
    public void method(){  //方法
        //基于接口的匿名内部类
        //1.需求:使用接口A,并创建对象
        //2.传统方法,写一个类实现接口,创建对象并调用方法
//        IA tiger = new Tiger();
//        tiger.cry();
        //3.如果多个类都要求实现接口并只调用一次,通过传统方法会调用许多类,会造成代码冗余
        //4.通过匿名内部类简化开发
        //5.tiger的编译类型:IA
        //6.tiger的运行类型:Outer04$1(外部类名称+$+底层排序)
        /*
            底层:会分配 Outer04$1类
            class Outer04$1 implements IA{
                @Override
                public void cry() {
                    System.out.println("老虎叫唤!");
                }
            }
         */
        //7.jdk底层在创建匿名内部类Outer04$1,会立刻马上创建Outer04$1对象实例,并且把地址返回给tiger
        //8.匿名内部类使用一次就不能再使用了:new Outer04$1();错误
        //  对象可以反复使用。
        IA tiger = new IA(){
            @Override
            public void cry() {
                System.out.println("老虎叫唤!");
            }
        };
        tiger.cry();
        System.out.println("tiger匿名内部类的名称为:" + tiger.getClass());

        //基于普通类的匿名内部类
        //1.father的编译类型:Father
        //2.father的运行类型:Outer04$2
        //3.底层会创建匿名内部类
        /*
            class Outer04$2 extends Father{
                public Outer04$2(String name){  //自己的理解
                    super(name);
                }
                @Override
                public void test() {
                    System.out.println("father匿名内部类重写了test()方法");
                }
            }
         */
        //4.同时也直接返回了匿名内部类Outer04$2的对象
        Father father = new Father("Lucy"){
            //为什么要传入参数Lucy?因为在生成Outer04$2对象实例之前必须要先生成父类对象Father
            @Override
            public void test() {
                System.out.println("father匿名内部类重写了test()方法");
            }
        };
        System.out.println("father匿名内部类的名称为:" +father.getClass());
        father.test();

        //基于抽象类的匿名内部类
        //dog的编译类型是Animal
        //dog的运行类型是Outer04$3
        //必须重写抽象方法say();
        Animal dog = new Animal(){
            @Override
            void say() {
                System.out.println("小狗汪汪叫");
            }
        };
        dog.say();
    }
}

interface IA{  //接口
    void cry();  //抽象方法
}

//class Tiger implements IA{
//    @Override
//    public void cry() {
//        System.out.println("老虎叫唤!");
//    }
//}

class Father{  //其他外部类

    private String name;

    public Father(String name) {  //构造器
        this.name = name;
    }

    public void test(){  //方法

    }
}

abstract class Animal{
    abstract void say();
}

匿名内部类的使用细节:

①匿名内部类既是一个类的定义,同时它本身也是一个对象,它既有定义类的特征,也有创建对象的特征,可以调用匿名内部类方法;

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

③不能用访问修饰符修饰,它的地位是一个局部变量;

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

⑤匿名内部类直接访问外部类成员;

⑥外部其他类不能访问匿名内部类(因为匿名内部类是一个局部变量);

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

package com.pero.innerclass;

/**
 * 匿名内部类使用细节
 *
 * @version 1.3
 * @author Pero
 */

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f();
        System.out.println("outer05的hashCode值为:"+outer05.hashCode());
    }
}

class Outer05{
    private int n = 1;
    public void f(){
        //创建一个基于普通类的匿名内部类
        Person person = new Person(){
            @Override
            public void hi() {
                System.out.println("匿名内部类Outer05$1重写了hi()方法");
            }
        };
        person.hi();  //动态绑定机制,运行类型是Outer05$1

        //也可以直接调用
        //与上面的匿名内部类完全不一样,这是一个新的内部类
        //编译类型是Person,但是运行类型是Outer05$2
        new Person(){
            //重名属性
            private int n = 10;
            @Override
            public void hi() {
                System.out.println("匿名内部类中的属性n的值为:"+n);  //重名属性调用遵循,就近原则
                System.out.println("外部类中的属性n的值为:"+Outer05.this.n);
                System.out.println("匿名内部类Outer05$1重写了hi()方法");
                System.out.println("Outer05.this的hashCode为:"+Outer05.this.hashCode());
            }

            @Override
            public void ok(String str) {
                super.ok(str);
            }
        }/*.ok("jake")*/.hi();  //匿名内部类本身也是对象,可以直接调用方法
    }
}

class Person{  //举例普通类,也可以是接口或者抽象类
    public void hi(){
        System.out.println("普通类Person的hi()方法");
    }
    public void ok(String str){
        System.out.println("Person ok()"+str);
    }
}

匿名内部类实践

匿名内部类当作实参直接传递,简洁高效

package com.pero.innerclass;

/**
 * 匿名内部类的实践一:可当作实参进行直接传递,简洁高效
 *
 * @version 1.4
 * @author Pero
 */

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

        //当作实参直接传递
        f(new IL() {
            @Override
            public void say() {
                System.out.println("匿名内部类当作实参直接传递");
            }
        });

        //传统的实现办法
        f(new An());
    }

    //静态方法
    public static void f(IL il){  //通过接口类型调用方法say
        il.say();
    };
}

interface IL{
    void say();
}

//传统办法,创建一个类实现接口;在编程领域这种实现方式叫做硬编码
class An implements IL{
    @Override
    public void say() {
        System.out.println("传统方法实现传递实参");  //如果更改输出结果对后续对象会造成影响
    }
}

实践二:

package com.pero.innerclass;

/**
 * 匿名内部类的实践二:
 * 1.有一个铃声接口Bell,里面有个ring方法
 * 2.有个手机类CellPhone,具有闹钟功能alarmclock,参数是Bell接口类型
 * 3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:起床了
 * 4.在传入另外一个匿名内部类(对象),打印:上班了
 *
 * @version 1.5
 * @author Pero
 */

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

class  CellPhone{
    public void alarmClock(Bell bell){
        System.out.println("传入参数的运行类型:"+bell.getClass());
        bell.ring();  //动态绑定;(自我理解:传递的匿名内部类(对象)调用的该方法都会进行重写)
    }
}

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

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

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

②可以添加任何访问修饰符(public、protect、默认、private),地位就是一个成员;

③作用域和外部类的其他成员一样为整个类体;

④成员内部类直接访问外部类其他成员;

⑤外部类通过创建成员内部类对象访问内部类成员;

⑥外部其他类访问成员内部类有三种;

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

package com.pero.innerclass;

/**
 * 成员内部类(没有static修饰)
 *
 * @version 1.6
 * @author Pero
 */

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

        Outer08 outer08 = new Outer08();
        outer08.t();

        //6.外部其他类访问成员内部类时的方式
        //第一种:
        //用外部类的对象创建一个内部类的对象,相当于将new Inner08()当作outer08对象的成员
        // 编译类型:Outer08.Inner08,运行类型:Inner08;
        Outer08.Inner08 inner08 = outer08.new Inner08();

        //第二种:
        //在外部类中编写一个方法,可以返回一个Inner08的对象实例
        //编译类型:Outer08.Inner08,运行类型:Inner08;
        Outer08.Inner08 inner08Instance= outer08.getInner08Instance();
        inner08Instance.say();

        //第三种:先创建一个外部类对象,在外部类对象下在创建内部类对象
        //编译类型:Outer08.Inner08,运行类型:Inner08;
        Outer08.Inner08 inner081 = new Outer08().new Inner08();
        //自主理解(inner081引用指向的是内部类的对象地址)
    }
}

class Outer08{  //外部类

    private double d = 10.0;
    public String name = "lucy";

    public void hi(){
        System.out.println("外部类的hi()方法");
    }

    public class Inner08{  //成员内部类  2.可以添加任何访问修饰符  3.作用域为整个外部类的类体中

        private String name = "tom";
        public void say(){
            System.out.println("成员内部类大的say()方法"+name+d);  //1.直接调用外部属性  4.直接访问外部类成员
            //7.成员内部类和外部类属性重名时,调用遵守就近原则
        }
    }

    //在外部类中编写一个方法,可以返回一个Inner08的对象实例
    public Inner08 getInner08Instance(){
        return new Inner08();
    }

    public void t(){
        Inner08 inner08 = new Inner08();  //5.外部类访问内部类成员,创建对象在调用相关属性和方法
        inner08.say();
        System.out.println(inner08.name);
        System.out.println(Outer08.this.name);  //7.属性重名时如果使用外部类属性用外部类名.this.属性名称
    }
}

4)静态内部类(使用static修饰);

①可以直接访问外部类的所有静态成员,并且有static修饰;

②可以添加任意访问修饰符(public、protected、默认、private),地位就是一个成员;

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

④静态内部类直接访问外部类的所有静态成员;

⑤外部类通过创建对象在访问静态内部类成员;

⑥外部其他类访问静态内部类

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

package com.pero.innerclass;

/**
 * 静态内部类
 *
 * @version 1.7
 * @author Pero
 */

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

        Outer09 outer09 = new Outer09();
        outer09.hi();

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

        //方式二:
        //在外部类成员位置编写一个非静态方法,通过创建外部类对象调用该方法,返回静态内部类的对象实例
        Outer09.Inner09 inner091 = new Outer09().getInner09();

        //在外部类成员位置编写一个静态方法,用外部类直接调用静态方法
        Outer09.Inner09 inner09_ = Outer09.getInner09_();

    }
}

class Outer09{

    //私有属性
    private String name = "jake";

    //私有静态属性
    public static double salary = 2000;

    private static void cry(){}

    //静态内部类,放在外部类的成员位置,使用static修饰  3.作用域为整个外部类的类体,和其他成员一样
    public static class Inner09{  //2.可以添加任意访问修饰符(public、protected、默认、private),地位就是一个成员
        public static double salary = 3000;
        public void say(){
            // 遵守静态访问规则,无法访问非静态成员
            //System.out.println(name);  Non-static field 'name' cannot be referenced from a static context

            //访问静态内部类的成员
            System.out.println("静态内部类的工资为:"+salary);

            //如果外部类和静态内部类的成员重名时,静态内部类访问的话,默认遵循就近原则,
            // 如果想访问外部类的静态成员,可以使用(外部类名.静态成员名)去访问。
            // (注意:不用添加this,因为调用的成员是static修饰的)
            System.out.println("外部类的工资为:"+Outer09.salary);//1.直接访问外部类的静态成员
            cry();  //4.静态内部类直接访问外部类的所有静态成员;
        }
    }

    public void hi(){  //5.外部类通过创建对象在访问静态内部类成员;
        Inner09 inner09 = new Inner09();
        inner09.say();
    }

    public Inner09 getInner09(){  //定义一个非静态方法返回静态内部类对象
        return new Inner09();
    }

    public static Inner09 getInner09_(){  //定义一个静态方法返回静态内部类对象
        return new Inner09();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值