还是先总结一下上一个模块
接下来进入模块十三
异常
public class Demo01Exception {
public static void main(String[] args) throws ParseException {
//错误Error -> StackOverflowError
//method();
//运行时期异常 -> ArrayIndexOutOfBoundsException
int[] arr1 = new int[3];
//System.out.println(arr1[4]);
/*
编译时期异常:
注意看:编译时期异常是我们代码写错了嘛?不是,当我们调用方法的时候
该方法底层给我们抛了一个编译时期异常,所以导致我们一调用此方法
一编译,就爆红了
当我们一旦触发了这个异常,jvm就会将异常信息打印到控制台上,给程序员们看
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = "2000-10-10 10:10:10";
Date date = sdf.parse(time);
System.out.println(date);
}
public static void method(){
method();
}
}
由上图可以看出,异常有一套责任链机制,若不处理虚拟机会不断向上抛出异常,直到被处理.
简单了解创建异常的方法
注意,这里的NullPointerException是运行时异常而非编译时期异常,后者必须要有相关的显性处理方式,而前者则没有这样的要求(默认如果运行时出了异常没有处理方法的话就向上抛,但是不写处理方法编译期也不会报错)
下图是编译时异常若不处理会报错的实例:
这里就看出try-catch方法的优越性了,不仅负责任,而且被精准捕获并处理的异常不会影响后方代码的继续运行.
然后是finally代码块
示例代码:
package main.java.com.exception;
public class Demo09Exception {
public static void main(String[] args) {
int result = method();
System.out.println(result);
}
public static int method() {
try {
String s = "hello";
System.out.println(s.length());//空指针异常
return 2;
} catch (Exception e) {
return 1;
} finally {
System.out.println("我一定要执行");
return 3;
}
}
}
最终输出是
5
我一定要执行
3
finally的霸权:
finally的使用场景:
1.关闭资源
2.原因:对象如果没有用了,GC(垃圾回收器)回收,用来回收堆内存中的垃圾,释放内存,但是有一些对象GC回收不了,比如:连接对象(Connection),IO流对象,Socket对象,这些对象GC回收不了,就需要我们自己手动回收,手动关闭
将来不能回收的对象new完之后,后续操作不管是否操作成功,是否有异常,我们都需要手动关闭,此时我们就可以将关闭资源的代码放到finally中
示例代码:
public class Test {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("day13_exception_object\\1.txt");
fw.write("哈哈哈");//假如这里写失败或者写成功了
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if (fw!=null){
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
有关继承的异常抛出原理:
记住:
1.如果父类中的方法抛了异常,那么子类重写之后要不要抛?
可抛可不抛
2.如果父类中的方法没有抛异常,那么子类重写之后要不要抛?
不要抛
已经解释得很详细了,不再赘述.
总之,一定要负责任,不能一直向上throws直到返回给用户,必须在中途处理掉,至少也要在返回到用户的最后一层处理掉异常.
自定义异常也非常简单
接下来进入奥博杰克特类(Object类)的内容部分
Object类
需要注意的是,重写的toString方法可以在类中用alt+insert快速生成:
接下来是equals方法
同样的,可以用alt+insert键快速生成自定义类的重写的equals方法,会考虑到所有情况,只有当自己跟自己比较以及自定义类的字段都相同时才会返回true,否则返回false.
这一部分应当直接上源码:
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*
问题1:obj直接调用name和age调用不了,因为Object接收了Person类型的对象
属于多态,多态前提下不能直接调用子类特有内容
解决问题1:向下转型
问题2:如果传递的不是Person类型,就会出现类型转换异常
解决问题2:先判断类型,如果是Person类型,再强转成Person
问题3:如果传递null呢?,就不用判断类型了,直接给false
问题4:如果传递自己呢?就不用判断非空了,也不同判断类型了,直接给true
*/
/* public boolean equals(Object obj){
if (this==obj){
return true;
}
if (obj==null){
return false;
}
if (obj instanceof Person){
Person p = (Person) obj;
return this.name.equals(p.name)&&this.age==p.age;
}
return false;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}
以自定义的Person类为例,可以看到合理的equals方法应当考虑几种情况,当传递的Obj对象不是Person类或者为null时,直接返回false;如果类型相同,则将o向下强转成Person类型(这里是为了规避多态情况下无法使用子类特有成员的限制)后对属性值进行比较.一定要熟稔这里的经典源码.
String类已经重写好了equals方法,可以直接比较字符串内容.
最后了解一下Clone方法:
public class Person implements Cloneable{
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*
问题1:obj直接调用name和age调用不了,因为Object接收了Person类型的对象
属于多态,多态前提下不能直接调用子类特有内容
解决问题1:向下转型
问题2:如果传递的不是Person类型,就会出现类型转换异常
解决问题2:先判断类型,如果是Person类型,再强转成Person
问题3:如果传递null呢?,就不用判断类型了,直接给false
问题4:如果传递自己呢?就不用判断非空了,也不同判断类型了,直接给true
*/
/* public boolean equals(Object obj){
if (this==obj){
return true;
}
if (obj==null){
return false;
}
if (obj instanceof Person){
Person p = (Person) obj;
return this.name.equals(p.name)&&this.age==p.age;
}
return false;
}*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test03 {
public static void main(String[] args) throws CloneNotSupportedException {
Person p2 = new Person("涛哥", 16);
Object o = p2.clone();
Person p3 = (Person) o;//克隆了一个新对象
System.out.println(p2==p3);//比较地址值 false
System.out.println(p2.equals(p3));//true
}
}
接下来是拓展知识,对于引用数据类型的比较,Java有两个接口
public class Student implements Comparable{
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
/*
this:代表students[i]
o:代表students[i+1]
如果students[i].getScore()-students[i+1].getScore()>0
证明数组中的前面一个对象比后面一个对象的分数高
*/
@Override
public int compareTo(Object o) {
Student s = (Student) o;
return this.getScore()- s.getScore();
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
//创建一个数组
Student[] students = new Student[3];
Student s1 = new Student("张三", 100);
Student s2 = new Student("李四", 60);
Student s3 = new Student("王五", 80);
students[0] = s1;
students[1] = s2;
students[2] = s3;
for (int j = 0; j<students.length-1;j++){
for (int i = 0;i<students.length-1-j;i++){
//如果students[i]比students[i+1]大,就排序换位置
if (students[i].compareTo(students[i+1])>0){
Student temp = students[i];
students[i] = students[i+1];
students[i+1] = temp;
}
}
}
//遍历
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
}
}
然后是Comparator(比较器)接口
public class Student implements Comparator {
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
/*
o1代表students[i]
o2代表students[i+1]
如果o1的分数大于o2的分数-> compare方法返回正整数
如果o1的分数小于o2的分数-> compare方法返回负整数
如果o1的分数等于o2的分数-> compare方法返回0
*/
@Override
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
return s1.getScore()-s2.getScore();
}
}
测试类:
public class Test01 {
public static void main(String[] args) {
//创建一个数组
Student[] students = new Student[3];
Student s1 = new Student("张三", 100);
Student s2 = new Student("李四", 60);
Student s3 = new Student("王五", 80);
students[0] = s1;
students[1] = s2;
students[2] = s3;
Student student = new Student();
for (int j = 0; j<students.length-1;j++){
for (int i = 0;i<students.length-1-j;i++){
//如果students[i]比students[i+1]大,就排序换位置
if (student.compare(students[i],students[i+1])>0){
Student temp = students[i];
students[i] = students[i+1];
students[i+1] = temp;
}
}
}
//遍历
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
}
}
注意:Comparator的定义例子中使用了匿名内部类作为返回值