Day16-常用API
4. System
System类提供的public static long currentTimeMillis()用来返回当前时 间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。
此方法适于计算时间差。
System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。 该类位于java.lang包。
由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实 例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便 的进行调用。
成员变量
System类内部包含in、out和err三个成员变量,分别代表标准输入流
(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。
成员方法
native long currentTimeMillis():
该方法的作用是返回当前的计算机时间,时间的表达格式为当前计算机时
间和GMT时间(格林威治时间)1970年1月1号0时0分0秒所差的毫秒数。
北京时间 : 1970.1.1 8:00 00 000
void exit(int status):
该方法的作用是退出程序。其中status的值为0代表正常退出,非零代表
异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等
package com.demo._Date;
public class System_01 {
public static void main(String[] args) {
System.err.println("111");//111
System.out.println("222");//222
// 时间原点到现在的毫秒数(1970.1.1 8:00 00 000)
long now = System.currentTimeMillis();
System.out.println(now);//1611226807787
// 退出JVM虚拟机
System.exit(-1);//退出了下面的语句就不执行
System.out.println("=-=============");
}
}
5. Date
表特定的瞬间,精确到毫秒
5.1 构造方法
getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象
表示的毫秒数。
toString():把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat),zzz是时间标准。
其它很多方法都过时了。
5.3 SimpleDateFormat
Date类的API不易于国际化,大部分被废弃了,java.text.SimpleDateFormat
类是一个不与语言环境有关的方式来格式化和解析日期的具体类。
它允许进行格式化:日期à文本、解析:文本à日期
SimpleDateFormat() :默认的模式和语言环境创建对象
public SimpleDateFormat(String pattern):该构造方法可以用参数pattern
指定的格式创建一个对象,该对象调用:
public String format(Date date):方法格式化时间对象date
public Date parse(String source):从给定字符串的开始解析文本,以生成
一个日期。
5.4 使用方式
package com.demo._Date;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 获取当前系统时间
*/
public class Date_01 {
public static void main(String[] args) {
// 获取当前系统时间
Date now = new Date();
// Thu Jan 21 18:49:41 CST 2021
// Date重写了toString,但是是欧美风格
// 我们不太喜欢这种格式,所以 引入了格式化日期类
System.out.println(now);
/*
* 年 : y 月 : M 日 : d 时 : H 分 : m 秒 : s 毫秒 : S
*/
// 创建格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss SSS");
// 格式化,返回String
String strTime = sdf.format(now);
// 2021年01月21日 18:49:41 520
System.out.println(strTime);
}
}
5.4.1 字符串转Date
package com.demo._Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
// 字符串转date
public class Date_02 {
public static void main(String[] args) throws ParseException {
String strDate = "2021年01月21日 18:16:56 307";
// 注意 : 格式化的格式 要和 字符串中时间格式一致
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss SSS");
// 转换为date
Date date = sdf.parse(strDate);
// Thu Jan 21 09:41:59 CST 2021
System.out.println(date);
}
}
5.4.2 练习
package com.demo._Date;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Date d1 = new Date() ; 无参构造 是获取当前系统时间
*
* Date d2 = new Date(long millis) ; 有参构造,是获取从时间原点到指定毫秒数的时间
*
*/
public class Date_03 {
public static void main(String[] args) {
Date d1 = new Date(1000);//1000ms = 1s
// Thu Jan 01 08:00:01 CST 1970
System.out.println(d1);
// 问 : 如何获取当前 系统时间的前10分钟的时间
// 1 先获取当前时间毫秒数
long currentMillis = System.currentTimeMillis(); //时间戳
// 2 减去10分钟的毫秒数
currentMillis -= 1000*60*10;//600s = 600*1000ms
// 3 根据毫秒数创建时间对象
Date date = new Date(currentMillis);
// 创建时间格式化对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
// 格式化并打印
System.out.println(sdf.format(date));
}
}
6.Calendar
1、Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能。
2、获取Calendar实例的方法
使用Calendar.getInstance()方法
调用它的子类GregorianCalendar的构造器。
3、一个Calendar的实例是系统时间的抽象表示,通过get(int field)方法来取得想 要的时间信息。比如YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAY 、 MINUTE、SECOND
public void set(int field,int value)
public void add(int field,int amount)
public final Date getTime()
public final void setTime(Date date)
4、注意:
获取月份时:一月是0,二月是1,以此类推,12月是11
获取星期时:周日是1,周二是2 , 。。。。周六是7
package com.demo._Date;
import java.util.Calendar;
/**
* 日历类
*
*/
public class Date_04 {
public static void main(String[] args) {
// 创建日历对象
Calendar c = Calendar.getInstance();
// 获取当前是本周第几天,周日是第一天
System.out.println(c.get(Calendar.DAY_OF_WEEK));//5
// 获取今天是多少号(本月第几天)
System.out.println(c.get(Calendar.DAY_OF_MONTH));//21
// 获取年
int year = c.get(Calendar.YEAR);
// 获取月 从0开始,所以结果+1
int month = c.get(Calendar.MONTH) + 1;
// 获取日
int day = c.get(Calendar.DAY_OF_MONTH);
// 获取时
int hour = c.get(Calendar.HOUR);
// 获取分
int minute = c.get(Calendar.MINUTE);
// 获取秒
int second = c.get(Calendar.SECOND);
// 获取本周第几天
int weekday = c.get(Calendar.DAY_OF_WEEK);
// 把本周第几天转换为星期
String weekdayStr = "星期";
switch (weekday) {
case 1:
weekdayStr += "日";
break;
case 2:
weekdayStr += "一";
break;
case 3:
weekdayStr += "二";
break;
case 4:
weekdayStr += "三";
break;
case 5:
weekdayStr += "四";
break;
case 6:
weekdayStr += "五";
break;
case 7:
weekdayStr += "六";
break;
}
System.out.println(year+"年"+month+"月"+day+"日 "+hour+":"+minute+":"+second+" "+weekdayStr);//2021年1月21日 6:57:51 星期四
}
}
7. 扩展之Enum
枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看什么是枚举?如何定义枚举?
7.1 Enum之前定义枚举的方式
上述的常量定义常量的方式称为int枚举模式,这样的定义方式并没有什么错,但它存在许多不足,如在类型安全和使用方便性上并没有多少好处,如果存在定义int值相同的变量,混淆的几率还是很大的,编译器也不会提出任何警告,因此这种方式在枚举出现后并不提倡,现在我们利用枚举类型来重新定义上述的常量,同时也感受一把枚举定义的方式,如下定义周一到周日的常量
7.2 Enum之后定义枚举的方式
//枚举类型,使用关键字enum
enum Day {
MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
相当简洁,在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Day中分别定义了从周一到周日的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天
7.3 Enum使用
7.4 Enum优点
package com.demo._Enum;
/**
* Enumeration : enum枚举类型
*
* 简单来讲,就可以看做一个常量的集合
*
* 当我们需要一系列有限的值,并且用的较多,而不更改的情况下(使用常量的时候)
*
* 写枚举 更容易发现错误,因为在编译阶段对类型进行检查,从而减少错误
*
*/
public class Enum_01 {
public static void main(String[] args) {
String username = "admin";
String password = "root";
String result = login(username, password);
// 比如拼写错误,不会提示,很难找到,浪费时间
if ("SUCCESSS".equals(result)) {
System.out.println("登陆成功");
}else {
System.out.println("登陆失败");//登陆失败
}
}
// 登陆功能
public static String login(String username,String password){
if ("admin".equals(username) && "root".equals(password)) {
return "SUCCESS";
}else{
return "FAIL";
}
}
}
以上程序中,本来应该是登陆成功,由于我们不小心拼写错误,导致结果为登陆失败
但是 并不会提示我们拼写错误了,比如我们要使用SUCCESS和FAIL 的话,一般会定义为常量
package com.demo._Enum;
/**
* 优化第一种写法,降低拼写错误
*
* 上个程序中,本来应该是登陆成功,由于我们不小心拼写错误,导致结果为登陆失败
*
* 但是 并不会提示我们拼写错误了,比如我们要使用SUCCESS和FAIL 的话,一般会定义为常量
*
*/
public class Enum_02 {
public static void main(String[] args) {
String username = "admin";
String password = "root";
String result = login(username, password);
// 这里都写常量,可以避免拼写错误
//如果写Result.SUCCESS 会提示你写的有问题
//SUCCsESS cannot be resolved or is not a field
if (Result.SUCCESS.equals(result)) {
System.out.println("登陆成功");
} else {
System.out.println("登陆失败");
}
}
// 登陆功能
public static String login(String username, String password) {
if ("admin".equals(username) && "root".equals(password)) {
// 这里都写常量,可以避免拼写错误
return Result.SUCCESS;
} else {
return Result.FAIL;
}
}
}
/**
* 常量类
*/
final class Result {
public static final String SUCCESS = "SUCCESS";
public static final String FAIL = "FAIL";
}
这样的话,可以避免我们拼写错误,只需要保证Result类没拼错即可
但是从java1.5开始 引入enum之后,这种写法 显得比较繁琐
package com.demo._Enum;
/**
* 优化第二种写法,降低拼写错误
*
* 上个类,可以避免我们拼写错误,只需要保证Result类没拼错即可
但是从java1.5开始 引入enum之后,上面那种写法 显得比较繁琐
*/
public class Enum_03 {
public static void main(String[] args) {
String username = "admin";
String password = "root";
ResultEnum result = login(username, password);
// 这里都写常量,可以避免拼写错误
if (ResultEnum.SUCCESS == result) {
System.out.println("登陆成功");
} else {
System.out.println("登陆失败");
}
}
// 登陆功能
public static ResultEnum login(String username, String password) {
if ("admin".equals(username) && "root".equals(password)) {
// 这里都写常量,可以避免拼写错误
return ResultEnum.SUCCESS;
} else {
return ResultEnum.FAIL;
}
}
}
/**
* 没枚举
*/
enum ResultEnum {
SUCCESS,FAIL
}
8 扩展之Random
8.1 使用
随机数生成器从0开始
package com.demo._Random;
import java.util.Random;
/**
* 随机数生成器 从0开始
*
*/
public class Random_01 {
public static void main(String[] args) {
// 1 创建对象
Random r = new Random();
// 0~100 中 这 101个数内 随机生成
int i1 = r.nextInt(101);
System.out.println(i1);
Random random = new Random();
// 0~10 生成
int num = random.nextInt(11);
// +=10 就变成 10~20
num+=10;
System.out.println(num);
}
}
9. Math
提供科学计算和基本的数字操作方法
常用的方法都是静态的,直接类名调用即可,Math是在java.lang.Math 所以使用不需要导包
9.1 常用方法
abs 绝对值
acos,asin,atan,cos,sin,tan 三角函数
sqrt 平方根
pow(double a,doble b) a的b次幂
log 自然对数
exp e为底指数
max(double a,double b)
min(double a,double b)
random() 返回0.0到1.0的随机数
long round(double a) double型数据a转换为long型(四舍五入)
toDegrees(double angrad) 弧度—>角度
toRadians(double angdeg) 角度—>弧度
9.2 使用方法
package com.demo._Math;
/**
* 提供科学计算和基本的数字操作方法,一般为double
*
* 常用的方法都是静态的,直接类名调用即可,Math是在java.lang.Math 所以使用不需要导包
*
*/
public class Math_01 {
public static void main(String[] args) {
// abs : 绝对值
System.out.println( Math.abs(-1.2)); // 1.2
// ceil : 向上取整 , 只要小数大于0 就进位+1
System.out.println(Math.ceil(12.000000001)); // 13.0
// floor : 向下取整 , 不要小数
System.out.println(Math.floor(12.99999999)); // 12.0
// Max : 比较谁大
System.out.println(Math.max(2.2, 2.1)); // 2.2
// Min : 比较谁小
System.out.println(Math.min(2.2, 2.1)); // 2.1
// sqrt : 计算平方根,就是开平方
// NaN : Not a Number
System.out.println(Math.sqrt(16)); // 4.0
// cbrt : 计算立方根
System.out.println(Math.cbrt(8)); // 2.0
// random : 随机数,获取一个大于等于0.0 并且小于 1.0的随机数,不会等于1.0
System.out.println(Math.random());
// 生成大于等于0 且小于10
System.out.println(Math.random()*10);
// rint : 四舍五入 xxx.5 ,结果取偶数
System.out.println(Math.rint(2.4)); // 2.0
System.out.println(Math.rint(3.5)); // 4.0
System.out.println(Math.rint(2.5)); // 2.0
// pow : 几次幂 , 如 2的3次方
System.out.println(Math.pow(2, 3)); // 8.0
}
}
10. Number
10.1 DecimalFormat
数字格式化
# 表示任意数字,0-9任意单个数字
, 千分位
. 小数点
0 补位
package com.demo._Number;
import java.text.DecimalFormat;
/**
* 数字格式化 # 表示任意数字,0-9任意单个数字 , 千分位 . 小数点 0 补位
*
*/
public class Number_01 {
public static void main(String[] args) {
// 1 需求 : 千分位分割
DecimalFormat df = new DecimalFormat("###,###");
// 123,456
System.out.println(df.format(123456));
// 2 需求 : 加入千分位,保留两位小数
df = new DecimalFormat("###,###.##");
// 123,456.13
System.out.println(df.format(123456.126)); // 会进行四舍五入
// 123,456.2
System.out.println(df.format(123456.2)); // 如果位数不够两位,就拉倒,不显示,有几位显示几位,超过两位 就只显示两位
// 3 需求 : 加入千分位,保留两位小数,不够两位补0
df = new DecimalFormat("###,###.00");
System.out.println(df.format(123456.2)); // 123,456.20
}
}
10.2 BigDecimal 和 BigInteger
10.2.1 概述
8.1 BigInteger
1、Integer类作为int的包装类,能存储的最大整型值为2^31-1,Long类也是有限的, 最大为2^63-1。如果要表示再大的整数,不管是基本数据类型还是他们的包装类 都无能为力,更不用说进行运算了。
2、java.math包的BigInteger可以表示不可变的任意精度的整数。BigInteger 提供
所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。 另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、 位操作以及一些其他操作。
3、构造器
BigInteger(String val):根据字符串构建BigInteger对象
4、常用方法
public BigInteger abs():返回此 BigInteger 的绝对值的 BigInteger。
BigInteger add(BigInteger val) :返回其值为 (this + val) 的 BigInteger
BigInteger subtract(BigInteger val) :返回其值为 (this - val) 的 BigInteger
BigInteger multiply(BigInteger val) :返回其值为 (this val) 的 BigInteger
BigInteger divide(BigInteger val) :返回其值为 (this / val) 的 BigInteger。整数 相除只保留整数部分。
BigInteger remainder(BigInteger val) :返回其值为 (this % val) 的 BigInteger。
BigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟(this % val) 的两个 BigInteger 的数组。
BigInteger pow(int exponent) :返回其值为 (thisexponent) 的 BigInteger。
8.2 BigDecimal
一般的Float类和Double类可以用来做科学计算或工程计算,但在商业计算中, 要求数字精度比较高,故用到java.math.BigDecimal类。
BigDecimal类支持不可变的、任意精度的有符号十进制定点数。
构造器
public BigDecimal(double val)
public BigDecimal(String val)
常用方法
public BigDecimal add(BigDecimal augend)
public BigDecimal subtract(BigDecimal subtrahend)
public BigDecimal multiply(BigDecimal multiplicand)
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
10.2.2 使用
package com.demo._Number;
import java.math.BigDecimal;
import java.math.BigInteger;
public class BigInteger_01 {
public static void main(String[] args) {
BigInteger v1 = new BigInteger("100");
BigInteger v2 = new BigInteger("20");
// 加法
System.out.println(v1.add(v2)); // 120
// 减法
System.out.println(v1.subtract(v2)); // 80
// 乘法
System.out.println(v1.multiply(v2)); // 2000
// 除法
System.out.println(v1.divide(v2)); // 5
// 取余
System.out.println(v1.remainder(v2)); // 0
BigDecimal v3 = new BigDecimal(123);
BigDecimal v4 = new BigDecimal(123);
v3.add(v4);
v3.subtract(v4);
// .....
}
}
package com.demo._Number;
// 需求 1-10的阶乘
public class Biglnteger {
public static void main(String[] args) {
long result = sum(10);
System.out.println(result);//3628800
}
// 递归实现
public static long sum(long i){
if (i<=1) {
return 1;
}
return i * sum(i-1);
}
// 使用 BigInteger 实现 1~N 的阶乘
}
Day16-异常机制
1. 之前常见的运行时异常
1 空指针异常
2 下标越界
3 类型转换
4 栈内存移出
2. 异常概述
异常是Java中提供的一种识别及响应错误情况的一致性机制。有效地异常处理能使程序更加健壮、易于调试。
异常发生的原因有很多,比如:
1)用户输入了非法数据
2)要打开的文件不存在
3)网络通信时连接中断
4)JVM内存溢出
5)这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
如果程序报错,会终止程序生命周期执行(错误代码之后的代码都不会执行了)
1 异常机制
异常就是错误的另一种说法
在java中有一个专门模拟所有异常和错误的类(Throwable),所有的异常类都必须继承这个类
异常是我们程序员在写程序的时候,对某些高风险代码的操作规定了一个提醒机制,对于系统来说 就是处理异常的触发机制
2 异常机制的处理形式
try…catch… : 解决异常,一般用在客户端
throws : 抛出异常,一般用在类库端(服务端)
throw : 制造异常,异常源点,创建一个异常对象
用if…else…进行判断,也可以把大多数错误屏蔽掉,但是 只能处理我们预知的情况,对于无法预知的情况,就需要使用try…catch…解决了
3 不同异常机制的选择
有些错误我们不想处理,或者没有办法处理,或者不知道应该怎么处理(通常是类库端),一般使用throws把问题抛给调用处
在一个就是知道怎么处理,直接使用try…catch…处理问题即可,一般是main方法
4 finally语句块
必须执行的语句块
5 异常机制的继承体系
最大的异常类是 Throwable
直接两个子类 : Error(我们解决不了,比如栈溢出)和 Exception(所有的子类除了一个RunTimeException之外,其他全是编译时异常)
6 语法 :
try{
高风险代码;
}catch(异常类 变量){
处理措施;
}
目的 : 增强程序的鲁棒性,健壮性
3. 异常继承体系
4. Error
1. 概念
系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理。
比如:OOM(内存溢出错误)、VirtualMachineError(虚拟机错误)、StackOverflowError(堆栈溢出错误)等,一般发生这种情况,JVM会选择终止程序。
2. 示例
//堆栈溢出错误
public class TestError {
public static void recursionMethod() {
recursionMethod();// 无限递归下去
}
public static void main(String[] args) {
recursionMethod();
}
}
报错信息:
Exception in thread "main" java.lang.StackOverflowError
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
at com.TestError.recursionMethod(TestError.java:5)
5. Exception
5.1 概述
Exception是所有异常类的父类。分为非RuntimeException和RuntimeException 。
非RuntimeException
指程序编译时需要捕获或处理的异常,如IOException、自定义异常等。属于checked异常。
RuntimeException
指程序编译时不需要捕获或处理的异常,如:NullPointerException等。属于unchecked异常。一般是由程序员粗心导致的。如空指针异常、数组越界、类型转换异常等。
5.2 常用方法
package com.demo._Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* e.printStackTrace(); : 打印追踪栈帧
*
* e.getMessage(); : 获取错误信息
*
* e.getStackTrace() : 获取栈内存地址
*
*/
public class Exception_02 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("xxxxxxxxx");
} catch (FileNotFoundException e) {
// 打印追踪栈帧,(哪一行引起的错误)
e.printStackTrace();
// 获取异常信息
String msg = e.getMessage();
// xxxxxxxxx (系统找不到指定的文件。)
System.out.println(msg);
// 获取栈内存地址
// [Ljava.lang.StackTraceElement;@6d06d69c
System.out.println(e.getStackTrace());
}
}
}
5.3 TryCatch
5.3.1 第一种
package com.demo._Exception;
public class Exception_01 {
public static void main(String[] args) {
int a = 10;
int b = 0;
// 这种异常 我们通过if也可以搞定
try {
int c = a/b;
System.out.println(c);//try没有出现异常,就不会执行cathc语句
} catch (Exception e) { // 捕捉异常,需要使用异常对应的异常类或其父类(多态)
// 打印追踪栈帧
e.printStackTrace();
System.out.println("除数不能为0");
}
System.out.println("==============");
}
}
这种就是编译时异常,如果不出来,压根不能运行,这时候 我们要么抛出,要么处理
5.3.2 第二种
try{
高风险代码;
}catch(异常类 变量){
异常操作
}catch(异常类 变量){
异常操作
}
1 catch语句可以根据代码返回的异常编写多个
2 但是从上往下,必须是子类到父类,要不然会因为多态的原因,父类会把子类对象捕捉
3 从上往下可以是没有继承关系的,那样就不需要考虑顺序问题
4 多个异常,只会有一个执行,因为最先出错的语句之后,try中的代码就不再执行了
如果 多个异常中,我们的处理措施是一样的,可以直接写exception 没问题
但是 如果操作不同,就不能使用exception捕捉
比如 用户登陆,如果使用exception捕捉,你是不知道是用户名不对还是密码不对的,那么就只能提醒 “用户名或密码不对”
如果想分开提醒 到底是用户名错了还是密码错了 就只能分开捕捉
package com.demo._Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* try{
* 高风险代码;
* }catch(异常类 变量){
* 异常操作
* }catch(异常类 变量){
* 异常操作
* }
*
* 1 catch语句可以根据代码返回的异常编写多个
* 2 但是从上往下,必须是子类到父类,要不然会因为多态的原因,父类会把子类对象捕捉
* 3 从上往下可以是没有继承关系的,那样就不需要考虑顺序问题
* 4 多个异常,只会有一个执行,因为最先出错的语句之后,try中的代码就不再执行了
*
* 如果 多个异常中,我们的处理措施是一样的,可以直接写exception 没问题
*
* 但是 如果操作不同,就不能使用exception捕捉
* 比如 用户登陆,如果使用exception捕捉,你是不知道是用户名不对还是密码不对的,那么就只能提醒 "用户名或密码不对"
* 如果想分开提醒 到底是用户名错了还是密码错了 就只能分开捕捉
*
*/
public class Exception_04{
public static void main(String[] args) {
try {
// 可能会报 FileNotFoundException
new FileInputStream("xxx");
String s = null;
// 可能报空指针异常
s.equals("");
} catch(NullPointerException e1){
System.out.println("空指针");
}catch (FileNotFoundException e) {
System.out.println("找不到指定文件");
}catch(Exception e2){
// 不能把这个异常放到最上面,因为Exception可以获取大部分异常,其中包括空指针和FileNotFoundException
// 如果这个异常去上面了,就会导致 另外两个异常永远 也捕捉不到对象,因为多态的原因,exception都给捕捉了
System.out.println("其他异常");
}
}
}
5.3.3 第三种
java1.7开始,出现了一个新的写法,可以一次catch捕捉多个异常,
但是异常之间不能有继承关系,如果有继承关系,之间写父类
多个异常使用 | 隔开
异常类型 | 异常类型 | …
package com.demo._Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* java1.7开始,出现了一个新的写法,可以一次catch捕捉多个异常,
*
* 但是异常之间不能有继承关系,如果有继承关系,之间写父类
*
* 多个异常使用 | 隔开
*
* 异常类型 | 异常类型 | .....
*
*/
public class Exception_05 {
public static void main(String[] args) {
try {
// 可能会报 FileNotFoundException
new FileInputStream("xxx");
String s = null;
// 可能报空指针异常
s.equals("");
// 不能有继承关系,否则直接写父类
} catch (NullPointerException | FileNotFoundException e) {
System.out.println("出错啦~");
}
}
}
5.3.4 第四种
java1.7 异常有两个改进
1 多个异常可以在一个catch中同时捕捉, 需要使用 | 隔开
2 自动关闭资源
try(开启资源语句){
高风险代码;
}catch(异常类 变量){
处理语句;
}
5.4 Throws
5.4.1 第一种
throws 并不会把异常处理,而是一种提醒,告诉调用人员,这里有个异常/可能有个异常没有解决,你注意一下
如果你调用的方法 通过 throws 提醒了你,要么你也提醒调用你的地方,或者你 try 处理掉
package com.demo._Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* throws 并不会把异常处理,而是一种提醒,告诉调用人员,这里有个异常/可能有个异常没有解决,你注意一下
*
* 如果你调用的方法 通过 throws 提醒了你,要么你也提醒调用你的地方,或者你 try 处理掉
*
*/
public class Exception_03 {
public static void main(String[] args) {
try {
m1();
// 可以使用 FileNotFoundException 进行异常捕捉,同时也可以使用FileNotFoundException类的父类
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("=========");
}
public static void m1() throws FileNotFoundException{
m2();
}
public static void m2() throws FileNotFoundException{
m3();
}
public static void m3() throws FileNotFoundException{
// 找对应的文件,但是可能找不到出现异常,抛出 FileNotFoundException
new FileInputStream("D:/123.txt");
}
}
5.4.2 第二种
throws 也可以同时抛出多个异常,使用 逗号 隔开
因为 throws 只是一个提醒,所以无所谓顺序和继承关系
package com.demo._Exception;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* throws 也可以同时抛出多个异常,使用 逗号 隔开
*
* 因为 throws 只是一个提醒,所以无所谓顺序和继承关系
*/
public class Exception_06 {
public static void main(String[] args) throws IOException,
FileNotFoundException, Exception, NullPointerException {
}
}
5.4.3 注意-覆写不能有更宽泛的异常
子类中重写的方法,不能比父类有更宽泛的异常
也就是说,子类方法中抛出的异常,必须是父类方法抛出的异常类,或者是其子类
子类覆写后 异常 <= 父类 方法的异常
package com.demo._Exception;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 子类中重写的方法,不能比父类有更宽泛的异常
*
* 也就是说,子类方法中抛出的异常,必须是父类方法抛出的异常类,或者是其子类
*
* 子类覆写后 异常 <= 父类 方法的异常
*
*/
public class Exception_10 {
public static void main(String[] args) {
}
}
class A{
public void m1() throws IOException{
}
}
class B extends A{
@Override
// public void m1() throws IOException { // 可以
// public void m1() throws Exception { // 不行 Exception 是所有异常的父类
public void m1() throws FileNotFoundException { // 可以
}
}
5.5 Finally
5.5.1 基本使用
finally : 必须执行的语句块
我们在编程当中,会碰到程序出错的情况,但是某些情况下,尽管出错了,某些代码也仍然需要让他执行
比如关闭资源语句
1 finally 可以直接和 try… finally{} 一起使用
2 finally 不能单独使用
3 也可以和try…catch…finally… 一起使用
4 只有一种不执行 的情况
System.exit() : 关闭虚拟机
package com.demo._Exception;
/**
* finally : 必须执行的语句块
*
* 我们在编程当中,会碰到程序出错的情况,但是某些情况下,尽管出错了,某些代码也仍然需要让他执行
*
* 比如关闭资源语句
*
* 1 finally 可以直接和 try... finally{} 一起使用
* 2 finally 不能单独使用
* 3 也可以和try...catch...finally... 一起使用
* 4 只有一种不执行 的情况
* System.exit() : 关闭虚拟机
*
*/
public class Exception_07 {
public static void main(String[] args) {
try {
// 程序执行到这里,直接关闭虚拟机,参数为0表示正常关闭,非0表示异常关闭
System.exit(0);
int a = 10;
int b = 0;
int c = a/b;// 终止生命周期
} finally{
// 依然执行
System.out.println("我必须执行");
}
// 因为没有catch ,所以没有办法捕捉并处理异常,依旧会导致终止生命周期,所以 下面代码执行不到
System.out.println("main方法执行");
}
}
5.5.2 注意事项(return)
package com.demo._Exception;
public class Exception_08 {
public static void main(String[] args) {
System.out.println(m1());//11
}
public static int m1() {
int i = 10;
try {
// 这里的return语句也会执行,只不过后面优先级较高,把它给覆盖了
// 想要返回数据,如果是个运算的话,肯定要先把运算操作,运算完成,再返回
// 运算是个 i++ 当执行完之后, 要执行return语句的时候,发现有有优先级更高的,放弃return
return i++;
}finally{
// 当执行到这里的时候,上面的 i++ 就已经执行过了,所以 i 就变成 11了
System.out.println("m1中的i:"+i);
return i;
}
}
}
5.5.3 面试题
谈谈你对final的理解
1 final是什么,怎么用
2 finally是什么,怎么用
3 finalize是什么,怎么用
final理解
5.5.4 Finally 一个应用场景
package com.demo._Exception;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 由于finally必须执行,所以一般用于关闭资源
*
*/
public class Exception_9 {
public static void main(String[] args) {
FileInputStream fis = null;//局部变量必须初始化,
try {
// 打开文件流对象
//不能FileInputStream fis = new FileInputStream("e:/123.txt");fis是try语句的局部变量,finally访问不到
fis = new FileInputStream("e:/123.txt");
} catch (FileNotFoundException e) {
System.out.println("嘿~找不到啊");
}finally{
try {
if ( fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
5.6 Throw
异常源点,创建一个异常
throw new 异常类( 错误信息 );
详细用法参考 自定义异常
6.自定义异常类
6.1 定义异常类
自定义异常类
1 继承一个 已有的异常类,基本上就继承Exception即可,如果是运行时异常 就继承RuntimeException
2 无参构造
3 有参构造,传入错误信息,并把信息传递给父类 super(xxx)
package com.demo._Exception.demo;
/**
* 自定义异常类
*
* 1 继承一个 已有的异常类,基本上就继承Exception即可,如果是运行时异常 就继承RuntimeException
*
* 2 无参构造
*
* 3 有参构造,传入错误信息,并把信息传递给父类 super(xxx)
*
*/
public class UserException extends Exception {
public UserException() {
}
public UserException(String msg) {
super(msg);
}
}
6.2 应用场景
需求 : 完成用户登陆
1 如果用户名不是 admin 则 提示 用户名不存在
2 如果密码不是 root 则 提示 密码不正确
使用自定义异常类 做提示
6.2.1 用户类
JavaBean
package com.demo._Exception.demo;
/**
* 用户类
*
*/
public class User {
//用户名
private String username;
//密码
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User(String username, String password) {
super();
this.username = username;
this.password = password;
}
public User() {
super();
}
}
6.2.2业务类
package com.demo._Exception.demo;
/**
* 需求 : 完成用户登陆
*
* 1 如果用户名不是 admin 则 提示 用户名不存在 2 如果密码不是 root 则 提示 密码不正确
*
* 使用自定义异常类 做提示
*
*/
public class UserService {
public void login(User user) throws UserException {
if ("admin".equals(user.getUsername())) {
// 如果用户名是admin 就比较密码
if ("root".equals(user.getPassword())) {
System.out.println("登陆成功");
} else {
// 如果密码不正确 就抛异常 提示密码不正确
throw new UserException("密码不正确");
}
} else {
// 如果用户名不存在 就抛异常 提示用户名不存在
throw new UserException("用户名不存在");
}
}
}
6.2.3 客户端类
package com.demo._Exception.demo;
import java.util.Scanner;
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
System.out.println("请输入用户名和密码 : ");
Scanner scanner = new Scanner(System.in);
// 获取用户输入
String username = scanner.next();
String password = scanner.next();
// 封装为用户类对象
User user = new User(username, password);
// 创建业务类对象
UserService userService = new UserService();
// 登陆
try {
userService.login(user);
System.out.println("跳转到主页");
} catch (UserException e) {
// 打印追踪栈帧,没有必要了,只需要把错误提醒的信息 展示给页面即可
// 因为这里 有错 才是对的,所以这个追踪栈帧对程序员来说 没有任何价值
e.printStackTrace();
// 获取错误信息
String msg = e.getMessage();
// 传递到页面
System.out.println(msg);
}
}
}