成员内部类 (普通非静态内部类)
- 成员内部类不能包含 static 成员和 static 方法。也不能包含静态内部类。
- 成员内部类可以访问外部类的成员和方法,他提供了一个访问外部类数据的钩子。在实现上内部类对象秘密包含了一个指向外部类对象的引用,所以成员内部类对象与外部类对象是紧紧关联的,只能通过外部类对象来创建成员内部类。
public class MyList {
private Object[] values;
private int length;
public MyList(int length){
this.length = length;
values = new Object[length]; //创建引用数组
}
public Boolean put(int subscript,Object obj){
if(subscript < 0 || subscript >= length)
return false;
values[subscript] = obj;
return true;
}
public Object get(int subscript){
if(subscript <0 || subscript >= length)
return null;
return values[subscript];
}
public Selector getSelector(){
return new MySelector();
}
private class MySelector implements Selector{
private int current = 0;
public boolean end() {
return current == length;
}
public Object current() {
return values[current];
}
public void next() {
current++;
}
}
public static void main(String[] args){
MyList list = new MyList(10);
for(int i = 0; i < 10; i++){
list.put(i,new Integer(i));
}
Selector mySelector = list.getSelector();
while (!mySelector.end()){
System.out.println(mySelector.current());
mySelector.next();
}
}
}
interface Selector {
boolean end();
Object current();
void next();
}
- 在外部类的非静态方法中可以直接创建内部类,在其他地方必须指明内部类对象的类型:OuterClassName.InnerClassName。
- 通过外部类对象创建内部类需要使用:OuterClassObj.new InnerClassName() 。在内部类中获取外部类对象需要使用 OuterClassName.this 。
public class Outer {
public class Inner {
public Outer getOuter() {
return Outer.this;
}
}
@Override
public String toString(){
return "outer class";
}
public static void main(String[] args){
Outer outerObj = new Outer();
System.out.println(outerObj);
Outer.Inner innerObj = outerObj.new Inner();
System.out.println(innerObj.getOuter());
}
}
- 成员内部类还可以嵌套成员内部类,不管嵌套多少层,内部类总能透明的访问所有的外部类的成员。
public class A {
private void f(){
System.out.println("A.f()");
}
public class B {
private void f(){
A.this.f();
System.out.println("B.f()");
}
public class C {
private void f(){
A.this.f();
B.this.f();
System.out.println("c.f()");
}
}
}
}
## 局部内部类
- 在方法或者作用域中定义内部类的理由:
- 和成员内部类相同,实现某个接口,创建并返回对其的引用。
- 创建一个类辅助实现某个方案,但是又不希望这个类是公共可用的。
- 局部内部类和匿名内部类的区别是:局部内部类可以重载构造器,而匿名内部类只能用于初始化。
- 局部内部类与匿名内部类相同,可以访问当前代码块内的常量以及外部类的全部成员。
public class Outer {
private int value = 0;
public void increase(final int size){
final int fixedValue = 1;
int b = 2;
class Inner implements Increasable {
public void increase() {
value += size + fixedValue;
// b++; 非 final 类型
}
}
}
}
interface Increasable {
void increase();
}
public class Outer {
private int value;
private String name;
public Outer(int value){
this.value = value;
}
public void get() {
final int a = 1;
int b = 2;
if(value > 0){
final int c = 2;
int d = 3;
class Inner {
public String getName(){
return "get name from inner";
}
public void test(){
System.out.println(a);
//System.out.println(b);
System.out.println(c);
//System.out.println(d);
}
}
Inner inner = new Inner();
name = inner.getName();
}
}
}
- 局部内部类只能使用默认的包权限。
匿名内部类
- 所有的普通类,抽象类,接口都会被内部类当做“公共接口”来使用,并自动向上转型。
public class Outer {
public B getB(){
return new B(){
public void getName() {
System.out.println("B.getName");
}
};
}
public C getC(){
return new C(){
public void getName() {
System.out.println("C.name");
}
};
}
public D getD(){
return new D(){
@Override
public void getName(){
System.out.println("D.getName");
}
};
}
}
interface B {
void getName();
}
abstract class C {
abstract public void getName();
}
class D {
public void getName(){
System.out.println("c.getName");
}
}
- 同上一点,匿名内部类既可以扩展类,也可以实现接口,但是两者不能兼备,并且实现接口时也只能实现一个。
- 匿名内部类和普通内部类一样,不能在定义时包含 static 关键字,但是它继承的父类和接口可以包含。
- 匿名内部类与它的外部方法构成一个闭包关系,内部类可以直接使用当前代码块的常量(以及外部类的全部成员)。由于在实现上,匿名内部类访问外部方法的变量采用拷贝并在内部类保存一份的方式,所以两者有可能出现不一致的情况。为防止这种情况,Java 规定了内部类使用的参数必须使用 final 修饰:
- 对于基本数据类型,final 修饰即为不可修改。
- 而对于对象,final 修饰导致不能更改引用,但是对象的内容修改之后,两者数据还是一致的。因为他们都指向同一个对象。
public class Outer {
private int x = 1;
public Calculator getCalculator(final int y,String name){
return new Calculator(name) {
public int add() {
// 可以使用父类的静态方法和静态成员
System.out.println(className);
getClassName();
return x + y;
}
// 不能出现 static 关键字
// public static void test(){}
};
}
}
abstract class Calculator{
private String name;
public static String className;
public Calculator(String name){
this.name = name;
className = "calculator";
}
public abstract int add();
public static void getClassName(){
System.out.println(className);
}
}
- 匿名内部类实现的父类构造器带有参数的情况,并不需要 final 修饰。他们是通过参数传递来实现的,上一点是直接使用外部的常量。
静态内部类(嵌套类)
- 使用 static 修饰的内部类可以独立与外部类存在,与外部类没有任何联系。
- 静态内部类并不能访问非静态的外围类成员。
public class Outer {
private int x = 1;
private static int y = 2;
public static class Inner {
public static Outer getOuter(){
return new Outer();
}
public void test() {
System.out.println(y);
//System.out.println(x);
}
}
public Inner getInner() {
return new Inner();
}
public static void main(String[] args){
Outer outer = new Outer();
//Inner inner = outer.new Inner();
Inner inner = new Outer.Inner();
}
}
为什么需要内部类
- 通常内部类继承了某个类或者实现的某个接口,内部类的代码操作创建它的外围类的对象。所以内部类提供了某种进入外围类的接口。
- 通过内部类可以有效的解决多重继承,即内部类继承了多个非接口类型(类或者抽象类)。
- 其他特性:
- 内部类可以有多个实例,每个实例都有自己的状态信息,并且与外围类对象的信息相互独立。
- 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
- 创建内部类的对象的时刻并不依赖于外围类的创建(但是非静态内部类必须先有外部类)。
- 内部类没有令人迷惑的 is-a 关系(因为有的时候是 like-a ),他是一个独立的实体。
其他
- 接口中的内部类自动都是 public 和 static 的,所以可以在接口中创建某些公共代码,使得他们可以被接口的所有不同实现所共用。
- 可以将每个类的测试代码写在静态内部类中,内部类和外部类会单独编译,测试完去掉编译后的内部类文件即可。
public class TestBed {
public void f(){ System.out.println("f()"); }
public static class Tester {
public static void main(String[] args){
TestBed t = new TestBed();
t.f();
}
}
}