Java内部类

参考资料

[1]. 疯狂Java讲义(第三版) 李刚

内部类

非静态内部类

内部类因为是视同类成员,所以有四种权限,定义语法格式如下:

[public|protected|private]   class   类名
{
// 类体
}

示例程序如下:

package com.qunar;
/**
 * Cow interface
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class Cow {
    /**
     * 重量
     */
    private double weight;

    /**
     * 外部类的构造器
     */
    public Cow(){}

    /**
     * 外部类的构造器
     * @param weight
     */
    public Cow(double weight){
        this.weight = weight;
    }

    /**
     * 定义一个非静态内部类
     */
    class Cowleg
    {
        /**
         * 非静态内部类的实例变量,长
         */
        private double length;
        /**
         * 非静态内部类的实例变量,颜色
         */
        private String color;



        /**
         * 内部类的构造器
         */
        public Cowleg(){}

        /**
         * 内部类的构造器
         */
        public Cowleg(double length, String color)
        {
            this.length = length;
            this.color = color;
        }

        /**
         * getLength
         * @return double
         */
        public double getLength() {
            return length;
        }


        /**
         * setLength
         * @param length
         */
        public void setLength(double length) {
            this.length = length;
        }

        /**
         * getColor
         * @return
         */
        public String getColor() {
            return color;
        }

        /**
         * setColor
         * @param color
         */
        public void setColor(String color) {
            this.color = color;
        }


        /**
         * 非静态内部类的实例方法
         */
        public void info()
        {
            System.out.println("当前牛腿的颜色是:" + color + "高:" + length);
            // 直接访问外部类的private修饰的成员变量
            System.out.println("本牛腿所在奶牛重:"  + weight);
        }
    }

    /**
     * 测试方法
     */
    public void test()
    {
        Cowleg cl = new Cowleg(1.12, "黑白相间");
        cl.info();
    }

    /**
     * 执行
     * @param args
     */
    public static void main(String[] args){
        Cow cow = new Cow(378.9);
        cow.test();
    }
}

在内部类里,有下面的代码:

// 直接访问外部类的private修饰的成员变量
System.out.println("本牛腿所在奶牛重:"  + weight);

在CowLeg类的方法内直接访问其外部类的private实例变量,这是因为在非静态内部类对象里,保存了一个它所寄生的外部类对象的引用(当调用非静态内部类的实例时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例里)。如下图所示:
这里写图片描述
不同类型的变量的访问方式:

/**
 * DiscernVariable class
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class DiscernVariable {
    /**
     * 外部类的实例变量
     */
    private String prop = "外部类的实例变量";

    /**
     * InClass class
     *
     * @author shuaige
     * @date 2018/01/23
     */
    private class InClass
    {
        /**
         * 内部类的实例变量
         */
        private String prop = "内部类的实例变量";
        public void info()
        {
            /**
             * 局部变量
             */
            String prop = "局部变量";
            // 通过外部类类名.this.varName 访问外部类实例变量
            System.out.println("外部类的实例变量值:" + DiscernVariable.this.prop);
            // 通过this.varName访问内部类实例的变量
            System.out.println("内部类的实例变量值:" + this.prop);
            // 直接访问局部变量
            System.out.println("局部变量的值:" +prop);
        }
    }

    /**
     * 测试方法
     */
    public void test()
    {
        InClass in = new InClass();
        in.info();
    }

    /**
     * 执行方法
     * @param args
     */
    static public  void main(String[] args)
    {
        new DiscernVariable().test();
    }
}

如果需要从外部类访问内部类里的细节,代码如下:

/**
 * Outer class
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class Outer {
    private int outProp = 9;
    class Inner
    {
        private int inProp = 5;
        public void acessOuterProp()
        {
            // 非静态内部类可以直接访问外部类的private成员变量
            System.out.println("外部类的outProp值:" + outProp);
        }
    }

    public void accessInnerProp()
    {
        // 外部类不能直接访问非静态内部类的实例变量
        // 下面代码出现编译错误
        // System.out.println("内部类的inProp值:" + inProp);
        // 如需访问内部类的实例变量,必须显示创建内部类对象
        System.out.println("内部类的inProp值:" + new Inner().inProp);
    }

    /**
     * 执行类
     * @param args
     */
    public static void main(String[] args)
    {
        Outer out = new Outer();
        out.accessInnerProp();
    }

}

在静态方法里无法访问作为实例类成员的内部类,示例代码如下:

/**
 * StaticTest class
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class StaticTest {
    /**
     * 内部类
     */
    private class In{}

    /**
     * 执行方法
     * @param args
     */
    public static void main(String[] args)
    {
        // 下面代码引发编译异常,因为静态成员(main()方法)
        // 无法访问非静态成员(In类)
        new In();
    }
}

Java不允许在非静态内部类里定义静态成员,代码如下:

/**
 * InnerNoStatic class
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class InnerNoStatic {
    private class InnerClass
    {
        /**
         * 下面三个静态声明都将引发如下编译错误:
         * 非静态内部类不能有静态声明
         */

        /**
         * 静态初始化块
         */
        static
        {
            System.out.println("--------");
        }

        /**
         * 静态成员变量inProp
         */
        private static int inProp;

        /**
         * 静态方法test
         */
        private static void test(){}
    }
}

静态内部类

如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类或静态内部类。
静态内部类只能访问外部类的静态成员,代码如下:

/**
 * StaticInnerClassTest class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class StaticInnerClassTest {
    /**
     * 实例变量
     */
    private int prop1 = 5;
    /**
     * 静态变量
     */
    private static int prop2 = 9;
    /**
     * StaticInnerClass class
     *
     * @author shuaige
     * @date 2018/01/25
     */
    static class StaticInnerClass
    {
        /**
         * 可以包含静态成员变量
         */
        private static int prop2;
        static public void accessOuterProp()
        {
            // 下面代码出现错误
            // 静态内部类无法访问外部类的实例变量
            // System.out.println(prop1);
            // 但可以访问静态变量
            System.out.println(StaticInnerClassTest.prop2);
        }
    }
}

外部类如何访问静态内部类:

/**
 * AccessStaticInnerClass class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class AccessStaticInnerClass {

    /**
     * StaticInnerClass class
     *
     * @author shuaige
     * @date 2018/01/25
     */
    static class StaticInnerClass
    {
        /**
         * 静态成员变量
         */
        private static int prop1 = 5;
        /**
         * 非静态成员变量
         */
        private int prop2 = 9;
    }

    /**
     * 变量测试方法
     */
    public void accessInnerProp()
    {
        // 不能直接访问静态内部类的成员变量
        // System.out.println(prop1);
        // 访问静态成员
        System.out.println(StaticInnerClass.prop1);
        // 访问实例成员
        System.out.println(new StaticInnerClass().prop2);
    }
}

使用内部类

在外部类内部使用内部类

不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,因为静态成员不能访问非静态成员。

在外部类以外使用非静态内部类

内部类的权限列表:
1. 省略访问控制符的内部类,只能被与外部类处于同一个包中的其他类所访问。
2. 使用protected修饰的内部类,可被与外部类处于同一个包的其他类和外部类的子类所访问。
3. 使用public修饰符的内部类,可以在任何地方被访问。
4. 使用private修饰符的内部类,只能在外部类中使用。
在外部类以外的地方创建非静态内部类的对象,并把它赋给非静态内部类类型的变量,代码如下:
定义含有内部类的外部类

/**
 * Out class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class Out {
    /**
     * 定义一个内部类,不使用访问控制符,
     */
   final class In
    {
        /**
         *
         * @param msg
         */
        public In(String msg)
        {
            System.out.println(msg);
        }
    }
}

调用:

/**
 * CreateInnerInstance class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class CreateInnerInstance {
    public static void main(String[] args)
    {
        /**
         * 使用外部类访问内部类
         */
        Out.In in = new Out().new In("测试信息");
        /*
          上面的代码可以可以改为如下三行代码
          使用OutterClass.InnerClass的形式定义内部类变量
           Out.In in;
           创建外部类实例,非静态内部类实例将寄生在该实例中
           Out out = new Out();
           in  = out.new In("测试信息");
         */
    }
}

当创建一个子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象。下面程序定义了一个子类继承了Out类的非静态内部类In类:
简单来说就是扩展内部静态类

/**
 * SubClass class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class SubClass extends Out.In{
    /**
     * 显示定义SubClass的构造器
     * @param out
     */
    public SubClass(Out out) {
        // 通过传入的Out对象显式调用In的构造器
        // 非静态内部类In类的构造器必须使用外部类对象来调用
        // out代表外部类对象
        // super代表调用In类的构造器
        // 所以才能调用Out类的子类In类的构造器
        out.super("hello");
    }
}

在外部类以外使用静态内部类

定义一个带静态内部类的类

package com.qunar;
/**
 * StaticOut class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class StaticOut {
    // 定义一个静态内部类,不使用访问控制符
    // 即同一个包中的其他类可以访问该内部类
    static class StaticIn
    {
        public StaticIn()
        {
            System.out.println("静态内部类的构造器");
        }
    }
}

调用的时候会调用构造器

/**
 * CreateStaticInnerInstance class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class CreateStaticInnerInstance {
    public static void main(String[] args)
    {
        StaticOut.StaticIn in = new StaticOut.StaticIn();
        /*
        上面代码可改为如下两行代码
        使用OuterClass.InnerClass的形式定义内部类变量
        StaticOut.StaticIn in;
        通过new来调用内部类构造器创建静态内部类实例
        in = new StaticOut.StaticIn();
         */
    }
}

局部内部类

局部内部类的使用不多,在实际开发中不建议使用,示例代码如下。

package com.qunar;
/**
 * LocalInnerClass class
 *
 * @author shuaige
 * @date 2018/01/25
 */
public class LocalInnerClass {
    public static void main(String[] args)
    {
        /**
         * 定义局部内部类
         */
        class InnerBase
        {
            int a;
        }

        /**
         * 定义局部内部类的子类
         */

        class InnerSub extends InnerBase
        {
            int b;
        }

        // 创建局部内部类的对象
        InnerSub is = new InnerSub();
        is.a = 5;
        is.b = 8;
        System.out.println("InnerSub对象的a和b实例变量是:" + is.a + "," + is.b);
    }
}

匿名内部类

匿名内部类适合创建只需要一次使用的类,匿名内部类的语法格式如下:
new 实现类() | 父类构造器(实参列表)
{
// 匿名内部类的类体部分
}

实现一个接口的匿名内部类,必须实现它的全部方法

package com.qunar;
/**
 * AnonymousTest class
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class AnonymousTest {
    /**
     * 测试方法,此处因为使用了ProductClass接口
     * 所以需要传入一个匿名类,来实现接口里面所有的方法
     * @param p
     */
    public void test(ProductClass p)
    {
        System.out.println("购买了一个" + p.getName() + ",花掉了" + p.getPrice());
    }


    public static void main(String[] args)
    {
        AnonymousTest ta = new AnonymousTest();
        // 调用test方法时,需要传入一个Product参数
        // 此处传入其匿名实现类的实例
        ta.test(new ProductClass() {

            /**
             * 初始化块
             */
            {
                System.out.println("这里是初始化块");
            }

            /**
             * 获取价格
             * @return
             */
            @Override
            public double getPrice() {
                return 1100;
            }

            /**
             * 获取名称
             * @return
             */
            @Override
            public String getName() {
                return "iphone-x";
            }
        });
    }
}

实现一个抽象类,代码如下:
抽象类

/**
 * Device abstract class
 *
 * @author shuaige
 * @date 2018/01/23
 */
abstract class Device {
    /**
     * 名称
     */
    private String name;

    /**
     * 抽象方法getPrice
     * @return
     */
    public abstract double getPrice();

    /**
     * 无参构造器
     */
    public Device(){}

    /**
     * 带参数构造器
     * @param name
     */
    public Device(String name)
    {
        this.name = name;
    }

    /**
     * 获取name
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     * 设置name
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
}

实现示例:

/**
 * AnonymousInner  class
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class AnonymousInner {
    public void test(Device d)
    {
        System.out.println("购买了一个" + d.getName() + ",花掉了" + d.getPrice());
    }

    public static void main(String[] args)
    {
        AnonymousInner ai = new AnonymousInner();
        // 调用有参数的构造器创建Device匿名实现类的对象
        ai.test(new Device("电子示波器") {
            /**
             * 获取价格
             * @return
             */
            @Override
            public double getPrice() {
                return 67.8;
            }
        });

        // 调用无参数的构造器穿件Device匿名实现类的对象
        Device d = new Device() {
            // 初始化块
            {
                System.out.println("匿名内部类的初始化块...");
            }

            /**
             * 获取价格
             * @return
             */
            @Override
            public double getPrice() {
                return 56.2;
            }

            /**
             * 获取名称
             * @return
             */
            @Override
            public String getName() {
                return "键盘";
            }
        };
        ai.test(d);

    }
}

在Java 8之前,Java要求被局部内部类、匿名内部类访问的局部变量必须使用final修饰,Java 8更加智能,如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰,代码如下:
接口

/**
 * InterfaceAA  interface
 *
 * @author shuaige
 * @date 2018/01/23
 */
public interface InterfaceAa {
    /**
     * test
     */
    void test();
}

传入匿名类实现

/**
 * ATest  class
 *
 * @author shuaige
 * @date 2018/01/23
 */
public class ATest {
    public static void main(String[] args){
        int age = 8;
        InterfaceAa a = new InterfaceAa() {
            @Override
            public void test() {
                System.out.println(age);
                // 如果在这里再次改变age变量,将会报错
                a = 100;
            }
        };
        // 传入匿名类
        a.test();
    }
}

Java 8将这个功能称为“effectively final”,它的意思是对于被匿名内部类访问的局部变量,可以用final修饰,也可以不用final修饰,但必须按照有final修饰的方式使用,也就是一次赋值后,以后不能重新赋值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值