Java 第四周学习总结

一、异常

错误与异常

java中对于程序出现的异常情况分为两种:

  1. 错误(Error)
  2. 异常(Exception)
    Error:错误通常是系统级别的问题,比如说JVM内存溢出(StackOverflowError),JVM系统错误等,这些问题是程序员无法修复的问题,程序运行时出现的无法被程序员从业务上解决的问题,这些问题一般是系统级别的。错误不是我们关注的范畴
    Exception:异常通常是程序再运行期间,或者编译期间由编译器抛出的一些,可以被程序员处理的代码上的问题,比如(NullPointerExcepotion/ArrayIndexOutOfBoundsException),异常是程序员开发中需要解决的问题

Throwable

Throwable是Java中错误和异常的顶级父类,以下是Throwable和Error,Exception之间的关系
在这里插入图片描述

java中的所有错误从Error类继承,并且绝大多数类名称后缀以Error结尾
Java中的所有异常从Exception类继承,都是以Exception作为后缀结尾

异常概述

Exception:异常,一般在程序运行期间,或者编译期间由编译器抛出的异常信息,这些异常情况可以由程序员进行处理(抛出,捕获);java中的异常根据类型划分又分为两种类型:

  • 运行时异常(RuntimeException)
  • 检查异常(一般异常)

运行时异常

运行时异常一般在程序运行期间,出现了对应异常情况之后由JVM抛出,并且将异常的堆栈信息输出到控制台(或日志文件),java中的所有运行时异常都是从java.lang.RuntimeException继承而来。常见的运行时异常:

异常类型 说明
java.long.ArithmeticException 算数异常(比如被零除)
java.lang.NullPonterException 空指针异常(调用方法,属性的对象位null时)
java.lang.ArrayIndexOutOfBoundsException 数组索引越界
java.lang.ClassCastException 类型转换异常
java.util.InputMismatchException 输入的数据类型不匹配读取的异常

运行时异常即程序运行时才会产生的异常

检查异常

检查异常也称之为一般异常,或者编译期异常,这种类型异常通常在编译期间由编译器提示需要进行显式的处理:
在这里插入图片描述
常见的检查异常:

异常类型 说明
java.lang.ClassNotFoundException 未找到异常
java.io.FileNotFoundException 文件未找到异常
java.io.IOException IO异常
java.sql.SQLException 访问数据库的异常
java.text.ParseException 解析异常

检查异常是在程序编译时产生的

异常处理

异常既然产生则有必要进行合理的处理,Java中对于异常的处理分为两种方式:

  1. 异常抛出(throw/throws)
  2. 异常捕获(try/catch/finally)

Java程序中一旦出现异常,则出现异常问题的所在代码行之后的代码无法再执行

异常抛出

异常的抛出指的是将有可能出现的异常通过方法的结构向外抛出,交给下一级的调用者处理

/**
	 * 	抛出异常
	 * @throws ClassNotFoundException
	 */
public static void e1() throws ClassNotFoundException{
   	
    Class.forName("java.lang.Strin");		
}

抛出异常常见的关键字:

  • throws:用于方法的声明中,抛出有可能出现的异常
  • throw:用于语句块中,抛出指定类型的异常对象,throw一旦执行,则一定会出现该类型异常

语法区别:

  • throws
【修饰符】 返回值类型 方法名(【参数列表】) throws 异常类型名称{
   
    //方法体
}
public static void e1() throws ClassNotFoundException{
   	
	Class.forName("java.lang.String");		
}
  • throw
方法体{
   
    throw  异常类型对象
}
public static void main(String[] args) throws IOException  {
   	
    int i = 0;
    if(i == 0) {
   	
        //抛出异常对象
        throw new IOException();
    }
    System.out.println("hello");
}

在这里插入图片描述

对于存在继承关系的异常抛出问题

父类的结构:

public class Animal {
   
	
	public void eat(){
   
		System.out.println("吃东西");
	}
}

子类的结构:

public class Dog extends Animal{
   

    //编译错误
	public void eat() throws ClassNotFoundException{
   
		Class.forName("");
	}
}

对于以上程序:
子类Dog对父类Animal中的方法eat()方法进行了重写,但是由于父类方法没有抛出任何的异常,此时子类无法进行任何检查的抛出,否则会不兼容父类方法定义,因此以上程序在子类中会出现编译错误
在这里插入图片描述
解决方案由两种:

  1. 子类方法中对于异常捕获
  2. 在父类方法的声明上加上对应的异常类型抛出定义:throws ClassNotFoundException
    在这里插入图片描述

父类方法可以抛出比子类方法抛出的范围更大的异常,比如直接throws Exception

注意事项:
父类方法未抛出任何异常情况下,子类只能抛出运行时异常。
游离快和静态语句块中不能抛出任何异常,因为外界无法直接调用这两种语句块

异常捕获

异常的捕获即,将有可能出现异常的代码片段使用try语句块进行包裹,然后使用catch语句块将有可能产生的异常类型进行捕获,并作出处理。

异常捕获常见的关键字:

  • try
  • catch
  • finally

语法结构:

try{
   
    //有可能出现异常的代码片段
}catch(异常类型 变量名){
   
    //处理异常
}finally{
   
    //不论是否出现异常,始终执行
}
try {
   
    m1();
    PrintStream ps = new PrintStream("a/test/details.log");
} catch (ClassNotFoundException e) {
   
    e.printStackTrace();
} catch (FileNotFoundException e) {
   
    System.out.println("fileNotFound");
    e.printStackTrace();
} catch (IOException e) {
   
    System.out.println("IO");
    e.printStackTrace();
}

程序执行到try语句块,在执行期间如果出现了对应catch的异常类型,则直接进入catch语句块,如果catch语句块中没有合适的异常解决方案,则由JVM进行统一处理(打印异常的堆栈信息)

finally

finally一般用于异常捕获之后执行最终的处理工作,比如,清理资源,关闭流,关闭连接;finally中的代码无论是否出现异常,始终会执行。

try {
   
    //打开资源
    System.out.println("打开文件");
    System.out.println(10/2); 
}catch(Exception e) {
   
    e.printStackTrace();
}finally {
   
    //无论是否异常始终执行
    System.out.println("关闭文件");			
}

try,catch,finally的组织方式可以有多种:

//方法一:
try {
   

}finally {
   

}

//方法二:
try {
   

}catch(Exception e) {
   

}

//方法三:
try {
   

}catch(RuntimeException re) {
   

}catch(Exception e) {
   

}

//方法四:
try {
   

}catch(Exception e) {
   

}finally {
   

}

关于异常的常见面试题
1. 请你说出 final、finalize和finally的区别?
final是一个关键字用于修饰类,属性,方法
finalize是Object类中提供的一个方法,用于在jvm对对象清理时,对于当前对象执行一些最终的处理工作的
2. java中是否会存在内存溢出的问题?(指针)
理论上java不会存在内存泄漏问题,因为jvm提供了GC(垃圾回收:garbage collection)机制,会在适当的时候自动回收内存空间,不需要由程序员手动处理;但是如果使用第三方资源(比如:打开一个文件,打开了网络通道,打开数据库连接等)并且未及时的清理以及回收,将会导致内存泄漏。
3. 异常处理中finally和return的结合使用?
如果try语句块中有使用return,并且try语句块中没有任何异常时,程序首先会执行finally然后再执行return;但是对于基本类型的数据,finally的赋值是不会生效的,但是finally中操作引用类型的属性可以生效

//程序正常执行,返回 20;finally中的赋值无效
public static int m2() {
   
    int i = 10;
    try {
   
        i = 20;
        return i;		
    }catch(Exception e){
   
        e.printStackTrace();
    }finally {
   
        i = 30;
        System.out.println("finally");
    }
    return i;
}

//程序正常执行,返回对象中的name属性值被修改为“李四”;finally中的赋值生效
public static User m3() {
   
    User u = new User();
    try {
   
        u.name = "张三";
        return u;
    }catch(Exception e) {
   
        e.printStackTrace();
    } finally {
   
        u.name = "李四";
    }
    return u;
}

异常定位

在这里插入图片描述

自定义异常

概述
以上我们已经熟悉了java中的异常分类以及处理方式,其中异常分类主要包含检查异常和运行时异常,但是以上所有异常都是有JDK预定义好的异常类型,比如:空指针,索引越界,类型转换失败等代码语法方面的异常,并没有与实际项目相关一些业务方面的异常,比如:订单创建失败,用户权限不足,余额不足等异常情况;
因此,针对以上的需求,当预定的异常无法满足所有需要时,我们可以通过对JDK的异常进行扩展,自定义异常,以满足实际项目的需求。

自定义异常的使用

java中创建自定义异常十分简单,只需要对现有的异常类型,扩展即可,比如常见的方式为:继承Exception,声明一个无参的以及一个包含字符串类型参数的构造器即可。异常的定义通常用于标记程序运行时的异常情况,并不需要在异常中进行任何的业务逻辑处理,因此自定义异常中也无需定义任何的方法。

案例:

public class MyException extends Exception{
   

	public MyException() {
   
		super();
	}
	
	public MyException(String msg) {
   
		super(msg);
	}
}

自定义异常综合案例:

一有个银行账户A和账户B,现在需要从账户A转账到账户B,转账需要检查账户的余额是否足够,如果余额不足,则抛出一个 MoneyLessException,请实现!

账户类(Account.java)

public class Account {
   

	private int id;
	private String name;
	private double money;
	
    //构造器(略)
	//setter/getter(略)
    //toString(略)
}

账户管理类(AccountManager.java)

public class AccountManager {
   

	/**
	 * 	将指定账户中的余额转移指定数目到另一个账户中
	 * @param a1  	账户A
	 * @param a2 	账户B
	 * @param money 需要转账的金额
	 * @throws MoneyLessException
	 */
	public void transfer(Account a1,Account a2,double money) throws MoneyLessException {
   
		
		if(a1.getMoney() < money) {
   
			//余额不足
			throw new MoneyLessException("余额不足:"+(a1.getMoney() - money));
		}
		a1.setMoney(a1.getMoney() - money);
		a2.setMoney(a2.getMoney() + money);
		
		System.out.println(a1);
		System.out.println(a2);
	}
}

自定义异常类(MoneyLessException.java)

/**
 * 	余额不足异常
 * @author mrchai
 *
 */
public class MoneyLessException extends Exception {
   

	public MoneyLessException() {
   
		super();
	}
	
	public MoneyLessException(String msg) {
   
		super(msg);
	}
}

测试类(Test.java)

public class Test {
   

	public static void main(String[] args) throws MoneyLessException {
   
		
		Account a1 = new Account(1, "A", 500);
		Account a2 = new Account(2, "B", 100);
		
		AccountManager am = new AccountManager();
		am.transfer(a1, a2, 100);
	}

}

常用类之BigDecimal与DecimalFormat

BigDecimal

java.math.BigDecimal类从java.math.Number类继承而来,用于表示精度较高的数值类型的封装类型,一般用于精度要求较高的程序中,比如银行账户的金额属性,

常见构造器:

  • java.math.BigDecimal(String s)
  • java.math.BigDecimal(double d)
  • java.math.BigDecimal(long l)
  • java.math.BigDecimal(int i)

常见方法:

  • add(BigDecimal b):与另一个BigDecimal执行相加运算
  • subtract(BigDecimal b):与另一个BigDecimal执行相减运算
  • multiply(BigDecimal b):与另一个BigDecimal执行相乘运算
  • divide(BigDecimal b):与另一个BigDecimal执行相除运算
double d1 = 0.1;
double d2 = 0.2;
//		System.out.println(d1 + d2); 
//在涉及到一些敏感的浮点数运算时,不适合直接使用float和double,精度丢失

BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");

//相加 a + b =>  a.add(b)
System.out.println(b1.add(b2));  
System.out.println(b1.add(b2,MathContext.DECIMAL128)); 

//相减 a - b => a.subtract(b) 
System.out.println(b1.subtract(b2));

//相乘 a * b => a.multiply(b)
System.out.println(b1.multiply(b2));

//相除 a / b => a.divide(b) 
System.out.println(b1.divide(b2));

对于相除运算的使用:

BigDecimal d1 = new BigDecimal("10");
BigDecimal d2 = new BigDecimal("3");
//当两个数值相除为无限循环数时会出现算术异常
//向上取整
System.out.println(d1.divide(d2,RoundingMode.CEILING));
//向下取整
System.out.println(d1.divide(d2,RoundingMode.FLOOR));

//向上保留指定位小数点
System.out.println(d1.divide(d2, 2, BigDecimal.ROUND_CEILING));
//向下保留指定位小数点
System.out.println(d1.
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值