Java内部类

Java内部类_第1张图片
内部类.png

内部类

  • 内部类作为外部类的成员,可以使用人已访问控制符修饰。
  • 外部类的上一级程序单元是包,所以只有两个作用域:同一个包内、任意位置。因此需要两种权限:包访问权限、公开访问权限,对应省略访问修饰控制符和public访问控制符。因此如果一个外部类不使用任何访问控制符修饰,则只能被同一个包中其他类访问。
  • 内部类的上一程序单元是外部类,所以具有4个作用域:同一个类、同一个包、父子类、和任意位置,可以有4种访问权限控制。
  • 成员内部类的class文件格式为:OuterClass$InnerClass.class
  • 外部类成员变量、内部类成员变量与内部类里方法的局部变量同名,则可以通过使用this、外部类类名.this作为限定区分。

非静态内部类

定义:一个类放在另一个类的内部定义。
大部分时候都被作为成员内部类定义,而不是局部内部类。
可以直接访问外部类的private成员,反过来不成立

  • 因为在非静态内部类对象里,保存了一个它寄存的外部类对象的引用。

  • 非静态内部类的方法内访问某个变量时:
    1.系统优先在方法内查找是否存在该名字的局部变量,如果存在即使用;
    2.到该方法所在的内部类中查找是否存在改名字的成员变量,如果存在即使用;
    3.到内部类所在的外部类中查找,如不存在出现编译错误:提示找不到该变量。

  • 非静态内部类成员只有在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类成员,必须显示创建非静态内部类对象来进行访问。

  • 非静态内部类里不能有静态方法、静态Field、静态初始化块。


静态内部类

包括静态成员、非静态成员。

  • 静态成员不能访问非静态成员。
  • 静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
  • 静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。(静态内部类是外部类的类相关,不是外部类的对象相关。静态内部类对象不是寄存在外部类对象里的,而是寄存在外部类的类本身中。当静态内部类对象存在时,并不存在一个被他寄存的外部类,静态内部类对象里只有对外部类的类引用,没有对外部类的对象应用。)
  • 外部类不能直接访问静态内部类成员,可以使用静态内部类的雷鸣作为调用者来访问静态内部类的类成员。
public class A{
    static class B{
        private static int pro1 = 5;
        private int pro2 = 9;
    }
    public void accessInnerB(){
        System.out.println(B.pro1);
            System.out.println(new B().pro2);
  
    }
}

内部类的使用

(1)外部类内部使用内部类

不要再外部类的静态成员中使用非静态内部类,静态成员不能访问非静态成员。

(2)外部类以外使用非静态内部类

  • 内部类不能使用private访问控制权限,private修饰的内部类只能在外部类的内部使用。
  • 省略访问控制符的内部类,只能被与外部类处于同一个包中的其他类所访问。
  • 使用protected修饰的内部类,可悲与外部类处于同一个包中的其他类和外部类的子类所访问。
  • 使用public修饰的内部类,可以在任何地方被访问。

(3)在外部类以外使用静态内部类

语法:new OuterClass.InnerConstructor()


局部内部类

定义:把一个内部类放在方法中定义

  • 上一级程序单元都为方法,不是类,所以,所有的局部成员都不能使用static修饰。
  • 局部成员的作用域实在所在的方法,其他程序单元永远也不可能访问另一个方法中的局部成员,所以所有局部成员都不能使用访问控制符修饰。

生成的class文件命名格式为:
OuterClass$NInnerClass.class.

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接口名后的括号不能传入参数值。
  • 通过继承父类创建匿名内部类时,将拥有和父类相似的构造器,既拥有相同形参列表。
  • 如果匿名内部类需要访问外部类的局部变量,必须用final修饰符来修饰外部类的局部变量。
  • 通过接口创建匿名内部类
interface Product
{
    public double getPrice();
    public String getName();
}
public class AnonymousTest
{
    public void test(Product p)
    {
        System.out.println("购买了一个" + p.getName()
            + ",花掉了" + p.getPrice());
    }
    public static void main(String[] args)
    {
        AnonymousTest ta = new AnonymousTest();
        // 调用test()方法时,需要传入一个Product参数,
        // 此处传入其匿名实现类的实例
        ta.test(new Product()
        {
            public double getPrice()
            {
                return 567.8;
            }
            public String getName()
            {
                return "AGP显卡";
            }
        });
    }
}
  • 继承父类创建匿名内部类
abstract class Device
{
    private String name;
    public abstract double getPrice();
    public Device(){}
    public Device(String name)
    {
        this.name = name;
    }
    // 此处省略了name的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }
}
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("电子示波器")
        {
            public double getPrice()
            {
                return 67.8;
            }
        });
        // 调用无参数的构造器创建Device匿名实现类的对象
        Device d = new Device()
        {
            // 初始化块
            {
                System.out.println("匿名内部类的初始化块...");
            }
            // 实现抽象方法
            public double getPrice()
            {
                return 56.2;
            }
            // 重写父类的实例方法
            public String getName()
            {
                return "键盘";
            }
        };
        ai.test(d);
    }
}

闭包&回掉

闭包:能被调用的对象,保存了它的作用域信息。
回掉:某个方法一旦获得了内部类对象的引用后,就可以再合适的时候反过来去调用外部类实例的方法。(允许客户类通过内部类引用来调用其外部类的方法)

  • 接口
interface Teachable
{
    void work();
}
  • 程序员
public class Programmer
{
    private String name;
    //Programmer类的两个构造器
    public Programmer(){}
    public Programmer(String name)
    {
        this.name = name;
    }
    //此处省略了name的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }
    public void work()
    {
        System.out.println(name + "在灯下认真敲键盘...");
    }
}
public class TeachableProgrammer extends Programmer
{
    public TeachableProgrammer(){}
    public TeachableProgrammer(String name)
    {
        super(name);
    }
    //教学工作依然由TeachableProgrammer类定义
    private void teach()
    {
        System.out.println(getName() + "教师在讲台上讲解...");
    }
    private class Closure implements Teachable
    {
        /*
        非静态内部类回调外部类实现work方法,非静态内部类引用的作用仅仅是
        向客户类提供一个回调外部类的途径
        */
        public void work()
        {
            teach();
        }
    }
    //返回一个非静态内部类引用,允许外部类通过该非静态内部类引用来回调外部类的方法
    public Teachable getCallbackReference()
    {
        return new Closure();
    }
}
public class TeachableProgrammerTest
{
    public static void main(String[] args)
    {
        TeachableProgrammer tp = new TeachableProgrammer("李刚");
        //直接调用TeachableProgrammer类从Programmer类继承到的work方法
        tp.work();
        //表面上调用的是Closure的work方法,
        //实际上是回调TeachableProgrammer的teach方法
        tp.getCallbackReference().work();
    }
}


——整理自疯狂Java

你可能感兴趣的:(Java内部类)