Java学习之内部类

内部类顾名思义,就是在类中定义的一个类。内部类可以分为成员内部类、局部内部类以及匿名类,不过在介绍之前,我们先来说一下内部类到底有啥用。
《Think in java》中提到:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类而言都没有什么影响。
传统意义上的继承,对于具体的类而言只能实现单继承,要想多继承,则只能继承多接口,但是对于接口的实现,一个类只能实现一次接口,如果想要对接口进行不同方式的实现,那么就可以利用到内部类。多个内部类继承同一接口,从而可以达到在一个类中做出多个不同的响应事件。同时,多个内部类可以继承多个不同的实体类,从而进一步完善了多重继承的解决方案。

成员内部类

作为成员内部类,有以下几个特性要说明一下:

  1. 内部类可以随意使用外部类的成员方法及成员变量。
  2. 外部类不能直接修改内部类的成员方法或者成员变量,内部类的方法和属性不能被外部类使用,只有在内部类的范围之内是可知的。
  3. 内部类实例一定要绑在外部类实例上,内部类的对象实例化必须在外部类或者外部类的非静态方法中实现。
package com.java11.dyp;

public class Demo {
    int i=1;
    inClass inc=new inClass();
    public void outf(){
        System.out.println("这是外部类方法outf()");
        inc.inf();
    }
    class inClass{
        int y=0;            
        inClass(){
            System.out.println("这是内部类构造函数的第"+i+"次调用");
            i++;
        }
        public void inf(){
            System.out.println("修改外部类的属性值i="+i);
        }
    }
    public inClass doit(){
        inc.y=4;            //修改内部类的属性
        return new inClass();   
    }
    public static void main(String[] args) {
        Demo demo=new Demo();
        Demo.inClass in2=demo.doit();   //使用外部类的非静态方法实现实例化
        Demo.inClass in3=demo.new inClass();   //使用外部类对象调用构造函数
    }
}

下面举一个例子来说明内部不累向上转型为接口,实现同一个接口中接口方法的不同实现方式。

package com.java11.dyp;

interface OutInterface{ 
    public void f();
}

class OuterClass{
    private class InterClass1 implements OutInterface{
        public InterClass1(){
            System.out.println("InterClass1进行了实例化");
        }
        public void f(){
            System.out.println("这是InterClass1调用的f()方法");
        }
    }
    private class InterClass2 implements OutInterface{
        public void f(){
            System.out.println("这是InterClass2调用的f()方法");
        }
        public InterClass2(){
            System.out.println("InterClass2进行了实例化");
        }
    }
    public OutInterface doit1(){
        return new InterClass1();
    }
    public OutInterface doit2(){
        return new InterClass2();
    }
}

public class Demo {
    public static void main(String[] args) {
        OuterClass out=new OuterClass();
        OutInterface oif=out.doit1();
        oif.f();      //实现interClass1中的f()
        oif=out.doit2();
        oif.f();      //实现interClass2中的f()
    }
}

这里对上例的子类解释一下:该例中子类被定义为private权限,那么就只能由外部类进行访问,其他类不能对子类进行操作。但是由于外部类中提供了doit()方法,能够返回一个接口的引用,如 代码中定义了oif来接收这个返回值,oif能够使用向上转型的特点来调用子类中对接口重写的f()方法,这样不仅很好的隐藏了子类的具体内容和f()方法的细节,同时又能够满足对接口方法的使用,这也是内部类的用途之一。

当内部类和外部类的属性重名时,this关键词在内部类中的使用

public class Demo {
    private int x=2;
    private class inner{
        private int x=9;
        public void doit(int x){
            System.out.println("参数x="+(++x));
            System.out.println("子类属性x="+(++this.x));
            System.out.println("外部类属性x="+(++Demo.this.x));
        }
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        inner in=demo.new inner();
        in.doit(4)
    }
}
参数x=5
子类属性x=10
外部类属性x=3

在上述代码中,子类的this可以通过 this.xxx 来使用,区别于形参和外部类,外部类的属性则可以通过 外部类名.this.XXX 的方式来调用。

局部类

interface OutInterface2{

}

public class Demo {
    public OutInterface2 doit(final String x){
        class InnerClass2 implements OutInterface2{
            public InnerClass2(String s){
                s=x;
                System.out.println(s);
            }
        }
        return new InnerClass2("doit");
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.doit("Hello World!");
    }
}

作为局部内部类时,有几点需要注意:
1、内部类是定义该类的方法的一部分,并非外部类的一部分,所以在doit()方法的外部不能访问该内部类。
2、内部类可以访问当前代码块的常量以及此外部类的所有成员。

匿名类

先看下例:

interface OutInterface2{
    public int getValue();
}

public class Demo {
    public OutInterface2 doit(){
        return new OutInterface2(){
            private int i=0;
            public int getValue(){
                return i;
            }
        };
    }
    public static void main(String[] args) {
        Demo demo = new Demo();
        OutInterface2 out=demo.doit();
        System.out.println("i="+out.getValue());

    }
}

结果输出:
i=0
可能初看会感觉莫名其妙,但是确实是被Java编译器认可的。在doit()方法内部首先返回一个OutInterface2的引用,然后在return语句中插入一个定义内部类的代码,由于该类没有名称,所以将该类称为匿名内部类。这种类的作用就是创建一个实现于OutInterface2接口的匿名类的对象。
这里需要说一下,内部类定义结束后,需要加分号标识,这个分号并不是代表内部类结束的标志,而是代表创建OutInterface2引用表达式的标识。

最后在说一些内部类的其他知识点:

静态内部类

内部类前面加static就变成了静态内部类。它有以下特点:

  1. 静态内部类中可以声明static成员,但是在非静态内部类中不可以申明静态成员。
  2. 静态内部类不能够使用外部类的非静态成员,所以静态内部类在程序开发中比较少见。
  3. 如果创建静态内部类对象,不需要其外部类的对象。
    下面举个代码示例:
public class StaticInnerClass{
    int x=100;
    static class Inner{
        void doitInner(){
                //System.out.println("外部类"+x);    //调用外部类的成员变量x
        }
    }
}

在上述代码中,println是无法实现的,因为不能访问外部类的非静态成员。

内部类的继承

内部类的继承相对与一般类的继承会比较复杂,要设置专门的语法,这里也只做格式介绍:

public class OutputInnerClass extends ClassA.ClassB{
    public OutputInnerClass(Class a){
        a.super();
    }
}
class ClassA{
    class ClassB{

    }
}

某个类继承内部类时,必须硬性的给予这个类一个含参的构造方法,并且该构造方法的参数需为继承内部类的外部类的引用,同时在构造方法中使用a.super()语句,这样才为继承提供了必要的对象引用。

猜你喜欢

转载自blog.csdn.net/dypnlw/article/details/79408902