异常处理

Java第十课

开发注意事项

  1. 实体类的所有属性定义为私有,由相应的get/set方法

  2. 所有的属性要有DOC注释

  3. 类的描述注释要写(类的用途)

  4. 实体类的代码构成:

    • 属性

    • 构造方法

    • get/set

    • toString()

  5. 循环里面尽量不要定义变量

  6. 判断在一个数组中是否存在某个元素,不能直接比较数组元素就确定存在或不存在,一定要把数组中的元素都判断完成后,再确定是否存在

    // 错误代码
    int[] arr = {1,2,3,4};
    boolean exists = false;
    for(int i=0;i<arr.length;i++)
    {
       if(arr[i] != 3){
           sout("not exists");
           break;
      }
    }
    // 正确代码
    boolean exists = false;//假设不存在
    for(int i=0;i<arr.length;i++){
       if(arr[i] == 3){
           exists = true;
           break;
      }
    }
    if(exists){
       sout("存在");
    }else{
       sout("不存在");
    }
    // 分三步:1.定义存在的标志变量 2.判断并赋值给存在的变量;3.玄幻完成判断标志

重要的信息放在上面,不关心的放下面

学生管理系统

  1. 学生实体类Student

    public class Student {
       /**
        * 学生id
        */
       private int id;
       /**
        * 学生姓名
        */
       private String name;
       /**
        * 学生生日
        */
       private Date birthday;

       /**
        * 获取学生id
        * @return
        */
       public int getId() {
           return id;
      }

       /**
        * 设置学生id
        * @param id
        */
       public void setId(int id) {
           this.id = id;
      }

       /**
        * 获取学生姓名
        * @return
        */
       public String getName() {
           return name;
      }

       /**
        * 设置学生姓名
        * @param name
        */
       public void setName(String name) {
           this.name = name;
      }

       /**
        * 获取学生生日
        * @return
        */
       public Date getBirthday() {
           return birthday;
      }

       /**
        * 设置学生生日
        * @param birthday
        */
       public void setBirthday(Date birthday) {
           this.birthday = birthday;
      }

       @Override
       public String toString() {
           return "Student{" +
                   "id=" + id +
                   ", name='" + name + '\'' +
                   ", birthday=" + birthday +
                   '}';
      }
    }
  2. 学生业务接口类:IStudentService

    public interface IStudentService {
       /**
        * 添加学生
        * @param student 学生对象
        * @return 返回增加的结果
        */
       boolean add(Student student);
       /**
        *修改学生
        * @param student 学生对象
        * @return 修改的结果
        */
       boolean modify(Student student);
       /**
        * 根据参数id查找学生对象
        * @param id 学生的编号
        * @return 和id对应的学生对象,如果没有对应的,返回空对象
        */
       Student searchById(int id);

       /**
        * 返回参数日期后的学生列表
        * @param date 指定日期
        * @return 学生生日在日期后的学生列表,有可能是空的
        */
       Student[] searchList(Date date);
    }
  3. StudentService:学生接口实现类

    public class StudentService implements IStudentService {
       /**
        * 存放学生的数组
        */
       private Student[] students = new Student[5];
       /**
        * 目前的学生数
        */
       private int index;
       /**
        * 添加学生
        *
        * @param student 学生对象
        * @return 返回增加的结果
        */
       @Override
       public boolean add(Student student) {
           if (index < students.length) {
               students[index++] = student;
               return true;
          }
           return false;
      }

       /**
        * 修改学生
        *
        * @param student 学生对象
        * @return 修改的结果
        */
       @Override
       public boolean modify(Student student) {
           return false;
      }

       /**
        * 根据参数id查找学生对象
        *
        * @param id 学生的编号
        * @return 和id对应的学生对象,如果没有对应的,返回空对象
        */
       @Override
       public Student searchById(int id) {
           for (int i = 0; i < students.length; i++) {
               if (students[i].getId() == id) {
                   return students[i];
              }
          }
           return null;
      }

       /**
        * 返回参数日期后的学生列表
        *
        * @param date 指定日期
        * @return 学生生日在日期后的学生列表,有可能是空的
        */
       @Override
       public Student[] searchList(Date date) {
           // 方法需要返回什么就定义什么
           Student[] result = new Student[0];
           for (int i = 0; i < index; i++) {
               if (students[i].getBirthday().after(date)) {
                   //如果数组是空的
                   if (result.length == 0) {
                       result = new Student[1];
                       result[0] = students[i];
                  } else {
                       /*Student[] newArr = new Student[result.length + 1];
                       // 把result数组复制给newArr数组
                       System.arraycopy(result,0,newArr,0,result.length);
                       newArr[newArr.length-1] = students[i];
                       result = newArr;*/
                       // copyOf返回一个新的数组,参数result是源数组,第二个参数是新数组的长度
                       // =左边的result接收新数组,相当于让result指向了新数组的地址
                       result = Arrays.copyOf(result, result.length + 1);
                       // 新查找的学生赋值给数组的最后一个元素
                       result[result.length-1] = students[i];
                  }
              }
          }
           // 方法需要返回什么就返回什么
           return result;
      }
    }
  4. StudentManager:学生管理测试类

    public class StudentManager {
       /**
        * 学生业务类对象
        */
       private static IStudentService service = new StudentService();
       public static void main(String[] args) {
           Scanner scanner = new Scanner(System.in);
           // 菜单指令
           int cmd = 0;
           while (true) {
               System.out.println("请输入选项");
               System.out.println("1:增加");
               System.out.println("2:修改");
               System.out.println("3:查找单个学生");
               System.out.println("4:按日期查找");
               System.out.println("5:退出");
               // 接收用户的输入
               cmd = scanner.nextInt();
               switch (cmd) {
                   case 1:
                       add();
                       break;
                   case 2:
                       modify();
                       break;
                   case 3:
                       searchById();
                       break;
                   case 4:
                       searchList();
                       break;
                   case 5:
                       System.out.println("谢谢使用,再见");
                       System.exit(0);
              }
          }
      }

       /**
        * 增加学生
        */
       private static void add() {
           Scanner scanner = new Scanner(System.in);
           System.out.println("请输入学生的编号");
           int id = scanner.nextInt();
           System.out.println("请输入学生的姓名");
           String name = scanner.next();
           System.out.println("请输入学生的生日(如2021-03-21):");
           String birthday = scanner.next();
           Student student = new Student();
           student.setId(id);
           student.setName(name);
           SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
           try {
               student.setBirthday(format.parse(birthday));
          } catch (ParseException e) {
               e.printStackTrace();
               System.out.println("增加学生失败");
          }
           boolean result = service.add(student);
           // if(result == true) result自己就是布尔类型,不需要和true进行比较
           if (result) {
               System.out.println("增加成功");
          } else {
               System.out.println("增加失败");
          }
      }

       /**
        * 修改学生
        */
       private static void modify() {
           System.out.println("modify");
      }

       /**
        * 根据id查找学生
        */
       private static void searchById() {
           System.out.println("searchById");
      }

       /**
        * 根据日期查找学生
        */
       private static void searchList() {
           Scanner scanner = new Scanner(System.in);
           System.out.println("请指定日期(如:2021-03-21):");
           String date = scanner.next();
           try {
               Date d = new SimpleDateFormat("yyyy-MM-dd").parse(date);
               Student[] students = service.searchList(d);
               // 加强型的循环
               for (Student student : students) {
                   System.out.println(student);
              }
          } catch (ParseException e) {
               e.printStackTrace();
          }
      }
    }

异常

Java中的异常(Exception),又称为例外,是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类。
如果程序没有有效的处理异常情况,则异常会一致往上抛,最后被JVM抓住,从而中断程序。特别像人生病的过程,人的免疫系统是抓异常的过程。
学习异常:了解有哪些异常;如何处理异常
异常分两大类:
非运行异常(编译异常),都继承了Exception(或间接)
  • 出现的场景:

    1. IO输入输出异常(处理文件或网络时)

    2. 解析,转换数据异常(SimpDateFormat的parse方法)

      • parseException:解析异常

    3. 线程操作时异常(线程中断,停止)

    4. JDBC操作异常

    5. 反射操作异常

  • 为什么有非运行时异常?

    原因是这些场景容易出异常,出异常的几率较高,Java需要我们做异常的提前处理,保证程序运行中不至于因为异常而导致程序中断。

运行时异常,都继承了RuntimeException(或间接)
  • 在编码过程中不需要强制处理的异常,该类异常只有在一定条件下才会抛出异常,在程序运行过程中有可能抛出的异常。

  • 出现的场景:

    1. 数组越界:ArrayIndexOutOfBoundsException

      Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
      at day10.lesson.Test.main(Test.java:15)

      以上信息叫异常堆栈(一层一层的),异常信息是由最底层,最直接发生异常的地方抛出,一路往上抛,直到被try,catch住,或者抛进JVM中。

      在以上异常堆栈中可以观察到异常发生的包名day10.lesson,类名Test,方法名main,发生异常的代码位置Test.java:15,异常类型是"java.lang.ArrayIndexOutOfBoundsException"

    2. 数学运算中被零除:ArithmeticException

      Exception in thread "main" java.lang.ArithmeticException: / by zero
      at day10.lesson.Test.main(Test.java:15)
    3. 定义对象没有实例化就使用,发生空指针异常

    4. Scanner要求输入数字nextInt,结果输入了非数字,错误输入异常

两大类异常要熟记三到五个异常类

处理异常

try{} catch{} try块中抛出异常,catch块抓住异常,处理异常,让程序不中断继续运行

try{} catch{} finally{} finally块是无论发生异常还是未发生异常都要执行的语句,存在的意义是释放程序占用的系统资源(IO,JDBC使用的比较多)

try{} finally{} 比较少见,不处理异常

catch只抓指定的异常

异常的结构

顶层异常类:Throwable,代表了所有的异常

第二层:Error(错误,非常严重的错误),Exception(异常,可以处理的)

Error发生的错误大多和程序运行的环境有关,比如内存溢出,存储介质坏道,硬件出现问题

非运行时异常继承了Exception(或间接)

第三层:Exception的一个子类RuntimeException(运行时异常)

运行时异常都继承了RuntimeException(或间接)

throw和throws

面试必考

  • throw是抛单个异常,throws是抛多个异常

  • throw是在方法中抛单个异常,throws是在方法声明时抛多个异常

  • 一个是在方法中,一个是在方法声明部分

  • throw和catch是二选一(是矛盾的),throw是交由上级处理,catch是由自己处理

定义一个方法,该方法抛出一个非运行时异常

    private static void getDate() throws ParseException {
       SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
       Date date = format.parse("2021-03-21");
  }

throws抛出异常,转移异常,换一个地方处理异常

throw一般可以不用,JDK中有大量的非运行时异常自动throw,什么时候用throw?

当我们想主动抛出一个异常时用throw,这时候大多抛出的异常是自定义异常

自定义异常

自己定义异常类,让该类继承Exception

当程序运行时出现和业务规则冲突的情况时,让程序抛出一个自定义异常

比如:性别不是0或1,年龄大于60或小于18 throw new MyException();

定义一个异常类

public class AgeException extends Exception {
    /**
     * 异常构造方法
     * @param msg 异常信息的描述
     */
    public AgeException(String msg) {
        // 把异常传递给父类
        super(msg);
    }
}
    private static void getAge() throws AgeException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入年龄【18-60】");
        int age = scanner.nextInt();
        // 用户只要输入的是数字就不会出异常,但是如果不符合我们的
        // 业务规则,则要主动抛出一个异常
        if (age < 18 || age > 60) {
            throw new AgeException("输入的年龄不符合规范!");
        }
    }
try {
            getAge();
        } catch (AgeException e) {
            System.err.println(e.getMessage());
        }

异常和继承的关系

当子类的方法重写(override)父类的方法时,子类方法的异常不能强于父类方法的异常。

强于:子类方法的异常个数不能大于父类方法的异常个数(throws后面的异常数)

子类方法的异常不能是父类方法异常的父类(父类方法throwsParseException,子类不能抛出ParseException的父类,即:Exception)

子类方法的异常类型和父类方法类型是平级的,但不一致。

总结:子类重写的方法异常要么和父类方法的异常相同,要么比父类方法的异常个数少,要么包含在父类方法的一场中,要么是父类方法异常的子类。



posted @ 2021-03-21 16:54  若你  阅读(276)  评论(0编辑  收藏  举报