Java内部类与匿名类

一、内部类的基本语法

1、顶层类只能处于public和默认访问级别,而成员内部类可以处于publicprotected、默认和private四种访问级别。

2、实例内部类有以下特点:

a) 在创建实例内部类的实例时,外部类的实例必须已经存在。

b) 实例内部类的实例自动持有外部类的实例的引用。在内部类中,可以直接访问外部类的所有成员,包括成员变量和成员方法。并且在多重嵌套中,内部类可以访问所有外部类的成员。

c) 外部类实例与内部类实例之间是一对多的关系,一个内部类实例只会引用一个外部类实例,而一个外部类实例对应零个或多个内部类实例。在外部类中不能直接访问内部类实例,必须通过内部类的实例去访问。

d) 在实例内部类中不能定义静态成员,而只能定义实例成员

e) 如果实例内部类B与外部类A包含同名的成员(比如成员变量v),那么在类B中,this.v表示类B的成员,A.this.v表示类A的成员

3、静态内部类有以下特点:

a) 静态内部类的实例不会自动持有外部类的特定实例的引用,在创建内部类的实例时,不必创建外部类的实例

b) 静态内部类可以直接访问外部类的静态成员,如果访问外部类的实例成员,就必须通过外部类的实例去访问

c) 在静态内部类中可以定义静态成员和实例成员

d) 客户类可以通过完整的类名直接访问静态内部类的静态成员

4、局部内部类是一个在方法中定义的内部类,它的可见范围是当前方法,和局部变量一样,局部内部类不能用访问控制修饰符及static修改符来修饰。局部内部类具有以下特点:

a) 局部内部类只能在当前方法中使用。

b) 局部内部类和实例内部类一样,不能包含静态成员。

c) 在局部内部类中定义的内部类也不能被publicprotectedprivate这些访问控制修饰符修饰。

d) 局部内部类和实例内部类一样,可以访问外部类的所有成员,此外,局部内部类还可以访问所在方法中的final类型的参数和变量。不可以访问没有被final修饰的局部变量。

二、内部类的继承

在一个类A继承某个外部类B的内部类C时,要求类A的构造方法必须通过参数传递一个类B的实例的引用,然后在构造方法中调用super语句来建立类A实例与类B实例的关联关系。示例代码如下:

class B{
public B(){}
class C{
public C(){}
}
}
public class A extends B.C{
public A(B b){
b.super();
}
public static void main(String[] args){
B b = new B();
A a = new A(b);
}
}

三、匿名类

public class A {
A(int v){
System.out.println("another constructor");
}
A(){
System.out.println("default constructor");
}
void method(){
System.out.println("from A");
}
public static void main(String[] args){
A a = new A(){ //匿名类
void method(){
System.out.println("from anonymous");
}
};
a.method();
}
}
//以上”new A(){…}”定义了一个继承类A的匿名类,大括号内是类A的类体,”new A(){…}”返回
//匿名类的一个实例的引用。
//打印结果为:
default constructor
from anonymous
匿名类本身没有构造方法,但是会调用父类的构造方法。示例代码如下:匿名类有以下特点:

public static void main(){
int v = 1;
A a = new A(v){ //匿名类
void method(){
System.out.println("from anonymous");
}
};
a.method();
}
//以下代码的打印结果为:
another constructor
from anonymous
在以上 “new A(v){…}” 中,如果参数 v 是局部变量,并且在匿名类的类体中会使用它,那么 v 必须是 final 类型,否则会导致编译错误。
public static void main(){
int v = 1; //编译错误,v必须定义为final类型
A a = new A(v){ //匿名类
void method(){
System.out.println("from anonymous"+v); //使用局部变量v
}
};
a.method();
}

匿名类尽管没有构造方法,但是可以在匿名类中提供一段实例初始化代码,Java虚拟机会在调用了父类的构造方法后,执行这段代码。示例如下:
public static void main(){
int v = 1;
A a = new A(v){ //匿名类
{ //实例初始化代码---开始
System.out.println("initialize instance");
} //实例初始化代码---结束
void method(){
System.out.println("from anonymous");
}
};
a.method();
}
//打印结果如下:
another constructor
initialize instance
from anonymous
除了可以在外部类的方法内定义匿名类以外,还可以在声明一个成员变量时定义一个匿名类。示例代码如下:类 A 有一个实例变量 a ,它引用一个继承类 A 的匿名类的实例。注意:匿名类的实例只能有一种初始化方式。
abstract class A{
A a = new A(){
void method(){
System.out.println("inner");
}
};
abstract void method();
}
匿名类和局部内部类一样,可以访问外部类的所有成员,如果匿名类位于一个方法中,还能访问所在方法的 final 类型的变量和参数。匿名类除了可以继承类以外,还可以实现接口,示例代码如下:
public class Sample {
public static void main(){
Thread t = new Thread(new Runnable(){
public void run(){
System.out.println("hi");
}
});
t.start();
}
}

局部内部类的名字在方法外是不可见的,因此与匿名类一样,能够起到封装类型名字的作用,局部内部类与匿名类有以下区别:

匿名类和程序代码比较简短。

一个局部内部类可以有多个重载构造方法,并且客户类可以多次创建局部内部类的实例。而匿名类没有重载构造方法,并且只能创建一次实例。

四、内部接口及接口中的内部类

在一个类中也可以定义内部接口。

在接口中可以定义静态内部类,此时静态内部类位于接口的命名空间中。

五、内部类的类文件

对于每个内部类来说,Java编译器会生成独立的.class文件。这此类文件的命名规则如下:

成员内部类:外部类的名字$内部类的名字

局部内部类:外部类的名字$数字$内部类的名字

匿名类:外部类的名字$数字

六、内部类的特殊用法

从一个内部类的实例拿到所在外部类的实例可以使用this$0默认字段,this$0就是内部类自动保留的一个指向所在外部类的引用。

package com.dailey.example.java.innerclass;
import java.lang.reflect.Field;
public class Outer {//this$0
	public class FirstInner {//this$1
		public class SecondInner {//this$2
			public class ThirdInner {
			}
		}
	}

	public static void main(String[] args) throws Exception,
			NoSuchFieldException {
		Outer test = new Outer();
		FirstInner first = test.new FirstInner();
		FirstInner.SecondInner second = first.new SecondInner();
		FirstInner.SecondInner.ThirdInner third=second.new ThirdInner();
		//Outer
		Field outerfield = first.getClass().getDeclaredField("this$0");
		outerfield.setAccessible(true);
		Object object = outerfield.get(first);
		System.out.println(object instanceof Outer);
		//FirstInner
		Field firstInnerfied = second.getClass().getDeclaredField("this$1");
		firstInnerfied.setAccessible(true);
		object = firstInnerfied.get(second);
		System.out.println(object instanceof FirstInner);
		//SecondInner
		Field secondInnerfield = third.getClass().getDeclaredField("this$2");
		secondInnerfield.setAccessible(true);
		object = secondInnerfield.get(third);
		System.out.println(object instanceof FirstInner.SecondInner);
	}
}
 Java 反编译工具生成以上内部类代码可以看出:每个内部类都有一个指向所在最近的外部类的一个引用 ,只不过该引用不需要手动添加,编译器会负责
public class Outer$FirstInner {// FirstInner内部类有一个带外部类参数的构造子
	public Outer$FirstInner(Outer paramOuter) {
	}
	// ....
}

public class Outer$FirstInner$SecondInner {
	public Outer$FirstInner$SecondInner(Outer.FirstInner paramFirstInner) {
	}
	// ....
}

public class Outer$FirstInner$SecondInner$ThirdInner {
	public Outer$FirstInner$SecondInner$ThirdInner(
			Outer.FirstInner.SecondInner paramSecondInner) {
	}
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值