一、内部类
-
什么是内部类
内部类:把一个类定义在另一个类的内部,这个类就叫做内部类,而包含这个类的类就叫外部类。按照范围,内部类可以简单分为三种:静态内部类,成员内部类和局部内部类。这三种内部类的角色简单的对应了类变量、成员变量和局部变量。
package oop;
/*
* OuterClass是外部类,他包含了内部类InnerClass
*/
public class OuterClass {
/*
* InnerClass是内部类,因为他定义在类OuterClass中
*/
class InnerClass{
}
}
-
为什么需要内部类
想象这样一个场景,人和心脏的关系,人包含了心脏,这样的关系可以利用组合来实现也可以利用内部类来实现。利用组合来实现,也就是定义一个心脏类,在人类中引用;利用内部类来实现,即在人类中定义一个心脏类。但是利用哪一种方式来实现会更好呢?在人的内部会有很多私有属性是不让其他人直接访问的,和心脏密切相关的例如血管动脉等,这些属性不必让外部访问,也没必要对外提供接口,如果用组合关系来实现,那么心脏所涉及的属性将要全部对外提供访问的接口,并且在定义心脏的时候需要传入这些接口,这将会增加了编码的复杂性。除此之外,心脏作为人类的内部属性之一,也不希望能被其他类访问,如果心脏类在人类外部定义,那么除了人类之外的其他类也可以访问。简单总结,内部类具有以下特点:
1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之中,不允许同一个包中的其他类访问,也就是内部类可以用private修饰;
2. 内部类成员可以直接访问外部类中私有数据;
3. 对于创建那些只需要使用一次的类,匿名内部类更适合;
二、内部类详细
-
非静态内部类
非静态内部类即是成员内部类,可以简单这样理解,成员内部类的角色和成员变量、方法等扮演的角色相同,例如成员内部类可以用成员变量的修饰符修饰。
1. 成员内部类的定义可以使用例如private、final、static(即静态内部类)等修饰符,也可以使用default、protected、public等修饰符,即成员变量可以使用的修饰符,成员内部类都可以使用;
2. 成员内部类内部不能定义static修饰的成员,当然属性的宏替换例外,即static final修饰的属性除外,因为在类加载阶段就已经替换成常量,除此之外,根据静态不能方为非静态的原则,外部类的static修饰成员也不能引用成员内部类;
3. 成员内部类可以直接访问外部类的所有属性和方法,包括private、static修饰符修饰的属性和方法,其中,如果成员内部类中定义和外部类同名的属性和方法,则不能直接使用,需要通过OuterClass.this.filedName和OuterClass.this.functionName来调用外部类包含的属性和方法;
4. 想要在外部类之外使用成员内部类(权限和访问修饰符保持一致,例如private修饰的内部类不能在外部类以外访问,default不能在包以外访问),则需要先有外部类对象才能实例化内部类对象:OuterClass.InnerClass oi = new OuterClass().new InnerClass();或者OuterClass oc = new OuterClass;OuterClass.InnerClass oi = oc.new InnerClass();
根据内部类实例化的方式可以知道,内部类想要实例化必须要先有外部类的实例存在,因此非静态内部类内部不能用static修饰,除了宏替换. 在类加载阶段可以替换成常量。
package oop;
/*
* OuterClass是外部类,他包含了内部类InnerClass
*/
public class OuterClass {
/*
* InnerClass是内部类,因为他定义在类OuterClass中
*/
class InnerClass {
}
public static void main(String[] args) {
/*
* 在外部类以外引用内部类
*/
OuterClass.InnerClass oi = new OuterClass().new InnerClass();
}
}
-
静态内部类
用static修饰成员内部类,则该内部类是静态内部类。静态内部类的角色就相当于外部类中的静态成员(静态属性和静态方法),他直接属于外部类本身,而不是外部类的实例对象。
1. 静态内部类的修饰符和成员内部类一致;
2. 根据静态不能访问非静态的原则,静态内部类只能访问外部类中的静态成员;
3. 和成员内部类区别的是,静态内部类内部可以定义static和非static修饰的成员,而成员内部类内部不能用static修饰;
4. 要想在外部类以外访问静态内部类,不需要先有外部类的实例对象,直接使用外部类类名即可:OuterClass.InnerClass oi = new OuerClass.InnerClass();
package oop;
/*
* OuterClass是外部类,他包含了内部类InnerClass
*/
public class OuterClass {
/*
* InnerClass是内部类,因为他定义在类OuterClass中
*/
static class InnerClass {
}
public static void main(String[] args) {
/*
* 在外部类以外引用内部类
*/
OuterClass.InnerClass oi = new OuterClass.InnerClass();
}
}
-
局部内部类
局部内部类定义在方法或者代码块中,局部内部类的角色相当于局部变量,局部变量不能使用修饰符,因此局部内部类也不能使用任何修饰符,所以局部内部类内部不能定义static修饰的成员,而且java规定,局部内部类访问外部类的局部变量,则该局部变量必须用final修饰。
package oop;
/*
* OuterClass是外部类,他包含了内部类InnerClass
*/
public class OuterClass {
public void show(){
/*
* 方法中定义局部内部类
*/
class LocalClass {
}
}
}
但是在JDK 1.8当中,局部内部类访问外部类的局部变量,已经不需要再定义成final类型,如以下例子。
package oop;
/*
* OuterClass是外部类,他包含了内部类InnerClass
*/
public class OuterClass {
public void show(){
/*
* 方法中定义局部内部类
*/
int y = 4;
class LocalClass {
void function(){
System.out.println(y);
}
}
new LocalClass().function();
}
}
package oop;
public class Main {
public static void main(String[] args) {
/*
* 程序无报错,输出4
*/
new OuterClass().show();
}
}
-
匿名内部类
匿名内部类是局部内部类的特殊形式。
1. 匿名内部类适合只需要创建一次的类;
2. 使用匿名内部类的前提是该类必须继承一个类或者实现又给接口;
3. 如果匿名内部类要访问外部类的局部变量,则该局部变量必须用final修饰;
package oop;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class FrameDemo {
private Frame frame;
private void init(){
frame = new Frame("frame");
/*
* 方法中添加了WindowAdapter的匿名内部类,在匿名内部类中重写了windowClosing方法
* WindowAdapter实现了WindowListener接口
*/
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// TODO Auto-generated method stub
frame.dispose();
}
});
frame.setVisible(true);
}
}
附注:
本文如有错漏之处,烦请不吝指正,谢谢!