在java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客。在这篇博客中你可以了解到匿名内部类的使用、匿名内部类要注意的事项、如何初始化匿名内部类、匿名内部类使用的形参为何要为final。

一、使用匿名内部类内部类

匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:

  1. new 父类构造器(参数列表)|实现接口()
  2. {
  3. //匿名内部类的类体部分
  4. }

在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。

  1. public abstract class Bird {
  2. private String name;
  3.  
  4. public String getName() {
  5. return name;
  6. }
  7.  
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11.  
  12. public abstract int fly();
  13. }
  14.  
  15. public class Test {
  16.  
  17. public void test(Bird bird){
  18. System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");
  19. }
  20.  
  21. public static void main(String[] args) {
  22. Test test = new Test();
  23. test.test(new Bird() {
  24.  
  25. public int fly() {
  26. return 10000;
  27. }
  28.  
  29. public String getName() {
  30. return "大雁";
  31. }
  32. });
  33. }
  34. }
  35. ------------------
  36. Output
  37. 大雁能够飞 10000

在Test类中,test()方法接受一个Bird类型的参数,同时我们知道一个抽象类是没有办法直接new的,我们必须要先有实现类才能new出来它的实现类实例。所以在mian方法中直接使用匿名内部类来创建一个Bird实例。

由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。

对于这段匿名内部类代码其实是可以拆分为如下形式:

  1. public class WildGoose extends Bird{
  2. public int fly() {
  3. return 10000;
  4. }
  5.  
  6. public String getName() {
  7. return "大雁";
  8. }
  9. }
  10.  
  11. WildGoose wildGoose = new WildGoose();
  12. test.test(wildGoose);

在这里系统会创建一个继承自Bird类的匿名类的对象,该对象转型为对Bird类型的引用。

对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用。对于上面的实例,如果我们需要对test()方法里面内部类进行多次使用,建议重新定义类,而不是使用匿名内部类。

二、注意事项

在使用匿名内部类的过程中,我们需要注意如下几点:

  1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

2、匿名内部类中是不能定义构造函数的。

3、匿名内部类中不能存在任何的静态成员变量和静态方法。

4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

三、使用的形参为何要为final

参考文件:http://android.blog.51cto.com/268543/384844

我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。

      为什么必须要为final呢?

首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:

  1. public class OuterClass {
  2. public void display(final String name,String age){
  3. class InnerClass{
  4. void display(){
  5. System.out.println(name);
  6. }
  7. }
  8. }
  9. }

从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:

  1. public class OuterClass$InnerClass {
  2. public InnerClass(String name,String age){
  3. this.InnerClass$name = name;
  4. this.InnerClass$age = age;
  5. }
  6.  
  7. public void display(){
  8. System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );
  9. }
  10. }

所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。

直到这里还没有解释为什么是final?在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。

      简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。

      故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。

四、匿名内部类初始化

我们一般都是利用构造器来完成某个实例的初始化工作的,但是匿名内部类是没有构造器的!那怎么来初始化匿名内部类呢?使用构造代码块!利用构造代码块能够达到为匿名内部类创建一个构造器的效果。

  1. public class OutClass {
  2. public InnerClass getInnerClass(final int age,final String name){
  3. return new InnerClass() {
  4. int age_ ;
  5. String name_;
  6. //构造代码块完成初始化工作
  7. {
  8. if(0 < age && age < 200){
  9. age_ = age;
  10. name_ = name;
  11. }
  12. }
  13. public String getName() {
  14. return name_;
  15. }
  16.  
  17. public int getAge() {
  18. return age_;
  19. }
  20. };
  21. }
  22.  
  23. public static void main(String[] args) {
  24. OutClass out = new OutClass();
  25.  
  26. InnerClass inner_1 = out.getInnerClass(201, "chenssy");
  27. System.out.println(inner_1.getName());
  28.  
  29. InnerClass inner_2 = out.getInnerClass(23, "chenssy");
  30. System.out.println(inner_2.getName());
  31. }
  32. }

java提高篇(十)-----详解匿名内部类 ,形参为什么要用final的更多相关文章

  1. java提高篇(十)-----详解匿名内部类

    在java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始 ...

  2. 【转】java提高篇(十)-----详解匿名内部类

    原文网址:http://www.cnblogs.com/chenssy/p/3390871.html 在java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节 ...

  3. java提高篇(八)----详解内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二). 第一次见面 内部类我们从外面 ...

  4. java提高篇之详解内部类

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二). 第一次见面 内部类我们从外面 ...

  5. 详解匿名内部类 ,形参为什么要用final

    一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: new 父类构造器(参数列表)|实现接口() { //匿名内部类的类体部分 } 在这里我们看到使用匿名内部 ...

  6. java提高篇(十五)-----关键字final

    在程序设计中,我们有时可能希望某些数据是不能够改变的,这个时候final就有用武之地了.final是java的关键字,它所表示的是“这部分是无法修改的”.不想被改变的原因有两个:效率.设计.使用到fi ...

  7. java提高篇(十六)-----异常(一)

         Java的基本理念是“结构不佳的代码不能运行”!!!!! 大成若缺,其用不弊. 大盈若冲,其用不穷. 在这个世界不可能存在完美的东西,不管完美的思维有多么缜密,细心,我们都不可能考虑所有的因 ...

  8. java提高篇(十八)-----数组之一:认识JAVA数组

          噢,它明白了,河水既没有牛伯伯说的那么浅,也没有小松鼠说的那么深,只有自己亲自试过才知道!道听途说永远只能看到表明现象,只有亲自试过了,才知道它的深浅!!!!! 一.什么是数组      ...

  9. java提高篇(十四)-----字符串

          可以证明,字符串操作是计算机程序设计中最常见的行为. 一.String 首先我们要明确,String并不是基本数据类型,而是一个对象,并且是不可变的对象.查看源码就会发现String类为f ...

随机推荐

  1. Mvc分页:为IQueryable定义一个扩展方法,直接反回PagedList<T>结果集

    namespace Entity{ public interface IPagedList { /// <summary> /// 记录数 /// </summary> int ...

  2. 【转】浅析Windows编程的剪贴板

    摘要: 本文对Windows剪贴板机制作了深入.全面的阐述,具体内容包括:文本.位图.DSP.自定义格式剪贴板的使用和多数据项和延迟提交技术. 关键词: VC++6.0: 剪贴板机制:数据格式:延迟提 ...

  3. 如何诊断oracle数据库运行缓慢或hang住的问题

    为了诊断oracle运行缓慢的问题首先要决定收集哪些论断信息,可以采取下面的诊断方法:1.数据库运行缓慢这个问题是常见还是在特定时间出现如果数据库运行缓慢是一个常见的问题那么可以在问题出现的时候收集这 ...

  4. (一)问候Hibernate4

    第一节:Hibernate 简介 官网:http://hibernate.org/ Hibernate 是一个开放源代码的对象关系映射框架,它对JDBC 进行了非常轻量级的对象封装,使得Java程序员 ...

  5. js 获取时间对象代码

    /** * 获取时间对象 */function getDateObj(addDayCount) { var dd = new Date(); dd.setDate(dd.getDate()+addDa ...

  6. Glibc和GCC,ARM-LINUX-GCC的关系

    看到有些贴子/blog上提到「Glibc编译器」,这是个错误的用语.Glibc不是编译器,Glibc不是编译器,Glibc不是编译器.重要的事情说三遍.GCC才是编译器.

  7. 怎样用AIDL Service 传递复杂数据

    大家都知道在Android中通过AIDL可以跨进程调用Service中的数据,网上也有很多实例,但是大部分实例都是关于基本数据类型的远程调用,很少讲到复杂数据的调用,今天我用一个例子来演示一下怎样用A ...

  8. Mac apache配置问题解决

    AH00526: Syntax error on line 20 of /private/etc/apache2/extra/httpd-mpm.conf: Invalid command 'Lock ...

  9. 网站开发常用jQuery插件总结(十)菜单插件superfish

    网站对于菜单的依赖其实并不是很大,我们完全可以不使用菜单来设计网站,显示网站内容.但是如果网站的分类太多,“也许”使用菜单作为网站导航可以使 用户 更方便的寻找内容.superfish插件就是用于实现 ...

  10. 基于IAccessible接口的QQ窗口信息获取的实现

    这个技术现在已经封装成DLL免费开放给大家使用了,详情请加群221487171 可以访问官方网站下载 http://www.guihelper.com/ 主要技术(Microsoft Active A ...