IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Java基础课程-接口、内部类、回调函数讲解

    redarmy_chen发表于 2016-08-03 17:36:06
    love 0

    Java基础课程

    1.上章回顾与预习检查

    1.1 上章回顾

    • 简述import的作用?
    • 简述package的作用?
    • 什么是抽象类?
    • java支持多重继承吗?
    • super关键字的作用?
    • 静态块、语句块、构造函数在继承关系中的执行顺序是什么样子?
    • 方法重写与方法的重载区别?

    1.2 预习检查

    • 什么是接口?
    • 接口支持多重继承吗?
    • 谈谈你对内部类的理解?

    2. 本章任务

    • 熟练掌握接口的使用;
    • 熟练掌握内部类的使用;
    • 熟练掌握静态内部类的使用;
    • 熟练掌握匿名类的使用;
    • 熟练掌握回调函数

    3. 本章内容

    • 接口
    • 内部类
    • 回调函数

    1.1 接口

    1.1.1概念

    • Java中的接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
    • Java只支持单重继承,每个类只能有一个超类。但实际应用中有时候需要多重继承--使用接口,一个类可以实现多个接口。

    1.1.2接口的定义

        [public] interface 接口名[extends 父接口1,父接口2]{
            [public][static][final] 数据类型 属性名=常量值;
            [public abstract] 返回值类型 方法名([形参表]);
        }
    

    - 接口的属性必须用public static final修饰,是系统默认的,可部分省略或全部省略,但一般都写出final。
    int MAXSIZE = 50; 等价于
    public static final int MAXSIZE = 50;
    - 接口的方法默认为public abstract,一般不写修饰符,可省略
    - 接口中的方法都是抽象方法
    - 接口定义后不能直接创建对象,必须由类实现接口后创建类的对象。每个类只能继承一个基类,但可以实现多个接口
    - 类实现接口,就继承了接口中的所有成员变量和方法。由于接口中的方法都是抽象的,因此实现接口的类必须重写这些方法。
    代码示例1:

            /*
            *定义名字为IDemo的接口
            */
            public interface IDemo {
                 public int i=6;
                 public int getInt();
                 public void getDate(int id);
            }
    
            public class Demo implements IDemo{
                  public static void main(String[] args) {
                        int a=i;
    
                  }
                  @Override
                  public int getInt() {
                        return 0;
                  }
    
                  @Override
                  public void getDate(int id) {
                    // TODO Auto-generated method stub
    
                  }
             }
    

    1.1.3注意事项

    • 实现接口中的抽象方法时,除去掉关键字abstract外,方法头必须和接口定义中的方法头完全相同,并且public修饰符不能省略
    • 如果类实现了多个接口,必须重写这些接口中的所有方法。
    • 接口不是类,不能使用new直接实例化接口,但可以声明接口变量。接口变量可以指向一个实现了该接口的类的对象
    • 可以使用instanceof来判断对象是否实现了某个接口。
    • 虽然可以使用接口变量来引用实现接口的对象,但这种引用只能引用接口成员,否则会发生编译错误

    1.1.4接口的使用

    • 接口的使用与类的使用有些不同。在需要使用类的地方,会直接使用new关键字来构建一个类的实例,但接口不可以这样使用,因为接口不能直接使用new关键字来构建实例
    • 接口必须通过类来实现(implements)它的抽象方法,然后再实例化类。类实现接口的关键字为implements。
    • 如果一个类不能实现该接口的所有抽象方法,那么这个类必须被定义为抽象类
    • 一个类只能继承一个父类,但却可以实现多个接口
      代码示例1:

          interface IStart{
              void start();
          }
      
          interface IStop{
              void stop();
          }
      
          class Runner implements IStart,IStop{
              @Override
              public void start(){
              }
              @Override
              public void stop(){
              }
          }
      

    1.1.5接口和抽象类

    • 它们都具有如下特征:
      接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。
      接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
    • 区别(1.设计目的不同):
      接口体现的是一种规范,类似于整个系统的“总纲”,它制定了系统各模块应该遵循的标准。因此一个系统中的接口不应该经常改变。
      抽象类作为多个子类的共同父类,体现的是模板式设计。抽象类可以被当成系统实现过程中的中间产品,这个中间产品已经实现了部分功能,但这个产品还不能当成最终产品,必须更进一步的完善,这种完善可能有几种不同方式。
    • 区别(2.用法不同)
      这里写图片描述

    1.1.6接口的多态性

    接口的出现是为了更好的实现多态,而多态的实现不一定需要依赖于接口,类的多态就是子类继承父类,并重写父类方法,从而获得不同的实现;而接口跟类基本一样,实现接口并实现接口方法,不同的类实现接口可以有不同的方式从而表现,这就是接口的多态性.

    示例: 参考接口的应用.

    1.1.7接口的应用

    这里写图片描述

    代码实现部分:

    //1.IRunner接口
    public interface IRunner {
    
        /**
         * 启动前的准备
        */
        void start();
    
        /**
         * 运行
        */
        void run();
    
        /**
         * 停止
        */
        void stop();
    }
    //2.ISwimmer接口
    public interface ISwimmer {
    
        /**
         * 游泳
         */
        void swim();
    }
    //3.person类
    public class Person implements IRunner,ISwimmer{
    
        @Override
        public void swim() {
            System.out.println("游泳!!!!!!!");
        }
    
        @Override
        public void start() {
            System.out.println("弯腰、蹬腿");
    
        }
    
        @Override
        public void run() {
            System.out.println("摆动手臂 person 跑起来   " );
    
        }
    
        @Override
        public void stop() {
            System.out.println("person 减速直到停止");
    
        }
    
    
        public void eat(){
            System.out.println("吃饭@@@@@@");
        }
    
    }
    
    //3.Car类
    public class Car implements IRunner{
    
        @Override
        public void start() {
            System.out.println("打火、挂档");
    
        }
    
        @Override
        public void run() {
            System.out.println("松离合、给油 、换挡、 car 跑起来");
    
        }
    
        @Override
        public void stop() {
            System.out.println("踩刹车、踩离合 car 停下来");
    
        }
    
    }
    //4.Bird类
    public class Bird implements IRunner{
    
        @Override
        public void start() {
            System.out.println("蹬腿、跳");
        }
    
        @Override
        public void run() {
            System.out.println("摆动翅膀  Bird 飞起来   " );
        }
    
        @Override
        public void stop() {
            System.out.println("停止摆动翅膀  Bird 停下来   " );
        }
    
        public void fly(){
            System.out.println("fly@@@@@@@");
        }
    }
    
    //5.测试类
    public class TestDemo {
    
        public static void test(IRunner runner){
            runner.start();
            runner.run();
            runner.stop();
            if(runner instanceof Person){
                ((Person) runner).swim();
                ((Person) runner).eat();
            }else if(runner instanceof Bird){
                ((Bird) runner).fly();
            }
    
        }
    
        public static void main(String[] args) {
            test(new Person());
            test(new Car());
            test(new Bird());
    
        }
    }
    

    运行结果如下:

    弯腰、蹬腿
    摆动手臂 person 跑起来   
    person 减速直到停止
    游泳!!!!!!!
    吃饭@@@@@@
    打火、挂档
    松离合、给油 、换挡、 car 跑起来
    踩刹车、踩离合 car 停下来
    蹬腿、跳
    摆动翅膀  Bird 飞起来   
    停止摆动翅膀  Bird 停下来   
    fly@@@@@@@   
    

    1.1.8 接口的扩展

    1. 接口的多继承
      接口完全支持多继承,既一个接口可以可以有多个直接的父接口,子接口扩展某个父接口,将会获得父接口中定义的所有方法、常量属性、内部类等。继承的多个父接口排在extends关键字之后,其间以英文逗号分隔.
      有这样一个例子:超人具备飞、游泳和武功这些技能;现在有个超人类,他需要继承这几个技能
      案例1:

      public interface CanFight {
          void fight();
      }
      
      public interface CanSwim {
        void swim();
      }
      
      public interface CanFly {
        void fly();
      }
      
      public interface ISuperman extends CanFight,CanSwim,CanFly{
      
      }
      
      public class Superman implements ISuperman {
      
        @Override
        public void swim() {
                  // TODO Auto-generated method stub
                  System.out.println("会游泳");
        }
      
        @Override
        public void fly() {
                  // TODO Auto-generated method stub
                  System.out.println("会飞");
        }
      
        @Override
        public void fight() {
                  // TODO Auto-generated method stub
                  System.out.println("会武功");
        }
      
        public static void main(String[] args) {
                  Superman superman=new Superman();
                  superman.swim();
                  superman.fly();
                  superman.fight();
        }
      
      }   
      
    2. 查看Java API中List、Set、Collection接口,分析讲解说明.

    1.2 内部类

    1.2.1 概念

    1. 在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
      这里写图片描述
    2. 内部类一般来说包括四种:成员内部类、局部内部类、匿名内部类和静态内部类。
    3. 为何使用内部类
      • 内部类提供了某种进入外围类的窗户
      • 每个内部类都能独立地继承一个接口,而无论外围类是否已经继承了某个接口。因此,内部类使多重继承的解决方案变得更加完整。
      • 有时候一个类只需要在一处代码中使用一次,其他位置不会再用到这个类,这时使用匿名内部类来解决,代码更加简洁

    1.2.2 成员内部类

    1. 定义
      成员内部类是最普通的内部类,它的定义为位于另一个类的内部
    2. 用法如下

      class EnclosingClass{
          ... ...
          class ANestedClass{
              ... ...
          }
      }
      
      • 内部类在外部类中有特权:内部类可以毫无限制地访问外部类的所有成员
        案例2:

        class Circle {
            private double radius = 0;
            public static int count =1;
            public Circle(double radius) {
                this.radius = radius;
            }
            class Draw {     //内部类
                public void drawSahpe() {
                System.out.println(radius);  //外部类的private成员
                System.out.println(count);   //外部类的静态成员
                }
            }
            public static void main(){
                Circle circle=new Circle(2);
                Circle.Draw draw=circle.new Draw();
                draw.drawSahpe();
            }
        }
        

        输出结果:

        1.0
        1
        
    3. 成员内部类使用示例
      然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
      案例3:

      public class OuterClass{
          private int x=10;
          class InnerClass{
              int y=20+x;
          }
      
          public static void main(String[] args){
              OuterClass oc= new OuterClass();
              OuterClass.InnerClass ic=oc.new InnerClass();
              System.out.println("Outer:x="+oc.x);
              System.out.println("InnerClass:y="+ic.y);
          }
      }
      

      输出结果:

      Outer:x=10
      InnerClass:y=30
      

    注意:只有内部类才能够是私有的--安全机制,只有其外部类的方法才能够创建内部类对象。

    1.2.3 局部内部类

    1. 概念
      局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
    2. 用法如下
      案例4:

      public class People {
        public static void main(String[] args) {
                  class Woman extends People { // 局部内部类
                            int age = 3;
                            public void getAge() {
                                      System.out.println("age=" + age);
                            }
                  }
                  Woman woman=new Woman();
                  woman.getAge();
        }
      }
      

      输出结果:

      age=3
      

      注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的

    1.2.4 匿名内部类类

    1. 概念
      • 如果只需要为内部类建立一个对象,甚至不必为该类指定一个名字,这种类被称为匿名内部类。
    2. 满足下面的一些条件,使用匿名内部类是比较合适的
      • 只用到类的一个实例
      • 类在定义后马上用到
      • 类非常小(推荐在4行代码以下)
      • 给类命名并不会导致你的代码更容易被理解
    3. 用法如下

      //普通类
      class 类名 implement ActionListener{
          public void actionPerformed(ActionEven e){
              ... ...
          }
      }
      new 类名();
      
      //匿名类
      new ActionListener(){
          public void actionPerformed(ActionEvent e){
              ... ...
          }
      };
      
      • 由于匿名内部类没有名称,因此创建匿名内部类对象时,new运算符后是超类或接口的名称,其后的{}内部为匿名类的定义(匿名类对象的创建和匿名类的声明是在一起的)

      案例5:

      public class MyClick {
        public interface OnClickListener{
                  void onClick();
        }
      
        public static void main(String[] args) {
                  OnClickListener onclickL=new OnClickListener(){
                            @Override
                            public void onClick() {
                                      // TODO Auto-generated method stub
                                      System.out.println("匿名内部类");
                            }
                  };
                  onclickL.onClick();
        }
      }
      

      输出结果:

      匿名内部类
      
    4. 注意事项

      • 匿名类不能是抽象类,因为系统在创建匿名类的时候,会立即创建匿名类的对象。因此不允许将匿名类定义成抽象类。
      • 匿名类不能有构造方法,因为匿名类没有类名,无法定义构造方法,但匿名类可以定义实例初始化块,通过实例初始化块来完成构造方法需要完成的事情。
      • 匿名类不能定义任何静态成员、方法和类。
      • 匿名类不能是public、protected、private、static
      • 只能创建匿名类的一个实例

    1.2.5 静态内部类

    1. 定义
      静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。
    2. 代码示例

      public class Outter{
          public static void main(String[] args){
              Outter.Inner i=new Outter.Inner();
              i.init();
          }
          static class Inner{
              public void init(){
                  System.out.println("这里是静态内部类");
              }
          }
      }
      
    3. 注意事项
      • 生成(new)一个静态内部类不需要外部类成员(与成员内部类的区别)
        这点和类的静态成员属性有点类似
      • 静态内部类,只能访问外部类的静态成员
        这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

    1.3 回调函数

    1. 调用方式分:同步调用、回调和异步调用
      • 同步调用是一种阻塞式调用,也是我们写程序中经常使用的
      • 异步调用是一种类似消息或时间的机制,解决了同步调用的阻塞问题。举例来讲:A通知B后,他们各自走各自的路,互不影响。
      • 回调是一种双重调用模式,被调的接口被调用时也会调用对方的接口,本节主要讲述回调
        这里写图片描述
    2. 为什么会出现回调呢?
      下面举个通俗的例子:
      有一位老板很忙,他没有时间盯着员工干活,然后他告诉自己的雇员,干完当前这些事情后,告诉他干活的结果。下面通过案例5来向大家展示

    案例6
    首先创建一个回调接口,让老板得告知干完活如何找到他的方式

    public interface CallBackInterface{
        void execute();
    }
    

    创建回调对象,就是老板本人,因为员工干完活后要给他打电话,因此老板必须实现回调接口

    public class Boss implements CallBackInterface{
    
          @Override
          public void execute() {
                    // TODO Auto-generated method stub
                    System.out.println("老板:收到了");
          }
    
    }
    

    创建控制类,也就是员工对象,他必须持有老板的地址(回调接口)

    public class Employee {
          private CallBackInterface mCallBackInterface;
    
          public Employee(CallBackInterface callBackInterface){
                    this.mCallBackInterface=callBackInterface;
          }
    
          public void doSome(){
                    for(int i=0;i<10;i++){
                              System.out.println("进度:"+i);
                              if(i==9){
                                        System.out.println("员工:完成了");
                              }
                    }
                    mCallBackInterface.execute();
          }
    
    }
    

    创建测试代码

    public class Test {
          public static void main(String[] args) {
                    Employee emp=new Employee(new Boss());
                    emp.doSome();
          }
    }
    

    输出结果:

    进度:0
    进度:1
    进度:2
    进度:3
    进度:4
    进度:5
    进度:6
    进度:7
    进度:8
    进度:9
    员工:完成了
    老板:收到了
    

    5 总结

    1. 接口的概念与使用
    2. 内部类的分类与使用
    3. 内部接口回调

    6 预习任务

    • 数组
    • 接口

    以上都是个人整理资料部分,有问题欢迎大家留言!如需转载,请注明出处!



沪ICP备19023445号-2号
友情链接