黑马程序员技术交流社区

标题: 匿名内部类? [打印本页]

作者: 想学跑的猪    时间: 2013-4-12 10:43
标题: 匿名内部类?
不太懂什么是匿名内部类,求解

作者: 刘海东    时间: 2013-4-12 10:57
本帖最后由 刘海东 于 2013-4-12 11:14 编辑

1、匿名内部类,其实就是内部类的简写格式
2、定义匿名内部类的前提:内部类必须是继承一个类或实现接口
  1. Abstract class AbsDemo()           //先定义一个抽象父类
  2. {
  3.         Public void show();
  4. }

  5. Public void method()
  6. {
  7.         new AbsDemo()
  8.         {
  9.                 Void show()
  10.                 {
  11.                         System.out.println();
  12.                 }
  13.         }.show();
  14. }
复制代码
3、匿名内部类的格式:  new 父类或者接口(){定义子类的内容}
其实匿名内部类就是一个匿名子类对象,而且这个对象有点胖。可以理解为带内容的对象。

多次调用:
匿名内部类不适合多次调用,定义的方法最好不包括三个。

这个是我记的笔记,希望对你有帮助。
作者: love_java    时间: 2013-4-12 10:58
匿名内部类适合创建那种只需要一次使用的类,例如命令模式时所需要的Command对象。匿名内部类的语法有点奇怪,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。
    定义匿名内部类的格式如下:

[java] view plaincopyprint?
01.new 父类构造器(参数列表)|实现接口()  
02.{  
03. //匿名内部类的类体部分   
04.}  
new 父类构造器(参数列表)|实现接口()
{
  //匿名内部类的类体部分
}

从上面定义可以看出,匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。
关于匿名内部类还有如下两条规则:
1)匿名内部类不能是抽象类,因为系统在创建匿名内部类的时候,会立即创建内部类的对象。因此不允许将匿名内部类
定义成抽象类。
2)匿名内部类不等定义构造器,因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化块,
通过实例初始化块来完成构造器需要完成的事情。
最常用的创建匿名内部类的方式是需要创建某个接口类型的对象,如下程序所示:
[java] view plaincopyprint?
01.interface Product{  
02.   public double getPrice();  
03.   public String getName();  
04.}  
05.public class TestAnonymous{  
06.   public void test(Product p){  
07.   System.out.println("购买了一个"+p.getName()+",花掉       了"+p.getPrice());  
08.  }  
09. public static void main(String[]args){  
10.    TestAnonymous ta = new TestAnonymous();  
11.    ta.test(new Product(){  
12.    public double getPrice(){  
13.       return 567;  
14.    }  
15.   public String getName(){  
16.      return "AGP显卡";  
17.   }  
18.  });  
19. }  
20.}  
interface Product{
    public double getPrice();
    public String getName();
}
public class TestAnonymous{
    public void test(Product p){
    System.out.println("购买了一个"+p.getName()+",花掉       了"+p.getPrice());
   }
  public static void main(String[]args){
     TestAnonymous ta = new TestAnonymous();
     ta.test(new Product(){
     public double getPrice(){
        return 567;
     }
    public String getName(){
       return "AGP显卡";
    }
   });
  }
}

上面程序中的TestAnonymous类定义了一个test方法,该方法需要一个Product对象作为参数,但Product只是一个接口,
无法直接创建对象,因此此处考虑创建一个Product接口实现类的对象传入该方法---如果这个Product接口实现类需要重复
使用,则应该经该实现类定义一个独立类;如果这个Product接口实现类只需一次使用,则可采用上面程序中的方式,定义
一个匿名内部类。
    正如上面程序中看到,定义匿名类不需要class关键字,而是在定义匿名内部类时直接生成该匿名内部类的对象。上面
粗体字代码部分就是匿名类的类体部分。
由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。
对于上面创建Product实现类对象的代码,可以拆分成如下代码:

[java] view plaincopyprint?
01.class AnonymousProduct implements Product{  
02.  public double getPrice(){  
03.   return 567;  
04.    }  
05.  public String getName(){  
06.   return "AGP显卡";  
07.    }  
08. }  
09. ta.test(new AnonymousProduct());  
class AnonymousProduct implements Product{
  public double getPrice(){
   return 567;
    }
  public String getName(){
   return "AGP显卡";
    }
}
ta.test(new AnonymousProduct());

当通过实现接口来创建匿名内部类时,匿名内部类也不能显示创建构造器,因此匿名内部类只有一个隐式的无参数构造
器,故new接口名后的括号里不能传入参数值。
    但如果通过继承父类来创建匿名内部类是,匿名内部类将拥有和父类相似的构造器,此处的相似指的是拥有相同的形参
列表。







[c-sharp] view plaincopyprint?
01.abstract class Device{  
02.  private String name;  
03.  public Device(){  
04.  }  
05.  public Device(String name){  
06.   this.name = name;  
07.  }  
08.  public abstract double getPrice();  
09.  //此处省略了name属性的setter和getter方法   
10. }  
11. public class AnonymousInner{  
12.  public void test(Device d){  
13.   System.out.println("购买了一个"+d.getName()+",花掉了"+d.getPrice());  
14.  }  
15.  public static void main(String[] args){  
16.   AnonymousInner ai = new AnonymousInner();  
17.   //调用有参数的构造器创建Device匿名实现类的对象   
18.   ai.test(new Device("电子示波器"){  
19.    public double getPrice(){  
20.     return 67;  
21.    }  
22.   });  
23.   //调用无参数的构造器创建Device匿名实现类的对象   
24.   Device d = new Device(){  
25.    //初始化块   
26.    {  
27.     System.out.println("匿名内部类的初始化块...");  
28.    }  
29.    //实现抽象方法   
30.    public double getPrice(){  
31.     return 56;  
32.    }  
33.    public Sting getName(){  
34.     return "键盘";  
35.    }  
36.   };  
37.   ai.test(d);  
38.  }  
39. }  
abstract class Device{
  private String name;
  public Device(){
  }
  public Device(String name){
   this.name = name;
  }
  public abstract double getPrice();
  //此处省略了name属性的setter和getter方法
}
public class AnonymousInner{
  public void test(Device d){
   System.out.println("购买了一个"+d.getName()+",花掉了"+d.getPrice());
  }
  public static void main(String[] args){
   AnonymousInner ai = new AnonymousInner();
   //调用有参数的构造器创建Device匿名实现类的对象
   ai.test(new Device("电子示波器"){
    public double getPrice(){
     return 67;
    }
   });
   //调用无参数的构造器创建Device匿名实现类的对象
   Device d = new Device(){
    //初始化块
    {
     System.out.println("匿名内部类的初始化块...");
    }
    //实现抽象方法
    public double getPrice(){
     return 56;
    }
    public Sting getName(){
     return "键盘";
    }
   };
   ai.test(d);
  }
}

上面程序创建了一个抽象父类Device,这个抽象父类里包含两个构造器:一个无参数的,一个有参数的。当创建以Device
为父类的匿名内部类时,即可以传入参数(如上面程序中第一段粗体字部分),也可以不传入参数(如上面程序中第二段粗体
字部分)。
当创建匿名内部类时,必须实现接口或抽象父类里的所有抽象方法。如果有需要,也可以重写父类中的普通方法,如上面
程序的第二段粗体字代码部分,匿名内部类重写了抽象父类Device类的getName方法,其中getName方法并不是抽象方法。
    如果匿名内部类需要访问外部类的局部变量,则必须使用final修饰符来修饰外部类的局部变量,
否则系统将报错。

[java] view plaincopyprint?
01.interface A{  
02.  void test();  
03. }  
04. public class TestA{  
05.  public static void main(Strign[] args){  
06.   int age = 0;  
07.   A a = new A(){  
08.    public void test(){  
09.     //下面语句将提示错误:匿名内部类内访问局部变量必须使用final修饰   
10.     System.out.println(age);  
11.    }   
12.   };  
13.  }  
14. }  
interface A{
  void test();
}
public class TestA{
  public static void main(Strign[] args){
   int age = 0;
   A a = new A(){
    public void test(){
     //下面语句将提示错误:匿名内部类内访问局部变量必须使用final修饰
     System.out.println(age);
    }
   };
  }
}

上面程序代码是匿名内部类访问了外部类的局部变量,由于age变量没有使用final修饰符修饰,所以粗体字代码将
引起编译异常。


作者: 孤独的鸟人    时间: 2013-4-12 11:23
匿名内部类的语法规则如同匿名数组一样,当你只需要创建一个类的对象而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。
它的语法规则是这样的:     
new interfacename(){......}; 或 new superclassname(){......};   
例如:
public class Goods3
{
   public Contents cont()
{  
   return new Contents()
{
     private int i = 11;      
public int value()
{        
return i;      
}      
};   
}   
}   
这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简洁。     
在java的事件处理的匿名适配器中,匿名内部类被大量的使用。
例如在想关闭窗口时加上这样一句代码:   
frame.addWindowListener(new WindowAdapter(){     public void windowClosing(WindowEvent e){   System.exit(0);     }     });     
有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。
如果你想要初始化它的成员变量,有下面几种方法:     
1. 如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为final。     
2. 将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。     
3. 在这个匿名内部类中使用初始化代码块。

作者: zhangx    时间: 2013-4-12 11:43
楼上的几贴已经回复的相当清楚了,说白了就是单次使用(用完后就需要释放内存)的类。因为是单次使用,为了简单没必要取名的类。用法跟普通类相似。这是我的理解,希望共同进步。{:soso_e181:}
作者: 想学跑的猪    时间: 2013-4-12 12:11
能不能举一些实际开发中的一些例子说明一下,我们在什么时候可以用到内部类
作者: lyg2013    时间: 2013-4-12 13:10
关于JAVA内部类:一个内部类的定义是定义在另一个类内部的类。
  存在它的原因是:
  1.一个内部类的对象能够访问创建它的对象的实现,包括私有数据。即内部类实例对包含它的哪个类的实例来说,是特权的。
  2.对于同一个包中的其他类来说,内部类能够隐藏起来,换句话说,内部类不管方法的可见性如何,那怕是public,除了包容类,其他类都无法使用它。
  3.匿名内部类可以很方便的定义回调。
  4.使用内部类可以非常方便的编写事件驱动程序。
匿名类和内部类中的中的this :
有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。
这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名。如下面这个例子:
  1. public class A {
  2.   int i = 1;
  3.   public A() {
  4.     Thread thread = new Thread() {
  5.       public void run() {
  6.         for(;;) {
  7.           A.this.run();
  8.           try {
  9.             sleep(1000);
  10.           } catch(InterruptedException ie) {
  11.           }
  12.         }
  13.       }
  14.     };
  15.     thread.start();
  16.   }
  17.   public void run() {
  18.     System.out.println("i = " + i);
  19.     i++;
  20.   }
  21.   public static void main(String[] args) throws Exception {
  22.     new A();
  23.   }
  24. }
复制代码
在上面这个例子中, thread 是一个匿名类对象,在它的定义中,它的 run 函数里用到了外部类的 run 函数。
    这时由于函数同名,直接调用就不行了。这时有两种办法,一种就是把外部的 run 函数换一个名字,但这种办法对于一个开发到中途的应用来说是不可取的
    。那么就可以用这个例子中的办法用外部类的类名加上 this 引用来说明要调用的是外部类的方法 run。

其实它真正的目的仅仅为了定义回调--进一步就是事件驱动。
接口和回调:编程一个常用的模式是回调模式,在这种模式中你可以指定当一个特定时间发生时回调对象上的方法。
  1. /** 内部类Inner的声明 */
  2. public class Inner{
  3. private int size;
  4. /** 方法doStuff() */
  5. public void doStuff(int size){
  6. size++; //存取局部变量
  7. this.size++;  //存取其内部类的成员变量
  8. Outer.this.size++; //存取其外部类的成员变量
  9. System.out.println(size+" "+this.size+" "+Outer.this.size);
  10. }
  11. }//内部类Inner结束
  12. /** 类Outer中定义的实例方法testInner()方法 */
  13. public void testInner(){
  14. Inner i=new Inner();
  15. i.doStuff(5);
  16. }
  17. /** main()方法 */
  18. public static void main(String[] a){
  19. Outer o=new Outer();
  20. o.testInner();
  21. }
  22. }//类Outer结束

复制代码

作者: 尹丽峰    时间: 2013-4-12 13:12
简单的说就是只用一次 下次不用了 使用完成后释放资源!
作者: 杨武林    时间: 2013-4-12 13:17
匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,如下所示: new <类或接口> <类的主体> 这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法。

java 代码

interface pr   
{   
void print1();   
}   
   
public class noNameClass     
{   
public pr dest()   
{   
    return new pr(){   
     public void print1()   
     {   
      System.out.println("Hello world!!");   
     }   
    };   
}   
   
public static void main(String args[])   
{   
    noNameClass c=new     noNameClass();   
    pr hw=c.dest();   
    hw.print1();   
}   
}   
   
pr也可以是一个类但是你外部调用的方法必须在你的这个类或接口中声明外部不能调用匿名类内部的方法

Java中内部匿名类用的最多的地方也许就是在Frame中加入Listner了吧。
如下:

java 代码
import java.awt.*;   
import java.awt.event.*;   
   
public class QFrame extends Frame {   
    public QFrame() {   
           this.setTitle(\"my application\");   
   
           addWindowListener(new WindowAdapter() {   
                   public void windowClosing(WindowEvent e) {   
                   dispose();   
                   System.exit(0);   
}   
            });      
   
          this.setBounds(10,10,200,200);   
     }   
}   
内部匿名类,就是建立一个内部的类,但没有给你命名,也就是没有引用实例的变量。
new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
             dispose();
             System.exit(0);
     }
}
    new 是建立一个 WindowAdapter对象 ,后面一个 {} 表示这个括号中的操作作用于这个默认的对名象,而上面的Java程序中后面是一个函数体。
这个用法的作用是:创建一个对象的实例,并且 override 它的一个函数。打开 WindowAdapter 的代码可以发现。它是一个抽象类。它是对 WindowListener 接口的一个实现。Frame.addWindowListner(); 的参数是一个 WindowListner ,而实现上是传一个从WindowAdapter 派生出的一个匿名类。

1.怎样判断一个匿名类的存在啊?看不见名字,感觉只是父类new出一个对象而已,没有匿名类的名字。

先看段伪代码
abstract class Father(){
....
}
public class Test{
   Father f1 = new Father(){ .... }  //这里就是有个匿名内部类
}
一般来说,new 一个对象时小括号后应该是分号,也就是new出对象该语句就结束了。
但是出现匿名内部类就不一样,小括号后跟的是大括号,大括号中是该new 出对象的具体的实现方法。
因为我们知道,一个抽象类是不能直接new 的,必须先有实现类了我们才能new出它的实现类。
上面的伪代码就是表示new 的是Father的实现类,这个实现类是个匿名内部类。
其实拆分上面的匿名内部类可为
class SonOne extends Father{
  ...       //这里的代码和上面匿名内部类,大括号中的代码是一样的
}
public class Test{
   Father f1 = new SonOne() ;
}

2.匿名内部类的注意事项

   注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。

在使用匿名内部类时,要记住以下几个原则:
 ·匿名内部类不能有构造方法。  
 ·匿名内部类不能定义任何静态成员、方法和类。  
 ·匿名内部类不能是public,protected,private,static。  
 ·只能创建匿名内部类的一个实例。
  ·一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。  
 ·因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。

  ·内部类只能访问外部类的静态变量或静态方法。



匿名类和内部类中的中的this :
有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名

3.匿名内部类的作用

    Java的内部类和C++中的嵌套类有本质的不同:C++的嵌套类没有指向包装类的句柄。仅仅表达一个封装的概念;但是Java的内部类不同,它可以访问包装类的成员(这表示它拥有指向包装类的句柄)。
     匿名内部类是内部类的一种简化写法:return new Wrapper {
                                        ...
                                     };
     等价于:Wrapped extends Wrapper {
          ...
          }
          return new Wrapped();

  难道匿名内部类就只这一点作用吗?
  考虑一下这样的case:

  interface ICount {
    int count();
  }

  class Parent {
    int i = 0;
    int count() {
      return i++;
    }
  }
       有一个类Child,它既想继承Parent的count()方法,又想实现ICount接口中的count方法,这个时候怎么办呢?内部类就可以大显身手了:
  class Child extends Parent {
    ICount getCount() {
      return new ICount {
        int i = 0;
        int count() {
         return (i *= 2);
        }
      }
    }
  }


作者: 打工人    时间: 2013-4-12 23:15

如果问题未解决,请继续追问,如果没有问题了,请将帖子分类 改为“已解决”,谢谢
作者: zhuiyi0819    时间: 2013-4-13 12:10
匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,如下所示: new <类或接口> <类的主体> 这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口。它还创建那个类的一个新实例,并把它作为语句的结果而返回。要扩展的类和要实现的接口是new语句的操作数,后跟匿名类的主体。如果匿名类对另一个类进行扩展,它的主体可以访问类的成员、覆盖它的方法等等,这和其他任何标准的类都是一样的。如果匿名类实现了一个接口,它的主体必须实现接口的方法。
java 代码
interface pr   
{   
void print1();   
}   
  
public class noNameClass   
{   
public pr dest()   
{   
    return new pr(){   
     public void print1()   
     {   
      System.out.println("Hello world!!");   
     }   
    };   
}   
  
public static void main(String args[])   
{   
    noNameClass c=new     noNameClass();   
    pr hw=c.dest();   
    hw.print1();   
}   
}   
  
pr也可以是一个类但是你外部调用的方法必须在你的这个类或接口中声明外部不能调用匿名类内部的方法
Java中内部匿名类用的最多的地方也许就是在Frame中加入Listner了吧。
如下:
java 代码
import java.awt.*;   
import java.awt.event.*;   
  
public class QFrame extends Frame {   
    public QFrame() {   
           this.setTitle(\"my application\");   
  
           addWindowListener(new WindowAdapter() {   
                   public void windowClosing(WindowEvent e) {   
                   dispose();   
                   System.exit(0);   
}   
            });     
  
          this.setBounds(10,10,200,200);   
     }   
}   
内部匿名类,就是建立一个内部的类,但没有给你命名,也就是没有引用实例的变量。
new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
             dispose();
             System.exit(0);
     }
}
    new 是建立一个 WindowAdapter对象 ,后面一个 {} 表示这个括号中的操作作用于这个默认的对名象,而上面的Java程序中后面是一个函数体。
这个用法的作用是:创建一个对象的实例,并且 override 它的一个函数。打开 WindowAdapter 的代码可以发现。它是一个抽象类。它是对 WindowListener 接口的一个实现。Frame.addWindowListner(); 的参数是一个 WindowListner ,而实现上是传一个从WindowAdapter 派生出的一个匿名类。
1.怎样判断一个匿名类的存在啊?看不见名字,感觉只是父类new出一个对象而已,没有匿名类的名字。
先看段伪代码
abstract class Father(){
....
}
public class Test{
   Father f1 = new Father(){ .... }  //这里就是有个匿名内部类
}
一般来说,new 一个对象时小括号后应该是分号,也就是new出对象该语句就结束了。
但是出现匿名内部类就不一样,小括号后跟的是大括号,大括号中是该new 出对象的具体的实现方法。
因为我们知道,一个抽象类是不能直接new 的,必须先有实现类了我们才能new出它的实现类。
上面的伪代码就是表示new 的是Father的实现类,这个实现类是个匿名内部类。
其实拆分上面的匿名内部类可为
class SonOne extends Father{
  ...       //这里的代码和上面匿名内部类,大括号中的代码是一样的
}
public class Test{
   Father f1 = new SonOne() ;
}
2.匿名内部类的注意事项
   注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
在使用匿名内部类时,要记住以下几个原则:
 ·匿名内部类不能有构造方法。  
 ·匿名内部类不能定义任何静态成员、方法和类。  
 ·匿名内部类不能是public,protected,private,static。  
 ·只能创建匿名内部类的一个实例。
  ·一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。  
 ·因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
  ·内部类只能访问外部类的静态变量或静态方法。

匿名类和内部类中的中的this :
有时候,我们会用到一些内部类和匿名类。当在匿名类中用this时,这个this则指的是匿名类或内部类本身。这时如果我们要使用外部类的方法和变量的话,则应该加上外部类的类名
3.匿名内部类的作用
    Java的内部类和C++中的嵌套类有本质的不同:C++的嵌套类没有指向包装类的句柄。仅仅表达一个封装的概念;但是Java的内部类不同,它可以访问包装类的成员(这表示它拥有指向包装类的句柄)。
     匿名内部类是内部类的一种简化写法:return new Wrapper {
                                        ...
                                     };
     等价于:Wrapped extends Wrapper {
          ...
          }
          return new Wrapped();
  难道匿名内部类就只这一点作用吗?
  考虑一下这样的case:
  interface ICount {
    int count();
  }

  class Parent {
    int i = 0;
    int count() {
      return i++;
    }
  }
       有一个类Child,它既想继承Parent的count()方法,又想实现ICount接口中的count方法,这个时候怎么办呢?内部类就可以大显身手了:
  class Child extends Parent {
    ICount getCount() {
      return new ICount {
        int i = 0;
        int count() {
         return (i *= 2);
        }
      }
    }
  }




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2