1. 工作流程
- 当java编译一个包含内部类的.java文件的时候,会生成两个.class字节码文件,分别为
Outer.class
和Outer$Inner.class
- 也就是说一个内部类一旦是在某一个类的内部定义的,这个内部类就被永远打上了标记,永远不可能作为一个独立的类存在
- 新建外部类的实例对象的时候,内部类实例不会创建;反之你要想创建一个内部类的实例,必须先创建外部类的实例,再创建这个内部类的实例(static除外)
- OuterDemo.java
public class OuterDemo {
private String name;
private int age=100;
public OuterDemo(String name) {
super();
this.name = name;
System.out.println("Outer run name is "+name);
}
public class InnerDemo{
private int age;
public InnerDemo(int age) {
super();
this.age = age;
System.out.println("Inner run"+" age is "+age);
}
public void showOuterName(){
System.out.println(name);
}
public void showInnerAgeThis() {
System.out.println(this.age);
}
public void showInnerAgeNotThis() {
System.out.println(age);
}
public void showOuterAge() {
System.out.println(OuterDemo.this.age);
}
}
}
- TestDemo.java
OuterDemo out=new OuterDemo("outername");
OuterDemo.InnerDemo outIn1=out.new InnerDemo(23);
OuterDemo.InnerDemo outIn2=out.new InnerDemo(44);
outIn1.showInnerAgeNotThis();
outIn1.showInnerAgeThis();
outIn2.showInnerAgeNotThis();
outIn2.showInnerAgeThis();
outIn1.showOuterName();
outIn2.showOuterName();
outIn1.showOuterAge();
outIn2.showOuterAge();
- output
Outer run name is outername
Inner run age is 23
Inner run age is 44
23
23
44
44
outername
outername
100
100
- 通过以上代码,我们可以看到一个非静态内部类的实例对象是依靠它的外部类的实例对象而生存的,一个外部类的对象实例可以生成很多内部对象的实例,而这些内部对象的实例的变量名表示必须是
OuterDemo.InnerDemo
,以区分出这是依靠哪个外部类的内部类的对对象实例 - 我们也可以看到内部类的对象不管新建了几个实例,都能够轻松访问到创建他们的外部类对象内部的成员变量,哪怕是私有,内部类的实例通过Outer.this取得外部类对象的引用后就能轻松访问了。而内部类对象要是访问自己内部的成员变量,只需要用this引用就行
- 原则上来说,外部类能干的东西,内部类都能干,内部类是外部类的加强
2. 匿名内部类
我们首先想一下,如果让我们在一个名字为Outer的类中,完成一个其它类的定义,实例化,使用方法这样一个完整的流程,我们该怎么做呢?
如果是平常的话,我们会在文件夹新建立一个.java文件,然后在这个Outer类中新建实例对象,然后进行使用。
但是这样就需要多写一个.java文件,我们很懒惰,不想写,只想在这个现成的Outer.java文件里写完就完事了。
于是我们只能靠使用内部类实现这个偷懒的想法了,这其实就是内部类的由来。
为了进一步偷懒,当一个内部类要继承一个抽象类或者实现一个接口的时候,我们可以使用所谓的匿名内部类,这样连这个内部类的类名都不要了,就可以直接完成类的定义。
普通内部类的实现接口
如果使用普通的内部类实现接口,不使用匿名内部类,我们可以如下操作
- Inter
public interface Inter {
public void show();
}
- OuterDemo
public class OuterDemo {
public class ImplementDemo implements Inter{
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("implements ok");
}
}
public void method() {
ImplementDemo inner=new ImplementDemo();
inner.show();
}
}
- 主函数
new OuterDemo().method();
匿名内部类的实现接口
匿名内部类其实是对上面的一种简写
- Inter
public interface Inter {
public void show();
}
- OuterDemo
public class OuterDemo {
public void method1() {
new Inter() {
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("implements ok");
}
}.show();
}
public void method2() {
Object o=new Inter() {
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("implements ok");
}
};//注意大括号末尾的分号
// o.show();
// 这里的show方法无法使用,因为向上转型了
// 要想使用show方法已经不可能了,因为不知道如何向下转型
System.out.println(o.toString());
}
public void method3() {
//这里无法声明public,这个类是局部内部类,无法加修饰符
class DemoImple implements Inter{
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("method3 implements ok");
}
}
DemoImple demo=new DemoImple();
demo.show();
System.out.println(demo.toString());
}
}
- 主函数
new OuterDemo().method1();
new OuterDemo().method2();
new OuterDemo().method3();
- 结果
implements ok
mylearning.outerInnerLearn.OuterDemo$2@15db9742
method3 implements ok
mylearning.outerInnerLearn.OuterDemo$1DemoImple@6d06d69c
- 上面的法1是正常的匿名内部类的使用,其实格式就是那样定义的,我们只需要背过格式就行。一定注意抽象类和对象无法新建。
- 如果是
new Inter();
这样的语句是不合法的;但是如果是new Inter(){};
这就成了匿名内部类,又变成合法的了。 - 上面的法2是将这个匿名内部类对象用Object接受,但是这样就废了,因为向上转型,我们无法调用实现的方法,从输出结果可以看到这个对象是属于OuterDemo的一个内部类的,因为有$这个符号
- 方法3是局部内部类,和内部类其实差不多,只不过定义的位置有差别,通过调用函数新建对象,而不是通过OuterDemo的实例outer使用
outer.new InnerDemo();
来创建对象