java 内部类

内部类即定义在某个类中的类,它表示的是类之间的关系,与对象无关。即即使在某个类中定义了内部类,当实例化该外部类对象之后,并不代表该外部对象就包含有定义的内部类对象。内部类一种编译器现象,即java虚拟机并不知道所谓的内部类这个概念,这是后话。
java中存在着四种内部类,分别是常规内部类,局部内部类,匿名内部类和静态内部类。

常规内部类

常规内部类就是定义于某个类代码段当中的类,可以有访问修饰符public,private等,注意只有内部类可以是private,如下面代码段:

package ck.innerclass;

public class Father {

    /**
     * @param args
     */
     private int num;
     private int aaa;
     public int bbb;
     private int ccc;
    public Father(int inputnum1,int inputnum2)
    {
        num=inputnum1;
        aaa=inputnum2;
    }
    public class PublicInner
    {
        public void functest()
        {
            if(1==num)//can access the private
                System.out.print("I am public inner class\n");
            else if(2==aaa)
                System.out.print("hahah\n");
            else if(3==bbb)
                System.out.print("bbbb\n");
        }
    }
    private class PrivateInner
    {
        public void functest()
        {
            if(1==num)//can access the private
                System.out.print("I am private inner class\n");
        }
    }
    public void TestInnerclass()
    {
        PublicInner pubobj=new PublicInner();//correct
        PrivateInner obj=new PrivateInner();//correct
    }
}

class Test
{
    public void func()
    {
        Father myfFather=new Father(20,30);
        Father.PublicInner pubf=myfFather.new PublicInner();//correct
        Father.PublicInner pubf2=new Father(30,40).new PublicInner();//correct
//      Father.PrivateInner...//error
    }
}

上述代码中定义了一个Father类,在其中定义了两个内部类,分别是public和private的访问权限,区别很显然,若是public内部类,则任何Father对象都可以在外部构造该内部类对象,若是private内部类,则只能在外部类内部才可以。显然这符合java访问修饰符的基本规则。并且注意到,在内部类中可以直接去访问两个私有域num和aaa。
内部类之所以能够访问外部类的私有域,是因为内部类对象中总有一个隐式引用指向创建他的外部类对象。前面说过java内部类是一种编译器现象,与虚拟机无关。编译器在处理的时候会把内部类翻译成用$分隔外部类与内部类名的常规的.class文件,编译后会生成Father.class,Father$PrivateInner.class ,Father$PublicInner.class 三个常规类文件。既然是常规类文件,那么虚拟机是如何实现访问私有域成员呢?利用反射机制查看两个内部类的class文件,

joker@Joker-PC:~/workspace/TestOracle/bin/ck/innerclass$ javap -private Father\$PublicInner.class 
Compiled from "Father.java"
public class ck.innerclass.Father$PublicInner {
  final ck.innerclass.Father this$0;
  public ck.innerclass.Father$PublicInner(ck.innerclass.Father);
  public void functest();
}

可以发现编译器为Father$PublicInner.class生成了一个附加的实例域final ck.innerclass.Father this$0,并且在该类的构造函数中传入了外部类ck.innerclass.Father参数。类似实现大概就是下面这种:

public ck.innerclass.Father$PublicInner(ck.innerclass.Father myfather)
{
    this$0=myfather;
}

之后在内部类函数functest中访问外部类私有域的num时候,利用this$0来访问:

if(1==this$0.num)
                System.out.print("I am public inner class\n");
else if(2==this$0.aaa)
                System.out.print("hahah\n");
else if(3==bbb)//public可以直接访问
                System.out.print("bbbb\n");

一切好像已经明朗,但是,上述代码显然会出错,因为num是私有域,无法在外部去访问,一定还有别的原因。继续利用反射机制来查看外部类Father:

joker@Joker-PC:~/workspace/TestOracle/bin/ck/innerclass$ javap -private Father.class 
Compiled from "Father.java"
public class ck.innerclass.Father {
  private int num;
  private int aaa;
  public ck.innerclass.Father(int, int);
  public void TestInnerclass();
  static int access$0(ck.innerclass.Father);
  static int access$1(ck.innerclass.Father);
}

可以发现,编译器为外部类生成了两个static int access 0staticintaccess 1的静态方法,这两个静态方法的返回值作为参数传递给 外部类中需要被访问的private域,可以发现Father中定义了三个private域num,aaa,ccc但是并没有为ccc生成一个静态方法。即内部类访问外部类的num和aaa两个私有域的实现应当是下面:

if(1==access$0(myfather))
                System.out.print("I am public inner class\n");
else if(2==access$0(myfather))
                System.out.print("hahah\n");
else if(3==bbb)//public可以直接访问
                System.out.print("bbbb\n");

即只要有内部类试图访问私有域,就会在外部类中为该私有域生成一个静态的方法。

局部内部类

局部内部类,就是定义在函数体内部的类,局部内部类不能用访问修饰符进行声明,因为它的作用域已经被限定于这个局部的代码块当中,局部类能够与外界完全隐藏。

package ck.innerclass;

public class LocalClass {
    public void func(int a,final boolean b,final int c,final int d)
    {
        class testlocal
        {
            public void test()
            {
                if(b)
                    System.out.print("hahhaah\n");
                else if(1==c)
                    System.out.print("ggggg\n");
            }
        }
        testlocal cl=new testlocal();
        cl.test();
    }

}

在局部内部类中只能访问函数的final局部变量,即在上述代码中,在testlocal类中访问变量a就会报错,这是因为 当我们在外部调用func函数时,首先会实例化testlocal对象,然后调用其test函数,这时候func函数退出,那么局部变量会被回收,为了保证test函数的正常运行,需要对在局部类中对使用到的局部变量进行备份
对编译后的局部类的class文件进行反射查看:

joker@Joker-PC:~/workspace/TestOracle/bin/ck/innerclass$ javap -private LocalClass\$1testlocal.class 
Compiled from "LocalClass.java"
class ck.innerclass.LocalClass$1testlocal {
  final ck.innerclass.LocalClass this$0;
  private final boolean val$b;
  private final int val$c;
  ck.innerclass.LocalClass$1testlocal(ck.innerclass.LocalClass, boolean, int);
  public void test();
}

可以发现在局部类的构造函数中传入了三个参数,分别是外部类参数,一个boolean参数和一个int参数,并且编译器还为局部类省城了两个private final的域,显然,编译器为局部类需要访问的局部变量生成了一个副本。之所以需要是final修饰,是因为要保证局部变量与在局部类中建立的副本保持一致。

匿名内部类

如果我们需要创建一个类的对象,但是只需要使用一次不再重复使用,就可以为其创建一个匿名内部类,匿名内部类的语法如下:

new SuperClass(construction parameters)
{
    methods...
    datas...
}

对于匿名内部类需要注意,因为构造函数要与类名相同,匿名类没有名字所以不能有显式的构造函数。

package ck.Anonymous;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;

import javax.swing.JOptionPane;
import javax.swing.Timer;



public class TestAnonymous {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ActionListener listen=new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                System.out.print("Now time is "+new Date());
            }
        };
        Testcls cls=new Testcls();
        cls.testfunc(listen);
        JOptionPane.showMessageDialog(null,"quit?");
        System.exit(0);
    }


}

class Testcls
{
    public void testfunc(ActionListener act)
    {
        Timer clockTimer=new Timer(1000, act);
        clockTimer.start();
    }
}

如上述代码,在类Testcls中有个testfunc函数,该函数需要传入一个ActionListenner对象,但是ActionListenner是接口,不能实例化对象,上述代码传入了一个匿名内部类对象,如果不使用匿名内部类,常规做法如下:

//首先定义一个类实现接口:
class ImpleAnnoy implements ActionListener
{
    public void actionPerformed(ActionEvent e) {
        System.out.print("Now time is "+new Date());
    }
}
//实例化对象
public static void main(String[] args)
 {
    // TODO Auto-generated method stub
    ImpleAnnoy impcls=new ImpleAnnoy();
    ActionListener listen=impcls;
    Testcls cls=new Testcls();
    cls.testfunc(listen);
    JOptionPane.showMessageDialog(null,"quit?");
    System.exit(0);
};

上述是使用接口来创建匿名类,还可以通过继承某个超类来实现匿名类,如下:

public class TestAnonymous {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ActionListener listen=new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                System.out.print("Now time is "+new Date());
            }
        };
        Testcls cls=new Testcls();
        cls.testfunc(listen);
        JOptionPane.showMessageDialog(null,"quit?");
        System.exit(0);
    }
}
public class TestAnonymous {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        SuperClass PengK=new SuperClass(1, 2)
        {
            public void sonfunc()
            {
                System.out.print("I am Pengk\n");
            }
            private int age;
        };
    }

}

上述代码中 通过扩展SuperClass生成匿名内部类,并对其进行了扩展。如果不使用匿名内部类,通常的做法是:

//继承超类
class InheritClass extends SuperClass
{
    public InheritClass(int x,int y)
    {
        super(x, y);
    }
    public void sonfunc()
    {
        System.out.print("I am Pengk\n");
    }
    private int age;
}
//引用
public class TestAnonymous {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        InheritClass PengK=new InheritClass(1, 2);
        SuperClass CK=PengK;

}

匿名类可以使我们简写大量代码~

静态内部类

静态内部类只是为了把该类隐藏在另一个类的内部,即无隐式引用。与C++的嵌套类类似,静态内部类对外部类并没有特殊的访问权限。
如下:

package ck.innerclass;

public class StaticInner {
    private int a;
    public static class Innercls
    {

        public void func()
        {
        //  if(1==a)//error 没有特殊访问权限
                System.out.print("I am static\n");
        }
    }
}

对其编译后的内部类class文件利用反射进行查看,

joker@Joker-PC:~/workspace/TestOracle/bin/ck/innerclass$ javap -private StaticInner\$Innercls.class 
Compiled from "StaticInner.java"
public class ck.innerclass.StaticInner$Innercls {
  public ck.innerclass.StaticInner$Innercls();
  public void func();
}

可以发现编译器并没有为其像之前一样生成隐士引用this$0。

这是在学习core java时对于内部类的一些总结。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值