《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();
  }
}

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

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

 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时,就不是这样了。

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

普通内部类不能包含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》内部类读书笔记。还有几个内部类知识点没有搞明白,这里没做笔记,等下次充分理解后,再补充。

你可能感兴趣的:(《Thinking In Java》内部类)