内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
内部类可以是静态static的,也可用public,default,protected和private修饰。
内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。
广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。
成员内部类
public class OuterClass {
private int outerInt = 1;
private int outerInt2 = 2;
class InnerClass {
private String str = "inner class";
private int outerInt2 = 3;
public void value() {
System.out.println(str);
System.out.println("访问外部类的成员变量outerINt:" + outerInt);
System.out.println("访问自身成员变量outerINt2:" + outerInt2);
System.out.println("访问外部类的成员变量outerINt2:" + OuterClass.this.outerInt2);
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass c = outer.new InnerClass();
c.value();
}
}
成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。因为当某个外部类对象创建一个内部类对象时,此内部类对象会秘密的捕获一个指向外部类对象的引用。同时外部类要访问内部类的所有成员变量/方法,则需要通过内部类的对象来获取。
成员内部类不能含有static的变量和方法。
在成员内部类要引用与外部类对象同名的成员变量或者方法时,要使用OuterClassName.this来表示外部类对象;
创建内部类对象,可以使用OuterClassName.InnerClassName inner = OuterClassObject.new InnerClassName;
局部内部类
Thinking in Java给了这么两个例子:
定义在方法内:
public class Parcel4 {
public Destination destination(String s) {
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Destination d = p.destination("Tasmania");
}
}
定义在作用域里:
public class Parcel5 {
private void internalTracking(boolean b) {
if (b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() {
return id;
}
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
}
}
public void track() {
internalTracking(true);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
p.track();
}
}
局部内部类也像别的类一样进行编译,但只是作用域不同而已,只在该方法或条件的作用域内才能使用,退出这些作用域后无法引用的。
局部内部类只能访问方法体中的常量,即用final修饰的成员。
外围类看不见方法中的局部内部类的,但是局部内部类可以访问外围类的任何成员。
方法体中可以访问局部内部类,但是访问语句必须在定义局部内部类之后。
注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。
静态内部类
class Outter {
public Outter() {
}
static class Inner {
public Inner() {
}
}
}
public class Test {
public static void main(String[] args) {
Outter.Inner inner = new Outter.Inner();
}
}
静态内部类,就是修饰为static的内部类。声明为static的内部类,不需要内部类对象和外部类对象之间的联系,就是说我们可以直接引用outer.inner,即不需要创建外部类,也不需要创建内部类。
静态内部类不能访问外部类的非静态成员变量和方法。
嵌套类和普通的内部类还有一个区别:普通内部类不能有static数据和static属性,也不能包含嵌套类,但嵌套类可以。而嵌套类不能声明为private,一般声明为public,方便调用。
接口中可以存在静态内部类。(如果想要创建某些公共的代码。使得每个实现该接口的类可以共用)
声明内部类:OuterClassName.InnerClassName
创建内部类:OuterClassObject.new InnerClassName
创建内部类之前,必须是首先创建外部类对象。
匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
这里创建了一个实现Runnable接口的匿名对象,并通过new表达式返回了一个被自动向上转型为对Runnable的引用。
匿名内部类是不能加访问修饰符的。
匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备。而且实现接口,也只能一次实现一个。
要注意的是,new 匿名类,这个类是要先定义的,看下面例子:
public class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city) {
return new Inner() {
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
}
interface Inner {
String getName();
}
同时在这个例子,留意外部类的方法的形参,当所在的方法的形参需要被内部类里面使用时,该形参必须为final。这里可以看到形参name已经定义为final了,而形参city 没有被使用则不用定义为final。
因为匿名内部类,没名字,是用默认的构造函数的,无参数的,那如果需要参数呢?则需要该类有带参数的构造函数:
public class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
}
public Inner getInner(final String name, String city) {
return new Inner(name, city) {
private String nameStr = name;
public String getName() {
return nameStr;
}
};
}
}
abstract class Inner {
Inner(String name, String city) {
System.out.println(city);
}
abstract String getName();
}
注意这里的形参city,由于它没有被匿名内部类直接使用,而是被抽象类Inner的构造函数所使用,所以不必定义为final。
匿名内部类通过实例初始化,可以达到类似构造器的效果:
public class Outer {
public static void main(String[] args) {
Outer outer = new Outer();
Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
System.out.println(inner.getProvince());
}
public Inner getInner(final String name, final String city) {
return new Inner() {
private String nameStr = name;
private String province;
// 实例初始化
{
if (city.equals("gz")) {
province = "gd";
}else {
province = "";
}
}
public String getName() {
return nameStr;
}
public String getProvince() {
return province;
}
};
}
}
interface Inner {
String getName();
String getProvince();
}
内部类继承
内部类的继承,是指内部类被继承,普通类 extents 内部类。而这时候代码上要有点特别处理,具体看以下例子:
public class InheritInner extends WithInner.Inner {
// InheritInner() 是不能通过编译的,一定要加上形参
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner obj = new InheritInner(wi);
}
}
class WithInner {
class Inner {
}
}
可以看到子类的构造函数里面要使用父类的外部类对象.super();而这个对象需要从外面创建并传给形参。否则,编译器会报错。
内部类覆盖
当类A继承了类B,并且声明了一个和B类中同名的内部类时,覆盖并没有发生,这两个内部类时完全独立的两个实体,各自在自己的命名空间内;
内部类的使用场景和好处
1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
2.内部类使得多继承的解决方案变得完整;而且可以让多个内部类以不同的方式实现同一个接口,或继承同一个类
3.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。