Java基础10-接口,抽象类以及异常

时间:2021-07-18 13:56:57

一.接口
 1.什么是接口
  接口是特殊的抽象类,其内部方法都是抽象的
 
 2.接口与抽象类的区别:
  a.接口是interface;抽象类是abstract class
  b.接口的方法都是抽象的;抽象类的方法可以不是抽象的
  c.接口的成员有默认修饰符:方法是public abstract,变量是public static final;抽象类没有默认修饰符
  d.接口的实现使用implements关键字;抽象类的继承使用extends关键字
  e.接口可以被多实现;抽象类只能被单继承
  
 
3.什么时候使用抽象类和接口?
  a.当存在继承关系时使用抽象类;没有继承关系时就使用接口
  b.当方法都是抽象时使用接口;若既有抽象方法,又有实例方法时,使用抽象类
  
 
注:
  接口与接口,使用extends关键字;
  实现接口后,子类方法必须为public;
  
 
4.面试题一:
  一个类可以实现两个接口吗?任意两个接口都可以吗?
  
  答: 可以.不可以.
   因为若是两个接口的方法名相同,参数列表相同,但其返回值类型不同,此时子类就不能同时实现这两个接口了.
   如:
    interface A {
     int fun();
    }
    interface B {
     void fun();
    }
 
 5.面试题二:
  什么情况下,会将子类类型强转为父类类型呢?
  
  答:当一个子类对象能够匹配多个重载方法而且方法的形参都是该子类实现的父类类型们.
  这时会出现调用不明确的情况,所以需要进行强转为父类类型.
  如:
   interface Teacher {
    void teach();
   }
   interface Coder {
    void code();
   }
   
   class Person implements Teacher,Coder {
    public static void main(String[] args) {
     Person p = new Person();
     p.work( (Teacher)p );  //强转类型转换
     p.work( (Coder)p );    //强转类型转换
    }
    
    @Override
    public void teach(){ }
    
    @Override
    public void code(){ }
    
    public static void work(Teacher t) {
     t.teach();
    }
    
    public static void work(Coder c) {
     c.code();
    }
   }
   
二.策略设计模式-StrategyPattern
 使用接口,降低耦合性
 
 如:
  class TestDemo {
   public static void main(String[] args) {
    int[] arr={1,4,6,7,3,5,9,-6,-34,67,-12};
    Printer.print(arr);
    
    //需求: 要打印其中的正数 (此时就可以使用策略设计,添加过滤器,使用接口来实现)
    
    Printer.print(arr, new Filter(){  //add: 使用匿名内部类
     @Override
     public boolean accept() {
      return i >= 0;
     }  
    });
    
   } 
  }
  
  class Printer {
   public static void print(int[] arr) {
    for(int i=0;i<arr.length;i++)
     System.out.print(arr[i]+" ");
    System.out.println();
   }
   
   public static void print(int[] arr,Filter f) {       //add
    for(int i=0;i<arr.length;i++)
     if(f.accept(arr[i]))                         //add
      System.out.print(arr[i]+" ");
    System.out.println();
   }
  }
  
  interface Filter {
   boolean accept(int i);
  }
  
  
三.内部类: ( 内部类可以访问外部类成员 )
 
类中:
  成员内部类
  静态内部类
 
方法中: 
  局部内部类
  匿名内部类
 
 
1.什么是内部类
  在内部类中定义的类,就是内部类
  
 2.成员内部类: ( A$B.class )
 
 a.什么是成员内部类
   定义在类的成员位置的内部类,就是成员内部类
   创建该内部类的对象,必须先创建外部类对象,才能通过外部类对象创建内部类对象;
  注:
   创建格式为: 外部类名.内部类名 变量名= new 外部类名().new 内部类名();
   一个外部类可以创建多个内部类对象,但一个内部类对象只能对应一个外部类对象;
   
  b.什么时候使用成员内部类?
   当一个类想要访问另一个类的成员时
  
  
c.内部类访问外部类的成员的方式:
   使用: 外部类名.this.成员名 


  注:
   在方法中,"this" 代表当前方法的对象,而"外部类.this" 代表当前方法的对象的外部类对象
   外部类不能访问内部类成员
   
  
d.内部类中不能定义静态成员
   因为内部类必须依赖外部类对象才能创建对象,而static是指不需创建对象就可以使用
   
 
3.局部内部类: ( A$1B.class )
 
 a.什么是局部内部类
   定义在方法中的内部类,就是局部内部类
   
  b.局部内部类与成员内部类的区别:
   局部内部类,只能在方法中使用;成员内部类可以在外面使用
   创建局部内部类的代码在方法中,而且必须写在类定义的后面;
      
  
c.方法中的内部类怎么创建对象
   在方法中直接使用 new 类名()即可
   
  d.方法中的内部类怎么访问方法中的局部变量
   局部内部类访问方法的局部变量时,该局部变量必须使用final修饰;  
   
  如:
   class TestDemo {
    public static void main(String[] args) {
     new A().fun();
    } 
   }
   
   class A {
   
    class B {
     B() {
      System.out.println("成员内部类B..");
     }
    }
    
    void fun() {
    
     class B {
      B() {
       System.out.println("局部内部类B..");
      }
     }
     
     /*
     B b = new B(); 
//1.这边创建B对象是根据从内到外的优先级进行选择的,只有当内部不存在就会向外寻找
     */
     
     //需求: 当前代码下,
     //创建成员内部类B的对象:
A.B b2 = new A.B();
     //创建外类B的对象的方法:
包名.B b3 = new 包名.B();
     
    }
   }
   
   class B {
    B() {
     System.out.println("外部类B..");
    }
   }
   
   
 
4.匿名内部类 ( A$1.class )
 格式:
  new 父类名(){
   //重写方法
  }


  a.什么是匿名内部类
   没有名字的局部内部类,就是匿名内部类
  
  b.什么时候使用匿名内部类
   局部内部类只需使用一次,使用匿名内部类可以简化书写
 
  c.匿名内部类的class文件名
   A$1.class
   
 5.静态内部类
  a.什么是静态内部类
   使用static修饰的内部类
   
  b.静态内部类和普通的类中内部类的区别
   创建对象: 静态内部类不需创建外部类对象,就可以直接建立内部内部类 ( 外部类名.内部类名 变量名 = new 外部类名.内部类名(); )
       成员内部类需要通过创建外部类对象来创建自己的对象 ( 外部类名.内部类名 变量名 = new 外部类名().new 内部类名(); )
   访问外部成员: 静态内部类只能访问外部类的静态成员,而不能访问非静态成员
   定义静态成员: 静态内部类可以定义静态成员;而成员内部类是不可以定义静态成员( 静态变量/方法/代码块 ),可以定义静态常量
   


三.异常
 
 Throwable
 --Error
    --
 --Exception
    --RuntimeException
    --
  
 1.什么是异常
  异常就是程序运行过程中出现的一些错误, 使用throw关键字抛出的一些对象.
  可以通知调用处, 调用处下面的代码不会再执行了.
  
 2.异常的处理方式
 
 a.声明
   若是没有处理,则需要在方法上(参数列表后面)使用"throws 异常名"进行声明,将异常继续向上抛出
  
  b.捕捉
   try {
   
   }catch(异常名 变量名) {
    //处理代码
   }
  
 3.异常的分类
 
 a.编译时异常
   Exception中除了RuntimeException的部分, 编译之前在代码中必须对其进行声明或捕捉, 否则编译报错
   
  b.运行时异常
   RuntimeException或其子类, 在代码中可以不对其进行处理, 编译不报错.
   这类异常经常会出现, 例如数组索引越界, 空指针, 算数异常, 如果对其进行处理, 非常麻烦, 代码可读性也会下降.
   
 4.多个异常的处理方式
  a.声明
   在方法上使用"throws 异常1名,异常2名"进行声明
  
 
 b.捕捉
   try {
   
   }catch(异常1名 变量名) {
    //处理代码
   }catch(异常2名 变量名) {
    //处理代码
   }


  注:
   异常捕捉的书写顺序: 子类异常放在前面
  
 5.子类重写父类方法时注意的异常问题
  重写的条件之一: 子类的方法只能抛出父类方法异常的子集,不能抛出比父类更多的异常
 如:
  class A {
   void fun() throws Exception {
   
   }
  }
  
  class B {
   @Override
   public void fun() throws FileNotFoundException { //可以不抛,也可以抛出相同的异常,也可以抛出父类异常的子类!!!!
   
   }
  }
  
 6.finally的使用
  finally是配合try使用的, 只要执行了try中的代码, finally中的代码就一定会执行. (一些特殊情况除外:如System.exit(0); )
 
 注:
  class TestDemo {
   public static void main(String[] args) throws Exception {   
    /*
    打印结果一:(System.out,println(10/1);)
     10
     异常处理之前
     finally语句
     
    打印结果二:(System.out,println(10/0);)
     异常处理
     finally语句
    */
    try {
     System.out.println(10/1);           //
     System.out.println("异常处理之前");
     return ;
    }catch(Exception e) {
     System.out.println("异常处理..");
    
    }finally {    
     System.out.println("finally语句");
    }
   } 
  }
  
  
补充:
 猴子分桃问题
 注:
  class TestDemo {
   public static void main(String[] args) {   
    
    outer:
     for(int i=0;i<9999;i++) {
      int count=i; 
      for(int j=0;j<5;j++){
       if( (count-1)%5==0 )
        count = (count-1)/5*4;
       else
        continue outer;
      }  
      System.out.println(i);       
     }
    
   } 
   
  }