内部类

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

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

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

静态的内部类也叫嵌套类,接口(类与接口中的接口都是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();
}
}

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值