技术

Java内部类总结

2016-09-13  本文已影响197人  niaoge2016

概念介绍

内部类分类

广泛意义上的内部类一般分为以下几种类型:

//外部类
class Out {
  private int age = 12;
   
  //内部类
  class In {
      public void print() {
          System.out.println(age);
      }
  }
}

public class NormalInnerClass {
    public static void main(String[] args) {
        Out.In in = new Out().new In();
        in.print();
        //或者采用下种方式访问
        /*
        Out out = new Out();
        Out.In in = out.new In();
        in.print();
        */
    }
}

interface extendInnerClass{
    public int setAge();
}

//外部类
class Out {
  private int age = 12;
   
  public extendInnerClass getInnerClass(){
      // 局部内部类
      class innerClass implements extendInnerClass{
          public int setAge(){
              age++;
              return age;
          }
          
      }
      return new innerClass();
  }
}

public class NormalInnerClass {
    public static void main(String[] args) {
        Out out = new Out();
        System.out.println(out.getInnerClass().setAge());
    }
}

注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

class Out3 {
    private static int age = 12;
     
    static class In {
        public void print() {
            System.out.println(age);
        }
    }
}

public class staticInnerClass {
    public static void main(String[] args) {
        Out3.In in = new Out3.In();
        in.print();
    }
}

可以看到,如果用static 将内部内静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性

其次,因为内部类被静态化,因此Out.In可以当做一个整体看,可以直接new 出内部类的对象(通过类名访问static,生不生成外部类对象都没关系)

interface anonymous{
    public void getAnonymousAge();
}

class Out4 {
    private static int age = 12;
     
    public anonymous getAnonymous(){
        return new anonymous(){

            @Override
            public void getAnonymousAge() {
                System.out.println(age);
            }
        };
    }
}

public class anonymousInnerClass {
    
    public static void main(String[] args) {
        Out4 out = new Out4();
        out.getAnonymous().getAnonymousAge();
    }
        
}

匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

深入理解内部类

  1. 为什么成员内部类可以无条件访问外部类的成员?
    编译器会默认为成员内部类添加了一个指向外部类对象的引用,那么这个引用是如何赋初值的呢?
    虽然我们在定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用,所以成员内部类中的Outter this&0 指针便指向了外部类对象,因此可以在成员内部类中随意访问外部类的成员。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。

  2. 为什么局部内部类和匿名内部类只能访问局部final变量?
    示例代码:

public class Test {
    public static void main(String[] args)  {
         
    }
     
    public void test(final int a) {
        new Thread(){
            public void run() {
                System.out.println(a);
            };
        }.start();
    }
}

如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
为了解决数据不一致性问题,java编译器就限定必须将变量a限制为final变量,不允许对变量a进行更改(对于引用类型的变量,是不允许指向新的对象),这样数据不一致性的问题就得以解决了。

  1. 静态内部类有特殊的地方吗?
    从前面可以知道,静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。另外,静态内部类是不持有指向外部类对象的引用的,这个读者可以自己尝试反编译class文件看一下就知道了,是没有Outter this&0引用的。

常考知识点

  1. Static Nested Class 和 Inner Class的不同?
    在方法外部定义的内部类前面可以加上static关键字,从而成为Static Nested Class,它不再具有内部类的特性,所有,从狭义上讲,它不是内部类。Static Nested Class与普通类在运行时的行为和功能上没有什么区别,只是在编程引用时的语法上有一些差别,它可以定义成public、protected、默认的、private等多种类型,而普通类只能定义成public和默认的这两种类型。在外面引用Static Nested Class类的名称为“外部类名.内部类名”。在外面不需要创建外部类的实例对象,就可以直接创建Static Nested Class,例如,假设Inner是定义在Outer类中的Static Nested Class,那么可以使用如下语句创建Inner类:
    Outer.Inner inner = new Outer.Inner();
    由于static Nested Class不依赖于外部类的实例对象,所以,static Nested Class能访问外部类的非static成员变量。当在外部类中访问Static Nested Class时,可以直接使用Static Nested Class的名字,而不需要加上外部类的名字了,在Static Nested Class中也可以直接引用外部类的static的成员变量,不需要加上外部类的名字。
    在静态方法中定义的内部类也是Static Nested Class,这时候不能在类前面加static关键字,静态方法中的Static Nested Class与普通方法中的内部类的应用方式很相似,它除了可以直接访问外部类中的static的成员变量,还可以访问静态方法中的局部变量,但是,该局部变量前必须加final修饰符。

  2. 内部类可以引用它的包含类的成员吗?有没有什么限制?
    完全可以。如果不是静态内部类,那没有什么限制!
    如果你把静态嵌套类当作内部类的一种特例,那在这种情况下不可以访问外部类的普通成员变量,而只能访问外部类中的静态成员,例如,下面的代码:

class Outer  {  
   static int x;  
   static class Inner  {  
      void test() {  
               syso(x);  
      }  
   }  
}  

参考文献

[1]java中的内部类总结
[2]Java中为什么要使用内部类
[3]Java内部类详解

上一篇下一篇

猜你喜欢

热点阅读