java基础学习(九)内部类

本文介绍了Java中的内部类,包括静态和非静态内部类,局部内部类以及匿名内部类的使用和特性。内部类可以访问其外围类的所有成员,无需特殊条件。静态内部类无需外围类对象即可创建,而非静态内部类需要先创建外围类对象。同时探讨了内部类的继承、闭包概念以及如何在外部类内外使用和创建内部类对象。

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

创建内部类,将类的定义置于外围类的里面。在静态和非静态方法中,创建内部类对象的引用的方式是不一样的:

public class Parcel {
    //定义内部类
    class Contents{
        private int i=11;
        public int value(){return i;}
    }

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

    //外部类中,返回一个指向内部类的引用
    public  Contents contents(){ return new Contents();}
    public Destination to(String s){return new Destination(s);}

    //创建内部类的对象
    public void ship(String str){
        Contents c=contents();
        Destination d=to(str);
        System.out.println(d.readLabel());
    }

    //创建内部类的对象
    public static void main(String[] args) {
        Parcel p=new Parcel();
        //非静态方法之外的创建内部类对象,要具体指明这个对象的类型:OuterClassName.InnerClassName
        Parcel.Contents c=p.contents();
        Parcel.Destination d=p.to("test");

        Contents c1=p.contents();
        //不能直接调用contents()方法,因为不是静态的
       // Parcel.Contents c2=contents();
       // Contents c3=contents();
    }
}

当生产一个内部类的对象时,此对象与制造它的外围对象之间就有一种联系,它能访问其外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。

public class Sequence {
    private Object[] items;
    private int next=0;
    private int j=99999;
    public Sequence(int size){items=new Object[size];}
    public void add(Object x){
        if(next<items.length){
            items[next++]=x;
        }
    }

    //内部类 ,实现接口
    private class SequenceSelector implements Selector{
        private int i=0;
        //访问外围类中的私有成员 items
        //内部类可以访问外围类的方法和字段
        public boolean end(){ return i==items.length;}
        public Object current(){ return items[i];}
        public void next() { if(i<items.length) i++;}
        public int getJ(){return j;}

    }

    //返回内部类的实例对象
    public Selector selector(){ return new SequenceSelector();}

    public static void main(String[] args) {
        Sequence sequence=new Sequence(10);
        for(int i=0;i<10;i++){
            sequence.add(Integer.toString(i));
        }
        //当外围类的对象创建了一个内部类对象,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用
        //内部类对象只能在与其外围类的对象相关联的情况下才能被创建
        //创建内部类的引用,使用接口的形式
        Selector selector=sequence.selector();
        //也可以这样创建内部类对象
        Sequence.SequenceSelector sequenceSelector=(SequenceSelector) sequence.selector();
        //也这样产生内部类的实例
        SequenceSelector se=(SequenceSelector) sequence.selector();

        //内部类实例对象并不能直接访问外围类中的成员,只能通过自己的成员间接访问外部类的成员
        //se.j;
        se.getJ();

    }

}

interface Selector{
    boolean end();
    Object current();
    void next();
}

在内部类中也可以返回外围类的引用:

public class DotThis {
    void f(){
        System.out.println("this is a test");
    }
    public class Inner{
        //产生外围类的对象
        public DotThis outer(){
        //外部类名加上 .this
            return DotThis.this;
        }
    }
    public Inner inner(){
        return new Inner();
    }

    public static void main(String[] args) {
        DotThis dt=new DotThis();
        DotThis.Inner dti=dt.inner();
        dti.outer().f();
    }
}

也可以告知外部类,创建其某个内部类对象:

public class DotThis {
    void f(){
        System.out.println("this is a test");
    }
    public class Inner{}


    public static void main(String[] args) {
        DotThis dt=new DotThis();
        //告知外部类dt,创建其内部类。
        DotThis.Inner dti=dt.new Inner();
    }
}

在一个方法中定义内部类,该类被称为局部内部类:

public class Parcel5 {
    //定义一个方法
    public Destination destination(String s){
        //定义一个内部类,在方法之外不能访问PDestination类
        //即使该方法执行完成,该类还是存在的,其实与别的类一样,都被编译过。
        class PDestination implements Destination{
            private String label;
            private PDestination(String whereTo){
                label=whereTo;
            }
            public String readLabel(){
                return label;
            }
        }

        //返回内部类实例,只能在方法内部访问该类
        return new PDestination(s);
    }

    public static void main(String[] args) {
        Parcel5 p5=new Parcel5();
        Destination d=p5.destination("test");
        //不论构造器是私有private还是公共public,都不能直接创建内部类实例,因为该类不被访问
        //它的作用域是在方法内。
       // Destination d1=new PDestination("test");
    }
}

interface Destination{
    String readLabel();
}

在任意的作用域内定义一个内部类:

public class Parcel6 {
    //定义方法
    private void internalTracking(boolean b){
        if(b){
           //定义内部类,不管条件是否为真,该类都会与其他类一起被编译
            class TrackingSlip{
                private String id;
                TrackingSlip(String s){
                    id=s;
                }
                String getSlip(){
                    return id;
                }
            }
            //产生内部类的实例
            TrackingSlip ts=new TrackingSlip("slip");
            String s=ts.getSlip();
        }
        //不能在作用域外创建内部类的实例,也不能访问该内部类
        //TrackingSlip ts1=new TrackingSlip("test");
    }

    public static void main(String[] args) {
        Parcel6 p6=new Parcel6();
        p6.internalTracking(true);
    }
}

匿名内部类
匿名内部类既可以扩展(extends),也可以实现接口(implements),但是不能两者兼备,而且如果要实现接口,也只能实现一个接口。

public class Parcel7 {
    public Contents contents(){
        //返回值与类的定义结合在一起,类是匿名的,没有名字
        //创建一个 继承或实现 Contents类的匿名类的实例对象
        //自动向上转型为对Contents的引用
        return new Contents(){
            private int i=11;
            public int value(){
                return i;
            }
        };    //不要忘记分号,表示return句子的结束
    }

    public static void main(String[] args) {
        Parcel7 p7=new Parcel7();
        Contents contents=p7.contents();
    }
}

interface Contents{
    int value();
}

上面的例子实际上等价如下:

public class Parcel7b {
   //类MyContents实现接口Contents。
    class MyContents implements Contents{
        private int i=11;
        public int value(){
            return i;
        }
    }
    //返回实例对象
    public Contents contents(){
        return new MyContents();
    }

    public static void main(String[] args) {
        Parcel7b p7b=new Parcel7b();
        p7b.contents();
    }

}

interface Contents{
    int value();
}

上面的例子都是使用默认的无参构造器。也可以给基类构造器传递合适的参数。

public class Parcel8 {
    public Wrapping wrapping(int x){
        //向基类传递参数x,调用基类的有参构造器
        return new Wrapping(x){
            public int value(){
                //调用基类中的方法
                return super.value()*66;
            }
        };
    }
}

//基类
class Wrapping{
    private int i;
    public Wrapping(int x){
        i=x;
    }
    public int value(){
        return i;
    }
}

对匿名类中定义的字段进行初始化,因为匿名类没有构造器(根本没有类名),实例初始化的效果等价于构造器:

public class Parcel9 {

    public Destination destination(final String dest){
    //构造器的调用顺序也是先加载基类构造器,再加载匿名类本身的初始化操作(等价于构造器)
        return new Destination() {
            //在匿名类中,使用外部定义的对象dest,编译器会要求其参数引用是fianl的
            //但是如果外部定义的对象是传给基类,而不是在匿名类内部被直接使用,那就可以不加final.
            private String label=dest;
            public String readLabel() {
                return label;
            }
        };
    }

    public static void main(String[] args) {
        Parcel9 p9=new Parcel9();
        Destination d=p9.destination("TEST");
    }
}

使用匿名内部类创建工厂方法

public class Factories {
    public static void main(String[] args) {
        ServiceFactory fact=Implementation.factory;
        Service ser=fact.getService();
        ser.method1();
    }

}

//实现某个服务service的类
class Implementation implements Service{
    //私有构造器
    private Implementation(){}

    public void method1() {
        System.out.println("method1");
    }
    //利用static域,创建单一的工厂对象
    public static ServiceFactory factory=new ServiceFactory() {
        public Service getService() {
            return new Implementation();
        }
    };

}
//服务service接口
interface Service{
    void method1();
}
//工厂类
interface ServiceFactory{
    Service getService();
}

嵌套类
普通的内部类对象隐式地保存了一个指向创建它的外围类对象的引用,因此可以访问外围类的成员。但是如果不需要内部类对象与其外围类对象之间的这种关系,那么可以将内部类声明为static。这被称为嵌套类。
嵌套类意味着:
1、要创建嵌套类的对象,并不需要其外围类的对象
2、不能从嵌套类的对象中访问非静态的外围类对象

嵌套类与普通内部类的区别:普通内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含。

public class Parcel11 {
    //普通内部类
    class ParcelContents implements ContentsTest{
        //不能包含静态域
       // private static int i=11;

        //也不能包含静态方法
       // public static void test(){
       //     System.out.println("test");
       // }

        //更不能包含嵌套类
        //static class Test{}

        public int value() {
            return 0;
        }
    }

    //嵌套类
    static class ParcelDestination implements DestinationTest{
        //嵌套类 既可以包含静态域,静态方法,还包含嵌套类
        static String label="test";
        public static void test(){}
        static class Test{}

        public String read() {
            return null;
        }
    }

    //利用静态方法返回对象
    public static DestinationTest destination(){return new ParcelDestination();}

    public  DestinationTest non_destination(){return new ParcelDestination();}
    //利用非静态方法返回对象
    public ContentsTest contents(){return new ParcelContents();}

    public static void main(String[] args) {
        //没有用到外围的对象,直接通过静态方法返回嵌套类对象的引用
        DestinationTest d=destination();
        //非嵌套类,需要使用外部类对象,静态方法中不能访问非静态方法
        Parcel11 p11=new Parcel11();
        ContentsTest c=p11.contents();
    }

}

interface ContentsTest{
    int value();
}
interface DestinationTest{
    String read();
}

嵌套类在接口中
正常情况下,接口中不能放置任何代码,但是嵌套类可以作为接口的一部分。放到接口中的任何类都自动是public 和static。甚至可以在内部类中实现其外围接口。

public interface ClassInterface {
    void test();
    //在接口中的类自动是public 和static的
    //可以实现外围接口
    class Test implements ClassInterface{
        public void test(){}
    }
}

对于公共代码,可以被接口的所有不同实现所共用,可以使用接口内部的嵌套类。

public interface ClassInterface {
    void testIn();
    //在接口中的类自动是public 和static的
    //可以实现外围接口
    class Test implements ClassInterface{
        private int ii=99;
        public int value(){return  ii;}
        public void testIn(){}
    }
}

class Parcel12 implements ClassInterface{
    public void testIn() {}
    //利用接口中的嵌套类
    int x=new Test().value();
}

一般来说,内部类继承自某个类或实现某个接口,内部类的代码能够操作创建它的外围类的对象,提供了某种进入外围类的窗口。
内部类实现接口与外围类实现接口的区别:每个内部类都能独立地继承自一个(接口地)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响

public class Parcel13 {
    //无论外围类是否继承了接口的实现,内部类都可以独立继承该实现
    class MiniDog extends Dog{

    }
}

interface Animal{
    void song();
}
class Dog implements Animal{
    public void song(){}
}

内部类能有效解决“多重继承”
在一个类中利用两种方式实现两个接口:

//在一个类中实现两个接口
interface A{}
interface B{}
//方式1:直接实现两个接口
class X implements A,B{

}
//方式2:利用内部类,可以实现多个继承
class Y implements A{
    B makeB(){
        return new B(){};
    }
    C makeC(){
        return new C(){};
    }
}

如果拥有的是抽象类或者具体的类,而不是接口,那就只能使用内部类,才能实现多重继承。因为非接口类型都是单继承的。

//具体类和抽象类
class D{}
abstract class E{}

//因为是单继承,只能通过内部类实现多重继承
class Z extends D{
    E makeE(){
        return new E(){};
    }
}

内部类的一些特性:
1、内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立
2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
3、创建内部类对象的时刻并不依赖于外围类对象的创建。
4、内部类不是”is-a”关系,它是独立的实体。

闭包
定义:它是一个可调用的对象,它记录一些信息,这些信息来自于创建它的作用域。
内部类就是一个面向对象的闭包,不仅包含外围类对象(创建外围类的作用域)的信息,还自动拥有一个指向外围类对象的引用,在此作用域内,内部类有权操作所有成员,包括私有成员。

public class CallBack {
    public static void main(String[] args) {
        Call2 call2=new Call2();
        Increment increment=call2.getClosure();
        increment.increment();
    }
}

interface Increment{
    void increment();
}

class MyIncrement{
    //定义一个与接口一样的方法
    public void increment(){
    }
}
class Call2 extends MyIncrement{
    private int i=0;

    //覆写父类的方法
    @Override
    public void increment() {
        super.increment();
        i++;
    }

    //利用内部类实现对同名方法的调用
    private class Closure implements Increment{
        public void increment() {
            //调用外围类的同名方法
            Call2.this.increment();
        }
    }

    Increment getClosure(){
        return new Closure();
    }
}

内部类的继承
内部类的构造器必须连接到指向外围类对象的引用。
那个指向外围类对象的引用必须被初始化,而在导出类中不再存在可连接的默认对象。

public class InheritInner extends WithInner.Inner{
    //外部类构造器
    InheritInner(WithInner wi){
        wi.super();  //父类是object
    }

    public static void main(String[] args) {
        WithInner wi=new WithInner();
        //产生实例时,继承的内部类的构造器指向外围类对象的引用wi,
        //通过调用外围类的父类构造器,完成该引用的初始化
        InheritInner  ii=new InheritInner(wi);
    }

}

class WithInner{
    //内部类
    class Inner{

    }
}

覆盖内部类
当继承某个外围类并重新定义其内部类,并不会有啥变化,这两个内部类是完全独立的两个实体,各自在自己的命名空间内。

public class BigEgg extends Egg{
    //定义一个同名的内部类
    public class Yolk{
        public Yolk(){
            System.out.println("BigEgg.Yolk");
        }
    }

    public static void main(String[] args) {
        new BigEgg();
    }
}

class Egg{
    private Yolk yolk;
    class Yolk{
        public Yolk(){
            System.out.println("Egg.Yolk");
        }
    }
    public Egg(){
        System.out.println("new Egg()");
        yolk=new Yolk();
    }
}
//结果
new Egg()
Egg.Yolk

可以通过明确继承某个内部类,覆盖其中的方法:

public class BigEgg2 extends Egg2{
    //明确继承内部类
    public class Yolk extends Egg2.Yolk{
        //覆盖方法
        public Yolk(){
            System.out.println("BigEgg2.Yolk");
        }
        public void f(){
            System.out.println("BigEgg2.Yolk.f()");
        }
    }

    public Yolk getYolk(){
        return new Yolk();
    }

    public BigEgg2(){
        System.out.println("new BigEgg2");
    }

    public static void main(String[] args) {
        BigEgg2 e2=new BigEgg2();
        e2.getYolk().f();
    }
}

class Egg2{
    //内部类
    protected class Yolk{
        public Yolk(){
            System.out.println("Egg2.Yolk");
        }
        public void f(){
            System.out.println("Egg2.Yolk.f()");
        }
    }

    private Yolk y=new Yolk();
    public Egg2(){
        System.out.println("new Egg2");
    }
}
//结果
//内部类的基类
Egg2.Yolk
//外部类的基类
new Egg2
new BigEgg2
//内部类的基类
Egg2.Yolk
BigEgg2.Yolk
BigEgg2.Yolk.f()

局部内部类
在代码块里创建内部类,尤其是在一个方法体的里面创建内部类。局部内部类的名字在方法外是不可见的。
局部内部类可以访问当前代码块内的常量,以及此外围类的所有成员。

public class LocalInnerClass {
    private int i=999;
    Count getCount(final String name){
        final int value=666;
        //局部内部类
        class LocalCount implements Count{
            public LocalCount(){};
            //可以访问外围类成员
            int j=i;
            //也可以访问方法体中的成员
            int k=value;
        }
        return new LocalCount();
    }
    //局部内部类的名字在方法外不可见
    //Count c=new LocalCount();
}

interface Count{

}

总结:
1、在外部类的内部使用内部类
在外部类内部使用内部类时,与平常使用普通类没太大区别。一样可以直接通过内部类类名来定义变量,通过new调用内部类构造器来创建实例。
唯一存在的区别是:不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,因为静态成员不能访问非静态成员。
在外部类内部定义内部类的子类与平常定义子类也没有太大区别。

2、在外部类以外使用非静态内部类
如果希望在外部类以外的地方访问内部类(包括静态和非静态两种),则内部类不能使用private访问控制权限,private修饰的内部类只能在外部类内部使用,对于其他访问控制符修饰的内部类,则能在访问控制符对应访问权限内使用。
(1)省略访问控制符的内部类,只能被与外部类处于同一个包中的其他类所访问。
(2)使用protrcted修饰的内部类,可被与外部类处于同一个包中其他类和外部类的子类所访问。
(3)使用public修饰的内部类,可以在任何地方被访问。
在外部类以外的地方定义内部类包括静态和非静态两种)变量的语法格式如下:

OuterClass.InnerClass varName

可以看出,在外部类以外的地方使用内部类时,内部类完整的类名应该是OuterClass.InnerClass ,如果外部类有包名,则还应该增加包名前缀。

由于非静态内部类的对象必须寄生在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象。在外部类以外的地方创建非静态内部类实例的语法如下:

OuterClass.InnerClass varName=new OuterConstructor().new InnerConstructor()

可以看出,在外部类以外的地方创建非静态内部类的实例必须使用外部类实例和new来调用非静态内部类的构造器非静态内部类的构造器必须使用外部类对象来调用。

如果需要在外部类以外的地方创建非静态内部类的子类,则尤其要注意上面的规则:非静态内部类的构造器必须通过其外部类对象来调用。当创建一个子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造,调用非静态内部类的构造器时,必须存在一个外部类对象。

//非静态内部类In类的构造器必须使用外部类对象来调用,代码中的super代表调用In类的构造器,而out则代表外部类对象。
class SubClass extends Out.In
{

    public SubClass(Out out) 
    {    
        //通过传入的Out对象显式调用In的构造器
        out.super("hello");
    }

}

如果要创建SubClass对象时,必须先创建一个Out对象。这是合理的,因为SubClass是非静态内部类In类的子类,非静态内部类In对象里必须有一个对Out对象的引用,其子类SubClass对象里也应该持有对Out对象的引用。当创建SubClass对象时传给该构造器的Out对象,就是SubClass对象里Out对象引用所指向的对象。

非静态内部类In对象和SubClass对象都必须持有指向Out对象的引用,区别是创建两种对象时传入Out对象的方式不同:当创建非静态内部类In对象时,必须通过Out对象来调用new关键字;当创建SubClass类的对象时,必须使用Out对象作为调用者来调用In构造器。

非静态内部类的子类不一定是内部类,它可以是一个外部类。但非静态内部类的子类实例一样需要保留一个引用,该引用指向其父类所在外部类的对象。也就是说,如果有一个内部类子类的对象存在,则一定存在与之对应的外部类对象。

3、在外部类以外使用静态内部类
因为静态内部类是外部类类相关的,因此创建静态内部类对象时无须创建外部类对象。在外部类以外的地方创建静态内部类实例的语法如下:

//通过new来调用内部类构造器创建静态内部类实例
OuterClass.InnerClass varName=new     OuterClass.InnerConstructor()

可以看出,不管是静态内部类还是非静态内部类,它们声明变量的语法完全一样。区别是在创建内部类对象时,静态内部类只需要使用外部类即可调用构造器,而非静态内部类必须使用外部类对象来调用构造器。

因为调用静态内部类的构造器时无须使用外部类对象,所以创建静态内部类的子类也比较简单。

class StaticSubClass extends StaticOut.StaticIn{}

因此当需要使用内部类时,应该优先考虑静态内部类。

每个类都会产生一个.class文件,内部类也不例外。
这些类文件的命名规则:外围类的名字+“$”+内部类的名字。
如果内部类是匿名的,编译器会简单产生一个数字作为其标识符。
如果内部类是嵌套在其他的内部类中,可将其加在外围类标识符与$的后面。

参考文章:
http://www.cnblogs.com/-9527/p/5215654.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值