大多数时候,类被定义为一个独立的程序单元。在某些情况下,也会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类。包含内部类的类也被称为外部类。内部类的作用主要如下:
从语法角度来说,定义内部类与定义外部类的语法大致相同,内部类除需要定义在外部类里面之外,还存在如下两种区别。
定义内部类非常简单,只要把一个类放在另一个类内部定义即可。此处的“类内部”包括类中的任何位置,甚至在方法中也可以定义内部类(方法里定义的内部类被称为局部内部类)。
大部分情况下,内部类被作为成员内部类定义,而不是局部内部类。成员内部类是一种与成员变量、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。
成员内部类分为两种:静态成员内部类与非静态成员内部类,使用static修饰的成员内部类是静态成员内部类,没有使用static修饰的成员内部类则是非静态成员内部类。
经常看到多个类定义写在一个源文件中,但它们之间相互独立,所以这种情况并不是成员内部类。
因为成员内部类时定义在外部类当中作为其外部类的成员,所以可以使用任意的访问控制符如private,protected和public等修饰。
外部类的上一程序单元集是包,所以它只有两个作用域:同一个包内和任意位置,因此只需要两种访问权限:包访问权限和公开访问权限,正好对应省略访问控制符和public访问控制符。而内部类的上一程序单元为外部类,他就具有四个作用域:同一个类、同一个包、父子类和任何位置,因此可以使用四种访问权限。
在非静态内部类里可以直接访问外部类的private成员,这是因为在非静态内部类里保存了一个外部类对象的引用(当调用非静态内部类方法的时候,必须要有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例里)。
当在非静态内部类里使用某个变量的时候,系统优先在该方法内查找是否存在该名字的局部变量,如果存在则使用该局部变量,其次是非静态内部类的成员变量,再其次是外部类的成员变量。如果依旧不存在系统编译将会报错。
因此,如果局部变量,内部类成员变量,外部类成员变量名字相同的时候可以通过成员变量名,this关键词,外部类名加this来指定所要访问的变量。
非静态内部类可以访问外部类的成员,但是反过来就不成立了,如果外部类想要访问内部类成员变量,则必须要有一个内部类的实例,才能访问外部类变量。
根据静态成员不能访问非静态成员的规则,外部类的静态方法,静态代码块不能实例化非静态内布列,也不允许使用非静态内部类的方法和变量等。
同样,java不允许再非静态内部类当中定义静态成员。非静态内部类当中不能包含静态方法,静态成员变量,静态初始化块等。非静态内部类当中不能包含静态初始化块但是可以包含普通初始化块。
如果使用static来修饰某一个内部类,那这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类,也被称为静态内部类。
static关键词的作用是将类的成员变为类相关,而不是实例相关,即static修饰的成员属于整个类,而不属于某个对象。外部类的上一级程序单元是包,因此不能使用static关键词修饰,而内部类的上一级程序单元为外部类,因此可以使用static关键词修饰。
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员,即使是静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员。
静态内部类是外部类的一个静态成员,因此外部类的所有方法、所有初始化块中都可以使用静态内部类来定义变量创建方法等。
外部类依然不能直接访问静态内部类的成员,但可以用静态内部类的类名作为调用者来访问静态内部类的成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例变量。
package com.eight.Inner;
public class StaticInnerClassTest{
private int prop1 = 5;
private static int prop2 = 9;
static class StaticInnerClass{
private static int age;
public void accessOuterProp(){
//System.out.println(prop1);
//上句代码会报编译错误
System.out.println(prop2);
}
}
}
除此之外,java还允许再接口内定义内部类,接口默认使用public static修饰,也就是说接口内部类只能是静态内部类。
如果为接口内部类指定访问控制符,则只能指定public访问控制符;如果定义接口内部类时省略访问控制符,则给内部类默认是public访问控制权限。
定义类的主要作用就是定义变量、创建实例和作为父类继承。定义内部类的主要作用也是如此,但是使用内部类定义变量和创建实例则与外部类存在一些小小的差异。
在外部类使用内部类,与平常使用内部类的方式基本是一样的。唯一的区别在于不能在静态代码块部分使用非静态内部类。
如果希望在外部类以外的地方访问内部类,则不能使用private修饰内部类。这一点与其他类成员访问权限相同。同理被public,protect和默认修饰符所修饰的内部类所具有访问权限也与其他类成员相似。
当其他外部类可以访问内部类并创建实例时可以使用以下语法定义:
外部类名.内部类名 VarName
从上面的语法可以看出,在外部类以外的地方使用内部类时,内部类完整的类名应该是 ‘外部类名.内部类名’ 。如果外部类有包名则应该增加包名。
由于非静态内部类的对象必须寄生在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象。在外部类之外的地方创建非静态内部类实例的语法如下:
外部类名.new 内部类构造方法
从上面的语法格式可以看出,在外部类之外的地方创建非静态内部类实例必须使用外部类实例和new 来调用非静态内部类的构造器。
当需要在外部以外地方创建非静态内部子类,需要尤其注意以上规则:非静态内部类的构造器必须使用外部类对象来调用。
当创建一个子类的时候往往都会调用父类的构造器,因此在创建非静态内部子类时,必须保证让子类构造器调用非静态内部类的构造器。调用非静态内部类构造器时,必须存在一个外部类对象。
因为静态内部类是外部类类相关的,因此创建静态内部类对象时无须创建外部类对象。在外部类以外的地方创建静态内部类实例的语法如下:
new 外部类名.内部类构造方法();
不管是静态内部类还是非静态内部类,他们声明变量的语法都是完全一样的。区别只是在创建内部类对象时,静态内部类只需使用外部类即可调用构造器。而非静态内部类则需要先生成一个对象。
如果把一个内部类放在方法里定义,那他就是一个局部内部类,局部内部类仅在该方法里有效。由于局部内部类不能在外部类的方法以外地方使用,因此局部内部类不能使用static和访问控制符修饰。
匿名内部类适合创建那种只需要一次使用的类,创建内部类的时会立即创建一个该类的实例,这个类定义会立即消失,匿名内部类不能重复使用。
定义匿名内部类的格式如下:
new 实现接口()|父类构造器(实参列表){
//匿名类的实体部分
}
从上述定义可以看出,匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或者实现一个接口。
关于匿名内部类还含有以下两条规则:
最常用的创建匿名内部类的方式是需要创建某个接口类型的对象。
package com.eight.Inner;
interface Product{
double getPrice();
String getName();
}
public class AnonymousTest {
public void test(Product product){
System.out.println("购买了一个"+product.
getName()+",花掉了"+product.getPrice());
}
public static void main(String[] args) {
AnonymousTest ta=new AnonymousTest();
ta.test(new Product(){
@Override
public double getPrice() {
// TODO Auto-generated method stub
return 57858;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return "显卡";
}
});
}
}