Java异常和文件

一、异常

1.定义

异常:异常就是代表程序出现的问题

体系结构:

 最上层的是 Throwable 类,下面有两个子类:

① Error:代表系统级别的问题(属于严重问题,比如:内存溢出)。

           系统一旦出现问题,Sun 公司就会把这些错误封装成 Error 对象。

           Error 是给 Sun 公司自己用的,不是给程序员用的。(如硬件问题:开发人员无法处理)。

② Exception:异常,代表程序可能出现的问题。、

                   我们通常会用 Exception 以及他的子类来封装程序出现的问题。


异常分为两类:

① 运行时异常:RuntimeException及其子类,编译阶段不会出现异常提醒。

                        运行时出现的异常(如:数组索引越界异常)

 细节:

编译阶段不会报错,运行时代码出错而导致程序出现的问题,一般是由参数传递错误带来的问题。

② 编译时异常:直接继承于 Exception,编译阶段就会出现出现异常提醒(如:日期解析异常)。

手动处理后,代码则不会报错:

细节: 

① 编译阶段:java不会运行代码,只会检查语法是否错误,或者做一些优化。

 ② 编译阶段需要进行处理, 作用在于提醒程序员检查本地信息(语法是否正确,文件是否存在)。

2.作用

① 异常用来查看 bug 的关键参考信息

② 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况。


需求:创建一个学生对象,年龄只能是20-30岁,否则报错

在过去,我们只能通过打印到控制台的方式, 但调用者并不知晓,程序仍旧正常运行。

我们可以通过将异常作为返回值进行返回,这样调用者就会拿到一个异常,知晓底层出现的问题。

调用者在拿到异常之后,会有两种选择:

① 自己单独处理

② 打印在控制台上(默认)

3.异常的处理方式 

(1)JVM 默认的处理方式:

把异常的名称,异常原因以及异常出现的位置等信息输出在控制台。

程序立即停止执行,下面的代码则不会再执行了。

(2)自己处理(捕获异常)

目的:当代码出现异常时,可以让程序继续往下执行,而不是停止虚拟机。

细节:

在 try 中运行错误代码后,程序就会创建一个对应的异常对象(new ArithmeticException);

再拿着这个对象,和 catch 的小括号进行比对,看括号中的异常变量 e 是否可以接收这个对象:

        如果能被接收,就表示异常被捕获,继续执行 catch 中的代码,然后往下执行。

        如果不能接收,异常交给 JVM 进行处理


Question1:如果 try 中没有遇到问题,怎么执行?

如果 try 中没有遇到问题,会把 try 中的代码全部执行完毕,不会执行 catch 中的代码。

结论:只有当出现了异常,才会执行 catch 中的代码


Question2: 如果 try 中可能会遇到多个问题,会怎么执行?

如果 try 中出现多个问题,在遇到第一个问题后,就会直接跳转到对应的 catch 中进行捕获。

捕获成功后,执行 catch 中的代码。但 try 中剩下的代码不会在看了,直接跳过。 

如果没有对应的 catch 与之匹配,则交给 JVM 进行处理

Tips1:一般情况下,当可能出现多种异常时,我们会写多个异常与之对应,使其一定能成功捕获。

Tips2: JDK7后,我们可以在一个 catch 中捕获多个异常,中间用 | 隔开。

                表示如果出现了A异常或B异常,采用同一种处理方案。

注意:

如果要捕获多个异常,这些异常中如果存在继承关系的话,那么父类一定要写在最下面,否则报错。

因为 Exception 是异常体系中的顶级父类,任何异常都属于它,都可以被它接收(多态)。

所以 多个异常出现时,父类只能写在最下方。

如果写在上方,则一定能够被捕获,下方的异常则不会执行。 


在上面,我们捕获异常之后,都是通过直接书写打印语句进行处理的。

Throwable 定义了 3 个成员方法,可以直接 返回 / 输出 错误信息。

细节:

① getMessage 和 toString 方法,返回的都是 String 类型的错误提示信息,需要书写打印语句。

② printStackTrace 方法的返回值类型是 void,会直接在控制台进行输出错误信息。

     而且错误信息要比前两个方法更为全面,所以最为常用

③ printStackTrace 方法底层是利用 System.err.println 进行输出,将异常的错误信息以红色字体输出在控制台上(因为多线程的关系,输出顺序和代码的顺序可能不一致)。

④ printStackTrace 方法仅仅是打印异常的错误信息,并不会停止程序运行

(3)抛出异常

在一个方法中,如果出现了异常。

方法就没有继续运行下去的意义了,此时可以采取抛出处理。

让该方法结束运行,并告诉调用者出现了问题。

抛出异常分为两种:throws 和 throw

① throws:写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能会有那些异常。

作用:声明可能出现的异常,向上抛出,让调用者知晓,方便快速进行处理

        (调用者可以立马知晓会出现什么异常,进行捕获,可以避免不匹配的异常处理)

注意:

编译时异常:必须要声明

运行时异常:可以不声明 

 ② throw:写在方法内,用于结束方法。

                手动抛出异常对象,交给调用者,调用者针对异常进行处理。

public class CatchDemo4 {
    public static void main(String[] args) {
        int[] arr = {};
        int max = 0;
        try {
            max = getMax(arr);
        } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        }
        System.out.println(max);
    }

    //求数组的最大值
    public static int getMax(int[] arr) {
        if (arr == null) {
            //手动抛出异常
            throw new NullPointerException();
        }
        if (arr.length == 0) {
            //手动抛出异常
            throw new ArrayIndexOutOfBoundsException();
        }
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        return max;
    }
}

 注:手动抛出异常后,就结束了方法,方法中下面的的代码就不会再执行了。 

4.自定义异常

(1)引言

Question:为什么需要自定义异常呢?

假设现在要录入一个学生信息,姓名长度需要在 3~10 之间,年龄需要在 18~40 之间。

那么在 set 方法进行赋值时,数据不正确的话,该抛出何种异常呢?

我们发现,找不到一个合适的异常来描述这两个问题信息。

所以只能被迫采用运行时异常的父类 RuntimeException。

但这样的缺点是:无法单独处理这两个异常的信息,不能把姓名和年龄的处理方案分开。

所以,我们需要自定义异常类来描述这两个异常。


(2)步骤:

① 定义异常类(类名:异常名+Exception)

② 写继承关系(运行时异常:继承 RunTimeException;编译时异常:继承 Exception)

③ 写空参构造

④ 写带参构造

意义:为了让控制台的报错信息更加的见名知意。

通过自定义异常,可以针对不同的异常,设置不用的处理方案,且这些方案可以独立存在。

细节:

① 选择继承关系时,遵循:

     运行时异常 RunTimeException :表示由于参数错误而导致的问题

     编译时异常 Exception :提醒程序员检查本地信息

② 如果继承的是编译时异常 Exception,那么在方法中抛出该异常时,必须同时用 throws 声明可能出现的异常,否则报错。

二、File文件

1.定义

File 对象就表示一个路径,可以是文件的路径,也可以是文件夹的路径

这个路径可以是存在的,也允许是不存在的。

路径分为相对路径和绝对路径两种:

绝对路径:带盘符的;

相对路径: 不带盘符的,默认在当前项目下找。

2.方法

(1)获取 File 对象

public class FileObject {
    public static void main(String[] args) {
        //1.根据字符串表示的路径,变成File对象
        String str = "C:\\Users\\24285\\Desktop\\a.txt";
        File f1 = new File(str);
        System.out.println(f1);// C:\Users\24285\Desktop\a.txt

        //2.根据父路径字符串和子路径字符串创建File对象
        // 父级路径:C:\Users\24285\Desktop
        // 子级路径:a.txt
        String parent = "C:\\Users\\24285\\Desktop";
        String child = "a.txt";
        File f2 = new File(parent, child);
        System.out.println(f2);// C:\Users\24285\Desktop\a.txt

        //3.将一个File表示的路径和一个字符串表示的路径进行拼接,创建File对象
        File parent2 = new File("C:\\Users\\24285\\Desktop");
        String child2 = "a.txt";
        File f3 = new File(parent2, child2);
        System.out.println(f3);// C:\Users\24285\Desktop\a.txt
    }
}

 细节:

① 路径只有变成 File 对象后,才能调用 File 类的一系列方法(如修改、删除等),否则 Java 只认为是一个普通的字符串。

② 根据父路径字符串和子路径字符串创建 File 对象时:

最好直接使用:File f2 = new File(parent, child);

而不是手动拼接:File f2 = new File(parent + "\\" + child);

因为在不同的操作系统下,路径的分隔符不一样,windows系统中用 \\,Linux中用 /

此时如果代码部署到另一个系统上运行,就会发生错误。

直接使用  new File(parent, child) 的话,在底层会先获取 os ,根据不同的 os 选择不同的分隔符。

(2)常见成员方法 

① 判断

 细节:

存在是前提条件,只有存在,才能判断是不是文件或者文件夹。

如果文件或者文件夹根本不存在,那么即使名字的确是文件名或者文件夹名,也返回 false。

② 获取
I. length 方法

细节:

① length 方法只能获取文件的大小,单位是字节。

② length 方法不能获取文件夹的大小,如果想要获取文件夹的大小,需要计算这个文件夹中所有文件的大小,再累加计算总和。

II. getAbsolutePath 方法

 III. getPath 方法
public class FileGet {
    public static void main(String[] args) {
        File f1 = new File("D:\\text\\aaa.txt");//绝对路径
        File f2 = new File("File\\a.txt");//相对路径

        //3.getPath  返回定义文件对象时使用的路径
        String path1 = f1.getPath();
        System.out.println(path1);//D:\text\aaa.txt

        String path2 = f2.getPath();
        System.out.println(path2);//File\a.txt
    }
}

简单来说:就是构造方法中的参数是什么,返回的就是什么

Ⅳ. getName 方法
public class FileGet {
    public static void main(String[] args) {
        File f1 = new File("D:\\text\\aaa.txt");
        File f2 = new File("D:\\text");

        //4.getName  获取文件的名字(带后缀名)
        String name1 = f1.getName();
        System.out.println(name1);//aaa.txt

        String name2 = f2.getName();
        System.out.println(name2);//text
    }
}

 细节:

① 如果是文件,就会返回 文件名.后缀名(aaa:文件名        .txt:后缀名、扩展名)

② 如果是文件夹,返回的就是文件夹的名字

Ⅴ. lastModified 方法
public class FileGet {
    public static void main(String[] args) {
        File f1 = new File("D:\\text\\aaa.txt");

        //5.lastModified  返回文件的最后修改时间(时间毫秒值)
        long time = f1.lastModified();
        System.out.println(time);//1718450498844

        //将毫秒值转换成字符串表示的时间
        ZonedDateTime date = Instant.ofEpochMilli(time).atZone(ZoneId.of("Asia/Shanghai"));
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EE a");
        String str = dtf.format(date);
        System.out.println(str);//2024-06-15 19:21:38 周六 下午
    }
}
③ 创建和删除 

I.  createNewFile 方法
public class FileCreate {
    public static void main(String[] args) throws IOException {
        //1.createNewFile  创建一个新的空的文件
        File f1 = new File("D:\\text\\bbb.txt");
        boolean b1 = f1.createNewFile();
        System.out.println(b1);//true

        try {
            File f2 = new File("D:\\text\\ccc\\aaa.txt");
            boolean b2 = f2.createNewFile();
            System.out.println(b2);
        } catch (IOException e) {
            e.printStackTrace();
        }

        File f3 = new File("D:\\text\\ddd");
        boolean b3 = f3.createNewFile();
        System.out.println(b3);//true
    }
}

运行结果:

细节:

① 如果当前路径表示的文件是不存在的,则创建成功,方法返回 true;

    如果当前路径表示的文件是存在的,则创建失败,方法返回 false。

② 如果父级路径是不存在的,那么方法会产生 IOException 异常。

③ createNewFile 方法创建的一定是文件,如果路径中不含后缀名,则创建一个没有后缀的文件。

II. mkdir 方法
public class MakeDir {
    public static void main(String[] args) {
        //2.mkdir  make directory 创建文件夹(目录)
        File f1 = new File("D:\\text\\ccc");
        boolean b1 = f1.mkdir();
        System.out.println(b1);//true

        File f2 = new File("D:\\text\\ddd");
        boolean b2 = f2.mkdir();
        System.out.println(b2);//false

        File f3 = new File("D:\\text\\aaa\\bbb");
        boolean b3 = f3.mkdir();
        System.out.println(b3);//false
    }
}

运行结果:

细节:

① windows 当中路径是唯一的,如果当前路径已经存在,则创建失败,返回 false。

    虽然 ddd 文件夹不存在,但 ddd 文件存在。路径唯一,所以创建失败。

② mkdir 方法只能创建单级文件夹,无法创建多级文件夹。

III. mkdirs 方法
public class MakeDirs {
    public static void main(String[] args) {
        //3.mkdirs  创建多级文件夹
        File f1 = new File("D:\\text\\aaa\\bbb");
        boolean b1 = f1.mkdirs();
        System.out.println(b1);//true

        //3.mkdirs  创建多级文件夹
        File f2 = new File("D:\\text\\bbb");
        boolean b2 = f2.mkdirs();
        System.out.println(b2);//true
    }
}

运行结果:

细节:

mkdirs 既可以创建多级文件夹,也可以创建单级文件夹。


Question:既然 mkdirs 都可以,那为什么还需要 mkdir 呢?

在 mkdirs 的底层,也会先使用 mkdir 方法。

如果不行,才会执行自己的代码逻辑。

结论:在以后只要创建文件夹,都用 mkdirs 就行了

Ⅳ. delete方法
public class FileDelete {
    public static void main(String[] args) {
        //4.delete  删除文件和空文件夹
        File f1 = new File("D:\\text\\aaa.txt");
        boolean b1 = f1.delete();
        System.out.println(b1);//true

        File f2 = new File("D:\\text\\aaa");
        boolean b2 = f2.delete();
        System.out.println(b2);//false

        File f3 = new File("D:\\text\\bbb");
        boolean b3 = f3.delete();
        System.out.println(b3);//true
    }
}

运行结果:

细节:

delete 方法只能删除文件和空文件夹,且直接删除,不走回收站。

如果删除的是非空文件夹,则删除失败,返回 false 。


Question:如何删除一个有内容的文件夹(多级文件夹)呢?--> 递归

public class MultilevelFileDelete {
    public static void main(String[] args) {
        File file = new File("D:\\text");
        delete(file);
    }

    public static void delete(File src) {
        File[] files = src.listFiles();
        //1.先删除文件夹中的所有内容
        for (File f : files) {
            //是文件 --> 删除
            //不是 --> 递归进入
            if (f.isFile()) {
                f.delete();
            } else {
                delete(f);
            }
        }
        //2.再删除文件夹本身
        src.delete();
    }
}
④ 获取并遍历

I. listFiles 方法 (重点掌握)
public class ListFile {
    public static void main(String[] args) {
        File f = new File("D:\\text");
        //listFiles  获取当前该路径下所有内容(文件和文件夹)
        File[] files = f.listFiles();
        for (File file : files) {
            //file依次表示text文件夹中的每一个文件或者文件夹
            System.out.println(file);
        }
    }
}

运行结果:

 细节:

①  当调用者 File 表示的路径不存在时,返回 null。

② 当调用者 File 表示的路径是文件时,返回 null。

③ 当调用者 File 表示的路径是一个空文件夹时,返回一个长度为 0 的数组。

④ 当调用者 File 表示的路径是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回。

⑤ 当调用者 File 表示的路径是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File 数组中返回,包含隐藏文件。

⑥ 当调用者File表示的路径是需要权限才能访问的文件夹时,返回null。

II. listRoots 和 list 方法
public class FileDemo1 {
    public static void main(String[] args) {
        //1.listRoots  获取系统中所有的盘符
        File[] arr = File.listRoots();
        System.out.println(Arrays.toString(arr));

        //2.list()    获取当前该路径下所有内容(仅仅能获取名字)
        File f1 = new File("D:\\text");
        String[] arr2 = f1.list();
        for (String s : arr2) {
            System.out.println(s);
        }
    }
}

运行结果:

细节:

① 文件系统根就是盘符

② list 方法也可以获取当前该路径下所有内容,但仅仅能获取名字

Ⅲ. 文件名过滤器

需求:获取 D:\\text 文件夹里面所有的 txt 文件

1) list(FilenameFilter filter)
public class FileDemo2 {
    public static void main(String[] args) {
        File f = new File("D:\\text");

        String[] arr = f.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File src = new File(dir, name);
                return src.isFile() && name.endsWith(".txt");
            }
        });

        System.out.println(Arrays.toString(arr));//[bbb.txt]
    }
}

细节:

① accept 方法的形参,依次表示 text 文件夹里面每一个文件或者文件夹的路径

       参数一:父级路径

        参数二:子级路径

        返回值:如果返回值为true,就表示当前路径保留

                      如果返回值为false,就表示当前路径舍弃不要

② FilenameFilter 是一个函数式接口,可以使用 Lambda 表达式。


③ 用 list 方法实现的文件过滤器,获取的内容也仅仅只有名字。 

2)listFiles(FileFilter filter)
public class FileDemo3 {
    public static void main(String[] args) {
        File f = new File("D:\\text");
        //调用listFiles(FileFilter filter)
        File[] arr = f.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isFile() && pathname.getName().endsWith(".txt");
            }
        });
        System.out.println(Arrays.toString(arr));//[D:\text\bbb.txt]
    }
}
3)listFiles(FilenameFilter filter)
public class FileDemo4 {
    public static void main(String[] args) {
        File f = new File("D:\\text");

        //调用listFiles(FilenameFilter filter)
        File[] arr = f.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File src = new File(dir, name);
                return src.isFile() && name.endsWith(".txt");
            }
        });
        System.out.println(Arrays.toString(arr));//[D:\text\bbb.txt]
    }
}

细节:

accept 中的形参:

        dir:表示父级路径

        name:表示子级路径

dir + name 就是 2)中 accept 的形参 pathname

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值