一、异常
1.finally关键字
概述: 一定要执行的代码块
使用: 都是配合try…catch使用
try{
可能出现异常的代码
}catch(异常 对象名){
处理异常的方案 - > 将异常信息打印到日志中
}
public class Exercise01 {
public static void main(String[] args) {
String s = "a.txt";
try {
String s1 = null;
System.out.println(s1.length());
//空指针异常 NullPointerException异常
//如果下面没有捕获到,会自动向上抛,最后给jvm
add(s);
} catch (FileNotFoundException e) { //没有捕获到
e.printStackTrace();
}finally {
System.out.println("我必须执行"); //必须执行
}
System.out.println("删除功能");
System.out.println("修改功能");
System.out.println("查询功能");
}
private static void add(String s) throws FileNotFoundException{
if(!s.endsWith(",txt")){
throw new FileNotFoundException("文件找不到异常");
}
}
}
public class Exercise02 {
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());
//①此处发生了NullPointerException异常
return 2;
//未执行
}catch(Exception e) {
//②catch发现异常 , 捕获异常
e.printStackTrace();
//③打印异常信息 , 此时finally还未执行 , 所以先不return结束方法
return 1; //未执行
} finally {
System.out.println("我一定要执行"); //④打印finally内容
return 3; //⑤结束方法
}
}
}
finally的使用场景
1 . 关闭资源
2.对象如果没有用了 , GC(垃圾回收器)回收, 释放内存 ,但是有一些对象GC回收不了.
比如: 连接对象Connection , IO流对象 , Socket对象 , 此时GC回收不了的,就需要我们手动回收关闭资源
将来这写对象new完以后 , 后续操作不管是操作成功与否 , 我们最后都需要关闭 , 此时我们就可以将关闭资源的代码放到finally中
2.抛异常时注意的事项
-
父类方法抛异常了 , 那么子类重写之后要不要抛?
- 可抛可不抛 , 建议和父类写一样
class Fu{ public void method() throws Exception{ System.out.println("我是父类中的method方法"); } } class Zi extends Fu{ @Override public void method() throws Exception { System.out.println("我是子类中的method方法"); } //或者 public void method(){ System.out.println("我是子类中的method方法"); } }
-
父类方法没有抛异常 , 那么子类重写之后要不要抛 ?
- 不要抛
class Fu1{ public void method()/* throws Exception*/{ System.out.println("我是父类中的method方法"); } } class Zi1 extends Fu1{ @Override public void method()/* throws Exception*/{ System.out.println("我是子类的method方法"); } }
3.try_catch和throws的使用时机
- 如果异常处理之后,还想让后续的代码正常执行,我们使用try…catch
- 如果方法之间时递进关系(调用),我们可以先throws,但是到了最后需要用try…catch做一个统一的异常处理
4.异常处理方式的选择
前提:对于异常,使用相应的处理方式。此时的异常,主要指的是编译时异常。
- 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,保证不出现内存泄漏。
- 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally进行处理,不能throws。
- 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。
5.自定义异常
需求:键盘录入一个用户名,实现登录功能,如果登录失败,抛出LoginUserException
public class LoginUserException extends Exception {
public LoginUserException() {
}
public LoginUserException(String message) {
super(message);
/*
此时super将message传给父类Exception
public Exception(String message) {
super(message);
}
然后Exception再将message传给父类Throwable
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
Throwable收到String,先停止程序,随后打印错误信息
*/
//fillInStackTrace()是Java的一个方法,它用于获取和填充在异常发生时的方法调用栈信 息。这个方法通常在抛出异常时由Java虚拟机自动调用,并将获得的栈跟踪信息填充到异常对象中。
}
}
public class Exercise04 {
public static void main(String[] args) throws LoginUserException{
String username = "root";
String password = "123";
Scanner sc = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = sc.next();
System.out.print("请输入密码:");
String passwords = sc.next();
if(name.equals(username) && passwords.equals(password)){
System.out.println("登录成功");
}else{
throw new LoginUserException("登录失败,用户名或密码错误");
//通过传参来提示异常信息
}
sc.close();
}
}
小结:
1.定义一个类,继承Exception(此类为编译时期异常)
继承RuntimeException(此类为运行时期异常)
2.在自定义异常类中提供构造方法,便于我们传参,设置异常信息提示
6.打印异常信息的三个方法
Throwable中的方法:
String toString() -> 输出异常类型以及异常信息
String getMessage() -> 输出异常信息
void printStackTrace() -> 输出详细的异常信息(异常类型,异常信息,错误在哪一行等)
public class Exercise05 {
public static void main(String[] args) {
String s = "1.txt1";
try {
add(s);
} catch (FileNotFoundException e) {
//String toString() 返回类型为String 返回异常类型以及异常信息
// System.out.println(e.toString());
//String Message() 返回类型为String 返回异常信息
// System.out.println(e.getMessage());
//void printStackTrace() 没有返回值 输出详细的异常信息(异常类型,异常信息,错误在哪一行等)
e.printStackTrace();
}
}
private static void add(String s) throws FileNotFoundException{
if(!s.endsWith(".txt")){
throw new FileNotFoundException("文件找不异常");
}
}
}
二、Object类
概述: 所有的类的根类 , 父类 , 所有的类都会直接或者间接去继承Object
1.Object中的getClass()
public final Class<?> getClass()
:获取对象的运行时类型
因为Java有多态现象,所以一个引用数据类型的变量的编译时类型与运行时类型可能不一致,因此如果需要查看这个变量实际指向的对象的类型,需要用getClass()方法
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;
}
}
public class Test {
public static void main(String[] args) {
People people = new People("哈哈",88);
Class<? extends People> aClass = people.getClass();
System.out.println(aClass);
}
}
2.Object中的native方法
private static native void registerNatives();->将当前类中的native方法注册进来
static {
registerNatives();//注册本地方法
}
方法作用:当该类被加载的时候,调用该方法完成对该类中本地方法的注册
在Object类中,除了有registerNatives()这个本地方法之外,还有hashCode()、clone()等本地方法,而在Class类中有forName0()这样的本地方法等等。也就是说,凡是包含registerNatives()本地方法的类,同时也包含了其他本地方法。所以,显然,当包含registerNatives()方法的类被加载的时候,注册的方法就是该类所包含的除了registerNatives()方法以外的所有本地方法
registerNatives()注册当前类的本地方法
- native:关键字->代表的本地方法
- 本地方法是有方法体的:c语言编写,本地方法的方法体源码没有对我们开源,所以我们看不到方法体,简单理解为本地方法就是对java语言的扩展,比如:后面io流部分,很多功能java本身没有,比如读写,那么就需要调用本地方法进进行读写
- 位置:在本地方法栈运行
- 意义:跟系统打交道
1.本地方法:对java不能实现的功能进行扩充
2.本地方法是由C语言编写,源码没有开源
3.本地方法运行在本地方法栈中
3.GC垃圾回收机制简介
运行垃圾回收器,JVM将从堆内存中清理对象,清理对象的同时会调用对象的finalize()方法,JVM的垃圾回收器是通过另一个线程开启的,因此程序中的效果并不明显。
1.方法:System类中的方法:public static void gc() -> 运行垃圾回收器
2.作用:主要用于回收堆内存中的数据
3.堆内存中的数据什么时候被清理:如果我们的对象没有用了,GC底层会有很多精妙的算法,会做判断,做回收
比如: Person p = new Person()
p = null
GC ROOTS->可达性算法 ->从根节点出发,是否能到达对应的对象,如果到不了,证明此对象为垃圾,直接清理
4.要了解到的:
构造方法:new对象的
析构函数:销毁对象,C语言中才有这个析构函数的概念
Object中的finalize():相当于C语言中的析构函数,用于清理对象,在回收之前,会自动被调用;而且不是垃圾回收器直接调用的,而是垃圾回收器通知当前对象,自动调用此方法进行对象回收
public class Person {
@Override
protected void finalize() throws Throwable {
System.out.println(this+"....被回收了");
}
}
public class Test05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);
System.out.println("-----------");
person = null;
//手动运行垃圾GC
System.gc();
}
}
3.1Object中的finalize()
- 当对象被回收时,系统自动调用该对象的 finalize() 方法。(不是垃圾回收器调用的,是本类对象调用的)
- 永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。
- 什么时候被回收:当某个对象没有任何引用时,JVM就认为这个对象是垃圾对象,就会在之后不确定的时间使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize()方法。
- 子类可以重写该方法,目的是在对象被清理之前执行必要的清理操作。比如,在方法内断开相关连接资源。
- 如果重写该方法,让一个新的引用变量重新引用该对象,则会重新激活对象。
4.Object中的toString()
1.Object中的toString方法:
a.源码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
b.作用:返回该对象的字符串表示形式 -> 类名@十六进制数 -> 俗称对象的地址值
2.注意:
a.如果没有重写Object中的toString方法,直接输出对象名,会默认调用Object中的toString,会输出地址值
b.如果重写了Object中的toString方法,直接输出对象名,再输出地址值重写就没有了意义,所以我们重写toString之后应该输出对象的内容
public class People {
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) {
this.age = age;
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public People() {
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test {
public static void main(String[] args) {
People people = new People("哈哈",88);
System.out.println(people);
}
}
5.Object中的equals()
1.Object中的equals方法源码:
public boolean equals(Object obj) {
return (this == obj);
}
2.作用 : 比较的是两个对象的地址值是否相等
3.注意 :
- ==针对于基本类型来说,比较的是值
==针对于引用类型来说,比较的是地址值 - 如果没有重写equals,那么默认会调用Object中的equals方法,会比较对象的地址值
- 如果重写了equals,那么调用equals方法之后,再比较对象地址值,重写就没意义了,所以我们重写equals之后可以比较对象的内容
//手写 equals
@Override
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj == null){
return null;
}
if(obj instanceof Person){
Person per = (Person)obj;
return this.name.equlas(per.name) && this.age == per.age;
}
}
//快捷键重写equals
@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);
}
总结:
1.直接输出对象名,想要不输出地址值,重写toString方法
2.比较两个对象时,想比较两个对象的内容,重写equals方法
3.快捷键:
alt+insert->toString()-> 一路下一步
alt+insert->equals and hashCode -> 一路下一步
三、经典接口
1.java.lang.Comparable
我们知道基本数据类型的数据(除boolean类型外)需要比较大小的话,之间使用比较运算符即可,但是引用数据类型是不能直接使用比较运算符来比较大小的。那么,如何解决这个问题呢?
Java给所有引用数据类型的大小比较,指定了一个标准接口,就是java.lang.Comparable接口:
package java.lang;
public interface Comparable{
int compareTo(Object obj);
}
this-obj
那么我们想要使得我们某个类的对象可以比较大小,怎么做呢?步骤:
第一步:哪个类的对象要比较大小,哪个类就实现java.lang.Comparable接口,并重写方法
- 方法体就是你要如何比较当前对象和指定的另一个对象的大小
第二步:对象比较大小时,通过对象调用compareTo方法,根据方法的返回值决定谁大谁小。
- this对象(调用compareTo方法的对象)大于指定对象(传入compareTo()的参数对象)返回正整数
- this对象(调用compareTo方法的对象)小于指定对象(传入compareTo()的参数对象)返回负整数
- this对象(调用compareTo方法的对象)等于指定对象(传入compareTo()的参数对象)返回零
public class People implements Comparable{
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) {
this.age = age;
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public People() {
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
People p = (People) o;
return this.getAge() - p.getAge();
}
}
public class Test15 {
public static void main(String[] args) {
People[] arr = new People[3];
arr[0] = new People("嘻嘻",20);
arr[1] = new People("哈哈",19);
arr[2] = new People("嘿嘿",15);
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j].compareTo(arr[j+1])>0){
People temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
2.java.util.Comparator
package java.util;
public interface Comparator{
int compare(Object o1,Object o2);
}
那么我们想要比较某个类的两个对象的大小,怎么做呢?步骤:
第一步:编写一个类,我们称之为比较器类型,实现java.util.Comparator接口,并重写方法
- 方法体就是你要如何指定的两个对象的大小
第二步:比较大小时,通过比较器类型的对象调用compare()方法,将要比较大小的两个对象作为compare方法的实参传入,根据方法的返回值决定谁大谁小。
- o1对象大于o2返回正整数
- o1对象小于o2返回负整数
- o1对象等于o2返回零
public class Student implements Comparator {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
return s1.getAge() - s2.getAge();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test20 {
public static void main(String[] args) {
Student[] arr = new Student[3];
Student student1 = new Student("张三", 18);
Student student2 = new Student("李四", 15);
Student student3 = new Student("王五", 20);
arr[0] = student1;
arr[1] = student2;
arr[2] = student3;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if(arr[i].compare(arr[i],arr[i+1])>0){
Student temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
3.java.lang.Clone
在java.lang.Object类中有一个方法:
protected Object clone() throws CloneNotSupportedException
所有类型都可以重写这个方法,它是获取一个对象的克隆体对象用的,就是造一个和当前对象各种属性值一模一样的对象。当然地址肯定不同。
我们在重写这个方法后时,调用super.clone(),发现报异常CloneNotSupportedException,因为我们没有实现java.lang.Cloneable接口。
注意 : 此接口不 包含 clone
方法。需要在实现类中手动进行重写 ,
public class Animal implements Cloneable{
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public Animal() {
}
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 "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Animal animal = (Animal) o;
return age == animal.age && Objects.equals(name, animal.name);
}
}
public class Test18 {
public static void main(String[] args) throws CloneNotSupportedException {
Animal dog = new Animal("狗", 8);
Animal dog2 = new Animal("狗", 8);
System.out.println(dog==dog2); //false
System.out.println(dog.equals(dog2)); //true
System.out.println("----------------------");
Object o = dog.clone();
//相当于重新new一个对象Animal dog = new Animal("狗", 8);
Animal dog1 = (Animal) o;
//将clone好的对象转型
System.out.println(dog1==dog); //false
System.out.println(dog1.equals(dog)); //true
}
}
imal = (Animal) o;
return age == animal.age && Objects.equals(name, animal.name);
}
}
public class Test18 {
public static void main(String[] args) throws CloneNotSupportedException {
Animal dog = new Animal("狗", 8);
Animal dog2 = new Animal("狗", 8);
System.out.println(dog==dog2); //false
System.out.println(dog.equals(dog2)); //true
System.out.println("----------------------");
Object o = dog.clone();
//相当于重新new一个对象Animal dog = new Animal("狗", 8);
Animal dog1 = (Animal) o;
//将clone好的对象转型
System.out.println(dog1==dog); //false
System.out.println(dog1.equals(dog)); //true
}
}