zoukankan      html  css  js  c++  java
  • Java基础(十三)内部类(inner class)

      1.内部类是定义在另一个类中的类。使用内部类的原因有:

    • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
    • 内部类可以对同一个包中的其他类隐藏起来
    • 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷

      2.使用内部类访问对象状态

      内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。

      例如,外部类TalkingClock中有数据域beep,而内部类中没有定义数据域beep,但是却可以直接通过if (beep) Toolkit.getDefaultToolkit().beep();来引用外部类TalkingClock的对象的数据域。

    public class InnerClassTest
    {
       public static void main(String[] args)
       {
          TalkingClock clock = new TalkingClock(1000, true);
          clock.start();
       }
    }
    
    class TalkingClock
    {
       private int interval;
       private boolean beep;
    
       public TalkingClock(int interval, boolean beep)
       {
          this.interval = interval;
          this.beep = beep;
       }
    
       public void start()
       {
          ActionListener listener = new TimePrinter();
          Timer t = new Timer(interval, listener);
          t.start();
       }
    
       public class TimePrinter implements ActionListener
       {
          public void actionPerformed(ActionEvent event)
          {
             System.out.println("At the tone, the time is " + new Date());
             if (beep) Toolkit.getDefaultToolkit().beep();
          }
       }
    }

      3.使用内部类访问外部类的对象的作用域的具体实现

      (1)内部类对象总有一个隐式引用,它指向了创建它的外部类对象,这个引用在内部类中的定义是不可见的,将外围类对象的引用称为outer,则上面的引用语句可以修改为:

    if (outer.beep) Toolkit.getDefaultToolkit().beep();

      (2)编译器修改了所有的内部类的构造器,添加了一个外围类引用的参数,例如:编译器为TimePrinter类生成了一个默认的构造器,用来将外围类对象传递进来

    public TimerPrinter(Talking clock){
        outer = clock;
    }

      (3)在start方法中,执行ActionListener listener = new TimePrinter();时编译器实际上会将外围类对象this传递到TimerPrinter的构造器中:

    ActionListener listener = new TimePrinter(this);

      到此,就实现了内部类访问外围类的对象的数据域。

      使用正规语法在内部类中引用外部对象的数据域:

    if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();

      使用正规语法在内部类中编写内部对象的构造器

    ActionListener listener = this.new TimePrinter();

      显式地命名将外围类引用设置为其他的对象

    TalkingClock jabberer = new TalkingClock(100,true);
    TalkingClock.TimerPrinter listener = jabberer.new TimerPrinter();

      4.局部内部类

      (1)观察上述代码可以看到,TimerPrinter这个类只在start方法中创建这个类型的对象时使用了一次,这时就可以将TimerPrinter类定义成start方法的局部类,局部类不能用public或private声明,它的作用域被限制在这个局部类的块中。局部类的优势就是可以对外部世界完全隐藏起来,即使TalkingClock类中的其他代码也不能访问它,除了start方法,其他没有任何方法知道这个类的存在。

       public void start()
       {
           class TimePrinter implements ActionListener
           {
                public void actionPerformed(ActionEvent event)
                {
                     System.out.println("At the tone, the time is " + new Date());
                     if (beep) Toolkit.getDefaultToolkit().beep();
                }
           }
          ActionListener listener = new TimePrinter();
          Timer t = new Timer(interval, listener);
          t.start();
       }
    

      (2)局部类还有一个优点:它们不仅能够访问包含它们的外部类,还可以访问局部变量,但是,局部类访问的局部变量必须为final,即一旦赋值就不可变。

    class TalkingClock
    {
    private int interval;
    private final boolean beep;
       public void start(int interval, boolean beep)
    {
    ...
    }
    }

      5.匿名内部类

      如果只创建内部类的一个对象,那么这个内部类就不用命名了,可以直接使用代码块替代,这种类被称为匿名内部类

       public void start(int interval, boolean beep)
       {
          ActionListener listener = new ActionListener()
             {
                public void actionPerformed(ActionEvent event)
                {
                   System.out.println("At the tone, the time is " + new Date());
                   if (beep) Toolkit.getDefaultToolkit().beep();
                }
             };
          Timer t = new Timer(interval, listener);
          t.start();
       }

      6.静态内部类

      如果使用内部类只是为了把一个类隐藏在另外一个类的内部,而不需要在内部类中引用外围类对象,这个时候可以将内部类声明为static,以便取消产生的引用。

      例如,如果想要计算一下数组中最大值和最小值,可以有常规方法和静态内部类方法两种。

      (1)常规实现

    • 首先需要写一个包含两个值的Pair类:
    package test;
    
    public class Pair {
        private double first;
        private double second;
        public Pair(double f, double s)
        {
           first = f;
           second = s;
        }
        public double getFirst()
        {
           return first;
        }
        public double getSecond()
        {
           return second;
        }
    }
    • 然后需要写一个比较数组中的元素然后得到最大值和最小值的类,并返回一个Pari类型的对象
    package test;
    
    public class ArrayAlg {
        public static Pair minmax(double[] values)
           {
              double min = Double.POSITIVE_INFINITY;
              double max = Double.NEGATIVE_INFINITY;
              for (double v : values)
              {
                 if (min > v) min = v;
                 if (max < v) max = v;
              }
              return new Pair(min, max);
           }
    }
    • 最后可以这么来调用:
    package test;
    
    public class Main {
           public static void main(String[] args)
           {
              double[] d = new double[20];
              for (int i = 0; i < d.length; i++)
                 d[i] = 100 * Math.random();
              Pair p = ArrayAlg.minmax(d);
              System.out.println("min = " + p.getFirst());
              System.out.println("max = " + p.getSecond());
           }
    }

      这样一来确实是实现了,没有问题。但是,由于在大型项目中很可能会有类名和Pair冲突的情况,同时,两个类也显得很麻烦,并且最为重要的是,在Pari对象中不需要引用任何其他的对象,因此可以使用静态内部类来解决这些问题。

      (2)静态内部类实现

    package staticInnerClass;
    
    /**
     * This program demonstrates the use of static inner classes.
     * @version 1.02 2015-05-12
     * @author Cay Horstmann
     */
    public class StaticInnerClassTest
    {
       public static void main(String[] args)
       {
          double[] d = new double[20];
          for (int i = 0; i < d.length; i++)
             d[i] = 100 * Math.random();
          ArrayAlg.Pair p = ArrayAlg.minmax(d);
          System.out.println("min = " + p.getFirst());
          System.out.println("max = " + p.getSecond());
       }
    }
    
    class ArrayAlg
    {
    
       public static class Pair
       {
          private double first;
          private double second;
    
          public Pair(double f, double s)
          {
             first = f;
             second = s;
          }
    
          public double getFirst()
          {
             return first;
          }
    
          public double getSecond()
          {
             return second;
          }
       }
    
       public static Pair minmax(double[] values)
       {
          double min = Double.POSITIVE_INFINITY;
          double max = Double.NEGATIVE_INFINITY;
          for (double v : values)
          {
             if (min > v) min = v;
             if (max < v) max = v;
          }
          return new Pair(min, max);
       }
    }

      将Pair类放在ArrayAlg类中并声明为static,然后就可以通过下面的代码同时调用这两个类:使用ArrayAlg.Pair也不会出现重名的困扰。

        ArrayAlg.Pair p = ArrayAlg.minmax(d);
          System.out.println("min = " + p.getFirst());
          System.out.println("max = " + p.getSecond());

      如果不将Pair声明为static,由于没有可用的隐式(静态)ArrayAlg类型对象初始化内部类对象,编译器就会报错

    Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
        No enclosing instance of type ArrayAlg is accessible. Must qualify the allocation with an enclosing instance of type ArrayAlg (e.g. x.new A() where x is an instance of ArrayAlg).
    
        at staticInnerClass.ArrayAlg.minmax(StaticInnerClassTest.java:76)
        at staticInnerClass.StaticInnerClassTest.main(StaticInnerClassTest.java:15)

      静态类的对象在代码编译前就已经存在于内存中的对象,如果Pair类中不加static,在静态方法minmax中会return new Pair(min, max);此时,由于Pari不是静态类,所以在程序代码编译前无法执行,因此就会出现无法编译的错误。

  • 相关阅读:
    2016-10-17: source insight插件
    Reactor模式通俗解释
    2016-09-19: linux后台运行
    2016-08-16: 检测函数是否存在的C++模板
    2016-08-16: copy-and-swap
    2016-08-15:从YUV420P中提取指定大小区域
    2016-08-15: C++ traits
    2016-08-05:samba服务器配置
    LINQ 根据指定属性名称对序列进行排序
    Resharper
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9329555.html
Copyright © 2011-2022 走看看