内部类

  

 (总结) 内部类有两种情况:

  (1) 在类中定义一个类(私有内部类,静态内部类)

  (2) 在方法中定义一个类(局部内部类,匿名内部类)

 

 

 1、私有内部类 —— 在方法之间定义的内部类,非静态

  我们首先看看类中内部类的两个特点:

    (1) 在外部类的作用范围内可以任意创建内部类对象,即使内部类是私有的(私有内部类)。

       即内部类对包围它的外部类可见。

        外部类可以创建内部类的对象,并且内部类能够方便的引用到外部类对象

 

    (2) 在内部类中可以访问其外部类的所有域,即使是私有域。即外部类对内部类可见。    

        内部类可以引用外部类的私有域

  

  其实,内部类是Java编译器一手操办的。虚拟机并不知道内部类与常规类有什么不同。 

  总结一下编译器对类中内部类做的手脚吧:

    (1)  在内部类中偷偷摸摸的创建了包可见构造器,从而使外部类获得了创建权限。

    (2)  在外部类中偷偷摸摸的创建了访问私有变量的静态方法,从而 使 内部类获得了访问权限。

    这样,类中定义的内部类无论私有,公有,静态都可以被包围它的外部类所访问。

 

2、静态内部类  ——  在方法间定义的内部类,静态

  内部类也有静态的区别,静态内部类和私有内部类最大的区别在于,

  静态内部类中无法引用到其外围类的非静态成员。

  也就是说静态内部类无法得到其外围类对象的引用,

  那么自然也就无法访问外围类的非静态成员了。

  因此,静态内部类只能访问其外围类的静态成员,除此之外与非静态内部类没有任何区别。

 

3、局部内部类 —— 在方法中定义的内部类

   方法内部类也有两个特点

        (1)  方法中的内部类没有访问修饰符, 即方法内部类对包围它的方法之外的任何东西都不可见。

        (2)  方法内部类只能够访问该方法中的局部变量,

       所以也叫局部内部类。而且这些局部变量一定要是final修饰的常量。

  为了保持局部变量与局部内部类中备份域保持一致。 

  编译器不得不规定死这些局部域必须是常量,一旦赋值不能再发生变化了。

      所以为什么局部内部类应用外部方法的域必须是常量域的原因所在了。

 

 

 内部类的特点总结  

  (1)  在方法间定义的非静态内部类: 

         ● 外围类和内部类可互相访问自己的私有成员。

         ● 内部类中不能定义静态成员变量。

 

  (2) 在方法间定义的静态内部类:

         ● 只能访问外部类的静态成员。

  

  (3) 在方法中定义的局部内部类:

         ● 该内部类没有任何的访问控制权限

         ● 外围类看不见方法中的局部内部类的,但是局部内部类可以访问外围类的任何成员。

         ● 方法体中可以访问局部内部类,但是访问语句必须在定义局部内部类之后。

         ● 局部内部类只能访问方法体中的常量,即用final修饰的成员。

 

  (4) 在方法中定义的匿名内部类:

         ● 没有构造器,取而代之的是将构造器参数传递给超类构造器。 

--http://www.cnblogs.com/jiangao/archive/2012/02/23/2364119.html

 

代码目录

 

1.成员内部类(member inner class)

  不对外开开放,高内聚

    如果想从外部类的非静态方法之外的任何位置创建某个内部类的对象,

    必要具体的指明 这个对象的类型 OutClassName.InnerClassName 

package memberinner;

public class A {

    public void go(){
        System.out.println("约不约?");

    }

}

  

package memberinner;

public class Test {
    // B类是 test.class 中的一个内部类
    class B {
        public void go() {
            System.out.println("今晚有约( ⊙ o ⊙ )");
        }
    }

    public void show() {
        B b = new B();
        b.go();
    }

    public static void main(String[] args) {
        A a = new A();
        a.go();
        new Test().show();
    }

}

 

运行结果

 

其实,内部类是Java编译器一手操办的。虚拟机并不知道内部类与常规类有什么不同。编译器是如何瞒住虚拟机的呢?

 

对内部类进行编译后的class文件:A.class、Test.class Test$B.class 

  一个class一定会生成一个class文件,内部类也不例外,它也会生成一个Test$B.class的class文件。

  内部类始终还是一个类,它也具有一些类的基本特性
  这说明内部类B仍然被编译成一个独立的类(Test$B.class),而不是Test类的某一个域。 虚拟机运行的时候,也是把B作为一种常规类来处理的。

 

 

2.方法内部类(method inner class)

  A. 方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。

  B. 方法内部类使用时,在方法中创建内部类对象,用对象调用其内部类中的方法。

  注: 由于方法内部类不能在外部类的方法以外的地方使用呢,因此方法内部类不能使用访问控制符合 static 修饰符

  

  定义在方法中,比方法的范围还小。是内部类中最少用到的一种类型。

  像局部变量一样,不能被public, protected, private和static修饰。

  只能访问方法中定义的final类型的局部变量。

  方法内部类在方法中定义,所以只能在方法中使用,即只能在方法当中生成方法内部类的实例并且调用其方法

 

  它有一个特点,就是方法内的内部类连本方法的成员变量都不可访问,它只能访问本方法的final型成员。

  同时另一个需引起注意的是方法内部定义成员,只允许final修饰或不加修饰符,其它像static等均不可用。

 

   简单来说,就是暂时性的一种,局部的。来看例子:

 

package methodinner;

public class DuanZanShiChangZheng {
    public void noProblem() {
        // 这就像一个  精神病证明书  
        System.out.println("  短暂性精神病   ");
    }

}

 

package methodinner;

// 继承 精神病证明书  那就证明你这人有精神病  出门办事就不方便  一切有限制
// public class Test extends DuanZanShiChangZheng {
public class Test {
    private String member = "全局变量";
    final int n = 4;

    public void driver() {
        // 执行体
        final String member1 = "局部变量"; // 方法内的变量只有final变量才能被方法内部类访问
        System.out.println("我正在开车   =  " + member);
        /**
         *     每个内部类都能独立地继承自一个(接口的)实现,
         *  所以无论外围类是否已经继承了某个(接口的)实现, 对于内部类都没有影响
         *     内部类使得多重继承的解决方案变得完整。接口解决了部分问题, 
         *     而内部类有效地实现了“多重继承” 
         *     短暂,不对外,防止变成全局
         * 
         */
        // 开车开太快 出事了怎么办?
        // System.out.println("撞死人了"); // 如何使你要负责的车祸减轻法律制裁
        // 拿出 精神病证明书 减轻法律制裁(⊙o⊙) (文本注释最后一句话)
        // 调用 DuanZanShiChangZheng.class 中的 noProblem() 方法
        class B extends DuanZanShiChangZheng {
            public void show() {
                System.out.println(member1 + n);
            }
        }
        // 用的时候拿出来
        new B().noProblem();// 方法内部类里的方法只能在方法里调用
        new B().show();

        System.out.println("减轻应承受的法律制裁");
        // 虽然举例不好 但记忆深刻
    }

    public static void main(String[] args) {
        new Test().driver();
    }

}

 

 

 运行结果:

 

 

 

 

三、 匿名内部类 Anonymous inner class (Android运用最多)

  匿名内部类也就是没有名字的内部类

  正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写

  但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

 

    匿名内部类就是没有名字的局部内部类,不使用关键字class, extends, implements, 没有构造方法。

    什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的:


      a·只用到类的一个实例。
      b·类在定义后马上用到。
      c·类非常小(SUN推荐是在4行代码以下)
      d·给类命名并不会导致你的代码更容易被理解。


    在使用匿名内部类时,要记住以下几个原则:


      a·匿名内部类不能有构造方法。
      b·匿名内部类不能定义任何静态成员、方法和类。
      c·匿名内部类不能是public,protected,private,static。
      d·只能创建匿名内部类的一个实例。
      e·一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
      f·因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。

  关于匿名内部类还有如下两条规则:

    1)匿名内部类不能是抽象类,因为系统在创建匿名内部类的时候,

       会立即创建内部类的对象。因此不允许将匿名内部类 定义成抽象类。

    2)匿名内部类不等定义构造器,因为匿名内部类没有类名,

       所以无法定义构造器,但匿名内部类可以定义实例初始化块,

  

  以下是匿名内部类的实例:

    实例背景:输出每个Doctor博士每天做的事情

      1、白天大家都是“白天传授理论知识”;

      2、晚上每个人都是不一样的

  *用以前继承方法需要写多个实现类,而匿名内部类只需要实现“晚上做事workInNight()”这个方法就可以了

 

package anonymousinner;

public interface Qinshou { //接口

    public void workInNight();

} 
package anonymousinner;

public abstract class Doctor implements Qinshou { 
    // Doctor 实现 Qinshou 接口 
    public void workInDay() {
        System.out.println("Doctor = 白天传授理论知识");
    }

} 
package anonymousinner;

public class Doctor1 extends Doctor {
    // Doctor1 继承 Doctor 类
    public void workInNight() {
        System.out.println("Doctor1 = 晚上就教弹琴");
    }
    
} 
package anonymousinner;

public class Doctor2 extends Doctor {

    public void workInNight() {
        System.out.println("Doctor2 = 晚上,月黑风高夜,我要潜。。。我要潜心学习");
    }

} 
package anonymousinner;

public class Test {

    public static void main(String[] args) {
        Doctor1 d1 = new Doctor1();
        d1.workInDay();
        d1.workInNight();

        Doctor2 d2 = new Doctor2();// 实现类
        d2.workInDay();
        d2.workInNight();

        /**
         *     匿名内部类 ;语法三步骤 
         *     1.new 一个抽象类 或者 接口 
         *     2.加上一个花括号 
         *     3.给他添加为实现方法
         */
        Doctor d3 = new Doctor() {// new 抽象类

            public void workInNight() {// 实现
                System.out.println("Doctor = 抽象类 = 睡觉");
            }

        };

        d3.workInDay();
        d3.workInNight();
    }

}

 

 运行结果:

--http://zhchx0827.iteye.com/blog/1788799

 

方法中的内部类不能访问该方法的局部变量

  1)所谓“局部内部类”就是在对象的方法成员内部定义的类。

    而方法中的类,访问同一个方法中的局部变量,是天经地义的。那么为什么要加上一个final呢?

 
  2)原因是:编译程序实现上的困难,

    难在何处:内部类对象的生命周期会超过局部变量的生命期。为什么?

    表现在:局部变量的生命期:当该方法被调用时,该方法中的局部变量在栈中被创建(诞生)

        当方法调用结束时(执行完毕),退栈,这些局部变量全部死亡。

    而:内部类对象生命期,与其它类一样,当创建一个该局部类对象后,只有没有其它人再引用它时,它才能死亡。

    完全可能:一个方法已调用结束(局部变量已死亡),但该局部类的对象仍然活着。

    即:局部类的对象生命期会超过局部变量。

 
  3)退一万步:局部类的对象生命期会超过局部变量又怎样?

  问题的真正核心是:如果:局部内部类的对象访问同一个方法中的局部变量,是天经地义的,

  那么:只要局部内部类对象还活着,则:栈中的那些它要访问的局部变量就不能“死亡”(否则:它都死了,还访问个什么呢?),

  这就是说:局部变量的生命期至少等于或大于局部内部类对象的生命期。而:正是这一点是不可能做到的 


  4)但是从理论上:局部内部类的对象访问同一个方法中的局部变量,是天经地义的。

    所以:经过努力,达到一个折中结果:

    即:局部内部类的对象可以访问同一个方法中的局部变量,只要这个变量被定义为final.

    那么:为什么定义为final变可以呢?

    定义为final后,编译程序就好实现了:具体实现方法是:

      将所有的局部内部类对象要访问的final型局部变量,变成该内部类对象中的一个数据成员。

    这样,即使栈中局部变量(含final)已死亡,但由于它是final,其值永不变,

    因而局部内部类对象在变量死亡后,照样可以访问final型局部变量。

 

  归纳上述回答的真正核心是:局部内部类对象中包含有要访问的final型局部变量的一个拷贝,成为它的数据成员。

  因此,正是在这个意义上,final型局部变量的生命期,超过其方法的一次调用。

  严格来说,方法调用结束,所有的局部变量(含final)全死亡了。但:局部内部类对象中有final型局部变量的拷贝。

  其他:

    不管对象是不是final,他的生命周期都是 new开始,垃圾回收结束。 

    不管变量是不是final,他的生命周期都在于{}中。 
 

  类对象(class对象)与其它对象不同,类对象的生命周期 开始于类被加到内存中那一刻,结束于垃圾回收。 
  类变量(static)与类对象的生命周期相同。

/** 
*定义一个接口SuperInner,内部定义一个抽象方法m1(),无返回类型 
*/  
interface SuperInner{  
     public void m1();  
}
public class LocalInnerClassTest{  
      public static void main(String[] args){  
         Outer obj=new Outer();          //生成一个外部类对象  
         SuperInner si=obj.outer();        //调用外部类中的outer()方法,返回一个SuperInner类型对象赋值给si  
        si.m1();             //调用被覆盖的方法m1(),输出:Inner's m1() 20  
     }  
}
  
/** 
*定义一个类Outer,内部只定义一个方法outer(),返回类型为SuperInner 
*/  
class Outer{  
     public SuperInner outer(){  
        int a=10;                   //方法中定义一个局部变量a,并赋值为10  
        final int b=20;          //再定义一个final局部变量b,初始化为20  
  
        class Inner implements SuperInner{              //在outer()方法中定义一个局部内部类Inner,实现接口SuperInner  
           public void m1(){    //类中只有一个覆盖接口SuperInner的方法m1()  
//              System.out.println("Inner's m1()"+a);    //编译报错  
              System.out.println("Inner's m1() "+b);    //编译通过,输出:Inner's m1() 20  
           }  
        }  
        return new Inner();  
     }  
}   

 

  

 

转载于:https://www.cnblogs.com/Seven-cjy/p/6069463.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值