`

内部类

阅读更多
内部类对象可以直接访问外围对象的所有成员(包括私有的),而不需要任何特殊条件,就像调用自己的方法与属性成员一样。但外围类不能直接访问内部类中的方法,除非使用内部类的实例来访问(也能访问私有的)。

内部类自动拥有对其外围类所有成员的访问权。这是如何做到的呢?当某个外围类的对象创建了一个内部类对象时,此内部对象会有一个指向外围类对象的引用,然后在你访问此外围类的成员时,就是用那个引用来选择外围类的成员,编译器会帮你处理所有的细节。注,这只限于非静态的内部类。

构建内部类对象时,需要一个指向其外围类对象的引用,如果编译器访问不到这个引用就会报错。

静态的内部类也叫嵌套类,接口(类与接口中的接口都是static)叫嵌套接口。

匿名内部类与普通的内部类继承相比是有些受限的,虽然匿名内部类既可以继承类,也可以实现接口,但是不能两者兼备,而且如果实现接口,也只能实现一个接口。

非静态的内部类中不能定义static变量、方法、类,即非静态的内部类里不能定义一切static东西(static final形式的常量定义除外)。因为一个成员类实例必然与一个外部类实例关联,这个static定义完全可以移到其外部类中去。

内部(类中或接口中)接口一定static的。

接口中的内部类一定是public与static,但不一定是final(与数据成员不同),因为省略final时可以被继承。

非静态的内部类里不能定义接口,因为内部接口(嵌套接口)默认为static,是无法改变的,所以内部类里的接口只能定义在静态的内部类里面。

方法与块里定义的内部类只能是非静态的,不能加static,所以局部内部类里只能定义非静态的成员。局部内部类也能直接访问外部内所有成员。

静态的内部类可以定义非静态与静态的东西。

不能在静态内部类中直接(实例化外部类再访问是可以的)访问外部类中的非静态成员与方法。而非静态内部类是可以访问外部类的静态成员。

在方法与作用域内都可定义内部类。如果一个内部类在if条件语句中定义,则不管条件是否成立都会编译成类,但这个类的使用域只限于该if语句中,if外面不能访问。

设计模式总是将变化的事物与保持不变的事物分离开,比如模板方法模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。

内部类不能被重写:父类中的内部类不能被子类中的同名内部类重写。

为什么加上final后的局部变量就可以在内部类中使用了?因为加上final后,编译器是这样处理内部类的:如果这个外部局部变量是常量,则在内部类代码中直接用这个常量;如果是类的实例,则编译器将产生一个内部类的构造参数,将这个final变量传到内部类里,这样即使外部局部变量无效了,还可以使用,所以调用的实际是自己的属性而不是外部类方法的参数或局部变量。这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。

一个类对外提供一个公共接口的实现(接口与实现完全分离,而且隐藏了实现)是内部类的典型应用,以JDK Collection类库为例,每种Collection的实现类必须提供一个与其对应的Iterator实现,以便客户端能以统一的方式(Iterator接口)遍历任一Collection实例。每种Collection类的Iterator实现就被定义为该Collection类的私有的内部类。

内部类作用:
1、内部类方法可以访问该类所在的作用域中的数据,包括私有数据。
2、内部类可以对同一个包中的其他类隐藏起来。
3、当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
4、实现多继承。


>>>内部类机制<<<
如果有如下内部类:
Java代码 
public class Outer { 
    private boolean b; 
    private int i; 
 
    //内部类 
    private class Inner { 
        private boolean b; 
 
        Inner(boolean b) { 
            this.b = b; 
        } 
 
        public void print() { 
            System.out.println(Outer.this.b & this.b); 
        } 
    } 
 
    public void f(final String str) { 
        class Inner {//局部内部类 
            public void print() { 
                System.out.println(Outer.this.i); 
            } 
        } 
    } 

我们来使用Reflection反射外部及内部类,看看内部类是怎样访问外部类成员的:
D:\work\Test\bin>java Reflection Outer
Java代码 
class Outer 

//构造器 
public Outer(); 
//字段 
private boolean b; 
private int i; 
//方法  
static boolean access$0(Outer);// 内部类Outer$Inner通过该方法访问外部类b成员 
static int access$1(Outer); // 局部类Outer$1Inner通过该方法访问外部类i成员 
public void f(java.lang.String); 

 
class Outer$Inner 

//构造器  
Outer$Inner(Outer, boolean);// 在编译期会自动传入外部类实例 
//字段 
private boolean b; 
final Outer this$0;//指向外部类实例 
//方法 
public void print(); 

 
class Outer$1Inner 

//构造器  
  Outer$1Inner(Outer, java.lang.String);//第二个参数是引用的final类型的局部变量,也是通过构造器传入的 
//字段 
final Outer this$0; 
private final java.lang.String val$str;//存储引用的局部final类型变量 
//方法 
public void print(); 


非静态内部类创建方式:
Java代码 
OuterClassName.InnerClassName inner = new OuterClassName().new InnerClassName(); 

静态内部类创建方式:
Java代码 
OuterClassName.InnerClassName inner = new OuterClassName.InnerClassName(); 

继承内部类语法规则: 请参考 57. 继承内部类

静态内部类里可以使用this(而不像静态方法或块里是不能使用this的),此时的this指向静态内部类,而不是外部类,下面为LinkedList类里的内部类,代表一个节点的实现:
Java代码 
private static class Entry { 
Object element; 
Entry next; 
Entry previous; 
 
Entry(Object element, Entry next, Entry previous) { 
     this.element = element; 
     this.next = next; 
     this.previous = previous; 



在非静态的内部类里访问外围类相同属性成员时,需在this前加上外围类型(采用Outer.this.XX 来访问,其中Outer为外围类的类型),一般在访问自己成员时,可以省略this前自身的类型,但默认应该是有的:
Java代码 
public class Outer { 
    private int i = 1; 
    public void f(){ 
        System.out.println("f Outer.this.i=" + Outer.this.i);//1 
    } 
     
    private /*static*/class Inner { 
        private int i = 2; 
        public void p() { 
            System.out.println("p this.i=" + this.i);//2 
            System.out.println("p Inner.this.i=" + Inner.this.i);//2 
            //!!注,如果是静态的内部类时,下面语句不能编译通过 
            System.out.println("p Outer.this.i=" + Outer.this.i);//1 
        } 
    } 
 
    public static void main(String[] args) { 
        Outer outer = new Outer(); 
        outer.f(); 
        Outer.Inner inner = outer.new Inner(); 
        inner.p(); 
    } 


匿名内部类(方法或块中的内部类一样)使用外部类作用域内的局部变量需定义成final,但这个变量需在匿名内部类中直接使用,如果是作为匿名构造器的参数时不需要:
Java代码 
public class Wrapping { 
  private int i; 
  public Wrapping() {} 
  public Wrapping(int x) { i = x; } 
  public int value() { return i; } 

 
public class Parcel { 
  // 可以直接内部类或匿名的内部类访问,不需要定义成final 
  private int ii = 1; 
/*
  * 这里的参数 x 不需要定义成final,因为它只是
  * 作为构建匿名对象时传递的一个参数,而没有直接
  * 在匿名内部类中使用
  */ 
public Wrapping wrapping1(int x) { 
  // 调用匿名内部类的基类的带参构造函数 
  return new Wrapping(x) { // 传递参数 
   public int value() { 
    //调用外部内的域成员时可直接调用,但加this时就需在 
    //this前加外部类名,因为该内部类没有定义ii 
    return super.value() * 47 * Parcel.this.ii; 
   } 
  }; 

 
/*
  * 注,这里的参数 x 一定要定义成final,因为
  * 它被匿名内部类直接使用了
  */ 
public Wrapping wrapping2( final int x ) { 
  final int y = 1; 
  return new Wrapping() {  
   //不管是在定义时还是方法中使用都需要定义成final 
   private int i=y; 
   public int value() { 
    return i * x * 47; 
   } 
  }; 

public static void main(String[] args) { 
  Wrapping w = new Parcel().wrapping1(10); 
  w = new Parcel().wrapping2(10); 



匿名类中不可能有命名的构造器,因为它根本没有名字。但通过块初始化,就能够达到为匿名内部类创建一个构造器的效果,当然它受到了限制——你不能重载块方法,这不像普通内部类的构造函数:
Java代码 
abstract class Base { 
  public Base(int i) { 
    System.out.println("Base constructor, i = " + i); 
  } 
  public abstract void f(); 
}  
 
public class AnonymousConstructor { 
  public static Base getBase(int i) { 
    return new Base(i) { 
      {   //初始化块 
       System.out.println("Inside instance initializer"); 
      } 
      public void f() { 
       System.out.println("In anonymous f()"); 
      } 
    }; 
  } 
  public static void main(String[] args) { 
    Base base = getBase(47); 
    base.f(); 
  } 
} /* Output:
Base constructor, i = 47
Inside instance initializer
In anonymous f()
*///:~ 

实现接口的匿名内部类:
Java代码 
public class Outer { 
public Comparable getComp() { 
  return new Comparable() {// Comparable为比较器接口 
   //实现接口 
   public int compareTo(Object o) { 
    return 0; 
   } 
  }; 

但要注意,匿名内部类实现一个接口时,构造时不能带参数:
Java代码 
interface InnerI {} 
public InnerI getII() { 
  // 匿名内部内实现一个接口时,构造器不能带参数, 
  // 因为接口根本就没有构造器,更没有带参的构造器 
  // !!return new InnerI(int i) {}; 



内部类生成的class文件名规则:
Java代码 
public class A {//A.class 
    class B {//A$B.class 
        class C { 
        }//A$B$C.class 
    } 
 
    { 
        class B { 
        }//A$1B.class 
    } 
 
    B f() { 
        class D { 
        }//A$1D.class 
        return new B() { 
        };//A$1.class 
    } 
 
    B g() { 
        class E {//A$1E.class 
            B h() { 
                return new B() { 
                };//A$1E$1.class 
            } 
        } 
        return new B() { 
        };//A$2.class 
    } 
 
    static class F { 
    }//A$F.class 
 
    public static void main(String[] args) { 
        A a = new A(); 
        System.out.println(a.f().getClass().getName()); 
        System.out.println(a.g().getClass().getName()); 
    } 


package com.jelly.innerclass;

public class Sequence {
	private Object[] items;
	private int next;

	public Sequence(int size) {
		items = new Object[size];
	}

	public void add(Object x) {
		if (next < items.length) {
			items[next++] = x;
		}
	}

	private class SequenceSelector implements Selector {
		private int i = 0;

		public Object current() {
			return items[i];
		}

		public boolean end() {
			return i == items.length;
		}

		public void next() {
			if (i < items.length) {
				i++;
			}
		}
	}

	public Selector selector() {
		return new SequenceSelector();
	}

	public static void main(String[] args) {
		Sequence sequence = new Sequence(10);
		for (int i = 0; i < 10; i++) {
			sequence.add(Integer.toString(i));
		}
		Selector selector = sequence.selector();
		while(!selector.end()){
			System.out.println(selector.current());
			selector.next();
		}
	}

}


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics