天天看点

尚硅谷java入门356p-379p 面向对象和异常 2022.3.22

356p 接口练习:比较对象大小

尚硅谷java入门356p-379p 面向对象和异常 2022.3.22
/*
 * 定义一个接口用来实现两个对象的比较。
 * 
 */
public interface CompareObject {
  public int compareTo(Object o);
  //若返回值是 0,代表相等;若为正数,代表当前对象大;负数代表当前对象小
  
}

/*
 * 定义一个 Circle 类,声明 redius 属性,提供 getter 和 setter 方法
 */
public class Circle {
  
  private Double radius;

  public Double getRadius() {
    return radius;
  }

  public void setRadius(Double radius) {
    this.radius = radius;
  }

  public Circle() {
    super();
  }

  public Circle(Double radius) {
    super();
    this.radius = radius;
  }
    
}

/*
 * 定义一个 ComparableCircle 类,继承 Circle 类并且实现 CompareObject 接口。在 ComparableCircle 类中给出接口中方法 compareTo 的实现体,
 * 用来比较两个圆的半径大小。
 */
public class ComparableCircle extends Circle implements CompareObject{

  public ComparableCircle(double radius) {
    super(radius);
  }
  @Override
  public int compareTo(Object o) {
    if(this == o){
      return 0;
    }
    if(o instanceof ComparableCircle){
      ComparableCircle c = (ComparableCircle)o;
      //错误的写法
//      return (int)(this.getRedius() - c.getRedius());
      //正确的方式一:
//      if(this.getRadius() > c.getRadius()){
//        return 1;
//      }else if(this.getRadius() < c.getRadius()){
//        return -1;
//      }else{
//        return 0;
//      }
      //当属性 radius 声明为 Double 类型时,可以调用包装类的方法
      //正确的方式二:
      return this.getRadius().compareTo(c.getRadius());
    }else{
      return 0;
//      throw new RuntimeException("传入数据类型不匹配");
    }
  }
}

/*
 * 定义一个测试类 InterfaceTest,创建两个 ComparableCircle 对象,
 * 调用 compareTo 方法比较两个类的半径大小。
 * 
 */
public class InterfaceTest {
  public static void main(String[] args) {
    
    ComparableCircle c1 = new ComparableCircle(3.4);
    ComparableCircle c2 = new ComparableCircle(3.6);
    
    int compareValue = c1.compareTo(c2);
    if(compareValue > 0){
      System.out.println("c1 对象大");
    }else if(compareValue < 0){
      System.out.println("c2 对象大");
    }else{
      System.out.println("两个一样的");
    }
    
    int compareValue1 = c1.compareTo(new String("AA"));
    System.out.println(compareValue1);
  }
}      

357p java8中接口的新特性

 JDK8:除了定义全局变量和抽象方法之外,还可以定义静态方法,默认方法

public interface CompareA{
  //静态方法
  public static void method1(){
      System.out.println("CompareA:北京");
  }
  //默认方法
  public default void method2(){
      System.out.println("CompareA:上海");
  }
  default void method3(){
      System.out.println("CompareA:上海");
  }
}

public class SubClassTest{
   public static void main(String[] args){
       SubClass s=new SubClass();
       //s.method1;
       //SubClass.method1();
       //知识点1;接口中定义的静态方法,只能通过接口来调用
       CompareA.method1();
       //知识点2:通过实现类的对象,可以调用接口中的默认方法
       //如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
       s.method2();
       //知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
       //那么子类在没有重写此方法的情况下,默认调用的是父类中同名同参数的方法---类优先原则
       //知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法
       //那么在实现类没有重写此方法的情况下,报错---接口冲突
       //这就需要我们必须在实现类中重写此方法
       s.method3();
       
   }
}
class SubClass extends SuperClass implements CompareA,CompareB{
   public void method2(){
      System.out.println("SubClass:上海");
  }
  public void method3(){
      System.out.println("SubClass:深圳");
  }
  //知识点5:如何在子类(或实现类)的方法中调用父类,接口中被重写的方法
  public void myMethod(){
        method3();//自己定义的重写的方法
        super.method3();//调用的是父类声明的
        //调用接口中的默认方法
        CompareA.super.method3();
        CompareB.super.method3();
  }
}

public class SuperClass {
  public void method3(){
    System.out.println("SuperClass:北京");
  }
}

public interface CompareB{
  default void method3(){
      System.out.println("CompareB:上海");
  }
}      

358 java8接口新特性的应用 

/*
 * 练习:接口冲突的解决方式
 */
interface Filial {// 孝顺的
  default void help() {
    System.out.println("老妈,我来救你了");
  }
}

interface Spoony {// 痴情的
  default void help() {
    System.out.println("媳妇,别怕,我来了");
  }
}

class Father{
  public void help(){
    System.out.println("儿子,救我媳妇!");
  }
}

class Man extends Father implements Filial, Spoony {

  @Override
  public void help() {
    System.out.println("我该就谁呢?");
    Filial.super.help();
    Spoony.super.help();
  }
  
}      

359 内部类的分类 

类的内部成员之五:内部类

1.java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类

2.内部类的分类:成员内部类(静态和非静态)vs局部内部类(方法内,代码块内,构造器内)

3.成员内部类:

       一方面,作为外部类的成员:

      >调用外部类的结构

       >可以被static修饰

       >可以被四种不同的权限修饰

       另一方面,作为一个类:

       >类内可以定义属性,方法,构造器等

       >可以被final修饰,表示此类不能被继承,言外之意,不使用final,就可以被继承

       >可以被abstract修饰

public class InnerClassTest{
   public static void main(String[] args){
     //创建Dog实例(静态的成员内部类)
     Person.Dog dog=new Person.Dog();
     dog.show();
     //创建Bird实例(非静态的成员内部类)
     Person p=new Person();
     Person.Bird bird=p.new Bird();
     bird.sing();
     System.out.println();
     bird.display("黄鹂");
   }
}
class Person{
   String name="小明";
   int age;
   public void eat(){
      System.out.println("人,吃饭");
   }
   //静态成员内部类
   static class Dog{
       String name;
       int age;
       public void show(){
         System.out.println("卡拉是条狗");
         //eat();
       }
   }
   //非静态成员内部类
   class Bird{
        String name="杜鹃";
        public Bird(){
        }
        public void sing(){
            System.out.println("我是一只小小鸟");
            Person.this.eat();//调用外部类的非静态属性
        }
        public void display(String name){
             System.out.println(name);//方法的形参
             System.out.println(this.name);//内部类的属性
             System.out.println(Person.this.name);//外部类的属性
             
        }
   }
   public void method(){
      //局部内部类
      class AA{
      }
   }
   {
      //局部内部类
      class BB{
      
      }
   }
   public Person(){
        //局部内部类
        class CC{
        }
   }
}      

361 如何实例化成员内部类

 4.关注如下的三个问题

  4.1如何实例化成员内部类的对象

  4.2 如何在成员内部类中去区分调用外部类的结构

  4.3 开发当中,局部内部类的使用

public class InnerClassTest1{
    //开发中很少见
    public void method(){
        //局部内部类
        class AA{
        }
    }
    //返回一个实现了Comparable接口的类的对象
    public Comparable getComparable(){
        //创建一个实现了Comparable接口的类:局部内部类
        //方式一:
          class MyComparable implements Comparable{
                public int compareTo(Object o){
                      return 0;
                }
          }
          return new MyComparable();
          //方式二:
          return new Comparable(){
              public int compareTo(Object o){
                 return 0;
              }
          };
     }
}      

368 异常处理 局部内部类使用的一个注意点 

在局部内部类的方法中,比如show,如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如number)的话,要求此局部变量声明为final的

jdk7及之前版本,要求此局部变量显式的声明为final的

jdk8及之后的版本,可以省略final的声明

public class InnerClassTest{
    public void method(){
        //局部变量
        final int num=10;
        class AA{
           public void show(){
              //num=20;
              System.out.println(num);      

369 异常处理 异常概述

01、异常概述与异常体系结构

在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。

异常:

在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)

Java程序在执行过程中所发生的异常事件可分为两类:

Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。

public class ErrorTest{
   public static void main(String[] args){
        //1.栈溢出:java.lang.StackOverflowError
        //main(args);
        //2.堆溢出:java.lang.OutOfMemoryError
        Integer[] arr=new Interger[1024*1024*1024];
  }
}      
  • Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
  • 空指针访问
  • 试图读取不存在的文件
  • 网络连接中断
  • 数组角标越界

 370 异常的分类

对于这些错误,一般有两种解决方法:

一是遇到错误就终止程序的运行。

另一种方法是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。

捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。比如:除数为0,数组下标越界等

异常分类:编译时异常和运行时异常

一,异常体系结构

java.lang.Throwable

                ------java.lang.Error:一般不编写针对性的代码进行处理

                ------java.lang.Exception:可以进行异常的处理

                      -------编译时异常(checked)

                               -----IOExpection

                                    ------FileNotFoundExpection

                              -----ClassNotFoundExpection

                    --------运行时异常(unchecked)

                               ------NullPointerExpection

                               ------ArrayIndexOutOfBoundsException

                               ------ClassCastExpection

                               ------NumberFormatException

                               ------InputMismatchException

                               ------ArithmeticException

面试题:常见的异常有哪些?举例说明

371 常见异常的举例

public class ExceptionTest{
    //以下是运行时异常
    //NullPointerException
    public void test1(){
//        int[] arr=null;
//        System.out.println(arr[3]);
          String str="abc";
          str=null;
          System.out.println(str.charAt(0));
    }
    //IndexOutOfBoundsException
    public void test2{
        //ArrayIndexOutOfBoundsException
        int[] arr=new int[10];
        System.out.println(arr[10]);
        //StringIndexOutOfBoundsException
        String str="abc";
        System.out.println(str.charAt(3));
    }
    //ClassCastException
    public void test3(){
          Object obj=new Date();
          String str=(String)obj;
    }
    //NumberFormatException
    public void test4(){
        String str="123";
        str="abc";
        int num=Integer.parseInt(str);
    }
    //InputMismatchException
    public void test5(){
        Scanner scanner=new Scanner(System.in);
        int score=scanner.nextInt();
        System.out.prinln(score);
        scanner.close();
    }
    //ArithmeticException
    public void test6(){
          int a=10;
          int b=0;
          System.out.println(a/b);
    }
    //以下是编译时异常
    public void test7(){
          File file=new File("hello.txt");
          FileInputStream fis=new FileInputStream(file);
          int data=fis.read();
          while(data!=-1){
             System.out.println((char)data);
             data=fis.read();
          }
          fis.close();
    }      

372 异常处理方式概述 

在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。

Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。

方式一:try-catch-finally

方式二:throws + 异常类型

异常的处理:抓抛模型

过程一:抛:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成对应异常类的对象

并将此对象抛出,一旦抛出对象以后,其后的代码不再执行

过程二:抓:可以理解为异常的处理方式

1.try-catch-finally

2.throws

373 处理异常 try-catch方式

try{

     //可能出现异常的代码

}catch(异常类型1 变量名1){

    //处理异常的方式1

}catch(异常类型2 变量名2){

    //处理异常的方式2

}catch(异常类型3 变量名3){

    //处理异常的方式3

}

.....

finally{

     //一定会执行的代码

}

public class ExpectionTest1{
  public void test1(){
      String str="123";
      str="abc";
      try{
          int num=Integer.parseInt(str);
          System.out.println("hello----1");
      }catch(NumberFormatExpection e){
         // System.out.println("出现数值转换异常了,不要着急");
         //String getMessage():
         System.out.println(e.getMessage());
         //printStackTrace():
         e.printStackTrace();
      }
      System.out.println("hello----2");
}      

说明:

1.finally是可选的

2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配

3.一旦try中的异常对象匹配到某一个catch中,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的try-catch结构(在没有写finally结构的情况下)继续执行其后的代码

4.catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓

 catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面,否则,报错

5.常用的异常处理的方式

   1.String getMessage()

   2.printStackTrace()

6.在try结构中声明的变量,再出了try结构以后,就不能再被调用

7.结构可以嵌套

体会1:使用处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错,相当于我们使用将一个编译时可能出现的异常,延迟到运行时出现

体会2:开发中,由于运行时异常比较常见,所以我们通常不针对运行时异常编写try-catch-finally

针对于编译时异常,我们说一定要考虑异常的处理

374 finally的使用

1.finally是可选的

2.finally中声明的是一定会被执行的代码,即使catch中又出现异常了,try中有return语句,catch中有return语句等情况

3.像数据库连接,输入输出流,网络编程Socket等资源,JVM是不能被自动的回收的,我们需要自己手动的进行资源的释放,此时的资源释放,就需要声明在finally中

public class FinallyTest{
    @Test
    public void test2() {
        FileInputStream fis = null;
        try {
            File file = new File("hello1.txt");//文件可能不存在,而出现异常
            fis = new FileInputStream(file);

            int data = fis.read();
            while (data != -1) {
                System.out.print((char) data);
                data = fis.read();
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void testMethod(){
         int num=method();
         System.out.println(num);
    }
    public int method(){
         try{
             int[] arr=new int[10];
             System.out.println(arr[10]);
             return 1;
           }catch(ArrayIndexOutOfBoundsException e){
              e.printStackTrace();
              return 2;
          }finally{
              System.out.println("我一定会被执行");
          }
    public void test1(){
       try{
           int a=10;
           int b=0;
           System.out.println(a/b);
          }catch(ArithmetricException e{
             e.printStackTrace();
          }
          finally{
            System.out.println("我好帅啊");
          }
    }
}      

376 throws方式 

异常处理的方式二:throws+异常类型

1.throws+异常类型 写在方法的声明处,指明此方法执行时,可能会抛出的异常类型,一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码不再执行

2.异常处理有两种方式

第一种:真正的异常被处理掉了、

第二种:只是将异常抛给了方法的调用者,并没有真正将异常处理掉

public class ExceptionTest2{
        public static void main(String[] args){
             try{
                method2();
               }catch(IOException e){
                  e.printStackTrace();
               }
               method3();
        }
        public static void method3(){
             try{
                 method2();
              }catch(IOException e){
                 e.printStackTrace();
              }
        }
        public void method2() throws IOException{
            method1();
        }
        public static void method1() throws FileNotFoundException,IOException{
    File file = new File("hello1.txt");
    FileInputStream fis = new FileInputStream(file);
    
    int data = fis.read();
    while(data != -1){
      System.out.print((char)data);
      data = fis.read();
    }
    
    fis.close();
    
    System.out.println("hahaha!");
  }
}      

377 重写方法异常抛出的规则

方法重写的规则之一:

子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

public class OverrideTest{
   public static void main(String[] args){
        OverrideTest test=new OverrideTest();
        test.display(new SubClass());
   }
   public void display(SuperClass s){
      try{
          s.method();
      }catch(IOException e){
           e.printStackTrace();
      }
   }
}
class SuperClass{
     public void method() throws IOException{
     }
}
class SubClass extends SuperClass{
     public void method() throws FileNotFoundException{
     }
}      

378 开发中如何选择哪种方式处理异常 

1.如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理

2.执行的方法中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,我们建议这几个方法使用throw的方式进行处理,而执行的方式a可以考虑使用try-catch-finally方式进行处理。

379 手动抛出异常对象

public class StudentTest{
   public static void main(String[] args){
     try{
     Student s=new Student();
     s.regist(1001);
     System.out.println(s);
     }catch(Exception e){
        //e.printStackTrace();
        System.out.println(e.getMessage());
     }
   }
}
class Student{
   private int id;
   public void regist(int id){
        if(id>0){
            this.id=id;
        }else{
           //System.out.println("您输入的数据非法");
           //手动抛出异常对象
           //throw new RuntimeException("您输入的数据非法");
           throw new Exception("您输入的数据非法");
           
        }
  }
  public String toString(){
     return "Student [id="+id+"]";
  }
}