java内部类和嵌套类(静态内部类)
- 创建内部类
内部类的创建和使用与普通类并没有什么不同,只是把一个类的定义置于外围类里面。如:
public class Out{
class Inner{
}
public Inner b(){
return new Inner();
}
}
典型的情况如上所示,在外部类提供一个方法,该方法返回一个指向内部类的引用。
如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须具体地指明这个对象的类型:OutClassName.InnerClassName。
2. 链接到外部类
当生成一个内部类的对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用,通过此引用,内部类就可以访问其外围的方法和字段,包括private,就像拥有它们一样。
3. 使用.this和.new
- 如果需要生成对外部类对象的引用,可以使用外部类的名字紧跟.this,这样产生的引用自动地具有正确的类型。
public class Outer{
class Inner{
public Outer b(){
return Outer.this;
}
}
}
- 如果需要告知某些其他对象,去创建其某个内部类的对象。要实现此目的,必须在new表达式中提供对其他外部类对象的引用,这就需要.new语法。
public class Outer{
public class Inner{
}
public static void newInner(){
Outer out = new Outer();
Outer.Inner in = out.new Inner();
}
}
在这里要注意,不能用外部类的名字Outer,而是必须使用外部类的对象来创建该内部类对象。在拥有外部类对象之前是不可能创建内部类对象的,因为内部类会暗暗连接到创建他的外部类对象上。(当嵌套类(即静态内部类)则不需要对外部类对象的引用)
-
在方法和作用域内的内部类
内部类不仅可以定义在类中,也可以定义在一个方法里面或者任意的作用域内。 -
匿名内部类
- 匿名内部类是将对象的生成和类的定义结合在一起的形式。
public class Outer{
public InnerWithoutName withOutName(String param, final String finalParam){
return new InnerWithOutName( param ){ //有参构造器,也可以没有
private String lable = finalParam;
public String readStr(){ return lable;}
}
}
}
如果定义一个匿名类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。
- 在匿名类中不可能有命名构造器(因为它根本没有名字!),但通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果。
public class Outer{
public Inner in(){
new Inner(){
private int cost;
{
cost = Math.round(10); //实例初始化达到构造器效果
}
}
}
}
- 匿名内部类与正规的继承相比有些限制,因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼顾。而且如果是实现接口,也只能实现一个接口。
- 嵌套类(静态内部类)
- 如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static,这通常称为嵌套类。
- 内部类和嵌套类的区别:
1、创建嵌套类的对象,不需要其外围类对象,而创建普通内部类则需要通过外围类对象来创建;
2、嵌套类不能访问外围类中非static的对象/字段,而内部类可以;
3、普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但嵌套类可以包含所有这些东西。 - 接口内部的类
正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,放到接口中的任何类都自动地是public和static。如果想创建可以被某个接口的所有不同实现共用的代码,那么使用接口内部的嵌套类会显得很方便。
public interface ClassInterface{
class Test implements ClassInterface{ //默认public和static
public void howdy(){
System.out.println("howdy");
}
}
}
- 从多层嵌套类中访问外部类的成员:一个内部类被嵌套多少层并不重要,它能透明地访问所有它嵌入的外围类的所有成员。
- 为什么需要内部类
一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。
如果只是需要一个对接口的引用,则应该通过外部类实现那个接口,区别在于外部类实现接口不是总能享用到接口带来的方便,有时需要用到接口的实现。
所以,使用内部类最主要的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
- 如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。
- 使用内部类的一些特性:
1、内部类可以有多个实例,每个实例都有自己的状态信息,并且与其他外围类对象的信息相互独立;
2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类;
3、创建内部类对象的时刻并不依赖于外围类对象的创建。
- 内部类的继承
内部类的构造器必须连接到指向其外围类对象的引用,那个指向外围类对象的“秘密的”引用必须被初始化,而在导出类中不再存在可连接的默认对象。要解决这个问题,必须使用特殊的语法来明确说清它们之间的关联。
class WithInner{
class Inner{}
}
public class Inner extends WithInner.Inner{
// InheritInner(){} //默认构造器形式不能通过
InheritInner(WithInner wi){
wi.super();
}
}
可以看到InheritInner继承自内部类,但要生成一个构造器时,默认的构造器并不能用,而且不能只是传递一个指向外围类对象的引用,在构造器中必须使用如下语法才能提供必要的引用。
- 内部类可以被覆盖吗
如果继承类一个外围类并重新定义内部类时,会发生什么呢?也就是说,内部类可以被覆盖吗?实际上“覆盖”内部类就好像它是外围类的一个方法,其实并不起什么作用。下面的例子说明,当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化,这两个内部类是完全独立的两个实体,各自在自己的命名空间内。
class Egg{
private Yolk y;
protect class Yolk{
public Yolk(){ print("Egg Yolk()");}
public Egg(){
print("New Egg()");
y = new Yolk();}
}
}
public BigEgg extends Egg{
public class Yolk{
public Yolk(){print("BigEgg Yolk()");}
public static void main(String[] args){
new BigEgg();
}
}
}
输出:
NewEgg()
Egg Yolk() //注意并不是 BigEgg Yolk() ,说明并没有覆盖内部类
如果要实现这种“覆盖”的效果,需要明确的继承某个内部类。
public class BigEgg2 extends Egg2{
public class Yolk extends Egg2.Yolk{
public Yolk(){print("New Egg2()");} //new的时候同样是现初始化基类再子类
}
}
- 局部内部类
可以在代码块里创建内部类,称为局部内部类。局部内部类不能有访问说明符,因为它不是外围类的一部分,但它可以访问当前代码块内的常量,以及此外围类的所有成员。
已经有匿名内部类类为什么还要使用局部内部类?唯一的理由是,我们需要一个已命名的构造器或者需要重载构造器、或者需要不止一个该内部类的对象,而匿名内部类只能用于实例初始化。