Java内部类
2015-06-16 20:10
393 查看
在java中,可以在一个类内部定义另外一个类,也就是我们所说的内部类,内部类个人感觉最大的功能是为java多继承提供了支持(java类最多只能继承一个基类,不允许多继承)。
java内部类有四种,分别是成员内部类、局部内部类、匿名内部类和静态内部类(也称嵌套内部类),下面分别讲一下这四种内部类。
成员内部类的访问修饰符可以是public,protected,private和缺省。而器外部类只能是public,缺省或者定义为abstract。
上面的例子可以看到,内部类可以访问外部类的成员,如果成员名字有冲突时,默认是内部类自己的成员,这种情况下要想访问外部类的成员,需要使用this
外部类继承了B,而内部类继承了C,这样就实现了java的多继承。
这里我们可以看到,内部类和外部类都同时实现了C接口,这也是允许的。内部类和外部类可以以不同方法实现同一个接口。
创建成员内部类对象
必须先创建外部类的对象。有兴趣的同志可以去反编译一个内部类的字节码,就可以发现其实它的构造函数接受了一个外部类的对象作为参数,所以创建内部类对象必须要有一个外部类的对象。
用outter.new的方法去创建内部类对象。
局部内部类因为不会在其他地方被实例化,所以权限修饰符对于它来说就没有意义了,所以局部内部类没有权限修饰符。
通常是返回一个实例化对象。这里就引出了一个问题,test()方法执行完return后其生命周期就结束了,但是返回的内部类对象其生命周期并没有结束,所以如果内部类使用到外部方法的局部变量怎么办?答案是内部类只能访问外部类和外部类外部方法的final变量,我们看下面的代码:
试图访问非final变量i会提示错误。访问外部方法的非final变量是不允许的,因为前面说过了,外部函数生命周期结束的时候,内部类对象还是活着的,所以访问不到。
但是为什么final变量又可以呢?因为内部类将要访问的final变量作为参数传递给了自己的构造函数(这个可以通过反编译字节码看到)。
从上面我们可以看到,如果内部类变量名和外部类变量名一样,默认使用的是内部类的变量,要访问外部类的s变量时,我们要用Outter.this.s
匿名内部类和局部内部类一样,都没有访问修饰符
匿名内部类也不能用static修饰,这个因为如果是静态的话,没有名字我们都访问不到它
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。
上面的代码等价于下面的代码:
静态内部类不能访问外部类的非静态成员
静态内部类不依赖于外部类具体对象
方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
方便编写事件驱动程序
方便编写线程代码
java内部类有四种,分别是成员内部类、局部内部类、匿名内部类和静态内部类(也称嵌套内部类),下面分别讲一下这四种内部类。
成员内部类
成员内部类位于外部类的内部,看起来就像一个成员变量一样,下面是一个成员内部类的实例:class A{} class B{} interface C{} interface D{} class Outter extends A implements C{ //外部类 private int i = 0; public class Inner extends B implements C,D{ //内部类 int i = 1; void test(){ System.out.println(i); //1,内部类的i System.out.println(Outter.this.i);//0,外部类的i } } }
成员内部类的访问修饰符可以是public,protected,private和缺省。而器外部类只能是public,缺省或者定义为abstract。
上面的例子可以看到,内部类可以访问外部类的成员,如果成员名字有冲突时,默认是内部类自己的成员,这种情况下要想访问外部类的成员,需要使用this
外部类继承了B,而内部类继承了C,这样就实现了java的多继承。
这里我们可以看到,内部类和外部类都同时实现了C接口,这也是允许的。内部类和外部类可以以不同方法实现同一个接口。
创建成员内部类对象
public static void main(String[] args) { Outter outter = new Outter(); //必须要创建外部类对象 Outter.Inner inner = outter.new Inner(); }
必须先创建外部类的对象。有兴趣的同志可以去反编译一个内部类的字节码,就可以发现其实它的构造函数接受了一个外部类的对象作为参数,所以创建内部类对象必须要有一个外部类的对象。
用outter.new的方法去创建内部类对象。
局部内部类
局部内部类定义在外部类成员方法内部。class Outter extends A implements C { // 外部类 private int i = 0; public B test() { class Inner extends B implements C, D { // 内部类 } return new Inner(); }
局部内部类因为不会在其他地方被实例化,所以权限修饰符对于它来说就没有意义了,所以局部内部类没有权限修饰符。
通常是返回一个实例化对象。这里就引出了一个问题,test()方法执行完return后其生命周期就结束了,但是返回的内部类对象其生命周期并没有结束,所以如果内部类使用到外部方法的局部变量怎么办?答案是内部类只能访问外部类和外部类外部方法的final变量,我们看下面的代码:
class Outter extends A implements C { // 外部类 private String s = "Outer"; public B test(final int k) { int i = 0; final int j = 0; class Inner extends B implements C, D { // 内部类 String s = "Inner"; void f() { String s = "Inner.f"; System.out.println(s); //Inner.f System.out.println(this.s);//Inner System.out.println(Outter.this.s);//Outer //Cannot refer to the non-final local variable j defined in an enclosing scope // System.out.println(i);//error System.out.println(k);//0 System.out.println(j);//0 } } return new Inner(); } public static void main(String[] args) { Outter outter = new Outter(); // 必须要创建外部类对象 B b = outter.test(0); b.f(); } }
试图访问非final变量i会提示错误。访问外部方法的非final变量是不允许的,因为前面说过了,外部函数生命周期结束的时候,内部类对象还是活着的,所以访问不到。
但是为什么final变量又可以呢?因为内部类将要访问的final变量作为参数传递给了自己的构造函数(这个可以通过反编译字节码看到)。
从上面我们可以看到,如果内部类变量名和外部类变量名一样,默认使用的是内部类的变量,要访问外部类的s变量时,我们要用Outter.this.s
匿名内部类
匿名内部类非常常见,如果某个类只需要在一个外部类内部实例化一次的话,我们就可以使用匿名内部类。匿名内部类在安卓的时间监听器中应用比较多。在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。下面这段代码是一段Android事件监听代码:bt.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub } });
匿名内部类和局部内部类一样,都没有访问修饰符
匿名内部类也不能用static修饰,这个因为如果是静态的话,没有名字我们都访问不到它
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。
上面的代码等价于下面的代码:
private void setListener() { bt.setOnClickListener(new Listener()); } class Listener implements View.OnClickListener{ @Override public void onClick(View v) { // TODO Auto-generated method stub } }
静态内部类(嵌套内部类)
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,所以基本来说静态内部类仅仅是在外部类内部定义的类,不受限与外部类,和普通静态类没什么区别。class Outter { // 外部类 static class Inner { //静态内部类 } }
静态内部类不能访问外部类的非静态成员
静态内部类不依赖于外部类具体对象
什么时候需要使用内部类
每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整(最重要的特点)方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
方便编写事件驱动程序
方便编写线程代码
相关文章推荐
- Java知多少(94)键盘事件
- Java基础:泛型
- JAVA环境变量配置
- Maven eclipse 或者myeclipse 报错问题 Check $M2_HOME environment variable and mvn script match.
- Java内存与垃圾回收调优
- Java并发编程-15-并发任务间数据交换
- 简易Java(02):如何构建您自己的Java库?
- 我的Java基础知识总结1
- Java如何对HashMap按值进行排序
- Java Client for Google Cloud Storage
- Java 字符串分隔 split
- java两种创建String对象的区别
- 我的Java开发学习之旅------>Java String对象作为参数传递的问题解惑
- Spring注解详解
- Java套接字2
- Spring事务配置的五种方式(转载)
- 第一课Java
- Eclipse 警告提示:Access restriction:The type JPEGCodec is not accessible due to restriction on
- 在Eclipse安装ADT
- Java类被使用的几种情况