《Thinking In Java》内部类

《Thinking In Java》内部类

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

创建内部类

public class OuterTest1 {
private String name = "王天逾";
//创建内部类
class InnerClass1{
int i;
InnerClass1(int i){
this.i = i;
}
public void f1(){
System.out.println("姓名:"+name+" 年龄:"+this.i);
}
}
public InnerClass1 getInnerClass1(){
return  new InnerClass1(22);
}

public static void main(String[] args) {
OuterTest1 outerTest1 = new OuterTest1();
outerTest1.getInnerClass1().f1();
}
}

打印结果:

姓名:王天逾 年龄:22

外部类有一个方法getInnerClass1()返回一个内部类的引用。在内部类有个普通方法f1(),它可以访问外部类变量name,即使它在外围类中是private修饰符。也就是说,当生成一个内部类对象时,此对象与制造它的外围类对象直接就有了联系,它能够访问外围类对象的所有成员,内部类还拥有其外围类的所有的访问权。

.this 和 .new用法

  • .this用法介绍

如果你想在内部类中返回外部类对象的引用,可以使用外部类的名字.this

public class OuterTest1 {
public void f1(){
System.out.println(".this用法。。。");
}
class InnerClass{
public OuterTest1 getOuterObject(){
return  OuterTest1.this;
}
}
public InnerClass getInnerInstance(){
return  new InnerClass();
}
public static void main(String[] args) {
 OuterTest1 outerTest1 = new OuterTest1();
 outerTest1.getInnerInstance().getOuterObject().f1();
}
}
  • .new用法介绍

有时候你想要告知某些其他对象去创建其某个内部类对象,要实现这个目的,就必须在new表达式中提供对其外部类对象的引用,这就需要.new用法

此时删掉getInnerInstance()方法,在main()中创建内部类对象,看一下代码

public class OuterTest1 {
public void f1(){
    System.out.println(".new用法。。。");
}
class InnerClass{
public OuterTest1 getOuterObject(){
     return  OuterTest1.this;
  }
}

public static void main(String[] args) {
 OuterTest1 outerTest= new OuterTest1();
 outerTest.new InnerClass().getOuterObject().f1();
  }
}

<font color=#f00 size=3>注意:
这里必须使用外部类的对象来创建该内部类对象,之所以这么做,是因为内部类对象会默认连接到外部类对象中。但是,如果创建的是静态内部类,那么它就不需要对外部类对象的引用了。</font>

对于后半句,来写一下。是可以编译运行的。

 public class OuterTest1 {
   static class InnerClass{
public void getSomething(){
   System.out.println("这是静态内部类。。。");
   }
}
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
innerClass.getSomething();
   }
}

内部类与向上转型

public class OuterTest2 {
private class DestinationImpl implements Destination {
String s;

DestinationImpl(String s) {
this.s = s;
}

@Override
public void readDestination() {
System.out.println(s);
}
}

protected class ContentsImpl implements Contents {

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

public Destination getDestination() {
return new DestinationImpl("崇明岛");
}

public Contents getContents() {
return new ContentsImpl();
}

public static void main(String[] args) {
OuterTest2 outerTest2 = new OuterTest2();
outerTest2.getDestination().readDestination();
outerTest2.getContents().value();
}
}

内部类实现了接口,得到了对此接口的引用,与向上转型为这个对象的基类,是一样的。

在方法的内部类(局部内部类)

public class OuterTest3 {
public Destination getDestination(){
 class MyDestinationImpl implements  Destination{

 @Override
 public void readDestination() {
      System.out.println("111");
   }
 }
 return new MyDestinationImpl();
}

public static void main(String[] args) {
    OuterTest3 outerTest3  = new OuterTest3();
    outerTest3.getDestination().readDestination();
  }
}

MyDestinationImpl类是getDestination()的一部分,而不是OuterTest3的一部分。所以,在getDestination()之外是无法访问MyDestinationImpl的。return的时候使用了向上转型,因为Destination是MyDestinationImpl的基类。

在特定的作用域内的内部类

public class OuterTest3 {
 private void f1(boolean b){
 if(b){
      class MyClass {
      void f2(){
      System.out.println("5201314");
      }
 }
 MyClass myClass = new MyClass();
 myClass.f2();
    }
 }

public static void main(String[] args) {
   OuterTest3 outerTest3 = new OuterTest3();
   outerTest3.f1(true);
   }
}

MyClass是被嵌入在if语句的作用域内,在定义MyClass类之外的作用域是不可用的,除此之外,它与普通类没区别。

匿名内部类

public class OuterTest3 {
public Contents getContents() {
return new Contents() {
  @Override
  public int value() {
      return 520;
   }
  };
}

public static void main(String[] args) {
   OuterTest3 outerTest3 = new OuterTest3();
   System.out.println(outerTest3.getContents().value());
   }
}

匿名内部类:创建一个继承自父类的匿名内对象,通过new表达式返回的引用被自动向上转型为对父类(这里是Contents)的引用。

将上面匿名内部类改成正常形式。

public class OuterTest3 {

public Contents getContents() {
    return new ContentsImpl();
}

class ContentsImpl implements Contents {
    @Override
    public int value() {
        return 520;
    }
}

public static void main(String[] args) {
    OuterTest3 outerTest3 = new OuterTest3();
    System.out.println(outerTest3.getContents().value());
  }
}

在这里父类Contents不需要接收参数,使用了默认构造,那么,想要为父类传递参数呢?

class Fu {
int i;
Fu(int i) {
this.i = i;
}

public int f() {
return this.i;
  }
}

public class OuterTest3 {
public Fu getFuObject(int i) {
return new Fu(i) {
@Override
public int f() {
return super.f() * 100;
   }
  };
}

public static void main(String[] args) {
OuterTest3 outerTest3 = new OuterTest3();
System.out.println(outerTest3.getFuObject(2).f()); 
   }
}

只需简单的传递合适的参数给基类的构造器就好了。

在匿名内部类中定义字段时,还能够对其进行初始化操作

public class OuterTest3 {
public Contents getContents(final int i) {
  return new Contents() {
    int ii=i;
    @Override
    public int value() {
    return ii;
    }
 };
}

public static void main(String[] args) {
  OuterTest3 outerTest3 = new OuterTest3();
  System.out.println(outerTest3.getContents(100).value());
  }
}

如果定义一个匿名内部类,并且希望其使用外部定义的对象,那么参数引用必须是final类型。

为匿名内部类创建"构造器效果"

匿名内部类不可能有命名构造器(因为它没有名字),但可以通过实例初始化,就能够达到为匿名内部类创建构造器效果。

abstract  class BaseClass{

public abstract  void f();
BaseClass(int i){
System.out.println(i);
}
}

public class OuterTest3 {
 static  class  MyBase {
 public BaseClass getBaseClass(int i){
 return  new BaseClass(i) {
 {
 System.out.println("inside");
 }
 @Override
 public void f() {
 System.out.println("anonymous");
 }
 };
 }
 }

public static void main(String[] args) {
  MyBase myBase = new MyBase();
  myBase.getBaseClass(10).f();

}
}

打印结果:

10
inside
anonymous

这里没有为参数设置final int i,是因为匿名内部类中没有使用i,只是传递给了父类构造。

嵌套类

定义:如果不需要内部类对象与其外围类对象之间有关系,那么可以将内部类声明为static

普通内部类对象隐式地保存了一个引用,指向创建他的外围类对象,然而,当内部类为static时,就不是这样了。

<font color=#f00 size=3>

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

</font>

普通内部类不能包含static数据和字段,而静态内部类则可以

 public class OuterTest3 {
 private static  class MyContentImpl implements  Contents{
     private static int i=1;
     @Override
     public int value() {
         return 100+i;
     }
 }
 public static   Contents getContents(){
     return new MyContentImpl();

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

 }
}

在main()中,根本就没有生成外部类对象,而是使用static成员的普通语法来调用一个返回对Contents接口的引用。

接口内部的类

通常情况下,接口内部不能放置任何代码,但嵌套类可以,在接口里面,默认类是public static,由于是static,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则。

interface ClassInterfaces {

void f();

class OuterTest3 implements ClassInterfaces {

    @Override
    public void f() {
        System.out.println("接口内部类测试。。。");
    }


    public static void main(String[] args) {
        new OuterTest3().f();
    }
}
}
从多层嵌套类中访问外部类成员

一个内部类被嵌套多少层并不重要,它总能访问外部类的所有成员

public class MNA {
private void f1() {
    System.out.println("f1");
}

class A {
    private void f2() {
        System.out.println("f2");
    }

    public class B {
        private void f3() {
            f1();
            f2();
        }

    }
}

public static void main(String[] args) {
    MNA mna = new MNA();
    A a = mna.new A();
    A.B b = a.new B();
    b.f3();
}
}

内部类的继承

内部类的构造器必须连接到外围类的对象的引用,所以,那个指向外围类对象的引用必须被初始化,而导出类中在不再存在可连接的默认对象,要解决这个问题,需要使用enclosingClassReference.super();

class WithInner {
class Inner {
  public void f() {
    System.out.println("hh");
  }
 }

}

public class InheritInner extends WithInner.Inner {
   InheritInner(WithInner i) {
   i.super();
}

public static void main(String[] args) {
  WithInner wi = new WithInner();
  InheritInner inheritInner = new InheritInner(wi);
  inheritInner.f();
 }
}

通过i.super(),提供了必要的引用,然后程序编译通过。

内部类标识符

  • 每个类都会产生一个.class文件,内部类也不例外.

  • 外部类的名字+"$"+内部类的名字

  • 如果是匿名内部类,编译器会简单的产生一个数字作为其标识符。

  • 如果内部类嵌套在别的内部类中,只需将他们的名字加在外围类标识符与$的后面

声明

这篇博文是《Thinking in Java》内部类读书笔记。还有几个内部类知识点没有搞明白,这里没做笔记,等下次充分理解后,再补充。

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

推荐阅读更多精彩内容