java内部类

来源:互联网 发布:vc6.0 socket编程 编辑:程序博客网 时间:2024/06/11 21:06

java内部类特征

我们看如下代码:

@SuppressWarnings("all")public class OuterClass {public String name;private int age;public OuterClass(String name, int age) {super();this.name = name;this.age = age;}class InnerClass {private String phone;public void say() {System.out.println("hello, I am is innerClass!");}}public static void main(String[] args) {OuterClass outerClass = new OuterClass("xiaotang", 23);//在外部类的静态方法中实例内部类,需要通过外部对象 .new运算符 来实例化InnerClass innerClass = outerClass.new InnerClass();innerClass.say();}}

这是一个最简单的内部类使用,可以看到 在外部类的main方法中 实例化内部类需要用 .new运算符,但是在外部类的 非静态方法中则不需要,代码如下:
@SuppressWarnings("all")public class OuterClass {public String name;private int age;public OuterClass(String name, int age) {super();this.name = name;this.age = age;}class InnerClass {private String phone;public void say() {System.out.println("hello, I am is innerClass!");}}/** * @return 在外部类通过此方法获取内部类实例,只需通过普通的new即可 */public InnerClass InstantInnerClass() {return new InnerClass();}public static void main(String[] args) {OuterClass outerClass = new OuterClass("xiaotang", 23);               //实例化外部类中的内部类InnerClass innerClass = outerClass.InstantInnerClass();innerClass.say();}}
对比上面两种代码的写法,是不是能够发现:

在外部类中静态方法中实例化内部类,需要使用 .new运算符,显得麻烦些,在非静态方法中实例化内部类,则与普通类的实例没什么区别,所以,我们一般要使用内部类的实例都是采取第二种方案操作的,大家通过看java集合的源码(比如说 List中 Iterator的实现),里面的迭代器的操作,大量使用了这种方法!


内部类原理透析

在这里我首先需要补充说明:
内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为 OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class 和OuterClass$InnerClass.class。对于java虚拟机来说,它们就是两个普通的java类,并没有什么不同!
好学的你可能要问了,内部类和外部类最终会被编译器编译成两个不同的类啊,对于虚拟机来说,加载这两个类就是加载两个普通的类,一点关系都没有,那么内部类还有什么意义呢?不就是写两个类,谁不会啊?是的,我们先来看看 OuterClass$InnerClass的编译后的结构:
查看内部类编译后的结构,我们有三种方式
(1)直接查看编译后的内部类class文件
(2)通过反射去读取内部类信息
(3)通过 javap -private ClassName
在这里,我提一下第三种方式的操作吧!截图如下:

咱通过javap查看 内部类结构如下:
Compiled from "OuterClass.java"class OuterClass$InnerClass extends java.lang.Object{    private java.lang.String phone;    final OuterClass this$0;    OuterClass$InnerClass(OuterClass);    public void say();}
可以看到,内部类中 有一个外部类的实例 this$0,实例的名称可能因为不同的编译器可能不同, 并且会生成一个 形参为外部类实例的的构造方法,构造方法的作用是赋值 this$0,这些都是编译器做的事。看到这,你可能要问了:这不就是类的组合使用吗?在类B中定义 A a属性,然后a就可以调用A中的属性和方法了,为啥还要搞个内部类啊,这么麻烦咧!别急,且听我慢慢道来。
我们再来看看另外一个代码:
@SuppressWarnings("all")public class OuterClass {public String name;private int age;public OuterClass(String name, int age) {super();this.name = name;this.age = age;}class InnerClass {private String phone;//内部类直接调用外部类的私有属性和公有属性public void say() {System.out.println("hello,my name is " + name + ", age is " + age);}}}
可以看到:内部类直接调用外部类的私有属性了,这也印证了内部类的一个重要特点:
内部类可以调用外部类任意修饰符修饰的属型和方法,当然也就包括了私有属性,但是,如果使用类的组合结构能做到吗?答案肯定是做不到的!刚才已经说了,虚拟机加载的就是两个普通的类,那么只能是编译做的特殊处理了,接下来我们就一起看看编译器在这里面都做了哪些努力吧!
还是上面代码,我们通过查看 编译后的OuterClass$InnerClass.class文件可以看到:
import java.io.PrintStream;class OuterClass$InnerClass{  private String phone;  OuterClass$InnerClass(OuterClass paramOuterClass)  {  }  public void say()  {    System.out.println("hello,my name is " + this.this$0.name + ", age is " +         OuterClass.access$0(this.this$0));  }}
当内部类调用外部类(OuterClass)的私有属性时,会调用OuterClass的静态方法 access$0得到OuterClass的私有属性,大家有没有发现,OuterClass我们没有定义access$0方法啊,这就是编译器所做的努力!我们 javap OuterClass,查看access$0的指令集:
static int access$0(OuterClass);Code:   Stack=1, Locals=1, Args_size=1   0:aload_0   1:getfield#17; //Field age:I   4:ireturn   LineNumberTable:    line 4: 0 

通过指令集还原代码为:
static int access$0(OutClass outerClass)

{
return outerClass.age;
}
看到这大家是不是应该明白了,当我们在 内部类引用外部类的私有属性时,编译器就会在外部类中添加 access$0静态方法,返回对应的属性,如果引用了多个私有属性,则是access$00,acess$000方法,这个不同的编译器取的名字不一样的。好了,讲到这终于,把内部类的原理大概给说清楚了,欢迎大家和我一起讨论,一起学习提高!

0 0
原创粉丝点击