第十七章 枚举和异常
1.作业回顾
//1,写一个方法判断字符串是否对称。对称的字符串。"abcdefgfedcba"
public class Day16HomeWork {
public static boolean f1(String str) {
char[] arrs = str.toCharArray();
for (int i = 0; i < arrs.length / 2; i++) {
if(arrs[i] != arrs[arrs.length - 1 - i]) {
return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(f1("abcdefgfedcba"));//true
System.out.println(f1("abcddcba"));//true
System.out.println(f1("abcccccccccca"));//false
}
}
//2,写一个方法将字符串中的大写字符转换为小写字符,小写字符转换为大写字符。
//String f1(String str)。abcdeFGH--->ABCDEfgh
public class Day16HomeWork2 {
public static String f1(String str) {
char[] arrs = str.toCharArray();
for (int i = 0; i < arrs.length; i++) {
if (arrs[i] >= 97 && arrs[i] <= 122) {
arrs[i] = (char) (arrs[i] - 32);
} else if(arrs[i] >= 65 && arrs[i] <= 90){
arrs[i] = (char) (arrs[i] + 32);
}else {
//do nothing
}
}
return new String(arrs);
}
public static void main(String[] args) {
System.out.println(f1("abcdeFGH"));//ABCDEfgh
}
}
2.枚举
在Java编程语言中,可以使用enum关键字定义枚举类型。
例如,可以将星期几的枚举类型指定为:
//使用enum关键字声明一个枚举类
enum Day{
//枚举类的值,类型是Day
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
}
public class Day1702 {
//枚举是一种特殊的类型,它的值是一组预定义的常量。
//d是一个枚举类型的变量,它的类型是Day,Day.MONDAY是一个枚举值
public static void main(String[] args) {
Day d = Day.SUNDAY;
switch (d) {
case SUNDAY:
System.out.println("星期日");
break;
case MONDAY:
System.out.println("星期一");
break;
case TUESDAY:
System.out.println("星期二");
break;
case WEDNESDAY:
System.out.println("星期三");
break;
case THURSDAY:
System.out.println("星期四");
break;
case FRIDAY:
System.out.println("星期五");
break;
case SATURDAY:
System.out.println("星期六");
break;
}
}
}
所有的枚举类都继承于java.lang.Enum类。
编译器在创建枚举时会自动添加一些特殊方法。
public class Day1703 {
public static void main(String[] args) {
Day d = Day.SUNDAY;
//所有的枚举都隐式继承于java.lang.Enum,java.lang.Enum继承于java.lang.Object
System.out.println(d instanceof Object);//true
System.out.println(d instanceof Enum);//true
//遍历枚举
//每一个枚举类都有一个静态的values方法,它是编译器加的
//静态的values方法按照声明的顺序返回一个包含所有枚举值的数组
Day[] arr = Day.values();
for(Day day : arr) {
System.out.println(day);
}
System.out.println("******************");
//静态的valueOf(String)方法,将字符串转换成枚举值,它是编译器加的
Day d1 = Day.valueOf("SUNDAY");//将字符串SUNDAY转换为Day.SUNDAY
System.out.println(d1);//SUNDAY
//枚举值转换为字符串
//name(),toString()是实例方法,继承于java.lang.Enum
System.out.println(d1.name());//将Day.SUNDAY转换为字符串SUNDAY
System.out.println(d1.toString());//将Day.SUNDAY转换为字符串SUNDAY
System.out.println("******************");
//获取枚举的序号
System.out.println(Day.SUNDAY.ordinal());//0
System.out.println(Day.MONDAY.ordinal());//1
//枚举的比较
Day m1 = Day.MONDAY;
Day m2 = Day.MONDAY;
System.out.println(m1 == m2);//true
System.out.println(m1.equals(m2));//true
}
}
3.异常的基本概念
main方法将抛出一个异常,因为在一个null对象上调用了toString方法。
public class Day1704 {
public static void main(String[] args) {
String str = null;
//在一个null对象上调用方法会导致NullPointerException,空指针异常
str.toString();
//下面的语句不会得到执行,因为异常会导致程序执行终止
System.out.println("hello");
}
}
3.1 异常的传播
public class Day1705 {
public static void method1() {
method2();
}
public static void method2() {
String str = null;
str.toString();
}
public static void main(String[] args) {
method1();
}
}
查看栈的轨迹,method2中抛出的异常传递到method1,然后传递到main。
使用try catch处理异常。
public class Day1705 {
public static void method1() {
method2();
System.out.println("method1");
}
public static void method2() {
String str = null;
//尝试执行try代码块,如果捕获到了NullPointerException,就执行catch块
try {
str.toString();
System.out.println("hello");//不会执行
}catch(NullPointerException e) {
System.out.println("捕获到异常");
}
System.out.println("method2");
}
public static void main(String[] args) {
method1();
System.out.println("main");
}
}
因为method2处理了异常,因此异常不再传递给method1。因此method1和main方法将不受method2中异常的任何影响,打印语句将会执行。这是异常处理的基本概念。但是,method2中抛出异常后的语句将不会执行。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
//异常是预先的处理机制
public class Day1706 {
public static void main(String[] args) {
System.out.println("请输出日期字符串:yyyy-MM-dd");
Scanner s = new Scanner(System.in);
//用户输入的日期字符串
String str = s.nextLine();
//yyyy-MM-dd
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
//如果str和预期额格式不匹配,将解析错误,并抛出ParseException
Date date = sdf.parse(str);
System.out.println(date);
} catch (ParseException e) {
System.out.println("格式不正确,请检查");
}
}
}
3.2 使用finally执行代码
class connection{
public void open() {
System.out.println("打开连接");
}
public void close() {
System.out.println("关闭连接");
}
}
public class Day1707 {
public static void f(connection con) {
try{
con.open();//打开连接
//使用连接做一些工作
String str = null;
str.toString();
}catch(NullPointerException e){
System.out.println("捕获异常");
}finally {//不管有没有异常产生,finally块总是会执行
con.close();//关闭连接
}
}
public static void main(String[] args) {
connection con = new connection();
f(con);
}
}
即使在try和catch块中使用了return语句,finally依然会执行
class connection{
public void open() {
System.out.println("打开连接");
}
public void close() {
System.out.println("关闭连接");
}
}
public class Day1707 {
public static void f(connection con) {
try{
con.open();//打开连接
return;
}catch(NullPointerException e){
System.out.println("捕获异常");
}finally {//即使在try和catch块中使用了return语句,finally依然会执行
con.close();//关闭连接
}
}
public static void main(String[] args) {
connection con = new connection();
f(con);
}
}
不带catch的try和finally也是允许的,但会抛出异常。
class connection{
public void open() {
System.out.println("打开连接");
}
public void close() {
System.out.println("关闭连接");
}
}
public class Day1707 {
public static void f(connection con) {
try{
con.open();//打开连接
String str = null;
str.toString();//出现异常,执行finally块,然后将异常抛出给调用者
}finally {
con.close();//关闭连接
}
}
public static void main(String[] args) {
connection con = new connection();
f(con);
System.out.println("main");//不会执行
}
}
4.异常体系
Throwable是异常体系的最顶层类。Error(错误)和Exception(异常)继承于Throwable类。
当java虚拟机中发生动态链接故障,或其他硬件故障时,虚拟机会抛出一个Error。程序通常不会捕获或抛出Error。当Error发生时,在代码中没法做任何处理。比如StackOverFlowError和OutOfMemoryError。 在代码中不能处理Error,但是可以处理Exception。大多数程序抛出并捕获Exception类派生的对象。一个Exception表示发生了问题,但这不是一个严重的系统问题。大多数程序都会抛出并捕获Exceptions而不是Errors。
java平台定义了Exception类的许多子类。这些子类代表可能发生的各种类型的异常。例如ParseException表示解析出现异常,NullPointerException表示在一个null对象上调用了方法。
Exception分为两种:运行时异常(RuntimeException)和受检查异常(CheckedException)。
RuntimeException和所有继承于RuntimeException的子类称为运行时异常。运行时异常的例子:NullPointerException。 Exception的子类除了运行时异常,其他的都是受检查异常。受检查异常的例子: ParseException。
编译能通过,但是运行时发生的异常是运行时异常,NullPointerException。
编译不能通过,需要处理的异常是受检查异常,ParseException。
public class Day1708 {
public static void f1() {
//NullPointerException是运行时异常,编译时不会提醒需要捕获
String str = null;
str.toString();
}
public static void f2() {
//yyyy-MM-dd
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//在编译时提醒需要捕获的是受检查异常
try {
Date date = sdf.parse("12hji6");
} catch (ParseException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
}
}
运行时异常不需要捕获,因为产生运行时异常是因为代码编写问题,可以通过规范代码(增加条件判断)来避免运行时异常的发生。
public static void f1(String str) {
//通过规范代码(增加条件判断)来避免运行时异常的发生
if(String str != null;){
str.toString();
}
}
受检查异常需要在程序中做处理,因为这是没办法避免的。比如ParseException,转换的字符串可能来自于客户的输入,这个输入是事先无法预期的。
5.自定义异常
如果需要定义运行时异常,那么需要继承RuntimeException。
如果需要定义受检查异常,需要继承Exception。
5.1 使用运行时异常
//银行账户
public class Amount {
//货币类型
private String currency;
//金额
private int amount;
public Amount(String currency, int amount) {
super();
this.currency = currency;
this.amount = amount;
}
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
}
//运行时异常
//货币类型不匹配异常
public class CurrencyDoNotMatchException extends RuntimeException{
//message代表异常信息
public CurrencyDoNotMatchException(String message) {
super(message);
}
}
public class AmountAdder {
public static Amount add(Amount m1, Amount m2) {
if(m1.getCurrency().equals(m2.getCurrency())) {
return new Amount(m1.getCurrency(), m1.getAmount() + m2.getAmount());
}else {
//如果账户类型不匹配,就抛出一个CurrencyDoNotMatchException类的实例
throw new CurrencyDoNotMatchException("货币类型不匹配:m1:" + m1.getCurrency() + ", m2:" + m2.getCurrency());
}
}
public static void main(String[] args) {
AmountAdder.add(new Amount("DOLLAR", 5),new Amount("CNY", 5));
}
}
5.2 使用受检查异常
//受检查异常
//货币类型不匹配异常
public class CurrencyDoNotMatchException extends Exception{
//message代表异常信息
public CurrencyDoNotMatchException(String message) {
super(message);
}
}
public class AmountAdder {
//此方法声明其可能抛出一个CurrencyDoNotMatchException
public static Amount add(Amount m1, Amount m2) throws CurrencyDoNotMatchException {
if(m1.getCurrency().equals(m2.getCurrency())) {
return new Amount(m1.getCurrency(), m1.getAmount() + m2.getAmount());
}else {
//如果账户类型不匹配,就抛出一个CurrencyDoNotMatchException类的实例
throw new CurrencyDoNotMatchException("货币类型不匹配:m1:" + m1.getCurrency() + ", m2:" + m2.getCurrency());
}
}
//main方法处理的方式有两种
//1.抛出异常
// public static void main(String[] args) throws CurrencyDoNotMatchException {
// AmountAdder.add(new Amount("DOLLAR", 5),new Amount("CNY", 5));
// }
//2.捕获异常
public static void main(String[] args) {
try {
AmountAdder.add(new Amount("DOLLAR", 5),new Amount("CNY", 5));
} catch (CurrencyDoNotMatchException e) {
// System.out.println("捕获到异常,货币类型不匹配");
//打印异常调用栈的信息(实际中常用)
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.text.ParseException;
public class Day1709 {
//f1方法声明其将抛出多个异常
public static void f1() throws IOException, ParseException {
int a = 0;
if(a == 0) {
throw new IOException("IOException");
}else {
throw new ParseException("解析错误", 1);
}
}
// public static void main(String[] args) {
// //main方法需要捕获多个异常
// try {
// f1();
// } catch (IOException e) {
// e.printStackTrace();
// } catch (ParseException e) {
// e.printStackTrace();
// }
// }
public static void main(String[] args) {
//main方法可以捕获父类异常
try {
f1();
}catch(Exception e){
e.printStackTrace();
}
}
}
6.练习
1.写一个TestException类,在main方法中接受两个命令行参数,将他们转换成整数,并用第二个数除以第一个数,打印结果。测试一下情况,某个参数不是数字,第一个参数为0.