day16 异常和单例模式

异常和单例模式

1. 异常体系结构

顶层父类:Throwable

直接子类:Error Exception

运行时异常:RuntimeException

2. 异常处理

2.1 try-catch

try : 将有可能出现异常的代码 书写在try代码块中

try关键字不能单独出现 必须结合catch 或者 catch-finally 或者 finally

catch: 在catch代码块中书写捕获的异常 并且对捕获到的异常进行处理

catch也不能单独出现 必须结合 try

情况1:使用try-catch处理异常 但是程序没有出现异常 正常执行

package com.atguigu.test1;

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * try:将有可能出现异常的代码 书写在try代码块中
 * try关键字不能单独出现 必须结合catch 或者 catch-finally 或者 finally
 *
 * catch:在catch代码块中书写捕获的异常 并且对捕获到的异常进行处理
 * catch也不能单独出现 必须结合 try
 *
 * 情况1:使用try-catch处理异常 但是程序没有出现异常 正常执行
 *
 * */
public class Test2 {
    public static void main(String[] args) {
        try {
            Scanner scan = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = scan.nextInt();
            System.out.print("请输入除数:");
            int num2 = scan.nextInt();
            System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        }catch (InputMismatchException e){
            e.printStackTrace();  // 打印异常堆栈信息
        }
        System.out.println("感谢使用本程序!");
    }
}

情况2:catch代码块中捕获到异常 将执行catch代码块中的内容 然后程序继续执行 也就是不会中断程序

package com.atguigu.test1;

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * 情况2”catch代码块中捕获到异常 将执行catch代码块中的内容 然后程序继续执行 也就是不会中断程序
 * */
public class Test3 {
    public static void main(String[] args) {
        try {
            Scanner scan = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = scan.nextInt();
            System.out.print("请输入被除数:");
            int num2 = scan.nextInt();
            System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        }catch (InputMismatchException e){
            e.printStackTrace();
        }
        System.out.println("感谢使用本程序!");
    }
}

情况3:出现的异常和捕获的异常类型不匹配 依然会中断程序

package com.atguigu.test1;

import java.util.Scanner;

/**
 * 情况3:出现的异常和捕获的异常类型不匹配 依然会中断执行
 * */
public class Test4 {
    public static void main(String[] args) {
        try {
            Scanner scan = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = scan.nextInt();
            System.out.print("请输入除数:");
            int num2 = scan.nextInt();
            System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        }catch (ArithmeticException e){  // 如果出现的异常和捕获的异常类型不匹配,依然会中断执行,后续代码不会执行
            e.printStackTrace();
        }
        System.out.println("感谢使用本程序!");

    }
}

情况4:捕获多种异常 使用多个catch实现

在需要使用多个catch块处理异常的情况下 先子类后父类 否则编译报错

实际开发中 不能直接写父类异常来捕获所有的异常 因为这样书写 代码阅读性降低

package com.atguigu.test1;

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * 情况4:捕获多种异常 使用多个catch实现
 *
 * 在需要使用多个catch块处理异常的情况下 先子类后父类 否则编译报错
 * 实际开发中 不能直接写父类异常来捕获所有的异常 因为这样书写 代码阅读性降低
 * */
public class Test5 {
    public static void main(String[] args) {
        try {
            Scanner scan = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = scan.nextInt();
            System.out.print("请输入除数:");
            int num2 = scan.nextInt();
            System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        }catch (InputMismatchException e){
            e.printStackTrace();
        }catch (ArithmeticException e){
            e.printStackTrace();
        }
        System.out.println("感谢使用本程序1!");
        System.out.println("------------------------");


        try {
            Scanner scan = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = scan.nextInt();
            System.out.print("请输入除数:");
            int num2 = scan.nextInt();
            System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        }catch (InputMismatchException e){
            e.printStackTrace();
        }catch (ArithmeticException e){
            e.printStackTrace();
        }catch (RuntimeException e){
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("感谢使用本程序2!");
    }
}

getMessage()方法
package com.atguigu.test2;

import java.util.Scanner;

public class Test1 {
    public static void main(String[] args) {
        try {
            Scanner scan = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = scan.nextInt();
            System.out.print("请输入除数:");
            int num2 = scan.nextInt();
            System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        }catch (Exception e){
            e.printStackTrace();
            System.out.println(e.getMessage());  // / by zero  getMessage()方法 属于 printStackTrace()中的一部分
        }
        System.out.println("感谢使用本程序!");
    }
}

2.2 finally

finally:不管是否出现异常 以及 异常是否被捕获到 都将执行的代码块

通常用于释放/关闭资源 finally还可以提高代码的阅读性

finally不能单独出现 必须结合 try-catch 或者 try

package com.atguigu.test2;

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * finally:不管是否出现异常 以及 异常是否被捕获到 都将执行的代码块
 * 通常用于释放/关闭资源 finally还可以提高代码的阅读性
 * */
public class Test2 {
    public static void main(String[] args) {
        try {
            Scanner scan = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = scan.nextInt();
            System.out.print("请输入除数:");
            int num2 = scan.nextInt();
            System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        }catch (InputMismatchException e){
            e.printStackTrace();
        }finally {
            System.out.println("感谢使用本程序!");
        }
    }
}

finally不执行的唯一情况 :在执行finally之前退出JVM虚拟机 System.exit(int status)

package com.atguigu.test2;
/**
 * finally不执行的唯一情况:在执行finally之前退出JVM虚拟机 System.exit(int status)
 *
 * finally不能单独出现 必须结合 try-catch 或者 try
 * */
public class Test3 {
    public static void main(String[] args) {
        try {
            int a = 10;
            int b = 20;
            System.out.println(a + b);

            System.exit(0);  // 后续代码不再执行
        }finally {
            System.out.println("程序结束");  // 不会执行到
        }
    }
}

2.3 finally相关面试题

值传递和引用传递:

​ 基本数据类型属于值传递 传递的是一个值 在方法中对值的操作 不会影响原变量

​ 引用数据类型属于引用传递 传递的是一个地址 在方法中对地址的操作 将影响指向此地址所有的变量

​ String是特殊的引用数据类型 作为参数传递 在方法中对参数的操作 不会影响原遍变量

finally面试题:如果在try中已经返回值了,那么在finally中对返回值的操作是否影响 try中的返回值

​ 1.如果返回的是基本数据类型 不影响

​ 2.如果返回的是引用数据类型 影响

package com.atguigu.test2;

import java.util.Arrays;

/**
 * 值传递和引用传递:
 *      基本数据类型属于值传递 传递的是一个值 在方法中对值的操作 不会影响原变量
 *      引用数据类型属于引用传递 传递的是一个地址 在方法中对地址的操作 将影响指向地址所有的变量
 *      String是特殊的引用数据类型 作为参数传递 在方法中对参数的操作 不会影响原变量
 * finally面试题:如果在try中以及返回值了,那么在finally中对返回值的操作是否影响 try中的返回值
 *      1.如果返回的是基本数据类型 不影响
 *      2.如果返回的是引用数据类型 影响
 * */
public class Test4 {
    public static void main(String[] args) {
        System.out.println(getNum());  // 11

        System.out.println(Arrays.toString(getNums()));  // [4, 5, 6]

        String str = "abc";
        m1(str);

        System.out.println(str);  // abc
    }

    public static void m1(String str){
        str += "hello";
    }

    public static int getNum(){
        int num = 10;
        try {
            num++;
            return num;  // num是基本数据类型 已经return了,后续再进行修改将不会改变num
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            num++;
        }
        return num++;
    }

    public static int[] getNums(){
        int[] nums = {1, 2, 3};

        try {
            return nums;  // nums是由引用数据类型,已经return,后续再进行修改仍然会改变nums
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            nums[0] = 4;
            nums[1] = 5;
            nums[2] = 6;
        }
        return nums;
    }
}
2.4 throw和throws

throws: 表示声明方法可能会出现的异常 书写在方法定义形参列表(小括号)之后 可以声明多个异常

多个异常使用逗号分割

调用者根据方法声明的异常类型做出不同的处理:

​ 运行时异常-RuntimeException及其子类:调用者不是必须处理此异常

​ 检查异常(CheckedException)-直接Exception或者其子类 (不包含RuntimeException) :

​ 1.调用者使用try-catch处理异常

​ 2.调用继续声明给JVM虚拟机 (相当于不处理)

throw:表示抛出异常 书写在方法体内部 作为一条单独的语句 每条throw语句只能抛出一个异常

根据抛出异常类型的不同,方法声明也需要做不同的处理:

​ 1.如果抛出运行时异常 不必声明异常

​ 2.如果抛出检查异常 方法必须声明异常

package com.atguigu.test3;


import java.io.FileNotFoundException;
import java.util.Scanner;

/**
 * throws:表示声明方法可能出现的异常 书写在方法定义形参列表(小括号)之后 可以声明多个异常 多个异常使用逗号分割
 *
 * 调用者根据方法声明的异常类型做出不同的处理:
 *      运行时异常-RuntimeException及其子类:调用者不是必须处理此异常
 *      检查异常(CheckedException)-直接Exception或者其子类(不包含RuntimeException):
 *             1.调用者使用try-catch处理异常
 *             2.调用继续声明给JVM虚拟机(相当于不处理)
 * */
public class Test1 {
    public static void main(String[] args) throws ClassNotFoundException {
        m1();

        m2();  // 非运行时异常 使用声明在参数列表后的方式处理

        try {
            m3();  // 非运行时异常,使用try-catch方式处理
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        m4();  // RunTime异常及其子类异常,可以不进行处理
    }

    public static void m1(){
        Scanner scan = new Scanner(System.in);
        System.out.print("请输入被除数:");
        int num1 = scan.nextInt();
        System.out.print("请输入除数:");
        int num2 = scan.nextInt();
        System.out.println(num1 + "/" + num2 + "=" + num1 / num2);
        System.out.println("感谢使用本程序!");
    }

    public static void m2() throws ClassNotFoundException{

    }

    public static void m3() throws FileNotFoundException{

    }
    // RunTime异常可以不进行处理
    public static void m4() throws RuntimeException{

    }
}
package com.atguigu.test3;
/**
 * throw:表示抛出异常 书写在方法体内部 作为一条单独的语句 每条throw语句只能抛出一个异常
 *
 * 根据抛出异常类型的不同,方法声明也需要做不同的处理:
 *      1.如果抛出运行时异常 不必声明异常
 *      2.如果抛出检查异常 方法必须声明异常
 * */
public class Student {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) throws Exception {
        if (age >= 0 && age <= 130){
            this.age = age;
        }else{
            System.out.println("年龄不合适,请重新输入");
            throw new Exception("年龄不合适,请重新输入");  // 抛出非运行时异常,需要在参数列表后声明异常
        }

    }

    public static void main(String[] args) throws Exception {
        Student stu = new Student();
        stu.setAge(-1);  // 非运行时异常需要进行处理,这里使用在参数列表后声明异常的方式进行处理,使用throws的方式,后续代码不会执行,使用try-catch的方式后续代码可以执行

        System.out.println("123123");
        System.out.println("123123");
        System.out.println("123123");
    }
}

2.5 throw和throws的区别

throw和throws的区别

3. 自定义异常

自定义异常:实际开发中 JDK提供的异常不能满足具体的业务需求 此时 我们可以通过继承异常父类 来自定义异常

package com.atguigu.test4;

import java.io.IOException;

/**
 * 自定义异常:实际开发中 JDK提供的异常不能满足具体的需求 此时 我们可以通过继承异常父类 来自定义异常
 * */
public class Student {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) throws InputAgeException {
        if (age >= 0 && age <= 130){
            this.age = age;
        }else {
            throw new InputAgeException("年龄不合适,请重新输入,输入年龄为:" + age);
        }

    }

    public static void main(String[] args) {
        Student stu = new Student();

        try {
            stu.setAge(-1);
        } catch (InputAgeException e) {
            e.printStackTrace();
        }

        try {
            Runtime.getRuntime().exec("qweiq");
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("程序结束");
    }
}

package com.atguigu.test4;
/**
 * 自定义异常 继承JDK提供的异常父类即可
 * 三个父类 Throwable Exception RuntimeException
 * 其中继承RuntimeException表示此异常为运行时异常 方法不必声明 调用者不必处理
 * 继承其他父类 表示此异常为检查异常 方法必须声明 调用者必须处理
 * */
public class InputAgeException extends Throwable{
    public InputAgeException(String message){
        super(message);
    }
}

4. 设计模式

设计模式:由我们的前辈们在长期的开发实践中 总结出来的一套用于解决特定问题的套路

具体作者:GOF(四人组) Gang Of Four 共同编写的《设计模式》一书

设计模式一共有23种

常见的设计模式:工厂模式(简单工厂,工厂方法),单例模式(懒汉,饿汉),建造者模式,观察者模式,代理模式(静态代理,动态代理)

设计模式6/7大原则

1.开闭原则 源代码对扩展开放 对修改关闭

2.依赖倒置原则 我们的程序中应该依赖于抽象 而非依赖于具象

3.里式替换原则 我们程序中的父类参数或者返回值 应该可以使用子类替代

4.接口隔离原则 接口与接口之间属于组合关系 不存在依赖关闭 互相隔离 互不影响

5.迪米特原则 不要和陌生人说话 本类中的信息应该直接所属于本类 不能间接属于本类 或者 间接有关联关系

6.单一原则 一个类只描述一个事物 一个方法只做一件事

7.合成复用原则

5.静态工厂和工厂方法模式

创建对象不再有需要此对象的调用者来创建,而是交给第三方创建,调用者只负责使用

静态工厂代码示例
package com.atguigu.test6;
/**
 * 简单工厂/静态工厂:用于创建对象
 *
 * 汽车父类
 * */
public class Car {
    public String getCarInfo(){
        return "汽车信息";
    }
}

package com.atguigu.test6;
/**
 * 汽车工厂:专门用来造汽车
 * */
public class CarFactory {
    public static Car buildCar(String carName){
        if (carName.equalsIgnoreCase("bmw")){
            return new BMW();
        }else if(carName.equalsIgnoreCase("benz")){
            return new Benz();
        }
        return null;
    }
}
package com.atguigu.test6;

public class Benz extends Car{
    @Override
    public String getCarInfo() {
        return "Benz S500";
    }
}

package com.atguigu.test6;

public class BMW extends Car{
    @Override
    public String getCarInfo() {
        return "BMW 520";
    }
}

package com.atguigu.test6;

public class TestCar {
    public static void main(String[] args) {
        int age = -100;  // 一旦设置就无法修改

        BMW bmw = new BMW();
        System.out.println(bmw.getCarInfo());  // BMW 520

        Benz benz = new Benz();
        System.out.println(benz.getCarInfo());  // Benz S500

        System.out.println(CarFactory.buildCar("bmw").getCarInfo());  // BMW 520
        System.out.println(CarFactory.buildCar("benz").getCarInfo());  // Benz S500
    }
}

工厂方法代码示例
package com.atguigu.test7;

public class Car {
    public String getCarInfo(){
        return "汽车信息";
    }
}

package com.atguigu.test7;

public class Audi extends Car{

    @Override
    public String getCarInfo() {
        return "Audi A6";
    }
}
package com.atguigu.test7;

import com.atguigu.test7.Car;

public class Benz extends Car {
    @Override
    public String getCarInfo() {
        return "Benz S500";
    }
}

package com.atguigu.test7;

public class BMW extends Car{
    @Override
    public String getCarInfo() {
        return "BMW 520";
    }
}

package com.atguigu.test7;
/**
 * 汽车工厂 抽象的
 * */
public interface CarFactory {

    Car builder();
}
package com.atguigu.test7;

public class BMWFactory implements CarFactory{
    @Override
    public BMW builder() {
        return new BMW();
    }
}

package com.atguigu.test7;

public class BenzFactory implements CarFactory{
    @Override
    public Car builder() {
        return new Benz();
    }
}
package com.atguigu.test7;

public class AudiFactory implements CarFactory{

    @Override
    public Car builder() {
        return new Audi();
    }
}

package com.atguigu.test7;
/**
 * 根据new的不同类型的对象 去执行方法构造对应的对象
 * */
public class Test {
    public static void main(String[] args) {
//        CarFactory carFactory = new BenzFactory();  // Benz S500
//        CarFactory carFactory = new AudiFactory();  // Audi A6
        CarFactory carFactory = new BMWFactory();  // BMW 520
        System.out.println(carFactory.builder().getCarInfo());
    }
}

6. 单例模式

单例模式:是指程序运行期间只允许存在某个类的一个实例 单例

饿汉单例:类加载即初始化一个本类实例 通过方法返回

懒汉单例 :直到调用方法才创建本类实例 在方法中返回

饿汉模式
package com.atguigu.test8;
/**
 * 饿汉单例:直接初始化一个本类实例 通过方法返回
 * */
public class HungrySingleton {

    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return instance;
    }
}

懒汉模式
package com.atguigu.test8;
/**
 * 单例模式:是指程序运行期间只允许存在某个类的一个实例 单例
 * 懒汉单例:直到调用方法才创建本类实例 在方法中返回
 * */
public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton(){}

    public static LazySingleton getInstance(){
        if (instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }
}

测试是否只有一个实例
package com.atguigu.test8;
/**
 * 验证懒汉和饿汉单例模式new出来的多个对象是否是同一个对象
 * */
public class Test {
    public static void main(String[] args) {

        LazySingleton lazy1 = LazySingleton.getInstance();
        LazySingleton lazy2 = LazySingleton.getInstance();
        LazySingleton lazy3 = LazySingleton.getInstance();
        LazySingleton lazy4 = LazySingleton.getInstance();

        System.out.println(lazy1 == lazy2);  // true
        System.out.println(lazy3 == lazy4);  // true

        System.out.println("----------------------------");

        HungrySingleton hung1 = HungrySingleton.getInstance();
        HungrySingleton hung2 = HungrySingleton.getInstance();
        HungrySingleton hung3 = HungrySingleton.getInstance();
        HungrySingleton hung4 = HungrySingleton.getInstance();

        System.out.println(hung1 == hung2);  // true
        System.out.println(hung3 == hung4);  // true
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值