定义在类内部的类就称之为内部类。根据定义的不同可以分为成员内部类,局部内部类,匿名类,静态内部类。无论是哪一种内部类,我们都可以在内部类中直接存取外部类的私有成员变量。(私有成员属性&私有成员方法,因为内部类持有外部类的引用)
为什么要引入内部类
1.为了更好的封装,将内部类封装在外部类内,可以方便地访问外部类成员变量,同时,将其与其他类进行隔离,不允许同包其他类访问。
2.内部类的属性和方法即便是外部类也不能随意地访问。(可以通过在外部类实例化内部类进行访问,即使该变量是private类型)
3.有利于回调函数的编写。
3.当面向对象时,有利于我们对整体和成员的封装。
成员内部类
成员内部类的语法如下:
public class OuterClass {
private class Inner {
//...
}
}
成员内部类的特性除了最开始提到的可以存取所在外部类的所有成员变量之外,成员内部类的实例必须绑定在外部类的实例上,如果我们在外部类中初始化了一个内部类对象,那么内部类对象就会绑定在外部类对象上。
举例说明:
public class OuterClass {
InnerClass inner = new InnerClass(); //外部类中实例化内部类
private int a = 0;
private void take() {
a = inner.x;
a = inner.y;
//x = 1; //错误,外部类不能直接访问内部类变量
inner.hello();
inner.hi();
}
public InnerClass getInner() { //在外部类方法中返回内部类引用
return new InnerClass();
}
class InnerClass {
public int x = 0; //内部类公有属性
private int y = 0; //内部类私有属性
public void hello() {
x = a; //内部类可以直接访问外部类成员变量
}
private void hi() {
take();
}
}
}
从上面的例子我们可以得知,内部类可以随意访问所在外部类的成员,而内部类的成员只有在内部类的范围内是可知的,不能被外部类使用,比如例子中对内部类的成员属性进行赋值将会报错,但是我们可以通过内部类对象引用成员变量。
OuterClass outerClass = new OuterClass();
//内部类的实例化只能在外部类或外部类的非静态方法中实现
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
OuterClass.InnerClass innerClass1 = outerClass.getInner();
另外,在上面的例子中,如果不使用getInner()方法返回内部类对象引用,可以直接使用内部类实例化内部类对象,但是由于在主方法中来实例化内部类对象,必须在new操作符之前提供一个外部类的引用。
Question
1.为什么外部类可以通过实例化内部类直接访问内部类的private成员?
首先,我们得了解访问权限(Accessibility)如何规定的?
官方定义:
If the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class that encloses the declaration of the member or constructor.
从此我们可以了解到,如果一个类的成员被声明为private类型的,那么在它的顶级类(top level class)的范围内是可以访问的。那么它又是怎么实现的呢?
2.为什么只有内部类可以被声明为private或protected访问类型?
对于顶级类而言,只能声明为public访问权限类型或者缺省类型,public类型表示对所有类可见,缺省类型则为包级访问权限,对同一个package下的类可见。如果一个顶级类被声明为private类型,那么它对于其他类都是不可见的,这样类就毫无意义。说到这,顺便复习下类成员的访问权限,如果一个类的成员被修饰为private,则该成员只能在该类内部使用,在子类中是不可见的,并且对其他包的类也是不可见的。如果一个类的成员被修饰为public,那么除了可以在本类使用使用之外,还可以在子类和其他包的类中使用。如果一个类使用protected修饰符,那么只有本包内的子类或其他类可以访问此类中的成员。
Attention:类的权限设定会约束类成员的权限设定。
局部内部类
内部类不仅可以在类中定义,还可以在类的局部位置定义,如在类的方法或任意的作用域均可以定义内部类。
举例说明:
public class OuterClass {
public void take() {
class InnerClass {
//do something
}
}
}
Attention:局部内部类InnerClass是方法take()的一部分,不是外部类OuterClass的一部分,所以在take()的外部不能访问该内部类,而InnerClass可以访问当前代码块的成员以及外部类的所有成员。
匿名内部类
举例说明:
public class OuterClass {
public MyInterface handle() {
return new MyInterface() {
@Override
public void take() {
//do something
}
};
}
}
在handle()方法中,我们通过return后面插入一个定义内部类的代码,返回一个MyInterface接口的引用,由于这个类没有名称,所以将之称为匿名内部类。
静态内部类
在内部类前面添加static修饰符,这个内部类就变成了静态内部类,一个静态内部类中可以声明静态成员,在非静态内部类中不可以声明静态成员。静态内部类有一个最大的特点就是不可以使用外部类的非静态成员。当然,如果创建静态内部类的对象,不需要其外部类的对象。
public class OuterClass {
private int x = 0;
private static int y = 0;
static class InnerClass {
void handle() {
//System.out.println(x); //wrong
System.out.println(y); //right
}
}
}