Java中的内部类


我们作为刚入门的新手,定义一个类的主要方式是在一个文件中定义一个类。但是一个文件中也可以定义多个类,且其中任何一个类都不在其他类的内部,这种情况不在这节的讨论范围内。这节的讨论范围主要是在一个类的内部再定义一个类,最外层的类称为外部类,里面的则称为内部类。内部类又可以按照定义的形式不同分为 成员内部类、局部内部类、匿名内部类。下面做逐一说明。

一、定义类的一般做法

我们以往定义一个类,都只是在一个文件中定义一个类就已经足够满足使用需求了。如下:

public class Demo {
    
    private String name;
    private String sex;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public String getSex() {
        return sex;
    }
    
    public void setSex(String sex) {
        this.sex = sex;
    }
    
    public void doTest() {
        System.out.println("最常用的定义方式!");
    }

}

二、定义内部类

有的时候我们选择在一个类的内部再定义其他类,这就是内部类,外面的是外部类。下面对内部类中成员内部类、局部内部类、匿名内部类进行说明。

1、成员内部类
这种定义方式,可以在内部类中直接存取所在外部类的成员变量,也可以随意调用外部类中定义的成员方法,尽管这些成员变量或方法被修饰为private的。如下:

package com.ycz.test.innerClass;

/*
   *  成员内部类
 */
public class OuterClass {//定义外部类
    
    innerClass in = new innerClass();//在外部类中实例化内部类
    
    //定义一个外部类的方法
    public void ouf() {
        //调用内部类里面的方法,通过内部类对象
        in.inf();
    }
    
    /*
           *  定义内部类
     */
    class innerClass {
        
        innerClass(){//无参构造方法
            
        }
        
        //内部类中定义一个方法
        public void inf() {
            System.out.println("调用内部类里的方法!");
        }
        //内部类里面定义成员变量
        int y = 0;
    }
    
    //外部类里定义一个方法
    public innerClass doit() {
        //在外部类中不能直接访问内部类的成员变量,要通过内部类的对象来访问
        in.y = 24;//给内部类的成员变量重新赋值
        return new innerClass();
    }
    
    //主方法中测试
    public static void main(String args[]) {
        //实例化外部类对象
        OuterClass out = new OuterClass();
        out.ouf();
        //实例化一个内部类对象,通过外部类中定义的doit方法来获取
        //注意内部类的定义,要通过外部类
        OuterClass.innerClass in = out.doit();
        //内部类的实例化,通过外部类的对象来实现
        OuterClass.innerClass in2 = out.new innerClass();
        //可以一句代码实现,但是必须先实例化外部类对象,再通过外部类对象实例化内部类对象
        OuterClass.innerClass in3 = new OuterClass().new innerClass();
    }

}

可以发现,内部类对象的创建必须要依赖于外部类的对象,也就是说如果想要实例化一个内部类的对象,那么必须先创建一个外部类对象,然后基于该外部类对象创建内部类对象。因为内部类和外部类是存在依赖关系的,内部类依赖于外部类而存在。

代码:

package com.ycz.test.innerClass.demo1;

/*
 * 定义一个接口
 */
public interface OuterInterface {
    
    public void f();//定义一个抽象方法

}
package com.ycz.test.innerClass.demo1;

public class OuterClass {

    /*
     * 定义一个内部类,实现接口
     */
    private class InnerClass implements OuterInterface {

        // 内部类的构造方法
        InnerClass(String s) {
            System.out.println(s);
        }

        // 实现抽象方法
        @Override
        public void f() {
            System.out.println("内部类中的f()方法!");
        }

    }

    // 外部类中定义一个方法,返回类型为接口
    public OuterInterface doit() {
        return new InnerClass("访问内部类的构造方法!");
    }

}

测试类:

package com.ycz.test.innerClass.demo1;

/*
 * 测试内部类向上转型为接口
 */
public class Test1 {

    public static void main(String[] args) {
       OuterClass out = new OuterClass();//实例化外部类对象
       OuterInterface outInterface = out.doit();//调用外部类的方法,返回一个接口
       outInterface.f();
    }

}

控制台输出结果:
在这里插入图片描述
通过代码可以看出,这个将private修饰的内部类向上转型为一个接口,在程序中可以完全隐藏内部类的实现细节。在外部提供一个接口,接口中定义一个方法,而内部类实现该接口,实现了该接口中定义的抽象方法,如果某个类继承了这个外部类,那么由于权限的限制,该子类不能向下转型为内部类,也就无法访问内部类中定义的f()方法,但是却可以访问接口中的f()方法,而接口又被内部类实现了,这种方式仅为子类留下一个接口和外部类,而隐藏了实现细节,这是内部类的最基本的用途。

如果在外部类中定义的成员变量和内部类中的成员变量重名时,这时可以使用this关键字来区分。如下:

package com.ycz.test.innerClass.demo2;

/*
 * 测试用this关键字指向内部类对象和外部类对象
 */
public class OuterClass {
    
    private int x = 21;//外部类的成员变量
    
    //定义一个成员内部类
    private class Inner{
        private int x = 25;//内部类的成员变量
        //内部类的成员方法
        public void doit(int x) {
            x = 18;
            System.out.println("方法的局部变量x的值:" + x);
            System.out.println("内部类的成员变量x的值:" + this.x);//this是指向内部类对象
            System.out.println("外部类的成员变量x的值:" + OuterClass.this.x);//OuterClass.this是指向外部类对象
        }
    }
    
    public static void main(String []args) {
        new OuterClass().new Inner().doit(3);
    }
    
}

控制台输出结果:
在这里插入图片描述
可以看到,在内部类中,如果内部类成员变量和外部类成员变量重名时,使用this.成员变量名指向内部类成员变量,使用外部类名.this.成员变量名指向外部类成员变量。如果不重名,可以不使用this来指向,直接写变量名。需要注意的是,如果是在外部类中想访问内部类成员变量和方法,必须要通过内部类对象来访问。

2、局部内部类
局部内部类的一般做法是将内部类定义在方法中的。如下:

package com.ycz.test.innerClass.demo3;

/*
 * 定义一个接口
 */
public interface OuterInterface {
    
    
}

package com.ycz.test.innerClass.demo3;

/*
 * 局部内部类
 */
public class OuterClass {

    /*
     * 定义一个方法,该方法返回一个接口 方法的参数类型是String,而且是final修饰的,方法的局部变量相当于常量,不可更改
     */
    public OuterInterface doit(final String x) {
        // 在方法内定义一个内部类,即为局部内部类
        // 且该内部类是实现接口的
        // 这个内部类的生命周期是和方法同步的
        class InnerClass implements OuterInterface {

            // 内部类的构造方法
            InnerClass(String s) {
                System.out.println(x);
                System.out.println(s);
            }
        }
        return new InnerClass("访问内部类的构造方法!");
    }

}

测试代码:

package com.ycz.test.innerClass.demo3;


public class Test {

    public static void main(String[] args) {
        new OuterClass().doit("lalala");
    }

}

控制台输出结果:
在这里插入图片描述
注意,将内部类定义在doit()方法内部,内部类是方法的一部分,所以在doit()方法之外是不能访问这个内部类的,但是这个内部类可以访问当前代码块的常量及外部类的所有成员。以上代码中,doit()方法的参数是final修饰的,如果要在方法体中使用局部变量,那么局部变量需要加final修饰,也就是说在方法中的内部类只能访问final修饰的局部变量,其实加了final之后是相当于一个常量的,值是不能被更改的。

3、匿名内部类
顾名思义,匿名内部类的明显特点就是匿名,因为没有名称,所以在别的地方也就无法使用这个类了,匿名内部类只能使用一次,通常可以使用匿名内部类的方式来避免类爆炸现象。

package com.ycz.test.innerClass.demo4;

public interface People {
    
    public void run();

}
package com.ycz.test.innerClass.demo4;

/*
 * 测试匿名内部类
 */
public class DemoClass {

    public static void main(String[] args) {
        // 这个匿名类是实现了接口的
        People p = new People() {

            @Override
            public void run() {
                System.out.println("穿了特步健步如飞!");
            }

        };
        p.run();
    }

}

上面的代码中,定义了一个接口,普通的做法是定义一个类实现接口,这里其实也是继承了接口,只不过是匿名内部类实现了该接口,重写了接口中的抽象方法。

上面介绍的其实都是非静态内部类,实际上还有静态内部类,只要在内部类的前面加static修饰符就行了,静态内部类中可以声明static静态成员,而且静态内部类中是不能访问外部类的非静态成员的,只能访问静态成员。静态内部类有几个特点:
♦ 创建静态内部类的对象,不需要通过外部类对象。
♦ 不能从静态内部类的对象中访问非静态外部类对象。
♦ 不能在静态内部类中访问外部类的非静态成员。

package com.ycz.test.innerClass;

/*
 * 测试静态内部类
 */
public class StaticInnerClass {//外部类
    
    int x = 21;//非静态成员
    
    //定义静态内部类
    static class Inner{
        void doit() {
            //会报错,因为外部类的x成员是非静态的,在静态内部类中无法访问
            //System.out.println("外部类的成员变量x的值:" + x);
        }
    }

}

静态内部类在开发中是非常少见的,作为学习我们还是应该有所了解。关于内部类的使用就简单介绍这几种,内部类还可以被继承,但是继承内部类比较复杂,而却要设置专门的语法来完成,这里就不做介绍了。内部类是Java中类的比较高级的用法,我们初学者往往会一头雾水,不知所云,不过只要多看代码,多加练习,相信每一个人都能熟练的掌握!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值