内部类和匿名内部类-JAVA

Java内部类和匿名内部类的用法


一、内部类: 
      (1)内部类的同名方法
        内部类可以调用外部类的方法,如果内部类有同名方法必须使用"OuterClass.this.MethodName()"格式调用(其中OuterClass与MethodName换成实际外部类名及其方法;this为关键字,表示对外部类的引用);若内部类无同名方法可以直接调用外部类的方法。
        但外围类无法直接调用内部类的private方法,外部类同样无法直接调用其它类的private方法。注意:内部类直接使用外部类的方法与该方法的权限与是否static无关,它取决于内部类是否有同名方法。
[java]  view plain  copy
  1. package innerclass;  
  2. public class OuterClass {  
  3.     private void outerMethod() {  
  4.         System.out.println("It's Method of OuterClass");  
  5.     }  
  6.     public static void main(String[] args) {  
  7.         OuterClass t = new OuterClass();  
  8.         OuterClass.Innerclass in = t.new Innerclass();  
  9.         in.innerMethod();  
  10.     }  
  11.   
  12.     class Innerclass {  
  13.         public void innerMethod() {  
  14.            OuterClass.this.outerMethod();// 内部类成员方法与外部类成员方法同名时,使用this调用外部类的方法  
  15.            outerMethod();// 内部类没有同名方法时执行外部类的方法  
  16.         }  
  17.         private void outerMethod() {  
  18.             System.out.println("It's Method of Innerclass");  
  19.         }  
  20.     }  
  21. }  
     输出结果为:
[html]  view plain  copy
  1. It's Method of OuterClass  
  2. It's Method of Innerclass  
    (2)内部类访问外部类的变量必须声明为final
      方法中的局部变量,方法结束后这个变量就要释放掉,final保证这个变量始终指向一个对象。
   首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。
     注意,若使用JDK1.8,方法中内部类的方法是可以直接访问外部类的方法的局部变量,并且不需要声明为final类型。
[java]  view plain  copy
  1. public class OuterClass {  
  2.     int num1 = 0;// 成员变量  
  3.   
  4.     private void outerMethod() {  
  5.         int num2 = 0;// 方法内的局部变量  
  6.         class Innerclass_1 {  
  7.             public void innerMethod() {  
  8.                 System.out.println(num1);// 方法中内部类的方法,可以正常访问外部类的成员变量  
  9.                 System.out.println(num2);// JDK1.8以前,方法中内部类的方法,不能直接访问外部类的方法的局部变量,必须声明为final  
  10.             }  
  11.         }  
  12.     }  
  13. }  
      如果使用JDK1.8以前的版本,Eclipse会出现如下错误提示:

    (3)内部类的实例化
     内部类实例化不同于普通类,普通类可以在任意需要的时候实例化,而内部类必须在外层类实例化以后方可实例化,并与外部类建立关系
      因此在外部类中的非static方法中,是可以实例化内部类对象
[java]  view plain  copy
  1. private void outerMethod() {  
  2.     System.out.println("It's Method of OuterClass");  
  3.     Innerclass in = new Innerclass();//在外部类的outerMethod方法中实例化内部类是可以啊  
  4. }  
      但在static方法中,就要注意啦!!!! 不能在static方法中直接new内部类,否则出现错误:
     No enclosing instance of type OuterClass is accessible. Must qualify the allocation with an enclosing instance of type OuterClass (e.g. x.new A() where x is an instance of OuterClass).
     这是因为静态方法是在类实例化之前就可以使用的,通过类名调用,这时动态内部类都还没实例化呢,怎么用,总不能调用一个不存在的东西吧。
     如果想在Static方法中new内部类,可以把内部类声明为Static
[java]  view plain  copy
  1. public class OuterClass {  
  2.     private void outerMethod() {  
  3.         System.out.println("It's Method of OuterClass");  
  4.     }  
  5.   
  6.     public static void main(String[] args) {  
  7.         Innerclass in = new Innerclass();  
  8.         in.innerMethod();  
  9.     }  
  10.   
  11.     static class Innerclass {//把内部类声明为static  
  12.         public void innerMethod() {  
  13.             System.out.println("It's Method of innerMethod");  
  14.   
  15.         }  
  16.     }  
  17.   
  18. }  
     当然,一般不使用static的方式,而是推荐这种方法:x.new A() ,其中 x是外部类OuterClass的实例,A是内部类Innerclass
[java]  view plain  copy
  1. package innerclass;  
  2. public class OuterClass {  
  3.     private void outerMethod() {  
  4.         System.out.println("It's Method of OuterClass");  
  5.     }  
  6.     public static void main(String[] args) {  
  7.         OuterClass.Innerclass in = new OuterClass().new Innerclass();//使用x.new A()的方式  
  8.         in.innerMethod();  
  9.     }  
  10.     class Innerclass {  
  11.         public void innerMethod() {  
  12.             System.out.println("It's Method of innerMethod");  
  13.         }  
  14.     }  
  15. }  
      x.new A() ,其中 x是外部类OuterClass的实例,A是类部类Innerclass,当然可以拆分如下,这样就显然很明白啦:
[java]  view plain  copy
  1. public static void main(String[] args) {  
  2.     OuterClass out = new OuterClass();//外部实例  
  3.     OuterClass.Innerclass in = out.new Innerclass();//外部实例.new 外部类  
  4.     in.innerMethod();  
  5. }  

    (4)什么情况下使用内部类
     典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外层类的对象。所以你可以认为内部类提供了某种进
入其外层类的窗口。
    使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外层类是否已经继承了某个(接口的)实
现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角
度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
   (5)在静态方法中实例化内部类例子:(内部类放在静态方法中)
[java]  view plain  copy
  1. package javatest2;  
  2. public class JavaTest2 {  
  3.     public static void main(String[] args) {  
  4.         class Boy implements Person {  
  5.             public void say() {// 匿名内部类自定义的方法say  
  6.                 System.out.println("say方法调用");  
  7.             }  
  8.             @Override  
  9.             public void speak() {// 实现接口的的方法speak  
  10.                 System.out.println("speak方法调用");  
  11.             }  
  12.         }  
  13.         Person per = new Boy();  
  14.         per.speak();// 可调用  
  15.         per.say();// 不能调用  
  16.     }  
  17. }  
  18. interface Person {  
  19.     public void speak();  
  20. }  
       per.speak()可调用,而per.say()不能调用,这时因为per是Person对象,要想调用子类的方法,可以强制向下转型为:((Boy) per).say();或者直接改为Boy per = new Boy();。 从中可发现,要想调用内部类的自定义的方法,必须通过内部类的对象来调用。那么,匿名内部类连名字都没有,怎么调用内部类自定义的方法?
(二)匿名内部类
      匿名内部类也就是没有名字的内部类正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口,但最多只能继承一个父类,或实现一个接口。
关于匿名内部类还有如下两条规则:
    1)匿名内部类不能是抽象类,因为系统在创建匿名内部类的时候,会立即创建内部类的对象。因此不允许将匿名内部类定义成抽象类。
    2)匿名内部类不等定义构造器(构造方法),因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化块,
    怎样判断一个匿名类的存在啊?看不见名字,感觉只是父类new出一个对象而已,没有匿名类的名字。
先看段伪代码
[java]  view plain  copy
  1. abstract class Father(){  
  2. ....  
  3. }  
  4. public class Test{  
  5.    Father f1 = new Father(){ .... }  //这里就是有个匿名内部类  
  6. }  
   一般来说,new 一个对象时小括号后应该是分号,也就是new出对象该语句就结束了。但是出现匿名内部类就不一样,小括号后跟的是大括号,大括号中是该new 出对象的具体的实现方法。因为我们知道,一个抽象类是不能直接new 的,必须先有实现类了我们才能new出它的实现类。上面的伪代码就是表示new 的是Father的实现类,这个实现类是个匿名内部类。
    其实拆分上面的匿名内部类可为:
[java]  view plain  copy
  1. class SonOne extends Father{  
  2.   ...       //这里的代码和上面匿名内部类,大括号中的代码是一样的  
  3. }  
  4. public class Test{  
  5.    Father f1 = new SonOne() ;  
  6. }  
     先看一个例子,体会一下匿名内部类的用法:

    运行结果:eat something
    可以看到,我们直接将抽象类Person中的方法在大括号中实现了,这样便可以省略一个类的书写。并且,匿名内部类还能用于接口上
[java]  view plain  copy
  1. public class JavaTest2 {  
  2.     public static void main(String[] args) {  
  3.         Person per = new Person() {  
  4.             public void say() {// 匿名内部类自定义的方法say  
  5.                 System.out.println("say方法调用");  
  6.             }  
  7.             @Override  
  8.             public void speak() {// 实现接口的的方法speak  
  9.                 System.out.println("speak方法调用");  
  10.             }  
  11.         };  
  12.         per.speak();// 可调用  
  13.         per.say();// 出错,不能调用  
  14.     }  
  15. }  
  16.   
  17. interface Person {  
  18.     public void speak();  
  19. }  
        这里per.speak()是可以正常调用的,但per.say()不能调用,为什么呢?注意Person per = new Person()创建的是Person的对象,而非匿名内部类的对象。其实匿名内部类连名字都没有,你咋实例对象去调用它的方法呢?但继承父类的方法和实现的方法是可以正常调用的,本例子中,匿名内部类实现了接口Person的speak方法,因此可以借助Person的对象去调用。
        若你确实想调用匿名内部类的自定义的方法say(),当然也有方法:
      (1)类似于speak方法的使用,先在Person接口中声明say()方法,再在匿名内部类中覆写此方法。
      (2)其实匿名内部类中隐含一个匿名对象,通过该方法可以直接调用say()和speak()方法;代码修改如下:
[java]  view plain  copy
  1. public class JavaTest2 {  
  2.     public static void main(String[] args) {  
  3.         new Person() {  
  4.             public void say() {// 匿名内部类自定义的方法say  
  5.                 System.out.println("say方法调用");  
  6.             }  
  7.   
  8.             @Override  
  9.             public void speak() {// 实现接口的的方法speak  
  10.                 System.out.println("speak方法调用");  
  11.             }  
  12.         }.say();// 直接调用匿名内部类的方法  
  13.     }  
  14. }  
  15. interface Person {  
  16.     public void speak();  
  17. }  

猜你喜欢

转载自blog.csdn.net/guangmingsky/article/details/80384007