Java四种内部类使用

先来看下内部类定义:将一个类的定义放在另个一个类的定义内部,内部类分为:成员内部类、局部内部类、匿名内部类和静态内部类
成员内部类:
成员内部类是最普通的内部类,它的定义为位于另一个类的内部

public class Outer {
  private int val;
  public Outer(int val){
    this.val=val;
    System.out.println("Outer");
  }
  //内部类
  public class Inner {
    private int val;
    public Outer getOuterByInner() {
      //通过this获得所指向的外部类引用
      return Outer.this;
    }
    public Inner(){
      System.out.println("Inner");
    }
    public int getOuterVal(){
      //通过外部类.this访问外部类的所有成员
      return Outer.this.val;
    }
    public int getInnerVal(){
      //访问内部类val
      return val;
    }
  }
  public Inner getInner() { return new Inner(); }
  public static void main(String[] args) {
    //先创建外部类实例
    Outer outer = new Outer(5);
    //通过inner方法创建内部类实例
    Outer.Inner inner1 = outer.getInner();
    System.out.println(inner1.getOuterVal());
    //由于内部类是public,也可通过new创建
    Inner inner2 = outer.new Inner();
    System.out.println(inner2.getInnerVal());
    //由于inner1与inner2都是由outer实例所创建,它们指向的外部类对象引用一致
    System.out.println(inner1.getOuterByInner()==inner2.getOuterByInner());
  }
} /* Output:
Outer
Inner
5
Inner
0
true
*///:~
  1. 类Inner是Outer的成员,在拥有外部类对象前是不可能创建内部类对象的,因为内部类需要依赖外部类实例对象的引用才能被创建(静态内部类除外)。所以要先创建外部类对象,才能通过这个外部类对象创建内部类。
  2. 成员内部类可以访问外部类所有成员,即使是private的,当内部类拥有和外部类同名的成员变量或者方法时,必须通过外部类.this指明访问的是外部类的成员。
  3. 成员内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。
    内部类与向上转型:通过private内部类实现某一接口,可以很方便地隐藏实现细节。
interface  PInterface{
  int getValue();
}
class Upcasting {
  private  class ImplClass implements PInterface {
    private int value;
    ImplClass(int i ){
      this.value = i;
    }
    public int getValue() { return value; }
  }
  public PInterface getPInterface(int i) {
    //向上转型,隐藏PContents实现细节
    return new ImplClass(i);
  }
}

public class TestUpcasting {
  public static void main(String[] args) {
    Upcasting upcasting = new Upcasting();
    PInterface pinterface = upcasting.getPInterface(11);
    System.out.println(pinterface.getValue());
  }
} /* Output:
11
*///:~

局部内部类:
局部内部类是定义在一个方法或者一个作用域里面的类。

interface InnerInterface{
  String getStr();
}
public class LocalOuter {
  private String str;
  //默认s1为final
  public InnerInterface getInner(String s1,String s2) {
    //不在局部内部类中使用,可以被修改
    s2="new s2";
    //局部内部类,定义在方法中
    class Inner implements InnerInterface {
      private String innerstr;
      private Inner(String innerstr) {
        this.innerstr = innerstr;
        System.out.println(innerstr);
      }
      public String getStr() {
        //此处去掉注释会报错,一旦参数在局部类内部使用,则必须是final,s1不能被指向其他对象
        //s1="123";
        return s1;
      }
    }
    return new Inner(s2);
  }
  public static void main(String[] args) {
    LocalOuter outer = new LocalOuter();
    InnerInterface inner = outer.getInner("s1","s2");
    System.out.println(inner.getStr());
  }
}/* Output:
new s2
s1
*///:~

它和成员内部类的区别在于:
1. 局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
2. 局部内部类的访问仅限于方法内或者该作用域内。
3. 局部内部类和匿名内部类只能访问方法中或作用域中的final变量,由于内部类与局部变量的生命周期不同,内部类会创建局部变量的引用拷贝,为了避免局部变量与内部类中变量的数据不一致,要求将变量定义为final。
3.匿名内部类:匿名内部类是局部内部类的一种,顾名思义就是没有名字的局部内部类,只能使用一次,创建实例之后,类定义会立即消失(想要多次使用就要用到反射的知识了),不能定义构造方法。匿名内部类必须且只能继承一个类(抽象的、非抽象的都可以)或者实现一个接口。具体使用如下:

class AnonymousClass {
    private String str;
    public String getStr(){
        return str;
    }
     AnonymousClass(String s){
        this.str = s;
    };
}

public class AnonymousTest {
     //匿名内部类,继承AnonymousClass
    public AnonymousClass getInner(String s) {
        return new AnonymousClass(s) {
            public String getStr() {
                return "in Anonymous Inner Class:"+super.getStr();
            }
        };
    }
    public static void main(String[] args) {
        AnonymousTest anonymousTest = new AnonymousTest();
        AnonymousClass inner = anonymousTest.getInner("test");
        System.out.println(inner.getStr());
    }
}/* Output:
in Anonymous Inner Class:test
*///:~

上述匿名类指的是:创建一个继承自AnonymousTest的匿名类对象,通过new表达式返回的引用被自动向上转型为对AnonymousTest的引用。上述匿名类用局部内部类也能实现:

class AnonymousClass {
    private String str;
    public String getStr(){
        return str;
    }
     AnonymousClass(String s){
        this.str = s;
    };
}

public class AnonymousTest {
    class MyAnonymousClass extends AnonymousClass{
        MyAnonymousClass(String s){
            super(s);
        };
        public String getStr() {
            return "in Anonymous Inner Class:"+super.getStr();
        }
    }

    public AnonymousClass getInner(String s) {
        return new MyAnonymousClass(s);9
    }
    public static void main(String[] args) {
        AnonymousTest anonymousTest = new AnonymousTest();
        AnonymousClass inner = anonymousTest.getInner("test");
        System.out.println(inner.getStr());
    }
}/* Output:
in Anonymous Inner Class:test
*///:~

静态内部类:
将内部类声明为static的就是静态内部类。
前面三种内部类都隐式地保存了一个指向创建它的外部类对象引用,与外部类之间存有联系。在拥有外部类对象前是不可能创建内部类对象的,因为内部类需要依赖外部类实例对象的引用才能被创建,所以前面三种内部类是不能有static数据和static字段。(java中类加载过程是先加载类,static成员初始化,再创建对象。即static成员要求先加载类再创建实例对象,与先创建外部类实例再加载内部类相矛盾)当然static final除外,static final修饰的常量存于内存中的常量池 ,与变量不同,加载常量不需要加载类。
静态内部类,与外部类同级的类,不需要依赖于外部类,不能使用外部类的非static成员变量或者方法,可以自定义static或非static成员,接口中任何类都默认是public static的(抽象类中内部类默认是非static):

public interface ClassInInterface {
  void howdy();
  //接口中内部类默认是public static,内部类可以直接实现外部接口
  class TestStatic implements ClassInInterface {
    //静态内部类中可以自定义非静态变量
    private String s1 = "s1";
    private static String s2 = "s2";
    public void howdy() {
      System.out.println("Howdy!");
    }

    public String getS1() {
      return s1;
    }

    public static String getS2() {
      return s2;
    }

    public void setS1(String s1) {
      this.s1 = s1;
    }

    public static void setS2(String s2) {
      TestStatic.s2 = s2;
    }

    public static void main(String[] args) {
      TestStatic t1 = new TestStatic();
      TestStatic t2 = new TestStatic();
      t1.setS1("new s1");
      t1.setS2("new s2");
      System.out.println(t1.getS1());
      System.out.println(t2.getS2());
    }
  }
}/* Output:
new s1
new s2
*///:~

内部类的继承:
普通内部类的构造器必须连接到指向其外部类对象的引用,所以继承内部类时,生成的构造器必须传入被初始化过外部类的引用,在构造器内还需使用enclosingClassReference.super();即外引用.super()语法调用内部类构造器,并将引用传入。

class WithInner {
  public WithInner(){
    System.out.println("WithInner");
  }
  class Inner {
    public Inner(String s){
      System.out.println(s+"Inner");
    }
  }
}

//继承外部类.内部类
public class InheritInner extends WithInner.Inner {
  //内部类中指向外部类的引用必须被初始化
  InheritInner(WithInner wi,String s) {
    //子类构造器中必须加入引用(指向外部类的).super();
    wi.super(s);
    System.out.println(s+"InheritInner");
  }

  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner ii = new InheritInner(wi,"hello");
  }
}/* Output:
WithInner
helloInner
helloInheritInner
*///:~

当然静态内部类的继承就和普通的继承一样,因为它不依赖于外部类

class StaticWithInner {
  public StaticWithInner(){
    System.out.println("StaticWithInner");
  }
  static class StaticInner {
    public StaticInner(String s){
      System.out.println(s+"StaticInner");
    }
  }
}

//继承外部类.静态内部类
public class StaticInheritInner extends StaticWithInner.StaticInner {
  StaticInheritInner(String s) {
    super(s);
    System.out.println(s+"StaticInheritInner");
  }

  public static void main(String[] args) {
    StaticInheritInner ii = new StaticInheritInner("hello");
  }
}/* Output:
helloStaticInner
helloStaticInheritInner
*///:~

内部类的覆盖:
如果创建一个内部类,然后继承外部类并重新定义内部类时,内部类并不会被覆盖,两个内部类各自在自己的命名空间内是完全独立的。

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 {
  //与外部类的Yolk是完全独立的
  public class Yolk {
    public Yolk() { System.out.println("BigEgg.Yolk()"); }
  }
  public static void main(String[] args) {
    new BigEgg();
  }
} /* Output:
New Egg()
Egg.Yolk()
*///:~

继承外部类时重定义的内部类也需要继承外部类.内部类才能覆盖外部类中的内部类

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()"); }
  public void insertYolk(Yolk yy) { y = yy; }
  public void g() { y.f(); }
}
//继承外部类
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 BigEgg2() { insertYolk(new Yolk()); }
  public static void main(String[] args) {
    Egg2 e2 = new BigEgg2();
    e2.g();
  }
} /* Output:
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
*///:~

为什么在Java中需要内部类?
总结一下主要有以下四点:
  1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,
  2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
  3.方便编写事件驱动程序
  4.方便编写线程代码

参考: 《think in java》
https://www.cnblogs.com/dolphin0520/p/3811445.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值