一、异常
- 什么是异常:
就是程序出问题了!
体系结构
Throwable
子类
Error Exception
Error:严重问题: 无法直接使用代码方式解决(内存溢出…(加载大量图片的时候))
Exception:程序出现的这种问题,是可以通过代码方式解决
RuntimeException:运行时期异常
举例:
NullPointerException:都属于代码逻辑不严谨导致的!
只要不是运行时期的出现的都是属于编译时期异常
举例:
班长骑车出去 旅游
问题1:
骑行的过程中,地震了,路面坍塌了(不可抗力因素) :严重问题
问题2:
准备骑行的时候,车胎没气了 (应该事先检查的问题)
问题3:
在骑行过程中,中间的路面不平整,班长非得中间路面骑行,导致车胎没气! (no zuo no die) (自己原因导致的!)
举例:
public class ExceptionDemo1 {
public static void main(String[] args) {
//定义一个变量
int a = 10 ;
int b = 0 ;
System.out.println(a/b);
}
}
- 解决异常的方案:
try…catch…finally:标准格式
变形格式
try…catch
try…catch…catch…
try…finally
捕获异常的执行流程
1)首先执行try中的代码
2)如果程序在try中出问题了,执行catch语句----jvm创建当前异常类对象:异常类名 e = new 异常类名() ;
自动查看是否匹配catch语句中的异常对象,如果匹配,就执行处理的语句!
throws:抛出异常
举例:
public class ExceptionDemo2 {
public static void main(String[] args) {
//method1() ;
// method2() ;
// method3() ;
method4() ;
}
//JDK7以后:异常的处理
private static void method4() {
try{
int a = 10;
int b = 0;
int[] arr = {1, 2, 3, 4};
System.out.println(arr[4]);
System.out.println(a / b);
System.out.println(arr[0]);
}catch (ArithmeticException | ArrayIndexOutOfBoundsException e){ // jdk7以后的写法: catch捕获多个异常类名
System.out.println("程序出问题了...");
}
}
//方式2:针对多个异常处理,一次性处理,不分别try...catch...
private static void method3() {
//try...catch...catch
try {
int a = 10;
int b = 0;
int[] arr = {1, 2, 3, 4};
System.out.println(arr[4]);
System.out.println(a / b);
System.out.println(arr[0]);
}catch (Exception e){
System.out.println("出问题了...");
/* }catch(ArrayIndexOutOfBoundsException e){
System.out.println("访问数组中不存在的角标");
}catch(ArithmeticException e){
System.out.println("除数不能为0");*/
}
}
//代码中可能出现多个异常
//方式1:可以分别的try...catch...
private static void method2() {
try{
int a = 10 ;
int b = 0 ;
System.out.println(a/b);
}catch (ArithmeticException e){//内存中:ArithmeticException e = new ArithmeticException() ;
System.out.println("除数不能为0");
}
try{
int[] arr = {1,2,3,4} ;
System.out.println(arr[0]);
System.out.println(arr[4]);
}catch(ArrayIndexOutOfBoundsException e){//ArrayIndexOutOfBoundsException e = ArrayIndexOutOfBoundsException() ;
System.out.println("访问了数组中的不存在的角标...");
}
}
//如果代码中出现一个异常
private static void method1() {
//两个变量
//捕获异常:try...catch...
try{
//可能出现问题的代码包裹起来
int a = 10 ;
int b = 0 ;
System.out.println(a/b);
}catch(ArithmeticException e){ //实际开发中:catch语句:具体的异常类名 变量
//手动处理
System.out.println("除数不能为0");
}
}
}
- 编译时期异常和运行时期异常的区别
编译时期异常:必须要进行处理,否则代码通过不了,运行不了!
如果编译时期异常的代码使用的捕获异常,调用者不需要在处理了!
try…catch… (开发中:使用捕获)
如果使用throws:抛出异常 在方法声明上使用
调用者必须要抛出,否则报错!
运行时期异常:
调用者不需要显示处理,可以通过逻辑代码来优化
也可以显示处理
注意事项:
不管什么编译和运行时期,尽量在代码中使用try…catch处理…
举例:
public class ExceptionDemo3 {
public static void main(String[] args) throws ParseException {
System.out.println("今天天气不错");
// method() ; //调用者
method2() ;
System.out.println("可以踢球...");
}
//运行时期
private static void method2() {
try{
int a = 10 ;
int b = 0 ;
System.out.println(a/b);
}catch (ArithmeticException e){
System.out.println("除数不能为0");
}
/* int a = 10 ;
int b = 0 ;
if(b!=0){
System.out.println(a/b);
}else{
System.out.println("除数为0");
}*/
}
//编译时期
private static void method() throws ParseException {
//日期文本的字符串格式:
/* try {
//可能出现问题的代码
String s = "2021-5-28" ;
//s--->Date日期格式
//解析
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
Date date = sdf.parse(s) ; // public Date parse(String source) throws ParseException
} catch (ParseException e) {
System.out.println("解析出问题了...");
}*/
String s = "2021-5-28" ;
//s--->Date日期格式
//解析
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
Date date = sdf.parse(s) ; // public Date parse(String source) throws ParseException
}
}
举例:
面试题
throws和throw的区别?
这两个都是属于"抛出"
throws:抛出
1)抛出在方法声明上,而且方法声明上的异常类名可以跟多个,中间使用逗号隔开
2)throws的异常处理交给:调用者进行处理
3)throws表示抛出异常的一种可能性
4)throws后面跟的异常类名
throw:抛出
1)是在方法体中使用,并非在方法声明上使用(位置不一样)
2)throw的异常处理交给: 方法体中的逻辑语句判断
3)throw表单抛出的异常的肯定性(执行某段代码,一定会执行这个异常)
4)throw后面跟的异常对象: throw new XXXException() ;匿名对象
举例:
public class ExceptionDemo {
public static void main(String[] args) throws ParseException{
method() ;
// method2() ;
}
private static void method2() throws ArithmeticException{
int a = 20 ;
int b = 0 ;
//加入逻辑判断处理
if(b!=0){
System.out.println(a/b);
}else{
throw new ArithmeticException() ; //表示出现异常的肯定性
}
}
//抛出
private static void method() throws ParseException {
//日期文本---日期格式:解析
String source = "2022-6-30";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
Date date = sdf.parse(source);
System.out.println(date);
}
}
二、Finally关键字
-
捕获异常的标准格式:
try{
可能出现问题的代码
}catch(异常类名 变量名){
处理语句;
}finally{//一般情况 释放相关的底层系统资源 //io流中的流对象 (底层系统资源) //Statment:sql语句的执行对象 //PreparedStatement:预编译的对象 //Connection:数据库的链接对象 //ResultSet:结果集对象
}
-
finally特点:
就是释放相关的系统资源,而且finally语句一定会执行的!
除非一种特例,在执行finally语句,jvm退出了! (System.exit(0))
举例:
public class FinallyDemo {
public static void main(String[] args) {
//自己使用try...catch...finally:
//catch语句:针对异常的处理,不会手动处理,都是通过Throwable的功能
//public String getMessage():获取到详细消息字符串
//public String toString():
//异常类的描述 : +getMessage():获取到详细消息字符串
try{
String s = "2021-5-28" ;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
Date date = sdf.parse(s) ;
System.out.println(date);
}catch (ParseException e){
//String str = e.getMessage();//Unparseable date: "2021-5-28"
// String str = e.toString() ;//java.text.ParseException: Unparseable date: "2021-5-28"
// System.out.println(str);
//public void printStackTrace():跟踪堆栈打印错误输出流
e.printStackTrace() ;
System.exit(0);
}finally {
System.out.println("这里的代码一定会执行...");
}
}
}
- 面试题:
当前try…catch…finally:捕获异常的时候,如果catch语句里面有return语句,finally还会执行吗?
在前还是在后?
finally一定会执行,在return 前 (将方法已经结束—catch语句中的return语句已经返回路径!)
finally代码除非在执行前 jvm退出,----finally不会出现return,都是去释放资源… - final,finally,finalize的区别?
finally :
一般使用在异常处理中:捕获异常 格式 try…catch…finally
它里面的语句一定会执行(特例:在执行之前jvm退出)
try…catch…finally—应用场景:
业务层(service)代码中 使用捕获异常
在数据库访问层(dao)代码, 建议使用抛出throws
举例:
public class FinallyDemo2 {
public static void main(String[] args) {
System.out.println(getNum(10));
}
private static int getNum(int i) {
try{
i = 20 ;
int b = 0 ;
System.out.println(i/b); //出现异常了
}catch(Exception e){
i = 30 ; //i =30
return i ; //当前位置 已经形成返回路径 i = 30
}finally {
i = 40 ;
//return i ;
}
return i; // return 30
}
}
举例:
/* 自定义一个异常类 继承自RuntimeException
*/
public class MyException extends RuntimeException{
public MyException(){}
public MyException(String message){
super(message) ;
}
}
public class Teacher {
//打分的功能
public void checkStudentSocre(int socre){
//分值:1-100
if(socre<0 || socre>100){
//非法数据
throw new MyException("学生成绩非法...") ;
}else{
System.out.println("学生数据合法...");
}
}
}
/* 测试类
* 如何自定义一个异常类
* 异常体现的结构:
* Throwable
* error exception
* RuntimeException
* 很多运行时期异常
* IOException,ParseException..编译时期异常
* 自定义一个类:继承自RuntimeException
* 继承自 Exception
* 提供有参构造方法(String message)/无参构造方法
*/
public class ExceptionDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
System.out.println("请输入学生成绩:");
int socre = sc.nextInt() ;
//创建Teacher类对象
Teacher t = new Teacher();
t.checkStudentSocre(socre);
}
}
- 异常的使用注意事项:
1)子类继承父类, 重写父类的方法的时候,如果父类的方法存在抛出异常,那么子类重写的方法的异常
要么跟父类方法异常保持一致
要么是父类方法异常的子类异常
2)子类继承父类的时候,子类重写父类方法,如果父类的该方法没有抛出异常,那么子类只能去try…catch,不能throws,
一旦子类抛出,那么父类的方法也必须抛出(不推荐更改父类的代码!)
举例:
public class ExceptionDemo2 {
public static void main(String[] args) {
}
}
class Father{
/* public void method() throws Exception {
System.out.println("father method");
}*/
public void show(){
System.out.println("show father");
}
}
//子类要重写method方法
class Son extends Father{
/* public void method() throws ArithmeticException{
System.out.println("son method...");
}*/
public void show() {
//将日期文本--->日期格式
//String--->Date
try {
String s = "2021-5-28" ;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
Date date = sdf.parse(s) ; //throws----> 父类的方法也必须throws
} catch (ParseException e) {
e.printStackTrace();
}
}
}
三、I/O流
- java.io.File:文件和目录(文件夹)路径名的抽象表示形式。
构造方法
File(String parent, String child)
File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
举例:
public class FileDemo {
public static void main(String[] args) {
//请使用File文件类:描述D盘下的aaa文件夹下的a.txt文件
//D:\aaa\a.txt
// File(String parent, String child):参数1:父目录 参数2:指定文件或者文件夹
File file1 = new File("D:\\aaa","a.txt") ;
System.out.println(file1);//D:\aaa\a.txt
System.out.println("-----------------------------");
//File(String pathname):直接指定具体的文件路径 (推荐第二种)
File file2 = new File("d:\\aaa\\a.txt") ;
System.out.println(file2); // d:\aaa\a.txt
System.out.println("-----------------------------");
// File(File parent, String child)
File file3 = new File("d:\\aaa") ;
File file4= new File(file3,"a.txt") ;
System.out.println(file4);//d:\aaa\a.txt
}
}
- File类的相关的文件夹的创建
文件的创建
文件夹以及文件的删除功能
public boolean mkdir() :创建文件夹:如果已经存在,创建不成功,返回false!
public boolean mkdirs():创建多级目录的使用,如果父文件夹不存在,自动创建
public boolean createNewFile()throws IOException:创建文件,可能出现IO异常(举例:文件找不到)
public boolean delete():删除文件或者是文件夹
举例:
public class FileDemo2 {
public static void main(String[] args) throws IOException {
//描述目录:
//D:\JavaEE_2104\EE_day24创建一个code目录
// File file = new File("D:\\JavaEE_2104\\EE_day24\\code") ;
// public boolean mkdir() :创建文件夹:如果已经存在,创建不成功,返回false!
//System.out.println(file.mkdir());
//public boolean mkdirs():创建多级目录的使用,如果父文件夹不存在,自动创建
//D:\JavaEE_2104\EE_day24\code 创建很多个目录
//File file2 = new File("D:\\JavaEE_2104\\EE_day24\\code\\aaa\\bbb\\ccc") ;
//System.out.println(file2.mkdirs());
//public boolean createNewFile()throws IOException:创建文件,可能出现IO异常(举例:文件找不到)
//D:\JavaEE_2104\EE_day24\code 里面创建a.txt文件
/*File file3 = new File("D:\\JavaEE_2104\\EE_day24\\code\\a.txt") ;
System.out.println(file3.createNewFile());*/
//public boolean delete():删除文件或者是文件夹
//D:\JavaEE_2104\EE_day24\code :删除
// File file4 = new File("D:\\JavaEE_2104\\EE_day24\\code") ;
//System.out.println(file4.delete());
//先删除文件,在删除目录
/* File file4 = new File("D:\\JavaEE_2104\\EE_day24\\code\\a.txt") ;
System.out.println(file4.delete());
File file5 = new File("D:\\JavaEE_2104\\EE_day24\\code") ;
System.out.println(file5.delete());*/
//上面的路径:都是带盘符
//创建一个demo文件夹 如果没有指定盘符:相对路径:默认在当前项目路径的下创建文件或者是文件
File file = new File("demo") ;
System.out.println(file.mkdir());
File file2 = new File("demo\\a.txt") ;
System.out.println(file2.createNewFile());
}
}
- 重命名功能:
public boolean renameTo(File dest):对当前文件进行重命名
情况1:
当前指定文件路径和重命名后的路径一致的,那么仅仅是改名字
需要将D盘下的高圆圆.jpg改名个为杨桃.jpg
D:\高圆圆.jpg
D:\杨桃.jpg
情况2:如果路径不一致, ----改名并剪切
需要将当前项目下的杨桃.jpg
D:\高圆圆.jpg
举例:
public class FileDemo3 {
public static void main(String[] args) {
// D:\\高圆圆.jpg
// File file = new File("d:\\高圆圆.jpg") ;
//File file2 = new File("d:\\杨桃.jpg") ;
// public boolean renameTo(File dest):对当前文件进行重命名
File file = new File("杨桃.jpg") ;
File file2 = new File("d:\\高圆圆.jpg") ;
System.out.println(file.renameTo(file2));
}
}
- 判断功能
public boolean canRead():判断这个文件是否是可读
public boolean canWrite():是否可写
public boolean exists():是否存在
public boolean isDirectory():是否是文件夹 (使用居多)
public boolean isFile():是否是文件 (使用居多)
public boolean isHidden():是否是隐藏文件
举例:
public class FileDemo4 {
public static void main(String[] args) {
//表示:项目下的demo文件夹中a.txt
File file = new File("demo\\a.txt") ;
System.out.println(file.canRead());
System.out.println(file.canWrite());
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
System.out.println(file.isHidden());//默认的不隐藏
}
}
- File类的高级获取功能:
public String[] list():当前目录下的所有的文件夹以及文件的字符串数组
public File[] listFiles():获取当前某个目录下的所有的File数组对象
需求:
获取d盘下的所有的以.jpg结尾的文件
1)获取当前某个盘符下或者目录下的所有的文件以及文件夹的file数组
public File[] listFiles():获取当前某个目录下的所有的File数组对象
2)防止空指针:判断如果当前File数组不为空的时候,遍历
遍历,获取到每一个File对象
2.1)File对象如果是文件 isFile()
2.2)并且它还是以.jpg结尾的文件
2.3)输出文件对象.getName() 获取到
举例:
public class FileDemo5 {
public static void main(String[] args) {
//获取D盘下的所有文件以及文件数组
File file = new File("d:\\") ;
/* String[] strArray = file.list();
for(String s: strArray){
System.out.println(s);
}*/
File[] fileArray = file.listFiles();
if(fileArray!=null){
for(File f :fileArray){
//获取名称
//public String getName():获取文件或者目录的名称
// System.out.println(f.getName());
if(f.isFile()){
//再次判断
if(f.getName().endsWith(".jpg")){
System.out.println(f.getName());
}
}
}
}
}
}
综合:
/* 获取d盘下的所有的以.jpg结尾的文件
* File类获取功能:
* 使用文件名称过滤器来帮助我们完成过滤!
* 优点:在获取的时候就已经通过文件名称过滤器筛选了
*
* 获取字符串列表
* public String[] list(FilenameFilter filter)
* 获取文件列表
* public File[] listFiles(FilenameFilter filter) 形式参数接口:需要接口子实现类对象
* 使用匿名内部类的方式
* FilenameFilter:文件名称过滤器
* boolean accept(File dir,String name)
* 返回值true或者false取决于:是否将当前name文件添加在文件列表中
*/
public class FileTest {
public static void main(String[] args) {
//描述D盘
File file = new File("d:\\") ;
// public File[] listFiles(FilenameFilter filter) 形式参数接口:需要接口子实现类对象
File[] fileArray = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
// return false;
//封装name---File对象
/* File file = new File(dir,name) ;
//分步走
boolean flag1 = file.isFile();
boolean flag2 = file.getName().endsWith(".jpg");
return flag1 && flag2 ;*/
File file = new File(dir,name);
//一步走
return file.isFile() && file.getName().endsWith(".jpg") ;
}
});
for(File f :fileArray){
System.out.println(f.getName());
}
}
}
四、递归
-
递归:
方法调用方法本身的现象而不是方法嵌套方法!
举例
Math.max(10,Math.max(20,40)) ; 方法嵌套show方法调用自己本身:递归
public void show(int n){//5
if(n<0){
System.exit(0) ;
}
System.out.println(n) ; //5
show(n–) ;
} -
使用递归的前提条件:
1)必须有一个方法
2)必须存在出口条件(结束条件):否则就是死递归
3)有一定的规律
举例:
从前有座山,里面有个老和尚和小和尚,老和尚对小和尚说:
从前有座山,里面有个老和尚和小和尚,老和尚对小和尚说:
从前有座山,里面有个老和尚和小和尚,老和尚对小和尚说:
举例:
public class DiGuiDemo {
//构造方法不存在递归
/* public DiGuiDemo(){
DiGuiDemo() ;
}*/
}
/* 需求:求出5的阶乘
* 5 * 4 * 3 * 2 * 1
*/
public class DiguiDemo2 {
public static void main(String[] args) {
//普通方式
//for循环:求阶乘思想
int jc = 1 ;
for(int x = 2 ; x <=5 ; x ++){
jc *=x ;
}
System.out.println(jc);
System.out.println("---------------------------");
//使用递归思想
//定义一个方法
System.out.println(getNum(5));
}
//定义了一个求阶乘的方法
private static int getNum(int i) {
//出口条件:如果i=1,永远是1
if(i==1){
return 1 ;
}else{
//i不等于1
return i*getNum(i-1) ; //5 * getNum(4)
}
}
}