内部类分为以下四种:
- 静态内部类
- 成员内部类
- 局部内部类
- 匿名内部类
一、静态内部类
静态内部类: 在类中用static关键字声明的内部类。
静态内部类不依赖于外围类对象实例而独立存在
静态内部类的可以访问外围类中的所有静态成员,包括private的静态成员。
同时静态内部类可以说是所有内部类中独立性最高的内部类,其创建对象、继承(实现接口)、扩展子类等使用方式与外围类并没有多大的区别。
public class OuterClass {//外部类
public int aa; //普通成员变量
private static float f = 1.5f;//private的静态成员
static void myPrintln() {
System.out.println("静态方法");
}
protected static class StaticInnerClass{//protected的静态内部类
float a;
public StaticInnerClass() {
a = f;// 外围类的private静态变量
myPrintln();//外围类的静态方法
}
}
}
class OtherClass{
public static void main(String[] args) {
//创建静态内部类的对象
OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();
}
}
二、成员内部类
成员内部类是最普通的内部类,它的定义为位于一个类的内部
成员内部类可以访问外部类的所有成员属性和成员方法(包括静态成员,可以拥有private访问权限、protected访问权限、public访问权限及包访问权限)
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
成员内部类是不可以声明静态成员(包括静态变量、静态方法、静态成员类、嵌套接口),但有个例外:可以声明 static final的变量, 这是因为编译器对final类型的特殊处理,是直接将值写入字节码
成员内部类对象都隐式地保存了一个引用,指向创建它的外部类对象;或者说,成员内部类的入口是由外围类的对象保持着(静态内部类的入口,则直接由外围类保持着)
成员内部类中的 this关键字:
- 获取外部类对象:OuterClass.this
- 明确指定使用外部类的成员(当内部类与外部类的名字冲突时):
OuterClass.this.成员名
成员内部类访问外部类成员
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,默认情况下访问的是成员内部类的成员。
如果要访问外部类的同名成员,需要以下面的形式进行访问:
- 外部类.this.成员变量
- 外部类.this.成员方法
外部类访问成员内部类的成员
1.先创建一个成员内部类的对象
2.再通过指向这个对象的引用来访问。
创建成员内部类对象的一般方式:
在外部类的成员方法中创建成员内部类 (内部类名 对象名=new 内部类名();)与 在其他类中或静态方法中创建成员内部的方式是不一样的
public class Test {
public static void main(String[] args) {
//第一种方式:
Outter outter = new Outter();
Outter.Inner inner = outter.new Inner(); //必须通过Outter对象来创建
//第二种方式:
Outter.Inner inner1 = outter.getInnerInstance();
}
}
///////////////////////////////////////////////////////////////////////////////
class Outter {
private Inner inner = null;
public Outter() {
}
public Inner getInnerInstance() {
if(inner == null)
inner = new Inner();
return inner;
}
class Inner {
public Inner() {
}
}
}
上面的例子,如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
成员内部类被继承
在内部类的访问权限允许的情况下,成员内部类也是可以被继承的。由于成员内部类的对象依赖于外围类的对象。因此,继承了成员内部类的子类必须要与一个外围类对象关联起来。同时,子类的构造器是必须要调用父类的构造器方法,所以也只能通过父类的外围类对象来调用父类构造器。
class ChildClass extends OuterClass.InnerClass{
//成员内部类的子类的构造器的格式
public ChildClass(OuterClass outerClass) {
outerClass.super();//通过外围类的对象调用父类的构造方法
}
}
三、局部内部类
局部内部类: 就是在方法、构造器、初始化块中声明的类,在结构上类似于一个局部变量。
局部内部类的访问仅限于方法内或者该作用域内,局部内部类是不能使用访问修饰符。
局部内部类只能访问final的局部变量
局部内部类定义在实例环境中(构造器、对象成员方法、实例初始化块),则可以访问外围类的所有成员;但如果内部类定义在静态环境中(静态初始化块、静态方法),则只能访问外围类的静态成员
public class OuterClass {
private int a = 21;
static {//静态域中的局部内部类
class LocalClass1{
// int z = a; //错误,在静态的作用域中无法访问对象成员
}
}
{//实例初始化块中的局部内部类
class localClass2{
}
}
public OuterClass(){
int x = 2;
final int y = 3;
// x = 3;//若放开此行注释,编译无法通过,因为局部变量x已经是final类型
//构造器中的局部内部类
class localClass3{
int z = y; //可以访问final的局部变量
int b = a;//可以访问类的所有成员
//访问没有用final修饰的局部变量
int c = x;
}
}
public void createRunnable() {
final int x = 4;
//方法中的局部内部类
class LocalClass4 implements Runnable {//
@Override
public void run() {
System.out.println("局部final变量:"+x);
System.out.println("对象成员变量:"+a);
}
}
}
}
四、匿名内部类
匿名内部类也是不能有访问修饰符和static修饰符的。
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class、Outter$2.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
在创建这个匿名内部类后,便会立即用来创建并返回此内部类的一个对象引用。
作用:
匿名内部类用于隐式继承某个类(重写里面的方法或实现抽象方法)或者实现某个接口。
匿名内部类的访问限制:
在实例环境中(构造器、对象成员方法、实例初始化块),则可以访问外围类的所有成员;但如果内部类定义在静态环境中(静态初始化块、静态方法),则只能访问外围类的静态成员
匿名内部类的优缺点:
优点:
编码方便快捷;缺点:
只能继承一个类或实现一个接口,不能再继承其他类或其他接口。
只能用于创建一次对象实例;
一个例子:
class MyOuterClass {
private int x = 5;
void createThread() {
final int a = 10;
int b = 189;
// 匿名内部类继承Thread类,并重写Run方法
Thread thread = new Thread("thread-1") {
int c = x; //访问成员变量
int d = a; //final的局部变量
int e = b; //访问没有用final修饰的局部变量
@Override
public void run() {
System.out.println("这是线程thread-1");
}
};
// 匿名内部类实现Runnable接口
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("线程运行中");
}
};
}
}
内部类嵌套内部类
- 成员内部类可以继续包含成员内部类,而且不管一个内部类被嵌套了多少层,它都能透明地访问它的所有外部类所有成员;
- 成员内部类可以继续嵌套多层的成员内部类,但无法嵌套静态内部类;
- 静态内部类则都可以继续嵌套这两种内部类。
下面的例子是基于上面的例子进行改造:
class InnerClass{//成员内部类
private double aa; //与围类的变量aa的名字重复
public InnerClass(){
this.aa = OuterClass.this.aa + f;//明确指定两个aa的所属
initInnerClass();
}
public class InnerInnerCalss2{//成员内部类中的成员内部类
protected double aa = OuterClass.this.aa;//最外层的外围类的成员变量
}//InnerInnerCalss2
}//InnerClass
总结
类 型 | 访问修饰符 | 声明静态成员 | 绑定外围类 |
---|---|---|---|
静态内部类 | 四种访问修饰符 | 可以声明 | 不绑定 |
成员内部类 | 四种访问修饰符 | 除 final static 的变量外,其余静态成员都不行 | 绑定 |
局部内部类 | 不可以声明 | 不可以声明 | 取决于此内部类的声明环境 |
局部内部类 | 不可以声明 | 不可以声明 | 取决于此内部类的声明环境 |
为什么在Java中需要内部类
1. 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,
2. 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
3. 方便编写事件驱动程序
4. 方便编写线程代码