回顾与重点
模块十二回顾
-
权限修饰符:
public → protected → 默认 → private
a. 构造一般用public:便于new对象
b. 成员方法一般用public:便于调用
c. 属性一般用private:封装思想
-
final:最终的
a. 修饰类:不能被继承
b. 修饰方法:不能被重写
c. 修饰局部变量:不能二次赋值
d. 修饰对象:地址值不能改变,但是对象中的属性值可以改变
e. 修饰成员变量:需要手动赋值,不能二次赋值
-
代码块:
a. 构造代码块:
{ }
优先于构造方法执行,每new一次,构造代码块就执行一次
b. 静态代码块:
static { }
优先于构造代码块和构造方法执行,只执行一次。
静态代码块 > 构造代码块 > 构造方法 → 从执行顺序上来看
-
匿名内部类:
a. 格式1:
new 接口/抽象类() { 重写方法 }.重写的方法名();
b. 格式2:
接口/抽象类 对象名 = new 接口/抽象类() { 重写的方法 } 对象名.重写的方法名();
模块十三重点
- 分清楚什么是编译时期异常,什么是运行时期异常
- 知道处理异常的两种方式
- 知道finally关键字的使用场景
- 知道Object是啥
- 知道Object中toString以及equals方法的使用
- 知道重写Object中的toString以及equals方法的作用
第一章 API文档
-
什么叫做API: Application Programming Interface,简称API,又称之为应用编程接口。
即,定义出来的类以及接口,以及其中的方法等。
-
为了方便我们去查询开发好的接口以及类,以及其中的方法,会对应提供一个文档 → API文档
-
API文档作用:查询我们要使用的对象,以及方法,是我们程序员的”字典“
设置任务栏快捷菜单
JDK API文档使用
第二章 异常
2.1 异常介绍
概述:代码出现了不正常的现象;在Java中,异常就是一个一个的类
public class Demo01Exception {
public static void main(String[] args) {
// 错误 Error → StackOverflowError
// method();
// 运行时期异常 → ArrayIndexOutOfBoundsException
int[] arr1 = new int[3];
// System.out.println(arr1[4]);
/*
编译时期异常
注意看:编译时期异常是我们代码写错了吗?不是,当我调用方法的时候,该方法底层给我们抛了一个编译时期异常,
所以导致我们一编译,就爆红
当我们一旦触发这个异常,jvm就会将异常信息打印到控制台上,给程序员们看
*/
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = "2000-10-10 10:10:10";
// Date date = simpleDateFormat.parse(time);
// System.out.println(date);
}
public static void method() {
method();
}
}
2.2 异常出现的过程
public class Demo02Exception {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
method(arr);
}
public static void method(int[] arr) {
System.out.println(arr[6]);
System.out.println("我想要行");
}
}
2.3 创建异常对象(了解)
创建异常对象只是为了后面学习如何处理异常,其他的暂时没有啥意义。
- 关键字:throw
- 格式:throw new 异常
public class Demo03Exception {
public static void main(String[] args) {
String s = "a.tx1t";
method(s);
}
public static void method(String s) {
if (!s.endsWith(".txt")) {
// 故意创建异常对象,用throw说明此处有异常
throw new NullPointerException();
}
System.out.println("我要被执行了");
}
}
2.4 异常处理方式(重点)
2.4.1 异常处理方式一_throws
-
格式:在方法参数和方法体之间位置上写
throw 异常
-
意义:处理异常
将异常异常往上抛
import java.io.FileNotFoundException;
public class Demo04Exception {
public static void main(String[] args) throws FileNotFoundException {
String s = "a.txt1";
add(s); // 添加功能
delete(); // 删除功能
update(); // 修改功能
find(); // 查询功能
}
private static void add(String s) throws FileNotFoundException {
System.out.println("添加功能");
if (!s.endsWith(".txt")) {
// 故意制造异常
throw new FileNotFoundException("文件找不到");
}
System.out.println("文件执行了");
}
private static void find() {
System.out.println("查询功能");
}
private static void update() {
System.out.println("修改功能");
}
private static void delete() {
System.out.println("删除功能");
}
}
2.4.2 异常处理方式一_throws多个异常
-
格式:throw 异常1,异常2…
import java.io.FileNotFoundException; import java.io.IOException; public class Demo05Exception { public static void main(String[] args) throws FileNotFoundException, IOException { String s = "a.txt1"; add(s); // 添加功能 delete(); // 删除功能 update(); // 修改功能 find(); // 查询功能 } private static void add(String s) throws FileNotFoundException, IOException { System.out.println("添加功能"); if (s==null) { // 故意制造异常 throw new IOException("IO异常"); } if (!s.endsWith(".txt")) { // 故意制造异常 throw new FileNotFoundException("文件找不到"); } System.out.println("文件执行了"); } private static void find() { System.out.println("查询功能"); } private static void update() { System.out.println("修改功能"); } private static void delete() { System.out.println("删除功能"); } }
两个异常不能同时触发
-
注意:
如果throws的多个异常之间有子父类继承关系,我们可以直接throws父类异常;
如果不知道多个异常之间是否存在有子父类继承关系,我们可以直接throws Exception。
import java.io.FileNotFoundException; import java.io.IOException; public class Demo05Exception { public static void main(String[] args) throws /*FileNotFoundException, IOException*/ Exception { String s = "a.txt1"; add(s); // 添加功能 delete(); // 删除功能 update(); // 修改功能 find(); // 查询功能 } private static void add(String s) throws /*FileNotFoundException, IOException*/ Exception { System.out.println("添加功能"); if (s==null) { // 故意制造异常 throw new IOException("IO异常"); } if (!s.endsWith(".txt")) { // 故意制造异常 throw new FileNotFoundException("文件找不到"); } System.out.println("文件执行了"); } private static void find() { System.out.println("查询功能"); } private static void update() { System.out.println("修改功能"); } private static void delete() { System.out.println("删除功能"); } }
2.4.3 异常处理方式二_try…catch
-
格式
try { 可能出现异常的代码 } catch (异常 对象名) { 处理异常的代码 → 将来开发会将异常信息保存到日志文件中 }
import java.io.FileNotFoundException; public class Demo06Exception { public static void main(String[] args) { String s = "a.txt1"; try { /* try...catch没有捕获到的异常,最终会由jvm处理,并终止程序 */ /*int[] arr = null; System.out.println(arr.length); // NullPointerException*/ add(s); // 添加功能 } catch (Exception exception) { System.out.println(exception); } delete(); // 删除功能 update(); // 修改功能 find(); // 查询功能 } private static void add(String s) throws FileNotFoundException { System.out.println("添加功能"); if (!s.endsWith(".txt")) { // 故意制造异常 throw new FileNotFoundException("文件找不到"); } System.out.println("文件执行了"); } private static void find() { System.out.println("查询功能"); } private static void update() { System.out.println("修改功能"); } private static void delete() { System.out.println("删除功能"); } }
处理当前异常之后,之后的程序正常运行
2.4.4 异常处理方式二_多个catch
-
格式:
try { 可能出现异常的代码 } catch (异常 对象名) { 处理异常的代码 → 将来开发会将异常信息保存到日志文件中 } catch (异常 对象名) { 处理异常的代码 → 将来开发会将异常信息保存到日志文件中 } catch (异常 对象名) { 处理异常的代码 → 将来开发会将异常信息保存到日志文件中 } catch (异常 对象名) { 处理异常的代码 → 将来开发会将异常信息保存到日志文件中 } catch (异常 对象名) { 处理异常的代码 → 将来开发会将异常信息保存到日志文件中 }...
import java.io.FileNotFoundException; import java.io.IOException; public class Demo07Exception { public static void main(String[] args) { String s = "a.txt1"; try { add(s); // 添加功能 } catch (FileNotFoundException foundException) { System.out.println(foundException); } catch (IOException ioException) { System.out.println(ioException); } delete(); // 删除功能 update(); // 修改功能 find(); // 查询功能 } private static void add(String s) throws FileNotFoundException, IOException { System.out.println("添加功能"); if (s==null) { // 故意制造异常 throw new IOException("IO异常"); } if (!s.endsWith(".txt")) { // 故意制造异常 throw new FileNotFoundException("文件找不到"); } System.out.println("文件执行了"); } private static void find() { System.out.println("查询功能"); } private static void update() { System.out.println("修改功能"); } private static void delete() { System.out.println("删除功能"); } }
-
注意:
如果catch的多个异常之间有子父类继承关系,我们可以直接catch父类异常
如果不知道多个异常之间是否有子父类继承关系,我们也可以直接catch Exception。
import java.io.FileNotFoundException; import java.io.IOException; public class Demo07Exception { public static void main(String[] args) { String s = "a.txt1"; /*try { add(s); // 添加功能 } catch (FileNotFoundException foundException) { System.out.println(foundException); } catch (IOException ioException) { System.out.println(ioException); }*/ try { add(s); // 添加功能 } catch (Exception exception) { exception.printStackTrace(); // 将详细的异常信息打印到控制台上 } delete(); // 删除功能 update(); // 修改功能 find(); // 查询功能 } private static void add(String s) throws FileNotFoundException, IOException { System.out.println("添加功能"); if (s==null) { // 故意制造异常 throw new IOException("IO异常"); } if (!s.endsWith(".txt")) { // 故意制造异常 throw new FileNotFoundException("文件找不到"); } System.out.println("文件执行了"); } private static void find() { System.out.println("查询功能"); } private static void update() { System.out.println("修改功能"); } private static void delete() { System.out.println("删除功能"); } }
2.5 finally关键字
-
概述:代表的是不管是否触发了异常,都会执行的代码
特殊情况:如果之前执行了
System.exit(0)
终止当前正在执行的Java虚拟机 -
使用:都是配合
try...catch
使用try { 可能出现异常的代码 } catch (异常 对象名) { 处理异常的代码 → 将来开发会将异常信息保存到日志文件中 } finally { 不管是否有异常都会执行的代码 }
import java.io.FileNotFoundException; public class Demo08Exception { public static void main(String[] args) { String s = "a.txt1"; try { add(s); // 添加功能 } catch (FileNotFoundException e) { e.printStackTrace(); } finally { System.out.println("我必须得执行"); } } private static void add(String s) throws FileNotFoundException { System.out.println("添加功能"); if (!s.endsWith(".txt")) { // 故意制造异常 throw new FileNotFoundException("文件找不到"); } System.out.println("文件执行了"); } }
public class Demo09Exception { public static void main(String[] args) { int result = method(); System.out.println(result); } private static int method() { try { String s = null; System.out.println(s.length()); // 空指针异常 return 2; } catch (Exception e) { return 1; // return 1 代表的是将1返回,结束方法 } finally { System.out.println("我一定要执行"); // return 3; // 返回3,结束代码,所以不返回1了 } } }
当 finally里面得return 3 打开注释时,返回3,结束代码,所以不返回1了
finally的使用场景
-
关闭资源
-
原因:对象如果没有用了,GC(垃圾回收器)回收,用来回收堆中的垃圾,但是有一些对象GC回收不了,比如:连接对象(Connection)、IO流对象、Socket对象,这些对象GC回收不了,就需要我们自己手动回收,手动关闭。
将来不能回收的对象new完之后,后续操作不管是否操作成功,是否有异常,我们都需要手动关闭,此时我们就可以将关闭资源的代码放到finally中。
import java.io.FileWriter; import java.io.IOException; 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); } } } } }
-
2.6 抛异常时注意的事项
-
如果父类中的方法抛了异常,那么子类重写之后要不要抛?
可抛可不抛
-
如果父类中的方法没有抛异常,那么子类重写之后要不要抛?
不要抛
public class Demo10Exception {
public static void main(String[] args) {
}
class A{
public void method() /*throws Exception*/ {
}
}
class B extends A{
@Override
public void method() /*throws Exception*/ {
}
}
}
2.7 try…catch和throws的使用时机
-
如果处理异常之后,还想让后续的代码正常运行,我们使用
try...catch
-
如果方法之间时递进关系(调用),我们可以先用
throws
,但是到了最后需要用try...catch
做一个统一的异常处理-
编译时期异常,必须要处理,不处理报错(报红),没法运行
a. throws
b. try…catch
-
运行时期异常我们一般不处理,因为一旦出现运行时期异常异常,肯定是代码编写有问题,我们直接修改代码细节。
-
2.8 自定义异常
需求:键盘录入一个用户名,实现登录功能,如果登录失败,抛出LoginUserException
- 定义一个类
- 如果继承Exception就是编译时期异常
- 如果继承RuntimeException就是运行时期异常
public class LoginUserException extends Exception {
public LoginUserException() {}
public LoginUserException(String message) {
super(message);
}
}
import java.util.Scanner;
public class Demo11Exception {
public static void main(String[] args) throws LoginUserException {
// 1. 定义一个用户名,代表已经注册的用户
String name = "root";
// 2. 创建Scanner对象,录入用户名
Scanner sc = new Scanner(System.in);
System.out.println("请您输入需要登录的用户名");
String userName = sc.next();
// 3. 判断用户名是否和已经存在的用户名一致
if (name.equals(userName)) {
System.out.println("您已经登陆成功了");
} else {
throw new LoginUserException("登录失败了,用户名或者密码有问题");
}
}
}
2.9 打印异常信息的三个方法
Throwable类中的方法:
String toString() → 输出异常类型
String getMessage() → 输出的是异常信息
void printStackTrace() → 打印异常信息是最全的:包括异常类型、信息、以及出现的行数等等
public class LoginUserException extends Exception {
public LoginUserException() {}
public LoginUserException(String message) {
super(message);
}
}
public class Demo11Exception {
public static void main(String[] args) {
// 1. 定义一个用户名,代表已经注册的用户
String name = "root";
// 2. 创建Scanner对象,录入用户名
Scanner sc = new Scanner(System.in);
System.out.println("请您输入需要登录的用户名");
String userName = sc.next();
// 3. 判断用户名是否和已经存在的用户名一致
if (name.equals(userName)) {
System.out.println("您已经登陆成功了");
} else {
try {
throw new LoginUserException("登录失败了,用户名或者密码有问题");
} catch (Exception e) {
System.out.println(e.toString());
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
}
第三章 Object类
概述:所有类的根类(父类),所有的类都会直接或者间接继承Object类
3.1 Object中的toString
-
Object中的toString方法:返回该对象的字符串表示形式
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
注意:
a. 如果没有重写Object中的toString方法,直接输出对象名会默认调用Object中的toString方法,直接输出地址值。
b. 如果重写了Object中的toString方法,在输出地址值,重写没有意义,所以重写完toString之后,应该返回对象的内容。
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 + "]"; } }
public class Test01 { public static void main(String[] args) { Person p1 = new Person("金莲", 26); System.out.println(p1); // com.atguigu.b_object.Person@3b07d329 ,重写之后,输出的内容不再是地址值 Person [name=金莲, age=26] // 调用的是Object类中的toString,重写之后,输出的内容不再是地址值 Person [name=金莲, age=26] System.out.println(p1.toString()); // com.atguigu.b_object.Person@3b07d329 System.out.println("================"); ArrayList<String> list = new ArrayList<>(); list.add("张三"); list.add("李四"); list.add("王五"); // 调用的是AbstractCollection中的toString,重写过Object中的toString System.out.println(list.toString()); // [张三, 李四, 王五] } }
快速生成toString
alt + insert → 选择toString → 直接下一步
-
总结:
如果直接输出对象名不想输出地址值,就重写Object中的toString方法。
3.2 Object中的equals
-
概述: 比较的是两个地址之是否相等
public boolean equals(Object obj) { return (this == obj); } /* == 针对于基本数据类型来说,比较的是值 == 针对于引用数据类型来说,比较的是地址值 */
-
注意:
a. 如果没有重写Object中的equals方法,那么就会调用Object中的equals方法,比较对象的地址值
b. 如果重写了Object中的equals方法,那么就会调用重写后的equals方法,应该比较对象的内容
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 + "]"; } }
public class Test02 { public static void main(String[] args) { Person p1 = new Person("金莲", 26); Person p2 = new Person("金莲", 26); System.out.println(p1==p2); // false System.out.println(p1.equals(p2)); // false } }
小结:
- 如果直接输出对象名不想输出地址值,重写toString方法
- 如果想比较两个对象的内容,就重写一下equals方法
- 怎么重写:alt+insert → 选 toString 或者equals and hascode → 啥也不要管 → 一路下一步即可
3.3 Object中的clone方法
-
作用:复制一个属性值一样的新对象
-
使用:
需要被克隆的对象实现Cloneable
重写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 */ /*@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (obj instanceof Person) { Person person = (Person) obj; return this.name.equals(person.name)&&this.age==person.age; } return false; }*/ @Override public boolean equals(Object object) { if (this == object) return true; if (object == null || getClass() != object.getClass()) return false; Person person = (Person) object; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
public class Test03 { public static void main(String[] args) throws CloneNotSupportedException { Person person = new Person("涛哥", 16); Object object = person.clone(); Person person1 = (Person) object; // 克隆一个新对象 System.out.println(person==person1); // 比较地址值 false System.out.println(person.equals(person1)); // true } }
第四章 经典接口
4.1 java.lang.Comparable
我们知道基本类型的数据(除boolean类型外)需要比较大小的话,之间使用比较运算符即可,但是引用数据类型是不能直接使用比较运算符来比较大小的。那么,如何解决这个问题呢?
Java给所有引用数据类型的大小比较,指定了一个标准接口,就是 java.lang.Comparable
接口:
package java.lang;
public interface Comparable{
int compareTo(Object obj);
}
那么我们想要使得我们某个类的对象可以比较大小,怎么做呢?步骤:
第一步:哪个类的对象要比较大小;哪个类就实现 java.lang.Comparable
接口,并重写方法
- 方法体就是你要如何比较当前对象和指定的另外一个对象的大小
第二步:对象比较大小时,通过对象调用 compareTo
方法,根据方法的返回值决定谁大谁小
- this对象(调用compareTo方法的对象)减 指定对象(传入compareTo()的参数对象)大于0,返回正整数
- this对象(调用compareTo方法的对象)减 指定对象(传入compareTo()的参数对象)大于0,返回负整数
- this对象(调用compareTo方法的对象)减 指定对象(传入compareTo()的参数对象)等于0,返回零
代码示例:
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[j]
o:代表students[j+1]
如果students[j].getScore() - students[j+1].getScore() > 0
证明数组中前面一个对象比后面一个对象的分数高
*/
@Override
public int compareTo(Object o) {
if (o instanceof Student) {
Student s = (Student) o;
return this.getScore() - s.getScore();
}
return 0;
}
}
测试类
public class Test01 {
public static void main(String[] args) {
// 创建一个数组
Student[] students = new Student[3];
students[0] = new Student("张三", 100);
students[1] = new Student("李四", 60);
students[2] = new Student("王五", 80);
for (int i = 0; i < students.length-1; i++) {
for (int j = 0; j < students.length-1-i; j++) {
// 如果students[j]比students[j+1]大,就排序换序
if (students[j].compareTo(students[j+1])>0) {
Student temp = students[j];
students[j] = students[j+1];
students[j+1] = temp;
}
}
}
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
}
}
4.2 java.util.Comparator
思考:
(1) 如果一个类,没有实现Comparable接口,而两个类你又不方便修改(例如:一些第三方的类,你只有.class文件,没有源文件),那么这样类的对象也要比较大小,怎么办?
(2) 如果一个类,实现了Comparable接口,也指定了两个对象的比较大小规则,但是此时此刻我不想按照它预定义的方法比较大小,但是我又不能随意修改,因为会影响其他地方的使用,怎么办?
JDK在设计库之初,也考虑到这种情况了,所以又增加了一个 java.util.Comparator
接口。
package java.util;
public interface Comparator {
int compare(Object o1, Object o2);
}
那么我们想要比较某个类的两个对象的大小,怎么办呢?步骤:
第一步:编写一个类,我们称之为比较器类型,实现 java.util.Comparator
接口,并重写方法
- 方法体就是你要如何指定的两个对象的大小
第二步:比较大小是,通过比较器类型的对象调用compare()方法,将要比较大小的两个对象作为compare方法的实参传入,根据方法的返回值大小决定谁大谁小。
- o1对象减o2对象大于0,返回正整数
- o1对象减o2对象小于0,返回负整数
- o1对象减o2对象等于0,返回零
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[j]
o2:代表students[j+1]
如果o1的分数大于o2的分数 → compare方法返回正整数
如果o1的分数小于o2的分数 → compare方法返回负整数
如果o1的分数等于o2的分数 → compare方法返回零
*/
@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];
students[0] = new Student("张三", 100);
students[1] = new Student("李四", 60);
students[2] = new Student("王五", 80);
Student student = new Student();
for (int i = 0; i < students.length-1; i++) {
for (int j = 0; j < students.length-1-i; j++) {
// 如果students[j]比students[j+1]大,就排序换序
if (student.compare(students[j], students[j+1])>0) {
Student temp = students[j];
students[j] = students[j+1];
students[j+1] = temp;
}
}
}
for (int i = 0; i < students.length; i++) {
System.out.println(students[i]);
}
}
}