异常和单例模式
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的区别
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
}
}