JavaSE---IO流基础(异常、File类、递归)

时间:2022-08-27 08:27:29


6、IO流基础

6.1 异常

6.1.1 异常的概述

    异常:就是程序在运行时出现不正常情况。 在Java中,将异常封装成了一个类:Throwable。Throwable下分为两大体系:Error和Exception。

    Throwable

        |--Error:严重错误,如内存溢出

        |--Exception

                |--RuntimeException:不需要程序员处理,因为发生这类异常一般都是程序不严谨造成的,需要修改程序。//特殊异常类,抛时不需要声明 

                |--其他异常类:必须由程序员处理,因为这些异常在编译时会检测,如果不处理,则编译不通过。

6.1.2  异常的声明和处理

1、异常的声明和抛出

    Java中使用throws关键字用来声明异常,自己并不处理异常,而是将该异常传递给调用者,让调用者去处理这个异常。例如下面的方法:

public String readLine() throws IOException


    使用throw关键字用来抛出异常。

    提问:throws和throw的区别是什么呢?

    throws用在方法头上,跟的是异常类名,且可以跟多个异常类名(两者之间用逗号隔开);throws用来声明异常,也就是说该方法可能会发生异常,并不一定会发生,如果发生异常的话由该方法的调用者去处理。

    throw用在方法体内,跟的是异常对象名,且只能跟一个异常对象名;throw用来抛出异常,也即是说执行throw则一定是发生了某种异常,处理的的时候在该方法内部处理。

    那么什么时候使用throws呢?

    1)如果method()方法中调用了可能会抛出编译时期异常的方法(例如readLine()方法),那么就可以使用throws来声明method()方法。

    2)如果method()方法中有用throw关键字抛出的一个异常的语句,那么就必须在method()方法头上用throws声明。

    注意:RuntimeException异常(运行时异常)不需要用throws来声明。

    假如方法A调用了一个用throws声明的方法B,那么A该怎么处理呢?

    1)可以使用特有的语句去处理(try...catch...finally)。

    2)也可以使用throws来声明自己,这样做是为了让自己的调用者去处理。

    

    假设有一个ShenXian类,类中有一个shiFa方法。正常情况下,每个神仙都可以变法术,但法术也有偶尔失灵的情况(异常)。此时,可以有shiFa这个方法抛出一个Exception异常对象。

class ShenXian{
//连续五次施法则法术失灵
//因为shiFa这个方法有用throw抛出异常对象的语句,所以必须使用throws关键字在shiFa这个方法头上声明异常
public void shiFa(int i) throws Exception{
if(i == 5){
throw new Exception();//使用throw关键字抛出自定义异常对象。
}
System.out.println("第"+i+"次施法成功");
}
//main方法中调用了用throws声明的shiFa方法,所以main可以使用throws来声明自己,让自己的调用者(JVM)去处理。
public static void main(String [] args) throws Exception{
ShenXian shenxian = new ShenXian();
for (int i = 1; i < 7;i++){
shenxian.shiFa(i);
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

2、try-catch-finally

    try{
        需要被检测的代码;
    }
    catch(异常类 变量){
        处理异常的代码(处理方式)
    }
    finally{
        无论是否发生异常都会执行的代码
        释放资源,如关闭I/O流,断开数据库连接。
    }

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo {
public static void main(String[] args) {
String s = "2015-8-27";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date d = sdf.parse(s);
System.out.println(d);
} catch (ParseException e) {
//e.printStackTrace();
System.out.println("解析日期出问题了");
} finally{
System.out.println("清理运行后垃圾");
}

}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

3、自定义异常类  

    有时候我们需要自己定义异常类,那该如何处理呢?

    首先需要自己建立一个类,并继承Exception或者RuntimeException及其子类。如果继承Exception则自定义异常类是编译时异常类,如果继承RuntimeException则自定义异常类是运行时异常类。

    然后参照一些源码(例如IOExceprion),修改构造方法。

import java.util.Scanner;

public class MyExceptionDemo extends Exception{
public MyExceptionDemo(){}
public MyExceptionDemo(String message){
super(message);
}
}

public class Detector {
public void check(int score) throws MyExceptionDemo{
if(score>100||score<0){
throw new MyExceptionDemo("分数必须在0-100之间");
}
else{
System.out.println("分数没有问题");
}
}
}

public class Student {
public static void main(String[] args) {
Scanner sc = new Scanner (System.in);
System.out.println("请输入学生成绩:");
int score = sc.nextInt();

Detector t = new Detector();
try {
t.check(score);
} catch (MyExceptionDemo e) {
e.printStackTrace();
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

4、异常的注意事项

    异常在子类复写父类方法时的注意事项

    1)子类抛出的异常必须是父类的异常的子类或者子集,不能抛出父类没有的异常。

    2)父类或者接口没有异常抛出时,而子类方法内如果有异常发生,只能使用try,不能使用throws。

5、异常的面试题

    面试题1:finally里面的语句一定会被执行么?

    一般来说,finnally里面的语句一定会被执行,但是如果在执行之前,虚拟机已经退出了,那么就不会再执行了。

    面试题2:final,finally和finalize的区别

    final是最终的意思,可以修饰类、变量、方法。被final修饰的类不可以被继承,被final修饰的变量是常量,被final修饰的方法不可以被重写。

    finally是异常处理的一部分,用于执行必须要执行的操作,例如:关闭资源。

    finalize是Object类的一个方法,用于垃圾回收。

    面试题3:如果catch里面有return语句,请问finally里面的代码还会执行吗?如果会,请问是在return前,还是return后?

    通过看下面的程序,我们可以知道结果。

public class FinallDemo2 {
public static void main(String[] args) {
System.out.println(getInt());
}

public static int getInt() {
int a = 10;
try {
System.out.println(a / 0);
a = 20;
} catch (ArithmeticException e) {
a = 30;
return a;
/*
* return a在程序执行到这一步的时候,这里不是return a而是return 30;这个返回路径就形成了。
* 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40 再次回到以前的返回路径,继续走return
* 30;
*/
} finally {
a = 40;
}
return a;
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

    由此可以看出finally里面的代码在return语句之前执行。

6.2 File类

    File:文件和目录(文件夹)路径名的抽象表示形式    

6.2.1 常用方法

    1、构造方法:

    File(String pathname):根据一个路径得到File对象

    File(String parent, String child):根据一个目录和一个子文件/目录得到File对象

    File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象

    示例:

public class FileDemo {
public static void main(String[] args) {
// File(String pathname):根据一个路径得到File对象
// 把f:\\rom\\romdemo.txt封装成一个File对象
File file = new File("f:\\rom\\romdemo.txt");

// File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
File file2 = new File("f:\\rom", "romdemo.txt");

// File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象
File file3 = new File("f:\\rom");
File file4 = new File(file3, "romdemo.txt");

// 以上三种方式其实效果一样
}
}

    2、创建功能:

    public boolean createNewFile():创建文件 如果存在这样的文件,就不创建了

    public boolean mkdir():创建文件夹 如果存在这样的文件夹,就不创建了

    public boolean mkdirs():创建文件夹,如果父文件夹不存在,会帮你创建出来

    3、删除功能

    public boolean delete():可以用来删除文件,也可以用来删除文件夹

    注意:

    1)创建文件夹时如果不写盘符路径,默认在项目路径下

    2)要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹

    示例:

import java.io.File;
import java.io.IOException;

public class FileDemo {
public static void main(String[] args) throws IOException {
//在G盘根目录下创建文件a.txt
File file0 = new File("g:\\a.txt");
System.out.println("createNewFile:" + file0.createNewFile());

//在项目路径下创建a.txt文件
File file1 = new File("a.txt");
System.out.println("createNewFile:" + file1.createNewFile());

//在项目路径下创建爱你aaa\bbb
File file2 = new File("aaa\\bbb");
System.out.println("mkdirs:" + file2.mkdirs());

// 删除功能:删除bbb这个文件夹
File file4 = new File("aaa\\bbb");
File file5 = new File("aaa");
System.out.println("delete:" + file4.delete());
System.out.println("delete:" + file5.delete());
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

JavaSE---IO流基础(异常、File类、递归)

    4、重命名功能

    public boolean renameTo(File dest):如果路径相同,就是简单的重命名;如果路径不相同,就是剪切过去并且重命名

    示例:

import java.io.File;
import java.io.IOException;

/*
* 重命名功能:public boolean renameTo(File dest)
* 如果路径名相同,就是改名。
* 如果路径名不同,就是改名并剪切。
*
* 路径以盘符开始:绝对路径c:\\a.txt
* 路径不以盘符开始:相对路径a.txt
*/
public class FileDemo {
public static void main(String[] args) throws IOException {
// 创建一个文件对象
File file = new File("林青霞.jpg");
// 需求:修改这个文件的名称为"东方不败.jpg"
File newFile = new File("东方不败.jpg");
System.out.println("renameTo:" + file.renameTo(newFile));
}
}

    程序运行前:

JavaSE---IO流基础(异常、File类、递归)

    程序运行后:

JavaSE---IO流基础(异常、File类、递归)

JavaSE---IO流基础(异常、File类、递归)


    5、判断功能

    public boolean isDirectory():判断是否是目录
    public boolean isFile():判断是否是文件
    public boolean exists():判断是否存在
    public boolean canRead():判断是否可读
    public boolean canWrite():判断是否可写
    public boolean isHidden():判断是否隐藏

public class FileDemo {
public static void main(String[] args) throws IOException {
// 创建文件对象
File file = new File("a.txt");
file.createNewFile();


System.out.println("isDirectory:" + file.isDirectory());// false
System.out.println("isFile:" + file.isFile());// true
System.out.println("exists:" + file.exists());// true
System.out.println("canRead:" + file.canRead());// true
System.out.println("canWrite:" + file.canWrite());// true
System.out.println("isHidden:" + file.isHidden());// false
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

   6、获取功能

    public String getAbsolutePath():获取绝对路径
    public String getPath():获取相对路径
    public String getName():获取名称
    public long length():获取长度。字节数
    public long lastModified():获取最后一次的修改时间,毫秒值

    public String[] list():获取指定目录下的所有文件或者文件夹的名称数组
    public File[] listFiles():获取指定目录下的所有文件或者文件夹的File数组

    示例:

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/*
* 获取功能:
* public String getAbsolutePath():获取绝对路径
* public String getPath():获取相对路径
* public String getName():获取名称
* public long length():获取长度。字节数
* public long lastModified():获取最后一次的修改时间,毫秒值
*/
public class FileDemo {
public static void main(String[] args) throws IOException {
//创建文件对象
File file = new File("G:\\demo");
file.createNewFile();


System.out.println("getAbsolutePath:" + file.getAbsolutePath());
System.out.println("getPath:" + file.getPath());
System.out.println("getName:" + file.getName());
System.out.println("length:" + file.length());
System.out.println("lastModified:" + file.lastModified());

System.out.println("----------");
String[] strArray = file.list();
for (String s : strArray) {
System.out.println(s);
}
System.out.println("---------");

File[] fileArray = file.listFiles();
for(File f:fileArray){
System.out.println(f.getName());
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

    练习:判断F盘根目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称

import java.io.File;

public class FileDemo {
public static void main(String[] args) {
//封装f判断目录
File file = new File("f:\\");

//获取该目录下所有文件或者文件夹的File数组
File[] fileArray = file.listFiles();

//遍历该File数组,得到每一个File对象,然后判断
for(File f:fileArray){
//是否是文件
if(f.isFile()){
if(f.getName().endsWith(".jpg")){
System.out.println(f.getName());
}
}
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

  7、文件过滤器功能

    public String[] list(FilenameFilter filter)
    public File[] listFiles(FilenameFilter filter)

    使用文件过滤器来实现判断F盘根目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称

import java.io.File;
import java.io.FilenameFilter;

public class FileDemo2 {
public static void main(String[] args) {

File srcFile = new File("f:");
String[] strArray = srcFile.list(new FilenameFilter() {

@Override
public boolean accept(File dir, String name) {
File file = new File(dir,name);
return file.isFile()&&file.getName().endsWith(".jpg");
}
});
for(String s:strArray){
System.out.println(s);
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)


6.2.2 File类的练习题

    需求:把G:\评书\三国演义下面的视频名称修改为 00?_介绍.avi

import java.io.File;

public class FileDemo {
public static void main(String[] args) {
//封装目录
File srcFolder = new File("g:\\评书\\三国演义\\");
//获取该目录下所有文件的File数组
File[] fileArray = srcFolder.listFiles();

//遍历该File数组,得到每一个File对象
for(File file:fileArray){
//G:\评书\三国演义\三国演义_001_[评书网-今天很高兴,明天就IO了]_桃园三结义.avi
//改为:G:\评书\三国演义\001_桃园三结义.avi
String name = file.getName();
int startIndex = name.indexOf("_");
String numberString =name.substring(startIndex+1, startIndex+4);
int endIndex = name.lastIndexOf("_");
String nameString = name.substring(endIndex);
String newName = numberString.concat(nameString);

//重命名
File newFile = new File(srcFolder,newName);
file.renameTo(newFile);
}
}
}

    程序运行前:

JavaSE---IO流基础(异常、File类、递归)

    程序运行后:

JavaSE---IO流基础(异常、File类、递归)

6.3 递归

6.3.1 递归介绍

    递归就是方法定义中调用方法本身的现象。例如:

 public void show(int n) {
if(n <= 0) {
System.exit(0);
}
System.out.println(n);
}
    注意:

    1)递归一定要有出口,否则就是死递归

    2)递归的次数不能太多,否则就内存溢出

    3)构造方法不能递归使用

示例:请用代码实现求5的阶乘

    因为5! = 1*2*3*4*5,5! = 5*4!。所以可以使用循环实现,也可以使用递归实现

public class DiGuiDemo {
public static void main(String[] args) {
int jc = 1;
for (int x = 2; x <= 5; x++) {
jc *= x;
}
System.out.println("5的阶乘是:" + jc);

System.out.println("5的阶乘是:"+jieCheng(5));
}

/*
* 做递归要写一个方法:
* 返回值类型:int
* 参数列表:int n
* 出口条件:
* if(n == 1) {return 1;}
* 规律:
* if(n != 1) {return n*方法名(n-1);}
*/
public static int jieCheng(int n){
if(n==1){
return 1;
}
else{
return n*jieCheng(n-1);
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

6.3.2 递归的练习题:

    练习1:不死神兔问题。有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第二十个月的兔子对数为多少?

/*
* 有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第二十个月的兔子对数为多少?
* 分析:我们要想办法找规律
* 兔子对数
* 第一个月: 1
* 第二个月:1
* 第三个月:2
* 第四个月:3
* 第五个月:5
* 第六个月:8
* ...
*
* 由此可见兔子对象的数据是:
* 1,1,2,3,5,8...
* 规则:
* A:从第三项开始,每一项是前两项之和
* B:而且说明前两项是已知的
*
* 如何实现这个程序呢?
* A:数组实现
* B:变量的变化实现
* C:递归实现
*/
public class DiGuiDemo2 {
public static void main(String[] args) {
// 数组实现
int[] arr = new int[20];
arr[0] = 1;
arr[1] = 1;
for(int x = 2;x<arr.length;x++){
arr[x]=arr[x-1]+arr[x-2];
}
System.out.println(arr[19]);
System.out.println("-------");
//通过变量的变化实现
int a = 1;
int b =1;
for(int x =0;x<18;x++){
// 临时变量存储上一次的a
int temp = a;
a=b;
b+=temp;

}
System.out.println(b);
System.out.println("--------");

System.out.println(fib(20));
}
/*
* 方法:
* 返回值类型 int
* 参数列表 int n
* 出口条件:
* 第一个月是1,第二个月是1
* 规律:
* 从第三个月开始每一个月是前两个月之和
*/
public static int fib(int n){
if(n==1||n==2){
return 1;
}
else{
return fib(n-1)+fib(n-2);
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

    练习2:把F:\code目录下所有的.java结尾的文件的绝对路径输出在控制台上

import java.io.File;

public class FilePathDemo {
public static void main(String[] args) {
// 封装目录
File srcFolder = new File("F:\\code");
// 递归功能实现
getAllJavaFilePaths(srcFolder);
}

public static void getAllJavaFilePaths(File srcFolder) {
// 获取该目录下所有的文件或者文件夹的File数组
File[] file = srcFolder.listFiles();
// 遍历该File数组,得到每一个File对象
for (File newFile : file) {
// 判断该File对象是否是文件夹
if (newFile.isDirectory()) {
getAllJavaFilePaths(newFile);
} else {
// 判断是否以.java结尾
if (newFile.getName().endsWith(".java")) {
// 输出该文件的绝对路径
System.out.println(newFile.getAbsolutePath());
}
}
}
}
}

    运行结果:

JavaSE---IO流基础(异常、File类、递归)

    练习3:递归删除带内容的目录demo(F:\code\Eclipse_JavaSE_workspace\day20_DiGui\demo)。

import java.io.File;
import java.io.IOException;

public class FileDelectDemo {
public static void main(String[] args) throws IOException {
// 封装demo目录
File srcFolder = new File("demo");

delectFolder(srcFolder);
}

public static void delectFolder(File srcFolder) {
// 获取该目录下的所有文件或者文件夹的File数组
File[] fileArray = srcFolder.listFiles();
// 遍历该File数组,得到每一个File对象
for (File file : fileArray) {
// 判断该File对象是否是文件夹
if (file.isDirectory()) {
delectFolder(file);
} else {
System.out.println(file.getName() + "---" + file.delete());
}
}
System.out.println(srcFolder.getName() + "---" + srcFolder.delete());
}
}

    程序运行前:

JavaSE---IO流基础(异常、File类、递归)

    程序运行后:

JavaSE---IO流基础(异常、File类、递归)

JavaSE---IO流基础(异常、File类、递归)