-----------2015.5.4------------
为什么要使用内部类?
内部类可以实现独立的接口或者继承外部类没能继承的类,一定程度上解决单继承和实现接口无法实现的功能。使用内部类还可以隐藏信息,比如用外部类的一个public方法返回一个实现了某个接口的内部类的对象,别的类是不知道该对象是由哪个类创建的。
public class Test {
public static void main(String[] args) {
Test test = new Test();
Test.Inner inner = test.getInner();
inner.say();
}
class Inner{
void say(){
System.out.println("Inner say");
}
}
public Inner getInner(){
return new Inner();
}
}
1.完善多重继承
- C++作为比较早期的面向对象编程语言,摸着石头过河,不幸的当了炮灰。比如多重继承,Java是不太欢迎继承的。因为继承耦合度太高。比如你是一个人,你想会飞,于是就继承了鸟这个类,然后你顺便拥有了一对翅膀和厚厚的羽毛,可这些玩意你并不需要。所以Java发明了接口,以契约的方式向你提供功能。想想看,你的程序里成员变量会比函数多吗?况且多重继承会遇到死亡菱形问题,就是两个父类有同样名字的函数,你继承谁的呢?其实C++也可以做到这些,那就是定义没有成员变量的纯虚类,而且所有函数都是纯虚函数。可是这些都是要靠程序员自己把握,并没有把这些功能集成到类似Interface这样的语法里。
- 所以Java只支持单重继承,想扩展功能,去实现接口吧。很快Java的设计者就发现了他们犯了矫枉过正的错误,多重继承还是有一定用处的。比如每一个人都是同时继承父亲和母亲两个类,要不然你的身体里怎么能留着父母的血呢?Java内部类应运而生。
- 用来开发GUI的Java Swing使用了大量内部类,主要用来响应各种事件。Swing的工作就是在事件就绪的时候执行事件,至于事件具体怎么做,这由事件决定。这里面有两个问题:1.事件必须要用到继承2.事件必须能访问到Swing。所以必须把事件写成内部类。
- 内部类是面向对象的闭包,因为它不仅包含创建内部类的作用域的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。一般使用一个库或类时,是你主动调用人家的API,这个叫Call,有的时候这样不能满足需要,需要你注册(注入)你自己的程序(比如一个对象),然后让人家在合适的时候来调用你,这叫Callback。
- 当父类和实现的接口出现同名函数时,你又不想父类的函数被覆盖,回调可以帮你解决这个问题。
本帖主要参考了《Thinking in Java》关于内部类的讲解以及13、内部类(闭包与回调)
普通内部类
Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。
这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。
如果内部类成员变量和外部类成员变量重名,可用OutClass.this.xx InterClass.this.xx区分。
创建成员内部类的实例
<span style="font-size:18px;">public class Test {
public static void main(String[] args) {
}
class Inner{
void say(){
System.out.println("Inner say");
}
}
}</span>
静态内部类没有了指向外部的引用。可以直接创建静态内部类的对象,不需要必须创建外部类对象。
静态内部类只能使用外部类的静态数据,成员内部类则不管外部类数据是静态的还是非静态的,都可以使用。
但在任何非静态内部类中,不能有静态变量(但可以有静态常量static final),静态方法或者又一个静态内部类(内部类的嵌套可以不止一层),不过静态内部类中却可以拥有这一切.Why?原因可能和类加载有关,静态内部类会随外部类加载而加载,其内部的静态变量,静态方法也会一并被加载。非静态内部类的加载是在第一次使用时,也即会在外部类实例对象创建后。非静态内部类可以有静态常量的原因是静态常量在编译时已经确定,但为什么不能有静态变量,静态方法呢?
创建静态内部类的实例
Test.java
public class Test {
public static void main(String[] args) {
}
static class Inner{
void say(){
System.out.println("Inner say");
}
}
}
Test2.java
public class Test2 {
public static void main(String[] args) {
Test.Inner inner = new Test.Inner();
}
}
public class Test2 {
public static void main(String[] args) {
Test test = new Test();
Test.Inner inner = test.new Inner();
}
}
局部内部类
Java内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内
- public class Goods1 {
- public Destination dest(String s) {
- class GDestination implements Destination {
- private String label;
- private GDestination(String whereTo) {
- label = whereTo;
- }
- public String readLabel() {
- return label;
- }
- }
- return new GDestination(s);
- }
- public static void main(String[] args) {
- Goods1 g = new Goods1();
- Destination d = g.dest("Beijing");
- }
- }
public class Parcel6 {
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("chenssy");
String string = ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 parcel6 = new Parcel6();
parcel6.track();
}
}
局部内部类用到的局部参数,不需要使用final修饰
void f(){
class LocalClass{
int index;
void setIndex(int index){
this.index = index;
}
}
int i = 3;
new LocalClass().setIndex(i);
}
Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。
这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。匿名内部类可以访问外部类对象的成员,就是因为有外部类对象的引用,但要使用局部变量,就必须是局部变量用final修饰符修饰,因为局部变量的值如果被改变了,匿名内部类是不知道,不像外部类成员变量,即使改变了内部类也知道,所以要对使用的局部变量加final修饰。
匿名内部类的使用场景在Android或者Swing中很多,比如添加事件监听时,用的就都是匿名内部类。