只想把基础打好-Java内部类

内部类的类型

普通内部类(非静态)

public class Outer {
    class Inner{}
}

实际例子:

public interface Iterator<T> {
    boolean hasNext();
    T next();
}
public class MyList<T> {
   private Object[] list;
   private int size;
   private int currentSize;

   public MyList(int size){
       list=new Object[size];
       this.size=size;
   }
   public void add(T element){
       if(currentSize<size){
           list[currentSize++]=element;
       }else{
           //抛出异常
       }
   }
   public void remove(){
       if(currentSize>0){
           list[--currentSize]=null;
       }else{
           //抛出异常
       }
   }
   Iterator interator(){
       return new Itr<T>();
   }

class Itr<T> implements Iterator<T>{
    int cursor;       // index of next element to return

    @Override
    public boolean hasNext() {
        return cursor<currentSize;
    }

    @Override
    public T next() {
        if(hasNext()){
          //  lastRet++;
            int i=cursor;
            cursor++;
            return  (T)list[i];

        }

        return null;
    }
}
}

测试代码

  public static void main(String[] args){
        MyList<String> list=new MyList<>(10);
        list.add("today");
        list.add("is");
        list.add("week");
        list.add("?");
        Iterator iterator=list.interator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }

运行结果:

today
is
week
?

这是一个典型的迭代器模式。从上面例子可以看出,内部可以访问外围类的成员变量,当然也可以访问成员方法,因为生成内部类的对象的时候就与制造它的外围类对象就有了联系,内部类有外围类的所有元素的访问权。这是怎么做到的呢?

  protected void test(){
        Outer outer=new Outer();
       // Outer.Inner inner=new Outer().new Inner();
        Outer.Inner inner=outer.new Inner();
        inner.getI();
    }

    class Outer{
        int i=10;
        class Inner{
            public void getI(){
                System.out.println("outer class i="+i);
            }
        }
    }

从上面看出得到一个内部实例的例子,创建一个内部类实例的时候必须要得到一个外围类的实例,然后再按照outer.new Inner()来得到一个内部类对象。拥有外部类对象之前是不可能创建内部类实例的(除非是静态内部类,稍后说)。当通过外围类对象创建内部类实例的时候,秘密捕获了这个指向外围类的对象,当在内部类访问外围类的成员的就是用的这个对象调用外围的对象。幸运的是,编辑器帮你处理了这些细节。

public void getI(){
        //完整调用语句
               System.out.println("outer class i="+Outer.this.i);
           }

在java中比较常见的是集合类中使用迭代器。

局部内部类

public class Outer {
    public void test(){
        class Inner{}
        new Inner();
    }
  //  class Inner{}
}

可以在任意的方法或作用域定义内部类。我们可以创建一个类来辅助解决方案,而又不想这个类是共用的。如上,Inner定义在test方法中,所以在test方法外不能直接访问Inner类。

 class Outer{
        public IInner getInner(){
            class  Inner implements IInner{

                @Override
                public void innerPrint() {
                    System.out.println("innerclass in innerPrint");
                }
            }
            return new Inner();
        }

    }
interface IInner{
        void innerPrint();
}

return 语句中用了向上转型,当使用此方法时得到一个基类对象的引用就可以很好的隐藏实现细节,通过这种方式可以完全阻止任何任何依赖于类型的编码。也不是意味着getInner方法执行结束了之后Inner就不可用了。你可以在同一个子目录下的任意类使用Inner进行类标识,这并不会有命名冲突。

匿名内部类

将上面例子修改下

 class Outer{
        public IInner getInner(){
            return new IInner() {
                @Override
                public void innerPrint() {
                    System.out.println("innerclass in innerPrint");
                }
            };
        }

匿名内部类就是没有名字的类,自动向上转型为基类,在匿名内部类使用了默认的构造器来生成对象,那如果你的基类需要一个有参数的构造器怎么办呢?

public class Parcle {
    public Wrapping wrapping(int x){
        return new Wrapping(x){
            public  int value(){
                return super.value()*47;
            }
        };
    }
}

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

只需要地传递合适的参数给基类的构造器即可,这里是将x给new Wrapping(x),尽管Wrapping只是一个具有实现的普通类,但它还是被其导出类当作公共"接口"来使用。匿名内部类与正规的继承相比有些受限,因为匿名内部类即可以扩展类,也可以实现接口,但是不能两者兼备。而且如果实现接口也只能实现一个接口。
就像在android里面给View添加点击事件监听都是实现一个匿名内部类,如果在一个方法中定义,而想访问方法里的局部变量,那么由于作用域的特性必需在局部变量前加上final,使其成为常量。

静态内部类

如果不需要内部类与外围类之间有联系,那么可以将内部类声明为static。这样内部类的创建不需要依赖于外部类,因此静态内部类可能访问外围类的静态成员。它没有一个指向外围类的引用。静态内部类与非静态内部类还有一个区别,普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static和static 字段,也不能包含静态内部类,但是静态内部类可以。

接口内部的类

正常情况下,不能在接口 内部放置任何代码,但静态内部类可以作为接口的一部份,放到接口中的任何类都是public和static的。如果你想要创建某此公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会很方便。要做测试的话可以在每个类中都写一个main()方法,用来测试这个类。这样做有一个缺点,那就是必须带着那些已编译过的额外代码。那我们就可以用嵌套类来放置测试代码。

public class TestBed{
public void f(){
System.out.println("f()");
}
public static class Tester{
public static void main(String[] args){
TestBed t=new TestBed();
t.f();
}
}
}

这生成了一个独立的类TestBed$Tester,不想在发布的产品中包含它,在将产品打包前可以简单地删除TestBed$Tester.class

说说内部类的其它特性

多重嵌套内部类访问外部类的成员

一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。

class MNA{
private void f(){}
class A{
private void g(){}
public class B{
void h(){
f();
g();
}
}
}
}

闭包

闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看到非静态内部类是面向对象的闭包,因为它不仅包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。

内部类的继承

因为非静态内部类的构造器必须连接到指向其外围类的引用,所以在继承内部类的时候情况会有些复杂。问题在于指向外围类对象的“秘密的”引用必须被初始化,而在导出类中不再存在可连接的默认对象。要解决这个问题,必须使用特殊的语法来明确说清楚它们之间的关联:

class WithInner{
class Inner{}}
public class InheritInner extends WithInner.Inner{
InheritInner(WithInner wi){
wi.super();
}
public static void main(String[] args){
WithInner wi=new WithInner();
InheritInner ii=new InheritInner(wi);
}
}

可以看到InheritInner只继承自内部类,而不是外围类,但是当要生成一个构造器时,默认的构造器并不算好,而且不能只是传递一个指向外围类的引用 。此外,必须在构造器中使用如下语法:

wi.super();

内部类可以被覆盖吗

public class Egg {
    private Yolk y;
    protected class Yolk{
        public Yolk(){
            System.out.println("Egg.Yolk()");
        }
    }
    public Egg(){
        System.out.println("New Egg()");
        y=new Yolk();
    }
}

public class BigEgg extends Egg {
    public class Yolk{
        public Yolk(){
            System.out.println("BigEgg.Yolk()");
        }
    }
    public static void main(String[] args){
        new BigEgg();
    }
}

运行结果

New Egg()
Egg.Yolk()

这个例子说明,当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的个体,各自在自己的命名空间里。当然,明确地继承某个内部类也是可以的。

内部类标识符

由于每个类都会产生一个.class文件,其中包含了如何创建该类型的对象的全部信息,当然内部类也会产生一个.class文件,这些类文件 的命名有严格的规则,外围类的名字加上$再加上内部类的名字,如果是匿名内部类则是外围类名加$加上数字。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • Java 内部类 分四种:成员内部类、局部内部类、静态内部类和匿名内部类。 1、成员内部类: 即作为外部类的一个成...
    ikaroskun阅读 1,223评论 0 13
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,635评论 0 11
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 今天整理一下内部类,其中包含了内部类的特殊形式,对比普通类有什么区别和作用,内部类和外围类之间的联系,内部类的扩展...
    _小二_阅读 608评论 0 3
  • 今天在做任务的时候,发现在小米2上录音的时候会弹出如下对话框: 感觉在小米2上的坑超多,可是我不想弹出这个对话框,...
    扈扈哈嘿阅读 480评论 0 0