• [core java学习笔记][第六章接口与内部类]


    接口域内部类

    接口

    描述类具有什么功能,不给出具体实现。

    内部类

    用于设计协作关系的类集合

    代理

    实现任意接口的对象。

    6.1 接口

    接口声明

    public interface Comparable
    {
        int compareTo(Object other);    //接口声明中自动属于public 所以不需要public
    }
    

    接口声明不能提供的功能

    • 不能含有实例域
    • 不能在接口中实现方法

    注意:提供实例域和方法实现的任务应该由接口实现的那个类完成


    接口实现

    需要完成两点

    • 将类声明为实现给定的接口

      使用implements这个关键词

      class Employee implements Comparable<Emoloyee>
      
    • 对接口中的所有方法进行定义

      public int compareTo(Employee other)    
      {
          return Double.compare(salary,other.salary);
      }
      

      实现接口时必须把声明public

    具体代码见附录


    常用API

    • java.lang.Compareable<T>

      int compareTo(T other)  
      用这个对象与other比较,小于other返回负值,相等返回0,大于返回正值
      
    • java.util.Arrays

      static void sort(Object[] a)
      利用mergesort算法对数组a的元素进行排序,要求数组的类实现了Comparable接口的类
      
    • java.lang.Integer

      static int compare(int x,int y)
      比较x,y大小 x<y 返回负值
      

    6.1.1 接口特性

    • 可以使用instanceof判断一个对象是否有某个特定的接口

      if(anObject instanceof Comparable){...}
      
    • 接口还能被扩展

      public interface Powered extends Moveable
      {
          double milesPerGallon();    
      } 
      
    • 一个类可以有多个接口

      class Employee implements Cloneable,Comparable
      

    6.1.2 接口与抽象类

    • 接口类似抽象类,但是Java并不支持多个抽象类,即多继承,以避免复杂;
    • C++中的接口就是利用多继承完成

    6.2对象克隆

    对于每一个类,都需要如下三个判断

    1. 默认clone是否满足要求
    2. 默认clone是否能够通过调用可变对象的clone得到修补
    3. 是否不应该使用clone

    如果选择1,2 必须

    • 实现Cloneble 接口
    • 使用public访问修饰符重新定义clone方法

    在这里,Cloneable接口的出现与接口的正常使用没有任何关系。尤其是,接口并没有制定clone方法,这个方法继承自Object,接口在这里只是作为一个标记,表明类设计者知道要进行克隆处理。如果没有这个接口,会出现已检验异常(checked exception)

    Cloneable接口是Java提供的几个标记接口(tagging interface)之一

    • 使用super.clone()来调用Object的方法

      class Employee implements Cloneable
      {
          public Employee clone() 
          {
              return (Employee) super.clone();    //仅仅是一个浅拷贝而已        
          }
      }
      

    下面是 深拷贝 clone的实例

    class Employee implements Cloneable
    {
        ...
        public Employee clone() throws CloneNotSupportedException
        {
            // call Object.clone()
            Employee cloned=(Employee) super.clone();
            // clone mutable files
            clone.hireDay=(Date) hireDay.clone();
        }
    }
    

    必须谨慎使用clone,一旦为Employee类定义了clone方法,任何人都可以利用它克隆Manager对象。Employee的克隆方法能完成这项重任吗?很难确定。标准库只有5%实现了clone

    代码见附录

    6.3 接口与回调

    定义

    回调(callback)是一种常见的程序设计模式,在这种模式中,可以指出某个特定事件发生时应该采取的动作。例如: 可以指出再按下鼠标或选择某个菜单项时应采取什么行动。

    定时器也是一种回调

    定时器的代码见代码库

    6.4 内部类

    为什么要用内部类

    1. 内部类方法可以访问该类定义的所在的作用域的所有数据, 包括私有数据。(继承的类不行)
    2. 内部类可以对同一个包中的其他类隐藏。(即使定义成public也无所谓)
    3. 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷

    6.4.1 使用内部类访问对象状态

    • 可以直接访问。

    • InnerClassTest见代码库。

    6.4.2 内部类的特殊语法规则

    • OuterClass.this表示外围内引用。
      例:

      public void actionPerformd(ActionEvent event)
      {
          ...
          if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
      }
      
    • 采用outObject.new InnerClass() 来声明构造,具体见下面代码

    • 采用OuterClass.InnerClass 在外围类的作用域之外引用内部类

      • 在OuterClass

        既可以

        TimerPrinter listener =new TimerPrinter();
        

        又能

        TimerPrinter listener =this.new TimerPrinter();
        
      • 在main中

        只能

        TalkingClock clock=new TalkingClock(1000,true);
        TalkingClock.TimerPrinter listener = clock.new TimerPrinter();  
        

    6.4.3 内部类是否有用,必要安全

    • 内部类是一个编译器现象,与虚拟机无关。
    • 编译器会把类翻译成用$分隔外部类和内部类的常规类文件,虚拟机对此一无所知。

      如`TalkingClock$TimerPrinter.class

    • 内部类实际上通过一个access$000(outer)的方法来调用外围的私有变量

    • 注意如果内部类访问了私有数据域,就有可能通过附加在外围类所在包中的其他类访问它们,但做这些事情需要高超的技巧,程序员不可能无意之中就获得权限。

    6.4.4 局部内部类

    • 在TalkingClock示例代码中就会发现,TimePrinter这个类只在start方法中创建这个类型的对象时使用了一次,在这种情况下可以使用局部内部类。
    • 对外部世界完全隐藏

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

    6.4.5 由外部方法访问final变量

    • 内部类只能访问final局部变量
    • 如果想要更新,使用一维final数组
    • 这种用法以后慢慢细细思考 很复杂的样子

    6.4.6 匿名内部类

    • 假如只创建这个类的一个对象,就不必命名了。这种类被称为匿名内部类(anonymous inner class)

      public void start(int interval,final boolean beep)
      {
          ActionListener listener =new ActionListener()
          {
               public void actionPerformed(ActionEvent event)
             {     
                  int a=interval;
                  Date now = new Date();
                  System.out.println("At the tone,the time is "+now);
                  if(beep) Toolkit.getDefaultToolkit().beep();
             }
          }
          Timer t=new Timer(interval,listener);
          t.start();
      }
      
    • 通常的语法格式是

      new SuperType(construction parameters)
      {
          inner class methods and data        
      }
      
    • 还能够重载方法//实质是继承了后覆盖

      for(int i=0;i<dates.length;i++)
      dates[i]=new Date()
          {
              public int  compareTo(Date other)
              {
                  counter[0]++;
                  return super.compareTo(other)
              }
          };
      
    • 双括号初始化

      invite(new ArrayList<String>() {{add("Harry");add("Tony");}})
      

      注意这里的括号,外括号建立了ArrayList的一个匿名子类。内层括号则是一个对象构造块

    • 不常用: 在静态方法,知道当前类名

      new Object(){}.getClass().getEnclosingClass()
      

    6.4.7 静态内部类

    • 有时候使用内部类只是为了把一个类隐藏到另一个类的内部。并不需要内部类引用外围类对象。为此可以将内部类声明为static,以便取消引用

          Class ArrayAlg
          {
              public static class Pair
              {
                  ...
              }
          }
      

    6.5 代理

    有点难以后补

  • 相关阅读:
    Go语言 插入排序并返回排序前的索引
    使用patch-package定制node_modules 中的依赖包
    移动端 rem自适应布局 (750的设计稿)
    通过原型截获input.value的方法
    ts 使用 keyof typeof
    logrotate日志管理工具
    【LeetCode刷题】239.滑动窗口最大值
    【LeetCode刷题】剑指Offer 48.最长不含重复字符的子字符串
    【LeetCode刷题】912. 排序数组
    【LeetCode刷题】744. 寻找比目标字母大的最小字母
  • 原文地址:https://www.cnblogs.com/zy691357966/p/5480277.html
Copyright © 2020-2023  润新知