首页 > 代码库 > java中内部类的积累

java中内部类的积累

这里的静态,指以static关键字修饰的,包括类,方法,块,字段。

非静态,指没有用static 修饰的。

静态有一些特点:

1.全局唯一,任何一次的修改都是全局性的影响

2.只加载一次,优先于非静态

3.使用方式上不依赖于实例对象。

4.生命周期属于类级别,从JVM 加载开始到JVM卸载结束。

可参考 :http://blog.csdn.net/zhandoushi1982/article/details/8453522/。

 

关于静态内部类(嵌套类)和非静态内部类的区别,可参考:

http://www.jb51.net/article/74838.htm

(1)内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。

(2)非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。

(3)一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。

/* 下面程序演示如何在java中创建静态内部类和非静态内部类 */
class OuterClass{
  private static String msg = "GeeksForGeeks";
  // 静态内部类
  public static class NestedStaticClass{
    // 静态内部类只能访问外部类的静态成员
    public void printMessage() {
     // 试着将msg改成非静态的,这将导致编译错误 
     System.out.println("Message from nested static class: " + msg); 
    }
  }
  // 非静态内部类
  public class InnerClass{
    // 不管是静态方法还是非静态方法都可以在非静态内部类中访问
    public void display(){
     System.out.println("Message from non-static nested class: "+ msg);
    }
  }
} 
class Main
{
  // 怎么创建静态内部类和非静态内部类的实例
  public static void main(String args[]){
    // 创建静态内部类的实例
    OuterClass.NestedStaticClass printer = new OuterClass.NestedStaticClass();
    // 创建静态内部类的非静态方法
    printer.printMessage();  
    // 为了创建非静态内部类,我们需要外部类的实例
    OuterClass outer = new OuterClass();    
    OuterClass.InnerClass inner = outer.new InnerClass();
    // 调用非静态内部类的非静态方法
    inner.display();
    // 我们也可以结合以上步骤,一步创建的内部类实例
    OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();
    // 同样我们现在可以调用内部类方法
    innerObject.display();
  }
}



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

 

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

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

 

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

 

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

 

[java] view plain copy
 
  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实例。

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

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

 

[java] view plain copy
 
  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程序的角度来看是直接被调用:

 

[java] view plain copy
 
  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编译之后实际的操作如下:

 

[java] view plain copy
 
  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.       
  8.     public void display(){  
  9.         System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );  
  10.     }  
  11. }  

 

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

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

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

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

 

       四、匿名内部类初始化

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

 

[java] view plain copy
 
  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中内部类的积累