大数据开发基础入门与项目实战(一)Java SE之7.异常机制和File类

本文深入探讨了Java中的异常处理机制,包括异常的基本概念、分类、避免、捕获、finally的使用以及自定义异常的实现。此外,还详细讲解了File类的使用,涵盖文件和目录的操作,如获取文件信息、创建删除文件和目录以及遍历目录内容。

前言

本文主要介绍了Java中的异常机制和File类,详细介绍了异常机制的使用场景和注意事项,同时介绍了用File类获取文件基本信息和操作文件。

1.异常机制

(1)异常机制的基本概念

异常就是“不正常”的含义,在Java语言中主要指程序执行中发生的不正常情况
java.lang.Throwable类是Java语言中所有错误Error和异常Exception的超类:

  • Error类主要用于描述Java虚拟机无法解决的严重错误通常无法编码解决 ,如JVM挂掉了等;

  • Exception类主要用于描述因编程错误或偶然外在因素导致的轻微错误通常可以编码解决 ,如0作为除数等。

(2)异常机制的分类和结构

java.lang.Exception类是所有异常的超类,主要分为以下两种:

  • RuntimeException

    运行时异常,也叫作非检测性异常 ,在编译阶段不能被检测出来、只能在运行时才会暴露出来。

    其中RuntimeException类的主要子类:
    

    - ArithmeticException类

        算术异常
        

    - ArrayIndexOutOfBoundsException类

        数组下标越界异常
        

    - NullPointerException

        空指针异常
        

    - ClassCastException

        类型转换异常
        

    - NumberFormatException

        数字格式异常

  • IOException和其他异常

    其他异常,也叫作检测性异常 ,即在编译阶段都就被编译器检测出来的异常。

测试运行时异常,如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 8:03
 * @description javase-com.stage1.module4
 */
public class ExceptionTest {

    public static void main(String[] args) {

        // 1.非检测性异常
        System.out.println(5 / 0);          //  编译可以通过,运行阶段会发生算数异常

        System.out.println("程序执行结束了吗??");

    }
}

输出:

Exception in thread "main" java.lang.ArithmeticException: / by zero
  at com.stage1.module4.ExceptionTest.main(ExceptionTest.java:13)

可以看到,编译可以通过,运行阶段才会发生ArithmeticException异常,输出了异常信息;

并且在发生异常后没有继续向下执行、而是直接停止运行程序,这是因为当程序执行过程中发生异常但又没有手动处理时,由Java虚拟机采用默认方式处理异常,即打印异常的名称、异常发生的原因、异常发生的位置和终止程序。

再测试检测性异常,如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 8:03
 * @description javase-com.stage1.module4
 */
public class ExceptionTest {

    public static void main(String[] args) {

        // 1.非检测性异常
        // System.out.println(5 / 0);          //  编译可以通过,运行阶段会发生算数异常

        // 2.检测性异常
        Thread.sleep(1000);

        System.out.println("程序执行结束了吗??");

    }
}

此时在编译阶段就无法通过,提示Unhandled exception: java.lang.InterruptedException,不处理就无法到运行阶段。

可以得到,异常机制的结构如下:

exception structure

(3)异常的避免

代码中一旦出现异常并且不处理,就会抛出异常并且立即中断程序的执行,例如:

package com.stage1.module4;

import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 8:23
 * @description javase-com.stage1.module4
 */
public class ExceptionAvoidTest {

    public static void main(String[] args) {

        int a = 10;
        int b = 0;
        System.out.println(a / b);                      // ArithmeticException

        int[] arr = new int[3];
        int pos = 5;
        System.out.println(arr[pos]);                   // ArrayIndexOutOfBoundsException

        String str1 = null;
        System.out.println(str1.length());              // NullPointerException

        Exception ex = new Exception();
        IOException ie = (IOException) ex;              // ClassCastException

        String str2 = "123a";
        System.out.println(Integer.parseInt(str2));     // NumberFormatException

        System.out.println("程序执行结束了吗?");
    }
}

此时,在发生算术异常后就不会再向下执行,此时可以用if条件判断避免异常的发生。

修改上面的代码如下:

package com.stage1.module4;

import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 8:23
 * @description javase-com.stage1.module4
 */
public class ExceptionAvoidTest {

    public static void main(String[] args) {

        // ArithmeticException
        int a = 10;
        int b = 0;
        if (0 != b) System.out.println(a / b);

        // ArrayIndexOutOfBoundsException
        int[] arr = new int[3];
        int pos = 5;
        if (pos >= 0 && pos < 5) System.out.println(arr[pos]);

        // NullPointerException
        String str1 = null;
        if (null != str1) System.out.println(str1.length());

        // ClassCastException
        Exception ex = new Exception();
        if (ex instanceof IOException) {
            IOException ie = (IOException) ex;
        }

        // NumberFormatException
        String str2 = "123a";
        if (str2.matches("\\d+")) System.out.println(Integer.parseInt(str2));

        System.out.println("程序执行结束了!");
    }
}


可以看到,在开发中使用if条件判断可以避免异常的发生,但是过多的if条件判断会导致程序的代码加长、臃肿,可读性差。

(4)异常捕获的实现

在避免不了异常的发生时,就需要捕获可能发生的异常。异常捕获的语法格式如下:

try {
    编写可能发生异常的代码;
}
catch(异常类型 引用变量名) {
    编写针对该类异常的处理代码;
}
...   // 可以有多个catch语句块
finally {
    编写无论是否发生异常都要执行的代码;
}

例如:

package com.stage1.module4;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 8:51
 * @description javase-com.stage1.module4
 */
public class ExceptionCatchTest {

    public static void main(String[] args) {

        // 创建一个FileInputStream类型的对象与E:/Test/tmp.txt文件关联,即打开文件
        FileInputStream fis = null;
        try {                                                              // 尝试执行下面语句块中的代码
            System.out.println(111);
            fis = new FileInputStream("E:/Test/tmp.txt");           // 可能发生Unhandled exception: java.io.FileNotFoundException
            System.out.println(222);
        } catch (FileNotFoundException e) {                                // 捕获可能发生的FileNotFoundException异常
            System.out.println(333);
            e.printStackTrace();
            System.out.println(444);
        }

        // 关闭文件
        try {
            System.out.println(555);
            fis.close();
            System.out.println(666);
        } catch (IOException e) {
            System.out.println(777);
            e.printStackTrace();
            System.out.println(888);
        }

        System.out.println("世界上最真情的相依就是你在try我在catch,无论你发什么脾气,我都默默承受并静静的处理,到那时再来期待我们的finally!");
    }
}

文件E:/Test/tmp.txt存在时不会发生异常,执行输出如下:

111
222
555
666
世界上最真情的相依就是你在try我在catch,无论你发什么脾气,我都默默承受并静静的处理,到那时再来期待我们的finally

此时,两个try-catch语句块中都只执行了try块中的代码。

文件不存在时会抛出异常,如下:

111
333
444
555
java.io.FileNotFoundException: E:\Test\tmp.txt (系统找不到指定的文件。)
  at java.base/java.io.FileInputStream.open0(Native Method)
  at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
  at com.stage1.module4.ExceptionCatchTest.main(ExceptionCatchTest.java:20)
Exception in thread "main" java.lang.NullPointerException
  at com.stage1.module4.ExceptionCatchTest.main(ExceptionCatchTest.java:31)

其中,e.printStackTrace();是打印异常信息在程序中出错的位置及原因;

可以看到,在try语句块中发生异常后,不会再继续执行try语句块中的代码、而是直接执行对应的catch语句块中的代码;

并且第二个try-catch语句块中只捕获了IOException、而没有捕获NullPointerException,导致程序终止。

此时要想代码可以继续执行,则需要添加捕获NullPointerException的catch代码块,如下:

package com.stage1.module4;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 8:51
 * @description javase-com.stage1.module4
 */
public class ExceptionCatchTest {

    public static void main(String[] args) {

        // 创建一个FileInputStream类型的对象与E:/Test/tmp.txt文件关联,即打开文件
        FileInputStream fis = null;
        try {                                                              // 尝试执行下面语句块中的代码
            System.out.println(111);
            fis = new FileInputStream("E:/Test/tmp.txt");           // 可能发生Unhandled exception: java.io.FileNotFoundException
            System.out.println(222);
        } catch (FileNotFoundException e) {                                // 捕获可能发生的FileNotFoundException异常
            System.out.println(333);
            e.printStackTrace();
            System.out.println(444);
        }

        // 关闭文件
        try {
            System.out.println(555);
            fis.close();
            System.out.println(666);
        } catch (IOException e) {
            System.out.println(777);
            e.printStackTrace();
            System.out.println(888);
        } catch (NullPointerException e) {
            System.out.println(999);
            e.printStackTrace();
            System.out.println("000");
        }

        System.out.println("世界上最真情的相依就是你在try我在catch,无论你发什么脾气,我都默默承受并静静的处理,到那时再来期待我们的finally!");
    }
}

输出:

111
333
444
555
999
000
世界上最真情的相依就是你在try我在catch,无论你发什么脾气,我都默默承受并静静的处理,到那时再来期待我们的finallyjava.io.FileNotFoundException: E:\Test\tmp.txt (系统找不到指定的文件。)
  at java.base/java.io.FileInputStream.open0(Native Method)
  at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
  at com.stage1.module4.ExceptionCatchTest.main(ExceptionCatchTest.java:20)
java.lang.NullPointerException
  at com.stage1.module4.ExceptionCatchTest.main(ExceptionCatchTest.java:31)

可以看到,加了手动处理异常(异常捕获)与不进行处理的区别在于代码是否可以继续向下执行

可以说,异常捕获是将检测性异常的发生时机从编译阶段后移到运行阶段。

(5)异常捕获的注意事项

在捕获异常的时候,也可以理用多态的特性,即直接捕获Exception,如下:

try {
    System.out.println(555);
    fis.close();
    System.out.println(666);
} /*catch (IOException e) {
    System.out.println(777);
    e.printStackTrace();
    System.out.println(888);
} catch (NullPointerException e) {
    System.out.println(999);
    e.printStackTrace();
    System.out.println("000");
}*/ catch (Exception e) {
    System.out.println(999);
    e.printStackTrace();
    System.out.println("000");
}

此时看起来似乎更简洁、可以捕获的异常更多,但是代码的可读性降低了,因此还是更推荐前面的方式;

当有多个catch分支时,切记小类型应该放在大类型的前面,否则会报错Exception 'XXXException' has already been caught,例如catch (Exception e)不能放到catch (IOException e)的前面。

(6)finally的使用

finally语句块中主要编写无论是否发生异常都要执行的代码。

当没有异常时,如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 9:38
 * @description javase-com.stage1.module4
 */
public class ExceptionFinallyTest {

    public static void main(String[] args) {

        try {
            int a = 10;
            int b = 3;
            System.out.println(a / b);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("无论是否发生异常,都会运行到这里!");
        }

        System.out.println("程序执行结束啦!");

    }
}

输出:

3
无论是否发生异常,都会运行到这里!
程序执行结束啦!

当有异常时,输出:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 9:38
 * @description javase-com.stage1.module4
 */
public class ExceptionFinallyTest {

    public static void main(String[] args) {

        try {
            int a = 10;
            int b = 0;
            System.out.println(a / b);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("无论是否发生异常,都会运行到这里!");
        }

        System.out.println("程序执行结束啦!");

    }
}

输出:

java.lang.ArithmeticException: / by zero
  at com.stage1.module4.ExceptionFinallyTest.main(ExceptionFinallyTest.java:15)
无论是否发生异常,都会运行到这里!
程序执行结束啦!

因为发生的异常都被捕获了,所以程序可以正常执行结束,所以打印出程序执行结束啦!

无论是否发生异常,都会执行finally语句块。

再如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 9:38
 * @description javase-com.stage1.module4
 */
public class ExceptionFinallyTest {

    public static void main(String[] args) {

        try {
            int a = 10;
            int b = 0;
            System.out.println(a / b);
        } catch (ArithmeticException e) {
            e.printStackTrace();
            String str = null;
            System.out.println(str.length());
        } finally {
            System.out.println("无论是否发生异常,都会运行到这里!");
        }

        System.out.println("程序执行结束啦!");

    }
}

输出:

java.lang.ArithmeticException: / by zero
  at com.stage1.module4.ExceptionFinallyTest.main(ExceptionFinallyTest.java:15)
Exception in thread "main" java.lang.NullPointerException
  at com.stage1.module4.ExceptionFinallyTest.main(ExceptionFinallyTest.java:19)
无论是否发生异常,都会运行到这里!

此时,catch语句块中发生新的异常、但是没有被捕获,因此程序会终止执行,因此不能打印程序执行结束啦!

但是因为finally语句块在无论是否发生异常都会执行,因此finally语句块中的代码依然会执行,打印出无论是否发生异常,都会运行到这里!

根据finally语句块的特点,finally通常用于进行善后处理,如关闭已经打开的文件等。

综上,分析异常捕获代码的执行流程如下:

try {
    a;
    b;   // 可能发生异常的语句
    c;
} catch (Exception ex) {
    d;
} finally {
    e;
}

当没有发生异常时的执行流程为a b c e
当发生异常时的执行流程为a b d e

finally的笔试考点如下:

下面的代码中调用方法的输出结果是多少?

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 9:38
 * @description javase-com.stage1.module4
 */
public class ExceptionFinallyTest {

    public static int finallyMethod() {
        try {
            int[] arr = new int[5];
            System.out.println(arr[5]);
            return 0;
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
            return 1;
        } finally {
            return 2;           // 提前结束方法并返回数据
        }
    }

    public static void main(String[] args) {

        try {
            int a = 10;
            int b = 0;
            System.out.println(a / b);
        } catch (ArithmeticException e) {
            e.printStackTrace();
            /*String str = null;
            System.out.println(str.length());*/
        } finally {
            System.out.println("无论是否发生异常,都会运行到这里!");
        }

        System.out.println("-----------------------------");
        int ret = finallyMethod();
        System.out.println("ret = " + ret);

        System.out.println("程序执行结束啦!");

    }
}


输出:

java.lang.ArithmeticException: / by zero
  at com.stage1.module4.ExceptionFinallyTest.main(ExceptionFinallyTest.java:28)
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
  at com.stage1.module4.ExceptionFinallyTest.finallyMethod(ExceptionFinallyTest.java:13)
  at com.stage1.module4.ExceptionFinallyTest.main(ExceptionFinallyTest.java:38)
无论是否发生异常,都会运行到这里!
-----------------------------
ret = 2
程序执行结束啦!

因为finally会提前结束方法并返回数据,所以返回的是2而不是1。

(7)异常抛出的实现

在某些特殊情况下有些异常不能处理或者不便于处理时,就可以将该异常转移 给该方法的调用者,这种方法就叫异常的抛出。

当方法执行时出现异常,则底层生成一个异常类对象抛出,此时异常代码后续的代码就不再执行。

语法格式如下:

访问权限 返回值类型 方法名称(形参列表) throws 异常类型1, 异常类型2, ... { 方法体; }

public void show() throws IOException{}

实现如下:

package com.stage1.module4;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 10:28
 * @description javase-com.stage1.module4
 */
public class ExceptionThrowsTest {

    public static void print() throws IOException {
    // public static void print() throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("E:/Test/tmp.txt");
        System.out.println("抛出异常后是否继续向下执行了?");
        fis.close();
    }

    public static void main(String[] args) /*throws IOException*/ {

        try {
            print();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输出:

java.io.FileNotFoundException: E:\Test\tmp.txt (系统找不到指定的文件。)
  at java.base/java.io.FileInputStream.open0(Native Method)
  at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
  at com.stage1.module4.ExceptionThrowsTest.print(ExceptionThrowsTest.java:16)
  at com.stage1.module4.ExceptionThrowsTest.main(ExceptionThrowsTest.java:24)

其中,FileInputStream fis = new FileInputStream("E:/Test/tmp.txt");有可能发生文件找不到的异常;

throws FileNotFoundException表明如果发生了文件找不到的异常,不对异常进行处理,而是将异常抛给该方法的调用者;

在main方法中也可以抛出异常,此时是将异常抛给了Java虚拟机,但是不建议在main方法中抛出异常。

(8)方法重写中异常抛出规则的验证

在重写方法时的一个规则是不能抛出更大的异常 ,现在进行验证,创建父类ExceptionMethod,如下:

package com.stage1.module4;

import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 10:45
 * @description javase-com.stage1.module4
 */
public class ExceptionMethod {

    public void print() throws IOException {}
}

子类SubExceptionMethod如下:

package com.stage1.module4;

import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 10:45
 * @description javase-com.stage1.module4
 */
public class SubExceptionMethod extends ExceptionMethod {

    @Override
    public void print() throws IOException {}                           // 子类重写的方法可以抛出和父类方法中一样的异常
    // public void print() throws FileNotFoundException {}              // 子类重写的方法可以抛出更小的异常
    // public void print() {}                                           // 子类重写的方法可以不抛出异常
    // public void print() throws ClassNotFoundException {}             // 子类重写的方法不可以抛出平级但是不同的异常
    // public void print() throws Exception {}                          // 子类重写的方法不可以抛出更大的异常

}

可以看到,子类重写的方法可以抛出一样的异常、更小的异常以及不抛出异常,但是不能抛出更大的异常和平级不一样的异常,否则会报错'print()' in 'com.stage1.module4.SubExceptionMethod' clashes with 'print()' in 'com.stage1.module4.ExceptionMethod'; overridden method does not throw 'java.lang.XXXException'

在面临异常抛出或者捕获的选择时,可以参考下面的经验:

若父类中被重写的方法没有抛出异常时,则子类中重写的方法只能进行异常的捕获处理;

若一个方法内部又以递进方式分别调用了多个其他方法,则建议这些方法内可以使用抛出的方式处理到最后一层选择捕获方式处理。

例如:

package com.stage1.module4;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * @author Corley
 * @date 2021/8/4 10:28
 * @description javase-com.stage1.module4
 */
public class ExceptionThrowsTest {

    public static void print() throws IOException {
    // public static void print() throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("E:/Test/tmp.txt");
        System.out.println("抛出异常后是否继续向下执行了?");
        fis.close();
    }

    public static void secondPrint() throws IOException {
        print();
    }

    public static void thirdPrint() throws IOException {
        secondPrint();
    }

    public static void fourthPrint() throws IOException {
        thirdPrint();
    }

    public static void main(String[] args) /*throws IOException*/ {

        try {
            print();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("--------------------------------------");
        try {
            fourthPrint();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输出:

java.io.FileNotFoundException: E:\Test\tmp.txt (系统找不到指定的文件。)
  at java.base/java.io.FileInputStream.open0(Native Method)
  at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
  at com.stage1.module4.ExceptionThrowsTest.print(ExceptionThrowsTest.java:16)
  at com.stage1.module4.ExceptionThrowsTest.main(ExceptionThrowsTest.java:36)
java.io.FileNotFoundException: E:\Test\tmp.txt (系统找不到指定的文件。)
  at java.base/java.io.FileInputStream.open0(Native Method)
  at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
  at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
  at com.stage1.module4.ExceptionThrowsTest.print(ExceptionThrowsTest.java:16)
  at com.stage1.module4.ExceptionThrowsTest.secondPrint(ExceptionThrowsTest.java:22)
  at com.stage1.module4.ExceptionThrowsTest.thirdPrint(ExceptionThrowsTest.java:26)
  at com.stage1.module4.ExceptionThrowsTest.fourthPrint(ExceptionThrowsTest.java:30)
  at com.stage1.module4.ExceptionThrowsTest.main(ExceptionThrowsTest.java:43)
--------------------------------------

(9)自定义异常类的实现

Java官方提供的异常类非常多,但是有时候需要自己的实现业务需求、处理特定的异常,而Java官方又没有提供这种针对性的异常,此时就需要自定义异常。

自定义异常的流程如下:

  1. 自定义XxxException异常类继承Exception类或者其子类。

  2. 提供两个构造方法,即无参构造方法和字符串作为参数的构造方法。

如下,定义一个处理年龄不合理值的异常类,如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 11:16
 * @description javase-com.stage1.module4
 */
public class AgeException extends Exception {

    static final long serialVersionUID = -3387516993124229948L;

    public AgeException() {
    }

    public AgeException(String message) {
        super(message);
    }
}

查看源码可以看到,异常类中都定义了一个常量serialVersionUID,即序列化版本号,与Java的序列化机制有关。

(10)自定义异常类的使用

使用自定义异常类即抛出自定义的异常类实例,此时使用关键字throw,格式为throw new 异常类型(实参);

定义Person类如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 11:26
 * @description javase-com.stage1.module4
 */
public class Person {

    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0 && age < 200) {
            this.age = age;
        } else {
            System.out.println("年龄不合理!");
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 11:28
 * @description javase-com.stage1.module4
 */
public class PersonTest {

    public static void main(String[] args) {

        Person p1 = new Person("Corley", -18);
        System.out.println("p1 = " + p1);
    }
}

输出:

年龄不合理!
p1 = Person{name='Corley', age=0}

可以看到,虽然在set方法中对age进行了判断,还是可以创建Person对象,此时使用的是默认值。

此时使用自定义异常类进行处理,Person类如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 11:26
 * @description javase-com.stage1.module4
 */
public class Person {

    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) throws AgeException {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) throws AgeException {
        if (age > 0 && age < 200) {
            this.age = age;
        } else {
            // System.out.println("年龄不合理!");
            throw new AgeException("年龄不合理!");
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 11:28
 * @description javase-com.stage1.module4
 */
public class PersonTest {

    public static void main(String[] args) {

        Person p1 = null;
        try {
            p1 = new Person("Corley", -18);
        } catch (AgeException e) {
            e.printStackTrace();
        }
        System.out.println("p1 = " + p1);
    }
}

输出:

com.stage1.module4.AgeException: 年龄不合理!
  at com.stage1.module4.Person.setAge(Person.java:38)
  at com.stage1.module4.Person.<init>(Person.java:18)
  at com.stage1.module4.PersonTest.main(PersonTest.java:14)
p1 = null

此时不能成功创建Person对象;

同时打印出的异常信息是自己定义的。

也可以选择不抛出异常、而是立即处理,如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 11:26
 * @description javase-com.stage1.module4
 */
public class Person {

    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) /*throws AgeException*/ {
        setName(name);
        setAge(age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) /*throws AgeException*/ {
        if (age > 0 && age < 200) {
            this.age = age;
        } else {
            // System.out.println("年龄不合理!");
            try {
                throw new AgeException("年龄不合理!");
            } catch (AgeException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类如下:

package com.stage1.module4;

/**
 * @author Corley
 * @date 2021/8/4 11:28
 * @description javase-com.stage1.module4
 */
public class PersonTest {

    public static void main(String[] args) {

        /*Person p1 = null;
        try {
            p1 = new Person("Corley", -18);
        } catch (AgeException e) {
            e.printStackTrace();
        }
        System.out.println("p1 = " + p1);*/
        Person p1 = new Person("Corley", -18);
        System.out.println("p1 = " + p1);
    }
}

输出:

com.stage1.module4.AgeException: 年龄不合理!
  at com.stage1.module4.Person.setAge(Person.java:39)
  at com.stage1.module4.Person.<init>(Person.java:18)
  at com.stage1.module4.PersonTest.main(PersonTest.java:19)
p1 = Person{name='Corley', age=0}

此时,虽然也会抛出异常,但是可以创建Person对象,所以建议还是采用第一种方式。

可以看到,Java采用的异常处理机制将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。

在以后的开发中,异常处理可以遵循以下的原则:

  1. 尽量使用if条件判断避免异常的发生;

  2. 若实在避免不了,则进行异常的捕获;

  3. 若实在捕获不了,则进行异常的抛出;

  4. 若希望使用针对性的异常,则需要自定义异常。

2.File类

(1)File类的概念和文件操作

java.io.File类主要用于描述文件或目录路径的抽象表示信息,可以获取文件或目录的特征信息
如大小等。但是不能获取到文件的具体内容,要想获取到具体内容,需要使用到IO流。

File类的常用方法如下:

方法声明功能概述
File(String pathname)根据参数指定的路径名来构造对象
File(String parent, String child)根据参数指定的父路径和子路径信息构造对象
File(File parent, String child)根据参数指定的父抽象路径和子路径信息构造对象
boolean exists()判断路径表示的文件或目录是否存在
String getName()用于获取文件的名称
long length()返回由此抽象路径名表示的文件的长度
long lastModified()用于获取文件的最后一次修改时间
String getAbsolutePath()用于获取绝对路径信息
boolean delete()用于删除文件,当删除目录时要求是空目录
boolean createNewFile()用于创建新的空文件
boolean mkdir()用于创建目录
boolean mkdirs()用于创建多级目录
File[] listFiles()获取指定目录下的所有内容
boolean isFile()判断是否为文件
boolean isDirectory()判断是否为目录
File[] listFiles(FileFilter filter)获取目录下满足筛选器的所有内容

实现文件操作如下:

package com.stage1.module4;

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

/**
 * @author Corley
 * @date 2021/8/4 12:45
 * @description javase-com.stage1.module4
 */
public class FileTest {

    public static void main(String[] args) throws IOException {

        // 1.构造File类型的对象与E:/Test/tmp.txt文件关联
        File file = new File("E:/Test/tmp.txt");

        // 2.若文件存在则获取文件的相关特征信息,然后删除
        if (file.exists()) {
            System.out.println("文件名称:" + file.getName());
            System.out.println("文件大小:" + file.length());
            Instant instant = Instant.ofEpochMilli(file.lastModified());
            System.out.println("文件最后一次修改时间:" + instant);
            System.out.println("文件绝对路径:" + file.getAbsolutePath());
            System.out.println(file.delete() ? "文件删除成功" : "文件删除失败");
        }

        // 3.所文件不存在,则创建新的空文件
        else {
            System.out.println(file.createNewFile() ? "文件创建成功" : "文件创建失败");
        }

    }
}

第一次运行,输出:

文件创建成功

给文件添加内容并保存,再第二次运行,输出:

文件名称:tmp.txt
文件大小:40
文件最后一次修改时间:2021-08-04T05:13:35.798Z
文件绝对路径:E:\Test\tmp.txt
文件删除成功

其中,绝对路径是指以根目录开始的路径,例如C:\Windows\Resources\Themes\aero\zh-CN

相对路径是指以当前目录所在位置开始的路径,例如../en-US,开发中更推荐相对路径,这样项目具有更好的可移植性;

delete()createNewFile()方法的返回值都是布尔型,操作成功返回true、操作失败返回false;

lastModified()方法返回的是以1970年1月1日到现在的毫秒数,可以使用Date、Instant等类进行转换。

(2)File类实现目录操作

File类操作目录的示意如下:

package com.stage1.module4;

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

/**
 * @author Corley
 * @date 2021/8/4 12:45
 * @description javase-com.stage1.module4
 */
public class FileTest {

    public static void main(String[] args) throws IOException {

        // 1.构造File类型的对象与E:/Test/tmp.txt文件关联
        File file1 = new File("E:/Test/tmp.txt");

        // 2.若文件存在则获取文件的相关特征信息,然后删除
        if (file1.exists()) {
            System.out.println("文件名称:" + file1.getName());
            System.out.println("文件大小:" + file1.length());
            Instant instant = Instant.ofEpochMilli(file1.lastModified());
            System.out.println("文件最后一次修改时间:" + instant);
            System.out.println("文件绝对路径:" + file1.getAbsolutePath());
            System.out.println(file1.delete() ? "文件删除成功" : "文件删除失败");
        }

        // 3.所文件不存在,则创建新的空文件
        else {
            System.out.println(file1.createNewFile() ? "文件创建成功" : "文件创建失败");
        }

        System.out.println("----------------------------------------");
        // 4.实现目录的删除和创建
        // 创建单层目录
        File file2 = new File("E:/Test/tmp");
        if (file1.exists()) {
            System.out.println("目录名称:" + file2.getName());
            System.out.println(file2.delete() ? "目录删除成功" : "目录删除失败");
        } else {
            System.out.println(file2.mkdir() ? "目录创建成功" : "目录创建失败");
        }
        // 创建多层目录
        file2 = new File("E:/Test/tmp1/tmp2/tmp3/tmp4/tmp5");
        if (file1.exists()) {
            System.out.println("目录名称:" + file2.getName());
            System.out.println(file2.delete() ? "目录删除成功" : "目录删除失败");
        } else {
            System.out.println(file2.mkdirs() ? "目录创建成功" : "目录创建失败");
        }

    }
}

第一次执行,输出:

文件名称:tmp.txt
文件大小:0
文件最后一次修改时间:2021-08-04T07:14:33.320Z
文件绝对路径:E:\Test\tmp.txt
文件删除成功
----------------------------------------
目录创建成功
目录创建成功

第二次执行,输出:

文件创建成功
----------------------------------------
目录名称:tmp
目录删除成功
目录名称:tmp5
目录删除成功

在创建多层目录后,再执行删除操作,只能删除最底层的目录,要想删除多层目录和文件,则可以进行递归操作。

(3)File类实现目录及子目录的遍历

案例题目:
遍历指定目录以及子目录中的所有内容并打印出来。

先实现遍历目录中的内容如下:

package com.stage1.module4;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.time.Instant;

/**
 * @author Corley
 * @date 2021/8/4 12:45
 * @description javase-com.stage1.module4
 */
public class FileTest {

    public static void main(String[] args) throws IOException {

        // 1.构造File类型的对象与E:/Test/tmp.txt文件关联
        File file1 = new File("E:/Test/tmp.txt");

        // 2.若文件存在则获取文件的相关特征信息,然后删除
        if (file1.exists()) {
            System.out.println("文件名称:" + file1.getName());
            System.out.println("文件大小:" + file1.length());
            Instant instant = Instant.ofEpochMilli(file1.lastModified());
            System.out.println("文件最后一次修改时间:" + instant);
            System.out.println("文件绝对路径:" + file1.getAbsolutePath());
            System.out.println(file1.delete() ? "文件删除成功" : "文件删除失败");
        }

        // 3.所文件不存在,则创建新的空文件
        else {
            System.out.println(file1.createNewFile() ? "文件创建成功" : "文件创建失败");
        }

        System.out.println("----------------------------------------");
        // 4.实现目录的删除和创建
        // 创建单层目录
        File file2 = new File("E:/Test/tmp");
        if (file1.exists()) {
            System.out.println("目录名称:" + file2.getName());
            System.out.println(file2.delete() ? "目录删除成功" : "目录删除失败");
        } else {
            System.out.println(file2.mkdir() ? "目录创建成功" : "目录创建失败");
        }
        // 创建多层目录
        file2 = new File("E:/Test/tmp1/tmp2/tmp3/tmp4/tmp5");
        if (file1.exists()) {
            System.out.println("目录名称:" + file2.getName());
            System.out.println(file2.delete() ? "目录删除成功" : "目录删除失败");
        } else {
            System.out.println(file2.mkdirs() ? "目录创建成功" : "目录创建失败");
        }

        System.out.println("----------------------------------------");
        // 5.遍历指定目录以及子目录中的所有内容
        File file3 = new File("E:/Test");
        // 获取file3下的所有内容并记录到一维数组中
        File[] fileArray = file3.listFiles();
        for (File f : fileArray) {
            String name = f.getName();
            if (f.isFile()) {
                System.out.println(name);
            }
            if (f.isDirectory()) {
                System.out.println("[" + name + "]");
            }
        }

        System.out.println("----------------------------------------");
        // 6.遍历指定目录以及子目录中的所有内容的同时进行过滤
        // 匿名内部类:接口/父类类型 引用变量名 = new 接口/父类类型() {方法的重写 }
        /*FileFilter fileFilter = new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                // 过滤以.mp3为后缀的文件,即文件名以.mp3结尾时返回true表示保留,否则返回false表示丢弃
                return pathname.getName().endsWith(".mp3");
            }
        };*/
        // lambda表达式:接口/父类类型 引用变量名 = (参数列表) -> { 方法体 }
        FileFilter fileFilter = (File pathname) -> {
            return pathname.getName().endsWith(".mp3");
        };
        fileArray = file3.listFiles(fileFilter);
        for (File f : fileArray) {
            System.out.println(f);
        }

    }
}

输出:

文件名称:tmp.txt
文件大小:0
文件最后一次修改时间:2021-08-04T07:36:32.577Z
文件绝对路径:E:\Test\tmp.txt
文件删除成功
----------------------------------------
目录创建成功
目录创建成功
----------------------------------------
audio.mp3
[test]
test.txt
[tmp]
[tmp1]
----------------------------------------
E:\Test\audio.mp3

其中,listFiles()方法可以传入FileFilter接口类型的参数用于过滤文件名,此时需要在匿名内部类或者lambda表达式中重写accept(File dir, String name)方法。

再通过递归实现遍历子目录,如下:

package com.stage1.module4;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.time.Instant;

/**
 * @author Corley
 * @date 2021/8/4 12:45
 * @description javase-com.stage1.module4
 */
public class FileTest {

    // 自定义方法实现遍历指定目录及其子目录
    public static void iterateDir(File file) {
        for (File f : file.listFiles()) {
            String name = f.getName();
            if (f.isFile()) {
                System.out.println(name);
            }
            if (f.isDirectory()) {
                System.out.println("[" + name + "]");
                iterateDir(f);
            }
        }
    }

    public static void main(String[] args) throws IOException {

        // 1.构造File类型的对象与E:/Test/tmp.txt文件关联
        File file1 = new File("E:/Test/tmp.txt");

        // 2.若文件存在则获取文件的相关特征信息,然后删除
        if (file1.exists()) {
            System.out.println("文件名称:" + file1.getName());
            System.out.println("文件大小:" + file1.length());
            Instant instant = Instant.ofEpochMilli(file1.lastModified());
            System.out.println("文件最后一次修改时间:" + instant);
            System.out.println("文件绝对路径:" + file1.getAbsolutePath());
            System.out.println(file1.delete() ? "文件删除成功" : "文件删除失败");
        }

        // 3.所文件不存在,则创建新的空文件
        else {
            System.out.println(file1.createNewFile() ? "文件创建成功" : "文件创建失败");
        }

        System.out.println("----------------------------------------");
        // 4.实现目录的删除和创建
        // 创建单层目录
        File file2 = new File("E:/Test/tmp");
        if (file1.exists()) {
            System.out.println("目录名称:" + file2.getName());
            System.out.println(file2.delete() ? "目录删除成功" : "目录删除失败");
        } else {
            System.out.println(file2.mkdir() ? "目录创建成功" : "目录创建失败");
        }
        // 创建多层目录
        file2 = new File("E:/Test/tmp1/tmp2/tmp3/tmp4/tmp5");
        if (file1.exists()) {
            System.out.println("目录名称:" + file2.getName());
            System.out.println(file2.delete() ? "目录删除成功" : "目录删除失败");
        } else {
            System.out.println(file2.mkdirs() ? "目录创建成功" : "目录创建失败");
        }

        System.out.println("----------------------------------------");
        // 5.遍历指定目录以及子目录中的所有内容
        File file3 = new File("E:/Test");
        // 获取file3下的所有内容并记录到一维数组中
        File[] fileArray = file3.listFiles();
        for (File f : fileArray) {
            String name = f.getName();
            if (f.isFile()) {
                System.out.println(name);
            }
            if (f.isDirectory()) {
                System.out.println("[" + name + "]");
            }
        }

        System.out.println("----------------------------------------");
        // 6.遍历指定目录以及子目录中的所有内容的同时进行过滤
        // 匿名内部类:接口/父类类型 引用变量名 = new 接口/父类类型() {方法的重写 }
        /*FileFilter fileFilter = new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                // 过滤以.mp3为后缀的文件,即文件名以.mp3结尾时返回true表示保留,否则返回false表示丢弃
                return pathname.getName().endsWith(".mp3");
            }
        };*/
        // lambda表达式:接口/父类类型 引用变量名 = (参数列表) -> { 方法体 }
        FileFilter fileFilter = (File pathname) -> {
            return pathname.getName().endsWith(".mp3");
        };
        fileArray = file3.listFiles(fileFilter);
        for (File f : fileArray) {
            System.out.println(f);
        }

        System.out.println("----------------------------------------");
        // 7.使用递归获取目录及子目录中的内容
        iterateDir(new File("E:/Test"));

    }
}

输出:

文件名称:tmp.txt
文件大小:0
文件最后一次修改时间:2021-08-04T07:53:23.798Z
文件绝对路径:E:\Test\tmp.txt
文件删除成功
----------------------------------------
目录创建成功
目录创建成功
----------------------------------------
audio.mp3
[test]
test.txt
[tmp]
[tmp1]
----------------------------------------
E:\Test\audio.mp3
----------------------------------------
audio.mp3
[test]
test.txt
[tmp]
[tmp1]
[tmp2]
[tmp3]
[tmp4]
[tmp5]

可以看到,获取到了指定目录及其子目录中的所有内容。

总结

异常机制在Java中起着重要的作用,在遇到需要处理的异常时,就需要通过异常的避免异常的捕获和异常的抛出来解决异常。使用File类只能获取文件基本信息,要想读取和写入文件内容,则需要使用IO流。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东哥说AI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值