Java第十三课. 异常&集合框架
回顾
1. Object:toString()/equals()/clone():浅克隆,深克隆:实现克隆接口的对象本身,也会把克隆对象中关联的对象一起克隆
2. Finalize():每个对象都有:是垃圾回收器调用;当垃圾回收器检测到一个对象很久都没有引用的时候,回去调用该对象的Finalize()进行回收;
3. 日期时间对象:Date(掌握获取当前系统时间) Calendar:api里面获取/设置时间的方法
4. Date与Calendar互转:getTime() setTime()->Date转Calendar
5. SimpleDateFormat辅助类:(创建对象时,有参构造放的是格式:yyyy年MM月dd日 HH时mm分ss秒)将日期格式化输出 format()->Date转字符串 parse()->字符串转Date
1.Throwable
2. 异常Exception
2.1 Exception与Error的概念、区别
• 异常(Exception):异常指的是程序运行时发生的不正常事件;异常能够被程
序处理,保证程序继续运行下去;例如除数为0、文件没有找到、输入的数字格
式不对……
• 错误(Error):错误程序没法处理,例如内存泄漏。发生错误后,一般虚拟机会
选择终止程序运行,程序员需要修改代码才能解决相关错误;
2.2 常见的异常
1. java.lang.NullPointerException 空指针异常
2. ArrayIndexOutOfBoundsException 数组越界异常
3. java.lang.ArithmeticException: / by zero 算术异常(包含了除数不能为0的异常)
4. java.lang.ClassCastException 类型转换异常
问题引入:
出现了上面的问题带来的结果是什么?
1. 控制台(界面),上会显示异常的信息,给用户带来不来的体验
2. 发生异常的时候,程序立即结束,后续代码也无法执行
基于以上2点,我们要做异常的处理,同时还要知道有哪些常见的异常类型,这样才能针对性的处理
2.3 异常处理
2.31 方式1:try-catch-finally
try{
//监视作用
}catch (异常){
//捕获并处理
}finally{
//不管是否有异常发生,finally语句块中的语句始终保证被执行
//finally语句块主要用于解决资源泄露问题
}
可能发生异常的代码块,选中,右键 找到surround with ->try catch
2.311 try-catch
try {
System.out.println(string.length());
//2.java.lang.ArrayIndexOutOfBoundsException
//数组下标越界异常
array[3]=1;
//3. java.lang.ArithmeticException:
//算术异常(包含了除数不能为0)
System.out.println(1/0);
//4.java.lang.ClassCastException
//类转换异常
Object obj=apple;
Dog dog=(Dog)obj;
}catch (Exception e) {
System.out.println("异常了");
2.312 try-catch-catch…
try里面可以放很多可能发生异常的代码,但是一次只能捕捉一个来处理;
注意:try的后面可以有多重catch,但是注意层级关系:越高越往下写。
2.313 finally->总会执行
①try catch
②try 多个catch
③try..finally
④try catch finally
try {
System.out.println(string.length());
//2.java.lang.ArrayIndexOutOfBoundsException
//数组下标越界异常
array[3]=1;
//3. java.lang.ArithmeticException:
//算术异常(包含了除数不能为0)
System.out.println(1/0);
//4.java.lang.ClassCastException
//类转换异常
Object obj=apple;
Dog dog=(Dog)obj;
}catch (Exception e) {
System.out.println("异常了");
}finally {
//总会执行->释放资源
sc.close();
}
System.out.println("这是我正常执行的代码");
异常了
这是我正常执行的代码
2.314 finally注意事项
1. finally块前有return语句,finally依然被执行;
public static void main(String[] args) {
//finally块前有return语句,finally依然被执行;
try {
System.out.println(1/0);
} catch (Exception e) {
System.out.println("发生算术异常,除数不能为0");
return;//结束方法
}finally {
System.out.println("finally代码块");//于return之前执行
}
System.out.println("main方法运行结束");//不执行,11行执行完return表示结束方法运行
}
发生算术异常,除数不能为0
finally代码块
2. finally块前有System.exit(0)语句,finally不被执行;
public static void main(String[] args) {
//finally块前有System.exit(0)语句,finally不被执行;
try {
System.out.println(1/0);
} catch (Exception e) {
System.out.println("发生算术异常,除数不能为0");
System.exit(0);//结束jvm运行
}finally {
System.out.println("finally代码块");//不执行
}
System.out.println("main方法运行结束");//不执行
}
发生算术异常,除数不能为0
两个经典题:
1. 没有异常的情况下,测试return语句在try和finally中执行顺序
先执行try中的return,再执行finally中的return,最终以finally中的return 为准。
//先执行try中的return,再执行finally中的return,最终以finally中的return 为准。
@SuppressWarnings("finally")
public static int method1() {//结果0
int i=1;
try {
i++;
return i;//①先执行
} catch (Exception e) {
}finally {
return 0;//②也会执行,而且是在try的return执行完再执行,真正返回给调用处的结果
}
}
public static void main(String[] args) {
System.out.println(method1());//0
}
2. try里面有return ,finally里面没有return ,那么结果以try里面为准
@SuppressWarnings("finally")
public static int method2() {//结果2
int i=1;
try {
i++;
return i;//①这里有个return,会记录i的值=2 ,③返回结果,是之前记录的值
} catch (Exception e) {
}finally {
i++;//②执行finally
}
return 0;//在上面的代码没有发生异常的情况下,这里是不执行
}
public static void main(String[] args) {
System.out.println(method2());//2
}
2.32 方式2:throws(抛出异常)
异常一直向上抛,最顶点就是JVM;
可以称为声明了一个异常;可以理解为抛出异常,抛出给调用者,谁调用他就抛给谁;
2.33 方式3:throw :手动引发异常(了解)
手动引发异常,发生在方法中(我认为满足特定的条件就是异常发生了)
2.34 方式4:自定义异常(了解)
1.创建一个自定义异常类:
①继承 Exception(或其子类);
②添加一个带参数构造;
③重写 getMessage()-可写可不写
public class AgeException extends Exception{
private static final long serialVersionUID = 5332603740831531025L;
public AgeException(String message) {
//调用父类的构造方法
super(message);
}
public AgeException() {
// TODO Auto-generated constructor stub
}
//也可以重写父类的方法
@Override
public String getMessage() {
// TODO Auto-generated method stub
return super.getMessage();
}
}
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 AgeException {
if (age<=0) {
//手动
throw new AgeException("年龄不合法,至少>0");
}
this.age = age;
}
public static void main(String[] args) throws AgeException {
Student student = new Student();
student.setAge(0);
}
}
2.4 异常小结:
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
• 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
public static void main(String[] args) {
Class.forName("Apple");
}
//Exception, SQLException 非运行时异常;
• 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
//NullPointerException
• 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
//OutOfMemoryError, 内存溢出、StackOverflowError堆栈溢出
所有的异常类是从 java.lang.Exception 类继承的子类:
1. 分类:编译时异常、运行时异常
2. Exception异常:一般是逻辑或者数据内容的出错
Error错误:jvm底层的各种异常:内存溢出、堆栈溢出。
3.所有的编程语言都需要进行异常处理,针对是“运行期”。
4.处理异常作用:预先准备,避免程序因为异常而“终止”。
5.所有的异常类的父类的 Java.lang.Exception
6.Exception的四个直接子类
SQLException--数据库异常
ClassNotFountdException--找不到类
IOException--流异常
RuntimeException-运行期异常
7.RuntimeException-运行期异常的常见子类
ArithmeticException 算术异常,如:除数为0
IllegalArgumentException 方法接收到非法参数
ArrayIndexOutOfBoundsException 数组下标越界
NullPointerException 访问空引用
ClassNotFoundException 不能加载所需的类
NumberFormatException 字符串转换数字失败
8.异常处理
方式1:try{
//监视作用
}catch(){
//捕获作用 做处理
}finally{
//无论try中发生什么,finally都会执行,除非特殊情况
}
注意:1. try的后面可以有多个catch,但是注意层级关系:类层次越低的越上写,越高越往下写。
2. finally不是永远都会执行。有些特殊情况:finally语句中异常,退出程序...
方式2:throws语句用于抛出异常:不在方法中处理异常,向上抛。
方式3:throw语句用于手工抛出异常,很少用
2.5 常见面试题
2.51 异常Exception和错误error的区别
都是继承于Throwable ;
Exception异常:可以是可被控制(checked) ,是运行过程中的不正常事件,可以被异常机制处理,程序能够继续运行下去;
Error错误:jvm底层的各种问题:内存溢出,堆栈溢出。错误不能够被处理,发生错误后,程序就终止,程序员需要修改源代码才能解决错误;
2.52 try catch fianlly关键字的作用
try: 监视作用
catch: 捕获异常,处理异常
finally: 不管是否出现异常,都会执行 finally 中的代码块, finally 块是可选的,可视具体情况决定是否添加; finally 块必须和 try 块一起使用,不能单独存在。
3. 集合框架
[概念]: 特指java工具包(java.util)提供的对集合的一系列操作类
[作用]:1. 对静态数组的增强(集合长度可变. 支持链表,无序集以及映射集迭代器)
2.支持泛型(jdk1.5)
顶层接口:
List接口:有序集合
Collection -> Set接口:无序集合
Map 接口:映射集合
总体说明:主要的是学习集合框架中常见的接口,接口的实现类,以及对应的方法和应用场景
3.1 List接口有序集合实现类
3.1.1 ArrayList
先从最简单的一个类入手:ArrayList
构造方法摘要 |
---|
ArrayList() 构造一个初始容量为 10 的空列表。 |
ArrayList(Collection<? extends E> c) 构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。 |
ArrayList(int initialCapacity) 构造一个具有指定初始容量的空列表。 |
重要的方法:
boolean | add(Ee) 将指定的元素添加到此列表的尾部。 |
---|---|
void | add(int index,Eelement) 将指定的元素插入此列表中的指定位置。 |
void | clear() 移除此列表中的所有元素。 |
boolean | contains(Objecto) 如果此列表中包含指定的元素,则返回 true。 |
E | get(int index) 返回此列表中指定位置上的元素。 |
E | remove(int index) 移除此列表中指定位置上的元素。 |
int | size() 返回此列表中的元素数。长度 |
Object[] | toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组。 |
常见的方法:
public class TestArrayList01 {
public static void main(String[] args) {
// 创建一个ArrayList对象
ArrayList list01=new ArrayList();
//向对象中增加一个元素 参数类型Object
list01.add(1);
list01.add("我喜欢学习,不学习就难受");
list01.add(null);
list01.add(12.25);
//能否添加重复的元素? 能
list01.add("我喜欢学习,不学习就难受");
System.out.println(list01.size());//5
//将集合中的某个元素删除
//索引0表示的是集合中的第一个元素
list01.remove(0);
System.out.println(list01.size());//4
//添加一个Emp对象
list01.add(new Employee());//自定义对象 new
//继续删除
System.out.println(list01.remove(new Employee()));//false 两次new 不是同一个对象
System.out.println(list01.size());//5
if (list01.contains(12.25)) {
System.out.println("找到了~");
}
//遍历集合并输出
for (int i = 0; i < list01.size(); i++) {
System.out.println(list01.get(i));
}
}
}
我们发现,刚刚的代码有效的补充了我们之前案例中数组的不足:
1. 长度可变;
2. 数组定义,数组类型是固定,Arraylist中类型是Object,随意添加各种类型;
3. ArrayList中提供了大量已经实现的方法例如添加add,remove等等,让我们更多的时间关注业务,而不是算法;
[一个问题]:
add(任何类型)->当一个对象存放到集合中的时候,这个对象就自动被装箱(Object);
public class EmpManage2 {
//有几个员工?
//可以使用数组解决,但是如果数组一旦定义长度固定,以后不好扩展
//private Employee emps[]=new Employee[20];//默认值是null
//用集合解决
private ArrayList emps=new ArrayList();
/**
* 增加的操作,通常写成一个方法
* @param emp 员工对象
*/
public void addEmp(Employee emp) {
emps.add(emp);
System.out.println("增加成功");
}
/**
* 员工的显示
*/
public void showEmp() {
System.out.println("*******公司员工列表如下:**********");
for (Employee employee : emps) {
if (employee==null) {
break;
}
System.out.println(employee);
}
}
3.1.2 泛型
泛型 本质上认为是一种类型检查的机制,存放的元素到集合要进行类型的检查,取出来不需要强制转型了;
public class TestArrayList02 {
public static void main(String[] args) {
ArrayList<String> list01=new ArrayList<String>();
//list01.add(1);无法通过编译
//list01.add(new Object());
list01.add("abc");
//遍历
for (String string : list01) {
System.out.println(string);
}
}
}
刚才的查询员工加上泛型:
注意:
泛型里只能放引用类型,不能放基本数据类型;
3.2 LinkedList
构造方法摘要 |
---|
LinkedList() 构造一个空列表。 |
LinkedList(Collection<? extends E> c) 构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列。 |
方法摘要 | |
---|---|
void | addFirst(E e) 将指定元素插入此列表的开头。 |
void | addLast(E e) 将指定元素添加到此列表的结尾。 |
E | getFirst() 返回此列表的第一个元素。 |
E | getLast() 返回此列表的最后一个元素。 |
E | removeFirst() 移除并返回此列表的第一个元素。 |
E | removeLast() 移除并返回此列表的最后一个元素。 |
E | peek() 获取但不移除此列表的头(第一个元素)。 |
E | poll() 获取并移除此列表的头(第一个元素) |
E | pop() 从此列表所表示的堆栈处弹出一个元素。 |
void | push(E e) 将元素推入此列表所表示的堆栈。 |
E | set(int index, E element) 将此列表中指定位置的元素替换为指定的元素。 |
3.21 LinkedList新增的方法
public class TestLinkedList {
public static void main(String[] args) {
//构建LinkedList
LinkedList<String> list=new LinkedList<String>();
list.add("abc");
list.add("abc");
System.out.println(list.size());//2
//删除
list.remove(0);
System.out.println(list.size());//2
//差别来了
list.addFirst("first");
list.addLast("last");
//输出list
System.out.println(list);//LinkedList类重写了toString()
System.out.println(list.toString());
//删除
list.removeFirst();
list.removeLast();
System.out.println(list);
//其他方法和ArrayList几乎类似...
}
}
3.22 栈操作
public class TestLinkedList2 {
public static void main(String[] args) {
//构建LinkedList
LinkedList<String> list=new LinkedList<String>();
System.out.println("------模拟堆栈结构 先进后出--------");
//push(E e)将元素推入此列表所表示的堆栈。换句话说,将该元素插入此列表的开头。
list.push("A");
list.push("B");
list.push("C");
//输出
System.out.println(list);//[C, B, A]
//获取堆栈的第一个
System.out.println(list.get(0));//C
//peek() 获取但不移除此列表的头(第一个元素)。
System.out.println(list.peek());
//poll() 获取并移除此列表的头(第一个元素)
System.out.println(list.poll());//C
System.out.println(list);//[B, A]
//pop() 从此列表所表示的堆栈处弹出一个元素。和poll() 类似
System.out.println(list.pop());
System.out.println(list);//[A]
}
}
3.23 队列
public class TestLinkedList {
public static void main(String[] args) {
//构建一个LinkedList对象
LinkedList<Object> list=new LinkedList<Object>();
System.out.println("-----模拟队列 先进先出-------");
list.add("A");
list.add("B");
list.add("C");
System.out.println(list.toString());
//获取 叫号
//System.out.println("叫号:"+list.get(0));//获取
System.out.println("叫号:"+list.pop());//获取第一个并且从列表删除该元素
System.out.println("叫号:"+list.pop());//获取第一个并且从列表删除该元素
System.out.println(list);
}
}
3.24 [面试题]LinkedList与ArrayList相比
1. 相似点:都是List接口的实现类;可以在指定位置上插入元素,通过索引访问元素,都允许空元素null,元素可以重复,元素有序(索引),都是非线程同步,线程不安全;
2. 不同点: ①内存数据结构不一致:ArrayList是可变动态数组数据结构, LinkedList基于链表数据结构;
②对于查询和修改,ArrayList效率更高;
③对于插入和删除, LinkedList效率更高;
④ArrayList, LinkedList提供的API方法基本相同,只是LinkedList增加了很多方法: addFirst(),addLast(),removeFirst(),removeLast()
4. 练习
使用ArrayList实现:查询所有员工,增加,查找单个员工,删除,修改和之前的菜单功能
0 退出 1增加员工 2 显示所有员工 3 根据员工编号查询员工 4 删除员工 5 修改员工
public class Employee {
//员工编号
private int empNo;
//姓名
private String empName;
//部门
private String deptName;
//工资
private double salary;
public int getEmpNo() {
return empNo;
}
public void setEmpNo(int empNo) {
this.empNo = empNo;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Employee(int empNo, String empName, String deptName, double salary) {
super();
this.empNo = empNo;
this.empName = empName;
this.deptName = deptName;
this.salary = salary;
}
public Employee() {
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Employee [empNo=" + empNo + ", empName=" + empName + ", deptName=" + deptName + ", salary=" + salary
+ "]";
}
}
public class EmpManage {
//有几个员工?
//可以使用数组解决,但是如果数组一旦定义长度固定,以后不好扩展
//private Employee emps[]=new Employee[20];//默认值是null
//用集合解决
private ArrayList<Employee> emps=new ArrayList<Employee>();
Scanner scanner=new Scanner(System.in);
/**
* 显示主菜单
*/
public void showMenu() {
while(true) {
System.out.println("请输入您的选择 0 退出 1增加员工 2 显示所有员工 3 根据员工编号查询员工 4 删除员工 5 修改员工");
int choice=scanner.nextInt();
switch (choice) {
case 0:
System.out.println("系统退出,再见~");
System.exit(0);
break;
case 1:
inputEmp();
break;
case 2:
showEmp();
break;
case 3:
//查找的输入方法
findMenu();
break;
case 4:
//删除的输入方法
deleMenu();
break;
case 5:
//修改员工的菜单方法
updateMenu();
break;
default:
break;
}
}
}
/**
* 修改员工的菜单方法
*/
private void updateMenu() {
System.out.println("**********请输入要修改的 员工信息***********");
int empNo=scanner.nextInt();
String empName=scanner.next();
String deptName=scanner.next();
double salary=scanner.nextDouble();
Employee emp=new Employee(empNo, empName, deptName, salary);
//调用修改的方法
int flag=updateEmp2(emp);
if (flag==1) {
System.out.println("修改成功!");
}else {
System.out.println("修改失败!");
}
}
/**
* 修改员工的方法1
* @param emp 传进来新的值
*/
private int updateEmp(Employee emp) {
int flag=0;//修改失败
//遍历集合
for (int i = 0; i < emps.size(); i++) {
//从集合中获取的每一个对象
Employee emp1=emps.get(i);
if (emp1.getEmpNo()==emp.getEmpNo()) {
//修改
emp1.setEmpName(emp.getEmpName());
emp1.setDeptName(emp.getDeptName());
emp1.setSalary(emp.getSalary());
flag=1;
break;
}
}
return flag;
}
/**
* 修改员工的方法2
* @param emp 传进来新的值
*/
private int updateEmp2(Employee emp) {
int flag=0;//修改失败
//遍历集合
for (int i = 0; i < emps.size(); i++) {
//从集合中获取的每一个对象
Employee emp1=emps.get(i);
if (emp1.getEmpNo()==emp.getEmpNo()) {
//修改
emps.set(i, emp);
flag=1;
break;
}
}
return flag;
}
/**
* 删除的输入方法
*/
private void deleMenu() {
System.out.println("**********请输入要删除的 员工编号***********");
int empNo=scanner.nextInt();
int flag=deleEmp(empNo);
if (flag==1) {
System.out.println("删除成功!");
}else {
System.out.println("删除失败!");
}
}
/**
* 根据员工编号删除员工
* @param empNo
*/
private int deleEmp(int empNo) {
int flag=0;//删除失败
for (int i = 0; i < emps.size(); i++) {
if (emps.get(i).getEmpNo()==empNo) {
emps.remove(i);
//emps.remove(emps.get(i));
flag=1;
break;
}
}
return flag;
}
/**
* 根据员工编号查询员工的菜单方法
*/
private void findMenu() {
System.out.println("**********请输入要查找的 员工编号***********");
int empNo=scanner.nextInt();
int flag=findEmp(empNo);
if (flag==0) {
System.out.println("查无此人!");
}
}
/**
* 根据员工编号查询员工
*/
private int findEmp(int empNo) {
int flag=0;
for (Employee employee : emps) {
if (employee.getEmpNo()==empNo) {
System.out.println(employee);
flag=1;
break;
}
}
return flag;
}
/**
* 输入的方法
*/
public void inputEmp() {
System.out.println("**********请输入 员工编号,姓名,部门,基本工资*********");
int empNo=scanner.nextInt();
String empName=scanner.next();
String deptName=scanner.next();
double salary=scanner.nextDouble();
Employee emp=new Employee(empNo, empName, deptName, salary);
//调用增加的方法
addEmp(emp);
}
/**
* 增加的操作,通常写成一个方法
* @param emp 员工对象
*/
public void addEmp(Employee emp) {
emps.add(emp);
System.out.println("增加成功");
}
/**
* 员工的显示
*/
public void showEmp() {
System.out.println("*******公司员工列表如下:**********");
for (Employee employee : emps) {
System.out.println(employee);
}
}
}
测试类:
public class TestEmp {
public static void main(String[] args) {
EmpManage empManage=new EmpManage();
empManage.showMenu();
}
}