Java 内部类 随笔

来源:互联网 发布:类似天蝎的美剧 知乎 编辑:程序博客网 时间:2024/06/18 07:58

最近突然想到把Java基础过一遍, 想到就开始做, 先从内部类走起!


先尝试写了个内部类, 来看编译生成的文件, 生成了两个class 文件Outer.class和Outer$Inner.class.所以内部类的成员变量/方法名可以和外部类的相同。

网上搜了一下资料, 本来想看看官网有没有什么官方的描述, 苦于没找到,只能看别人写的随笔。

网上的说法,内部类分了4类: 常规内部类(又有人叫成员内部类)、静态内部类、局部内部类、匿名内部类。

1. 常规内部类(成员内部类)

       就是做为外部类的成员,可以直接使用外部类的所有成员和方法,包括private和静态成员。

      当成员内部类访问和外部类同名的方法或成员的时候,实际上访问的是内部类的成员和方法。如果 成员内部类想引用外部类对象,可以用

外部类名.this.方法

                外部类名.this.成员变量

      

       要创建内部类对象,可以使用 外部类名.内部类名  变量 = 外部类对象.new 内部类名() 来创建, e.g: Outer.Inner i = o.new Inner();

       或通过外部类的方法返回。 如下例:

package javaLearing.InnerClass;public class Outer {    public String str1 = "str1";    protected String str2 = "str2";    private String str3 = "str3";    private static String str4 = "str4";    private void print() {        System.out.println("Outer str1:" + str1);        System.out.println("Outer str2:" + str2);        System.out.println("Outer str3:" + str3);        System.out.println("Outer str4:" + str4);    }    public Inner getInnerInstance() {        return new Inner();    }    public class Inner {        public String str3 = "Inner str3";        private String str4 = "Inner Str4";        public void print() {            System.out.println("Inner class print");            System.out.println("Outer str1:" + str1);            System.out.println("Outer str2:" + str2);            System.out.println("Outer str3:" + str3);            System.out.println("Outer str4:" + str4);            Outer.this.print();            System.out.println("Outer:" + Outer.this);            System.out.println("Outer str3 actual should be:" + Outer.this.str3);            System.out.println("Outer static str4 actual should be:" + Outer.str4);        }    }    public static void main(String[] args) {        Outer o = new Outer();        Outer.Inner i = o.new Inner();        Outer.Inner i2 = o.getInnerInstance();        i.print();        i2.print();    }}

结果:

Inner class printOuter str1:str1Outer str2:str2Outer str3:Inner str3Outer str4:Inner Str4Outer str1:str1Outer str2:str2Outer str3:str3Outer str4:str4Outer:javaLearing.InnerClass.Outer@15db9742Outer str3 actual should be:str3Outer static str4 actual should be:str4Inner class printOuter str1:str1Outer str2:str2Outer str3:Inner str3Outer str4:Inner Str4Outer str1:str1Outer str2:str2Outer str3:str3Outer str4:str4Outer:javaLearing.InnerClass.Outer@15db9742Outer str3 actual should be:str3Outer static str4 actual should be:str4


注意: 成员内部类不能含有static 的变量和方法。

生成的class 文件如下:



反编译看看
(去网上下了一个jd-gui,http://jd.benow.ca/. (我也试了一下jdec(http://jdec.sourceforge.net/), 反编译的代码看的我egg pain. 还有就是java 自带的javap -v命令也是看起头大):


看内部类的购造函数,就了解为什么可以访问外部类的成员变量了。

2. 静态内部类

    用static 修饰的内部类。 只能访问外部类的静态成员变量和方法。所以也不能通过this 来访问外部类的实例成员变量和方法。

package javaLearing.InnerClass;public class Outer2 {    public static String str1 = "str1";    public static String str2 = "str2";    public Inner getInnerInstance() {        return new Inner();    }    private static void print() {        System.out.println("Outer str1:" + str1);        System.out.println("Outer str2:" + str2);    }        public static class Inner {        private String str1 = "Inner str1";        public void print() {            System.out.println("Inner class print");            System.out.println("Outer str1:" + Outer2.str1);            System.out.println("Outer str2:" + str2);            System.out.println("Inner str1:" + str1);            Outer2.print();        }    }    public static void main(String[] args) {        Outer2.Inner i = new Outer2.Inner();        i.print();    }}

结果

Inner class printOuter str1:str1Outer str2:str2Inner str1:Inner str1Outer str1:str1Outer str2:str2

生成的class 文件如下:


反编译看看:


静态内部类并没有持有外部类的引用。

3. 局部内部类

定义在方法体或者一个作用域内的内部类。不能带private, public, protected修饰符。可以带final, abstract。类似一个局部变量。 可以访问外部类的成员变量和方法。 可以访问作用域的参数和局部变量。 相应的, static 方法/语句块中定义的内部类,只能访问静态成员。

package javaLearing.InnerClass;interface InnerInterface{    public void print();}public class Outer3 {    public String str1 = "str1";    protected String str2 = "str2";    private String str3 = "str3";    private static String str4 = "str4";         public void print()    {        System.out.println("Outer3 str1:" + str1);        System.out.println("Outer3 str2:" + str2);        System.out.println("Outer3 str3:" + str3);        System.out.println("Outer3 str4:" + str4);    }        public static void InnerPrint()    {        class Inner implements InnerInterface{            private String str1 = "Static Method Inner str1";            public void print() {                System.out.println("Static Method Inner class print");                System.out.println("Inner str1:" + str1);                System.out.println("Outer3 str4:" + Outer3.str4);            }        }                Inner i = new Inner();        i.print();    }        public InnerInterface getInnerInstance(boolean b) {        int var = 10;        Outer3 o = new Outer3();        class Inner implements InnerInterface{            private String str1 = "Inner str1";            public void print() {                System.out.println("Inner class print");                System.out.println("Method parameter:" + b);                System.out.println("Method variable:" + var);                System.out.println("Method o:" + o);                System.out.println("Inner str1:" + str1);                System.out.println("Outer3 str1:" + Outer3.this.str1);                System.out.println("Outer3 str2:" + Outer3.this.str2);                System.out.println("Outer3 str3:" + Outer3.this.str3);                System.out.println("Outer3 str4:" + Outer3.str4);                Outer3.this.print();            }        }        if(b)        {            class Inner2 extends Inner{                private String str2 = "Inner2 str2";                public void print() {                    System.out.println("Method parameter:" + b);                    System.out.println("Method variable:" + var);                    System.out.println("Inner2 class print");                    System.out.println("Inner str1:" + super.str1);                    System.out.println("Inner2 str2:" + str2);                }            }            Inner2 i = new Inner2();            i.print();        }                return new Inner();    }    public static void main(String[] args) {        InnerInterface i = new Outer3().getInnerInstance(true);        i.print();        Outer3.InnerPrint();    }}
结果

Method parameter:trueMethod variable:10Inner2 class printInner str1:Inner str1Inner2 str2:Inner2 str2Inner class printMethod parameter:trueMethod variable:10Method o:javaLearing.InnerClass.Outer3@15db9742Inner str1:Inner str1Outer3 str1:str1Outer3 str2:str2Outer3 str3:str3Outer3 str4:str4Outer3 str1:str1Outer3 str2:str2Outer3 str3:str3Outer3 str4:str4Static Method Inner class printInner str1:Static Method Inner str1Outer3 str4:str4

注意: 在局部内部类中访问方法的参数和局部变量,是可以访问得到的,如上面的例子,但是如果做修改, 不管是在内部类内还是在方法中,会报编译时错误:


网上的贴子说要把参数和局部变量声明成final.至于为什么要声明成final, 我们知道被声明成final的变量是不能被改变的,显示声明成final就是让其不能被改变。 那变了又会怎么样呢?

首先, 我们看,上面的代码

生成的class文件如下:


(这里看到,在内部类名前有一个数字。 很明显,在不同作用域中是可以存在同名的内部类的,生成的class文件以数字编号来区分。)

我们来反编译看看, 




从代码看,大概意思是内部类自己拷贝了一份参数和局部变量的引用。 为什么要这样做, 是因为局部变量的生命周期和局部内部类实例的不一样的,比如方法块完了,把内部类实例返回了,局部变量在方法块结束的时候就去释放去了。

即然有了拷贝,就有可能有不一致。 所以用final. 这是网上的原话, 我很好奇对象是怎么做的拷贝,按引用计数原理,只要有引用就不会被释放,对象是不是就直接在内部类内部用成员变量保存一份引用? 那这个一致性针对对象似乎不太说得通, 不过换一想,如果外部让局部变量指向别的对象引用,也算是一种修改,但这个却无法反应到内部类里面,也算是造成了不一致。


4. 匿名内部类

正如其名,没有名字的内部类。 
        匿名内部类可以继承一个类或实现一个接口,这里的ClassOrInterfaceName是匿名内部类所继承的类名或实现的接口名。但匿名内部类不能同时实现一个接口和继承一个   类,也不能实现多个接口。如果实现了一个接口,该类是Object类的直接子类,匿名类继承一个类或实现一个接口,不需要extends和implements关键字。
把类的定义和类的创建放在了一起,格式如下:
new ClassOrInterfaceName(){/*类体*/}
<pre name="code" class="java">package javaLearing.InnerClass;interface NiMinInterface {    public void print();}abstract class NiMinClass {    public NiMinClass(String name, String city) {        System.out.println(city);        city = "change city";        System.out.println(city);    }    abstract public void print();}public class Outer4 {    public NiMinInterface getInnerInstance(String param) {        Outer4 o = new Outer4();        return new NiMinInterface() {            @Override            public void print() {                System.out.println("NiMin Inner class");                System.out.println(param);                System.out.println(o);            }        };    }    public NiMinClass getNiMinClass(String name, String city) {        return new NiMinClass(name, city) {            private String nameStr = name;            private String cityStr = city;            @Override            public void print() {                System.out.println("NiMin Inner class");                System.out.println(nameStr);                System.out.println(cityStr);            }        };    }    public static void main(String[] args) {        NiMinInterface i = new Outer4().getInnerInstance("param");        i.print();        NiMinClass j = new Outer4().getNiMinClass("name", "city");        j.print();    }}


结果
<pre name="code" class="java"><pre name="code" class="java">NiMin Inner classparamjavaLearing.InnerClass.Outer4@15db9742citychange cityNiMin Inner classnamecity


生成的class文件如下:


针对匿名的内部类,因为没有类名,所以用编号来表示。
来看看返编译的结果,带参数的匿名内部类:



下面是摘自 http://blog.csdn.net/a1259109679/article/details/48156407, 个人觉得挺有用:
为什么在Java中需要内部类?总结一下主要有以下四点:


  1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,


  2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。


  3.方便编写事件驱动程序


  4.方便编写线程代码

最后补充一点知识:关于成员内部类的继承问题。一般来说,内部类是很少用来作为继承用的。但是当用来继承的话,要注意两点:

  1)成员内部类的引用方式必须为 Outter.Inner.

  2)构造器中必须有指向外部类对象的引用,并通过这个引用调用super()。这段代码摘自《Java编程思想》

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class WithInner {
    class Inner{
         
    }
}
class InheritInner extends WithInner.Inner {
      
    // InheritInner() 是不能通过编译的,一定要加上形参
    InheritInner(WithInner wi) {
        wi.super(); //必须有这句调用
    }
  
    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner obj = new InheritInner(wi);
    }
}



0 0
原创粉丝点击