Java 异常处理机制
1.1 异常的概念 1.程序出错或崩溃等意外情形为异常(Exception)。 2.三类程序错误:
编译错误:特别是初学阶段//盲猜红色波浪线
运行时错误:运行环境 JVM 出错;内存不足等。
逻辑错误:除数为 0;数组下标越界;打开不存在的文件;使用空指针等。
数组下标越界异常示例:
1 | public class Error { |
很明显的下标越界返回:
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4 at com.company.phase04.Error.main(Error.java:7)
首先,编译程序不检查逻辑错误,逻辑错误与程序员有关。
其次,采用判断语句方式进行异常处理比较困难,对程序员要求高,代码量大。较好的处理方式是使用异常机制。
先来看看 Java 异常的继承关系 ↓
1 | Throwable |
其实除了上面还有下面这些继承接口:
ThreadDeath、IOError、VituallMachineError 都继承自 Error;
SQLException 继承自 Exception;
ArithmeticException、ClassCastException、NullPointerException 继承自 RuntimeException。
检查性异常:要么通过 rey-catch 进行捕获,要么用 throws 抛出,否则编译无法通过。
1.1.2 异常的分类
Throwable 类:异常基类,所有异常都继承自 java.lang.Throwable 类,两直接子类。
Error 类:严重错误,程序员无须处理此类异常。
Exception 类:同 Java 应用程序抛出和处理的非严重错误。分为:
(1)运行时异常,即 RuntimeException 类:程序员不做处理,程序也通过编译。如除数为 0 异常。
(2)检查异常,又称为非运行时异常,程序员必须在编程时进行处理,否则就无法编译通过。检查异常示例:
1 | package com.company.phase04; |
1.2 异常的捕获及处理
发生异常后如何操作? 1.立即处理:捕捉异常并处理。 //try-catch 2.暂不处理:抛出并由上一级去处理。//throws
最上一级就到虚拟机那边了。
1.2.2 异常的两种处理方式
(1) 捕捉异常并处理
try{…}catch{…}
try 段为可能出异常的语句。
catch 段 e 引用异常实例。
finally 段,有无异常发生都要执行,经常用于回收资源。
1 | package com.company.phase04; |
除了 printStackTrace 外还可以 System.out.print(e),不过显示的内容没前者深。
也有说用 printStackTrace 会导致锁死的,用 logger.error,再说。
1 | package com.company.phase04; |
InputStream 中的 read,读取一个数据字节并返回下一个数据字节,如果到了末尾就返回-1。
close()方法用于关闭此 FileInputStream 并释放与此流链接的所有系统资源。
操作的类如果实现了 AutoCloseable 接口,就可以在 try 语句块退出时自动调用 close 方法关闭资源。
FileInputStream->InputStream->Closeable->AutoCloseable
(2)抛出并由上一级去处理
throws
1 | package com.company.phase04; |
(3))练习一下 1.计算圆的面积,半径不能为零和负数。
1 | package com.company.phase04; |
2.求平均数,参数不能为负数,如果参数存在负数,则抛出运行时异常,异常消息为”参数为负数。
1 | package com.company.phase04; |
3.参考 PPT 代码,变成读取自定义文本文件,并显示其文本内容。√ (在上面) 4.要求用户输入数字,捕获并处理用户输入错误的异常。//自制的,个人感觉良好
1 | package com.company.phase04; |
————————第一课到此结束————————
第二节课没去,第三节课是代课,国信蓝桥学习。
细讲 Set 集合(HashSet)
Set 接口主要有两个实现类 HashSet 和 TreeSet。
1 | import java.util.* ; |
在终端输入以下命令运行此程序。
1 | javac TestSet.java |
运行结果:
在次添加王云是否成功:false
显示集合内容:[雷静,王云,刘静涛,南天华]
集合里是否包含南天华:true
从集合中删除”南天华”…
集合里是否包含南天华:false
集合中的元素个数为:3
HashSet 判断重复元素是依靠其底层引入的 hashcode。
hashcode 最初定义在 Object 类中,如果两个对象相等,那么这两个对象的 hashcode 值相同,因此根据逆否定理可知如果两个对象的 hashcode 值不同,那么这两个对象不相等。
但是如果两个对象的 hashcode 值相同,则这两个对象可能相同,也可能不同,这就要引入 equals()来比较了。
向 HashSet 增添元素时,HashSet 会计算该元素的 hashcode,根据 hashcode 是否唯一来操作。
如果 hashcode 值与 HashSet 集合中的某个元素的 hashcode 值相同,那么 HashSet 就会进一步调用 equals()方法来判同,如果相同就会忽略掉新增元素。
可见这类做法是很严格的,跟 Hash 打上关系都会这样?
因此,当使用 HashSet 存放某个自定义对象时,就得在这个对象的定义类中重写 hashcode()和 equals()方法。
hashcode 是对象的映射地址。
equals()用于比较连个对象。
重写时,hashcode()需要自定义”映射地址”的映射规则。
equals()方法需要自定义对象的”比较规则”。
一般而言,映射规则和比较规则都需要借助于对象的所有属性进行计算。
下面看其方法的一个示例:
在看实例之前先看看 instanceof 二元操作符,类似于== < > 之类的。
它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
1 | import java.util.ArrayList; |
回来看示例。
1 | public class Vehicle { //自定义一个Vehicle类/还是对象? |
运行结果:true、true、false
细讲 Set 集合(TreeSet)
TreeSet 类在实现了 Set 接口的同时,也实现了 SortedSet 接口,是一个具有排序功能的 Set 接口类。
TreeSet 集合中的元素默认是按照自然升序排列,并且 TreeSet 集合中的对象需要实现 Comparable 接口。
1 | import java.util.Set; |
运行结果:[刘静涛, 南天华, 王云]
可见 String 类会进行字典顺序的排序,String 类实现了 Comparable 接口。
比较器
JDK 提供了 Comparable 和 Comparator 两个接口,都可以用于定于集合元素的排序规则。
若想定义自己的排序方式,简单的方法是让加入 TreeSet 集合中的对象所属的类数显 Comparable 接口来实现 compareTo(object o)方法,它也只有这一个方法,达到排序的目的。
这里的 compareTo(object o)和传统的 compareTo()方法似乎是有区别的。。
//compareTo(object o)是 Comparable 接口唯一的方法返回整数
先来看看菜鸟教程找到的 compareTo()方法
1 | public static void main(String args[]) { |
首先,返回值是整型。
其次,优先是比 ASCLL 值,怎么比?
所选字符串的首字符与被比字符串的首字符比,如果一致就比第二个,比下去。
不一致的返回相差的 ASCLL 码值,遇到长度不一样但是前面值都一样的情况就返回长度差距值。
回到正题。
来个需求,学生对象有两个数学,对应学号与姓名。希望将这些学生对象加入 TreeSet 集合后,按照学号大小从小到大进行排序,如果学号相同再按照姓名自然排序。
来看看实例怎么做到的。
1 | import java.util.Set; |
来看外援优化版
1 | class Student implements Comparable<Student> {...}//先是泛型的使用,表明compareble接口是Student类型的 |
Comparable 接口与 conpareTo() 内部比较器
如果程序员想定义自己的排序方式,一种简单的方法就是让加入 TreeSet 集合中的对象所属的类实现 Comparable 接口,通过实现 compareTo(Object o)方法,达到排序的目的。
Comparable 接口实现了在 TreeSet 集合中的自定义排序。这种方法是通过集合元素重写 compareTo(Object o)[笑死了确定不是 T?]方法定义排序规则的。因为是在类内部实现比较,所以也通常将 Comparable 称为内部比较器。
Conparetor 接口与 compare() 外部比较器
Comparator 可以理解为一个专用的比较器,当集合中的对象不支持自比较或者自比较的功能不能满足程序员的需求时,就可以写一个实现 Comparator 接口的比较器来完成两个对象之间的比较,从而实现按比较器规则进行排序的功能。例如要比较的对象是 JDK 中内置的某个类,而这个类又没有实现 Comparable 接口,因此我们是无法直接修改 JDK 内置类的源码的,因此就不能通过重写 compareTo(Object o)方法来定义排序规则了,而应该使用 Comparator 接口实现比较器功能。
1 | import java.util.*; |
Collection 工具类
静态类,可以直接 Collection.sort();这样调用。
这里的 sort(List list,Comparetor c);
如果就填个 list,list 内存放 obj 对象,就是根据 obj 所属类重写内部比较器 Comparable 中的 compareTo()方法定义的。
至于后面的 Comparetor c 可以自定义规则放上去。
shuffle(List list)方法可以随机排序。
reverse(List list)可以翻反转 list 集合中数据元素的顺序。
提一句话:关于 toString 与 compareTo/compare 自动调用问题。
toString 会在打印的时候自调用(print)
compareTo 之类的,疑似在创建/构造对象的时候回自调用。
2021/10/26 晚来的字符流字节流缓冲流~二进制流
不用看,都是自学的,老师讲的什么垃圾。
啥是输入流输出流?
流是一种 FIFO 的数据结构。//先进先出
输入流就是数据输入到内存,输出流就是将内存中的数据输出出去。
一般情况都会要个 buf 当缓冲区,buf 这个缓冲区大小可以自定,让数据从文件一块块的挖到内存里。
按流向分:
·输入流:java.io.InputStream 抽象类 /Reader
·输出流:java.io.OutputStream 抽象类 /Writer 这两者是字符流的输入与输出
·其他流:如缓冲流,二进制流什么的
按处理单元分:
·字节流:FileInputStream/FileOutputStream|输入字节流/输出字节流。
带 Stream 的可以说就是字节流了。
字节流就是将内容转换为二进制进行传输,1 字节=8b/二进制,也就是 8 位通用字节流,
因为二进制可以传输任何类型的数据,因此字节流也可以传输任何类型的数据。//比如复制 jpg 为 txt 后缀,再自己改 txt 后缀为 jpg 也可以,
系统的后缀其实没啥用,可以说是只是打开方式的一种,比如画图板啥的。
·字符流:Reader/Writer|输入字符流/输出字符流。
真实使用不一定就这两者,也可能会用到它们的子类,反正也是字符流。
字符流是 16 位的 unicode 字符流(只处理字符,如文本文件)
·其他流//暂时不知
实例:利用字符流进行文本复制,并更改改标签内容
1 | package com.company.phase06; |
运行结果:
你好,我是{name},目前正在{what},个人爱好{aihao}
你好,我是乐乐侠,目前正在拯救世界,个人爱好世界和平
d 盘目录下也多了个文件,写的就是 ↑ 这玩意儿
关于缓冲流。就是自带一个 buf,自带缓冲。在你拿捏不定缓冲区大小的时候可以使用。
二进制流就是 BufferadReader 改 DataInputStream 这样,其他基本上都差不多,也就是将字节流转换为二进制流,其他各种流原理都差不多,先输入再输出。
1 | package com.company.phase06; |
2021/11/4 反射
反射:
反射机制是在【运行状态】中:
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
反射提供的功能:
在运行时判断任意一个对象所属的类;
在运行时狗仔任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。
1 | package com.company.phase07; |
接下来才是重点
关于 Class
T:表示确定是 java 类
?:不确定是 java 类,没准是接口呢
1 | package com.company.phase07;//这个类名,com目录下的company目录下的phase07 |
运行结果:
class com.company.phase07.Person
class com.company.phase07.Person
class com.company.phase07.Person
三者相等皆为 true。
接下来,通过反射来获取方法
1 | public static void demo02(){ |
接下来获取所有的接口,java 单继承,多实现
1 | public static void demo03(){ |
运行结果:
interface com.company.phase07.MyInterface
interface com.company.phase07.MyInterface2
获取所有的父类
1 | public static void demo04() { |
运行结果:
class java.lang.Object
它的父类就是 object
获取所有的构造方法
1 | public static void demo05() { |
运行结果:
public com.company.phase07.Person()
public com.company.phase07.Person(int)
public com.company.phase07.Person(int,java.lang.String,int)
获取所有的属性
1 | public static void demo06() { |
运行结果:public java.lang.String com.company.phase07.Person.desc
这边又要在 Person 里加 public 类型的属性,不然 private 的可拿不到
现在讲如何获取 private 修饰的方法或者属性
其实很简单,方法改成 xx.getDeclaredMethods 或 xx.getDeclaredFields 就行
重点是 Decleared。
接下来获取当前反射所代表类(接口)的对象(实例)[看起来有点重要]
1 | public static void demo08() { |
运行结果:interface Method…..
newInstance()和 new()区别
1、两者创建对象的方式不同,前者是实用类的加载机制,后者则是直接创建一个类:
2、newInstance 创建类是这个类必须已经加载过且已经连接,new 创建类是则不需要这个类加载过
3、newInstance: 弱类型(GC 是回收对象的限制条件很低,容易被回收)、低效率、只能调用无参构造,new 强类型(GC 不会自动回收,只有所有的指向对象的引用被移除是才会被回收,若对象生命周期已经结束,但引用没有被移除,经常会出现内存溢出)
@SuppressWarnings(value={“all”})
这样可以抑制来自编译器的警告,可以跳过检查,()里面的值与参数可以更改
通过反射来操作对象、属性与方法
1 | public static void demo01() throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException { |
运行结果:
1
private Method…..
private Method…..张三
通过反射来操作构造方法
1 | //先来复习下 |
Properties 类的使用
Properties 类继承了 Hashtable,以 Map 的形式进行放置值, put(key,value) get(key)。
主要的方法有:
properties()构造方法;
setProperties(String,String)设置值;/可以理解为 key 和 value
load(Reader/LineReader/InputStream)加载文件;
store(OutputStream/Write,String)保存到文件;
loadFromXML(InputStream)加载 xml 文件;
storeToXML(OutputStream,String,String)保存到 xml 文件;
getproperty(String,或再加个 String)获取属性;
propertyNames()输出 key 值;
stringPropertyNames()输出 key 值;
list(PrintStream/PrintWriter)输出打印;