Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

java基础(十一)之内部类

2017-06-10
nivelle

内部类

内部类既可以访问自身的数据域,也可以访问创建他的外围类对象的数据域。

使用OuterClass.this引用外围类。使用outerObject.new InnerClass()构造内部类的对象。在外围类的作用域之外,使用OuterClass.InnerClass引用内部类。

局部类可以访问局部变量,但是这些局部变量必须被声明为final。 当创建一个对象时,编译器必须检测对局部变量的访问,为每一个局部变量创建相应的数据域,并将局部变量拷贝到构造器中,以便将这些数据域初始化为局部变量的副本。为了使得局部变量与其拷贝保持一致,局部变量设置为final,但可以使用一个长度为1的数组使得可以在内部类修改这个数据。

final int[] counter = new int[1];
    for(int i=0; i<dates.length; i++)
        dates[i] = new Date()
            {
                public int compareTo(Date other)
                {
                    counter[0]++;
                    return super.compareTo(other);
                }
            };

将一个类的定义放在另一个类的定义内部,这就是内部类.


package test;


public class Parcel2 {

    class Contents {
    
        private int i = 11;

        public int value() {
        
            return i;
            
        }
    }

    class Destination {
    
        private String label;

        Destination(String whereTo) {
        
            label = whereTo;
            
        }

        String readLable() {
        
            return label;
            
        }
        
    }

    public Destination to(String s) {
    
        return new Destination(s);
        
    }

    public Contents contents() {
    
        return new Contents();
        
    }

    public void ship(String dest) {
    
        Contents c = contents();
        
        Destination d = to(dest);
        
        System.out.print(d.readLable());
        
    }

    public static void main(String[] args) {
    
        Parcel2 p = new Parcel2();
        
        p.ship("Tasmania");
        
        Parcel2 q = new Parcel2();
        
        Parcel2.Contents c = q.contents();
        
        Parcel2.Destination d = q.to("Borneo");
        
    }
}


如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须像在main()方法那样,具体指明这个对象的类型:OuterClassName.InnerClassName.

链接到外部类

当生成一个内部类的对象时,此时对象与制造它的外围对象之间有了一种联系,所以它能访问器外围对象的所有成员,而不需要任何特殊条件

内部类自动拥有对其外围类所有成员的访问权,当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向外围类对象的引用.然后,在访问此外围类的成位员时,就是用哪个引用来选择外围类的成员.

使用.this与.new

如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this.这样产生的引用自动地具有正确的类型,这一点在编译期就被知晓并受到检查,因此没有任何运行时开销.

package test;

/**
 * Created by nivelle on 2017/7/30.
 */
public class DotThis {

    void f() {

        System.out.print("dothis.f()");

    }

    public class Inner {

        public DotThis outer() {

            return DotThis.this;

            //a plain "this" would be inner's "this"

        }
    }

    public Inner inner() {

        return new Inner();

    }

    public static void main(String args[]) {

        DotThis dtt = new DotThis();

        DotThis.Inner dti = dtt.inner();

        dti.outer().f();

    }
}


有时可能想知道某些其他对象,去创建其某个内部类对象.要实现此目的,必须在new 表达式中提供对其他外部类对象的引用,这是需要使用.new语法.

public class DotNew{
  
  public class Inner{}

  public static void main(String args[]){

    DotNew dn = new DotNew();

    DotNew.Inner dni = dn.new Inner();
  }
}


要想创建内部类的对象,必须使用外部类的对象来创建该内部类对象,就像如上程序.这也解决了内部类名字作用域的问题,因此不必也不能声明:dn.new DontNew.Inner();

在拥有外部类对象之前是不能创建内部类对象的,这是因为内部类对象会连接到创建它的外部类对象上,但是,若创建的是嵌套类(静态内部类),那么就不需要对外部类对象的引用.


public class Parcel3 {

    class Contents{
        private int i=11;
        public int value(){return  i;}
    }

    class Destination{
        private  String label;
        Destination(String whereTo){
            this.label = whereTo;
        }
        String readLabel(){return  label;}
    }

    public static  void main(String args[]){
        Parcel3 p =new Parcel3();
        Parcel3.Contents c = p.new Contents();
        Parcel3.Destination d = p.new Destination("tasmania");
    }
}



在方法和作用域内的内部类

可以在一个方法里面或者任意作用域内定义内部类,这么做有两个理由:

  • 你实现了某类型的接口,于是可以创建并返回对其的引用

  • 你要解决一个复杂问题,想创建一个辅助类,但又不希望这个类是公共可用的

局部内部类:


public class Parcel5 {
    public Destination destination(String s) {
        class PDestination implements Destination {
            private String label;

            private PDestination(String whereTo) {
                label = whereTo;
            }

            public String realdLabel() {
                return label;
            }
        }
        return new PDestination(s);
    }

    public static void main(String args[]) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("nivelle");
    }
}


PDestination 类是destination()方法的一部分,而不是Parcel5的一部分,所在destination()方法之外不能访问PDestination. return 向上转型为Destination 的引用,它是PDestination的基类.

内部类被嵌入在if()语句内,这并不是说类的创建是有条件的,它其实与别的类一起编译过了,然而在内部类定义外,它是不可以的,除此之外,它与普通的类一样.

匿名内部类


public class Parcel7 {
    public Contents contents() {
        return new Contents() {
            private int i = 11;

            @Override
            public int value() {
                return i;
            }
        };
    }

    public static void main(String args[]) {
        Parcel7 p = new Parcel7();
        Contents c = p.contents();
        System.out.println(c.value());
    }
}


contents()方法将返回值的生成与表示这个返回值的类定义结合在一起.这个类是匿名的,他没有名字.

创建一个继承自Contents的匿名类对象,通过new表达式返回的引用自动向上转型为Contents的引用.

在匿名内部类末尾的的分号,并不是用来标记此内部类结束的.实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了匿名内部类罢了.

public class Parcel9 {
    public Destination destination(final String dest) {
        return new Destination() {
            private String label = dest;

            @Override
            public String realdLabel() {
                return label;
            }
        };
    }

    public static void main(String args[]) {
        Parcel9 p = new Parcel9();
        Destination d = p.destination("nivelle");
        System.out.println(d.realdLabel());
    }
}

如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final.

嵌套类

如果不需要内部类与其外围类对象之间有联系,那么可以将内部类申明为static.称为嵌套类.普通内部类对象隐式地保存了一个引用,指向创建它的外围类对象.然而,当内部类是static的时,就不这样来了.嵌套类意味着:

  • 要创建嵌套类的对象,并不需要其外围类的对象
  • 不能从嵌套类的对象中访问非静态的外围类对象.

普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据,也不能包含嵌套类.但嵌套类可以包含这些:

public class Parcel11 {
    private static  class ParcelContents implements  Contents{
        private  int i =11;
        public int value(){return  i;}
    }

    protected  static  class ParcelDestination implements  Destination{
        private String lable;
        private  ParcelDestination(String whereTo){
            lable = whereTo;
        }
        public String realdLabel(){return  lable;}
        public static  void f(){}
        static  int x =10;
        static  class AnotherLevel{
            public  static  void f(){}
            static  int x =10;
        }
    }
    public static  Destination destination(String s){
        return  new ParcelDestination(s);
    }

    public static  Contents contents(){
        return  new ParcelContents();
    }

    public static  void main(String args[]){
        Contents c= contents();
        Destination d = destination("nivelle");
    }
}

接口内部的类

放到接口中的任何类都自动地是public和static的.因为类是static的,只是将嵌套类置于接口的命名空间内,这并不违反接口的原则.甚至可以在内部类中实现其外围接口.

public interface ClassInInerface {
    void howdy();
    class Test implements  ClassInInerface{
        public void howdy(){
            System.out.println("nivelle");
        }
        public static  void main(String args[]){
            new  Test().howdy();
        }
    }
}


如果想创建公共代码,使得他们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会显得很方便.

内部类应用场景

  • 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整.

  • 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

  • 方便编写事件驱动程序

  • 方便编写线程代码


评论