1. 异常
1.1 异常概念
异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是:
- 异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.
1.2 异常体系
异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable
,其下有两个子类:java.lang.Error
与java.lang.Exception
,平常所说的异常指java.lang.Exception
。
Throwable体系:
- Error:严重错误Error,代表的系统级别的错误,无法通过处理的错误,只能事先避免,好比绝症。
- Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。
1.3 异常分类
public class Main {
public static void main(String[] args) throws ParseException {
//编译时异常(在编译阶段必须要手动处理,否则代码报错)
String time = "2024年4月16日";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
Date parse = simpleDateFormat.parse(time);
System.out.println(parse);
//运行时异常(编译阶段不需要处理,是代码运行时出现的异常)
int[] arr= {1,2,3};
System.out.println(arr[3]);
}
}
1.4 异常的作用
比如作用一:越界异常,就知道哪里错了,或者初始化对象数组,但是没有赋值,操作数组就会出现空指针异常。
比如作用二:如果对某个对象的age赋值,但是假如超过40岁,则告诉调用者,那么就用异常,即如果age>40则,抛异常
1.5 异常的处理方式
1.5.1 JVM默认处理方式
1.5.2 自己处理(捕获异常)
1.6 灵魂4问
-
如果try中没有遇到问题,怎么执行?
- 会把try里面所有的代码全部执行完毕,不会执行catch里面的代码
注意: 只有当出现了异常才会执行catch里面的代码
- 会把try里面所有的代码全部执行完毕,不会执行catch里面的代码
-
如果try中可能会遇到多个问题,怎么执行?
- 会写多个catch与之对应
细节: 如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面
- 在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开,表示如果出现了A异常或者B异常的话,采取同一种处理方案。
- 会写多个catch与之对应
-
如果try中遇到的问题没有捕获,怎么执行呢?
- 相当于try…catch的代码白写了,最终还是交给虚拟机进行处理。
执行过程就是,数组越界后,会new一个ArrayIndexOutofBoundsException对象,然后拿着这个对象和下面的catch对比,发现没有可以接受的,就把这个异常对象交给jvm处理,jvm就会输出异常并停止程序。
- 相当于try…catch的代码白写了,最终还是交给虚拟机进行处理。
-
如果try中遇到了问题,那么try下面的其他代码还会执行吗?
- 下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体,但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理
哪怕后面再出现一个异常,那么也不会被捕获到。
- 下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体,但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理
1.7 异常中的常见方法
细节:printStackTrace最常用,因为包含了上面两个所有信息,而且仅仅是打印信息,不会停止程序运行,底层使用了System.err.println输出信息
小扩展:
1.8 抛出异常throw
需求:定义一个方法找到数组中最大的值
public class Main {
public static void main(String[] args){
int[] arr=null;
int max=0;
try {
max = getMax(arr);
} catch (NullPointerException e) {
System.out.println("空指针异常");
}catch (IndexOutOfBoundsException e) {
System.out.println("索引越界异常");
}
System.out.println(max);
}
public static int getMax(int[] arr)/*throws NullPointerException,IndexOutOfBoundsException*/{
//手动创建一个异常独享,病把这个异常交给方法的调用者处理
//此时方法就会结束,下面的代码不会再执行了
if(arr==null){
throw new NullPointerException();
}
if(arr.length==0){
throw new IndexOutOfBoundsException();
}
int max_value = arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i]>max_value){
max_value=arr[i];
}
}
return max_value;
}
}
输出:
空指针异常
0
1.9 练习:
public class GirlFriend {
private String name;
private int age;
public GirlFriend() {
}
public GirlFriend(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
if(name.length()<3||name.length()>10){
throw new RuntimeException();
}
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
if(age<18||age>40){
throw new RuntimeException();
}
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
GirlFriend girlFriend = new GirlFriend();
while (true) {
try {
System.out.println("请输入女朋友姓名:");
String name = scanner.next();
girlFriend.setName(name);
System.out.println("请输入女朋友年龄:");
String agestr = scanner.next();
int age = Integer.parseInt(agestr);
girlFriend.setAge(age);
break;
} catch (NumberFormatException e) {
System.out.println("年龄格式输入错误,请输入数字!");
}
catch (RuntimeException e){
System.out.println("姓名长度或年龄范围有误!");
}
}
System.out.println(girlFriend);
}
}
1.10 自定义异常
由于上一步的练习使用了运行时异常,不太符合具体情况,因此需要自定义异常:
public class NameFormatException extends RuntimeException{
public NameFormatException() {
}
public NameFormatException(String message) {
super(message);
}
}
public class AgeOutOfBoundsException extends RuntimeException{
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
public class GirlFriend {
private String name;
private int age;
public GirlFriend() {
}
public GirlFriend(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
if(name.length()<3||name.length()>10){
throw new NameFormatException(name+"超出范围!");
}
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
if(age<18||age>40){
throw new AgeOutOfBoundsException(age+"超出范围!");
}
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
GirlFriend girlFriend = new GirlFriend();
while (true) {
try {
System.out.println("请输入女朋友姓名:");
String name = scanner.next();
girlFriend.setName(name);
System.out.println("请输入女朋友年龄:");
String agestr = scanner.next();
int age = Integer.parseInt(agestr);
girlFriend.setAge(age);
break;
} catch (NumberFormatException e) {
e.printStackTrace();
}
catch (AgeOutOfBoundsException e){
e.printStackTrace();
}
catch (NameFormatException e){
e.printStackTrace();
}
}
System.out.println(girlFriend);
}
}
1.11 Finally
除非虚拟机停止,比如在try里面,写了一个System.eixt(0),那么finally里面的代码就不会执行了
2. File类
2.1 概述
java.io.File
类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
2.2 构造方法
- 构造举例,代码如下:
// 文件路径名
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname);
// 文件路径名
String pathname2 = "D:\\aaa\\bbb.txt";
File file2 = new File(pathname2);
// 通过父路径和子路径字符串
String parent = "d:\\aaa";
String child = "bbb.txt";
File file3 = new File(parent, child);
// 通过父级File对象和子路径字符串
File parentDir = new File("d:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child);
小贴士:
- 一个File对象代表硬盘中实际存在的一个文件或者目录。
- 无论该路径下是否存在文件或者目录,都不影响File对象的创建。
2.3 常用方法
判断功能的方法
public boolean exists()
:此File表示的文件或目录是否实际存在。public boolean isDirectory()
:此File表示的是否为目录。public boolean isFile()
:此File表示的是否为文件。
方法演示,代码如下:
//1.对一个文件进行判断
File file1 = new File("C:\\Users\\gao\\Desktop\\JavaLearn\\src\\Main.java");
System.out.println(file1.exists());//true
System.out.println(file1.isDirectory());//false
System.out.println(file1.isFile());//true
//2.对一个文件夹进行判断
File file2 = new File("C:\\Users\\gao\\Desktop\\JavaLearn\\src");
System.out.println(file2.exists());//true
System.out.println(file2.isDirectory());//true
System.out.println(file2.isFile());//false
//3.对一个不存在的路径进行判断
File file3 = new File("C:\\Users\\gao\\Desktop\\JavaLearn\\aaa");
System.out.println(file3.exists());//false
System.out.println(file3.isDirectory());//false
System.out.println(file3.isFile());//false
注意:如果一个文件夹或文件不存在,那么输出是否是文件或文件夹都会输出false,因为他不存在
获取功能的方法
-
public long length()
:返回由此File表示的文件的长度。- 细节1:这个方法只能获取文件的大小,单位是字节
- 细节2:这个方法无法获取文件夹的大小,如果需要获取文件夹大小,需要把这个文件夹的所有文件大小累加在一起。
-
public String getAbsolutePath()
:返回此File的绝对路径。 -
public String getPath()
:返回定义文件时的路径。 -
public String getName()
:返回由此File表示的文件或目录的名称。- 细节1:定义的是文件,则返回的文件名+扩展名,如a.txt
- 细节2:定义的是文件及,则返回的是文件夹的名字
-
public long lastModified
: 返回文件的最后修改时间(时间毫秒值)方法演示,代码如下:
//1.返回文件的大小 File file = new File("C:\\Users\\gao\\Desktop\\JavaLearn\\src\\Main.java"); System.out.println(file.length());//276 //2.返回文件的绝对路径 System.out.println(file.getAbsoluteFile());//C:\Users\gao\Desktop\JavaLearn\src\Main.java File file1 = new File("src//Main.java"); System.out.println(file1.getAbsoluteFile());//C:\Users\gao\Desktop\JavaLearn\src\Main.java //3.返回文件定义时的路径 System.out.println(file.getPath());//C:\Users\gao\Desktop\JavaLearn\src\Main.java System.out.println(file1.getPath());//src\Main.java //4.获取文件名字 System.out.println(file.getName()); System.out.println(file1.getName()); //5.获取文件最后修改时间 System.out.println(file.lastModified());//1713927862885 Date date = new Date(file.lastModified()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒"); System.out.println(sdf.format(date));//2024年04月24日11时04分22秒
API中说明:length(),表示文件的长度。但是File对象表示目录,则返回值未指定。
创建删除功能的方法
public boolean createNewFile()
:创建一个新的空文件。- 细节1:如果创建的文件不存在,则创建成功,返回true,如果创建的文件已经存在,则创建失败,返回false
- 细节2:如果父级路径是不存在的,则会报错,IOException
- 细节3:createNewFile一定创建的是一个文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
public boolean mkdir()
:创建单级文件夹。- 细节1:windows当中的路径是唯一的,如果当前路径已经存在,则创建失败,返回false
- 细节2:mkdir只能创建单级文件夹,无法创建多级文件夹
public boolean mkdirs()
:创建多级文件夹。- 细节:既可以创建单级文件夹,也可以创建多级文件夹
public boolean delete()
:删除文件、文件夹。- 细节:默认只能删除文件和空文件夹,直接删除,不走回车站。如果删除有内容的文件夹,则删除失败
方法演示,代码如下:
//1.创建空文件
boolean newFile = new File("./a.txt").createNewFile();
System.out.println(newFile);
//2.创建文件夹
boolean mkdir = new File("./aaa").mkdir();
System.out.println(mkdir);
//3.创建多级文件夹
boolean newFile1 = new File("./bbb/ccc").mkdirs();
System.out.println(newFile1);
API中说明:delete方法,如果此File表示目录,则目录必须为空才能删除。
2.4 目录的遍历
public File[] listFiles()
:返回一个File数组,表示该File目录中的所有的子文件或目录。 重点掌握
//1.创建File
File file = new File("C:\\Users\\gao\\Desktop\\JavaLearn");
File[] files = file.listFiles();
for (File file1 : files) {
System.out.println(file1);
}
C:\Users\gao\Desktop\JavaLearn\.gitignore
C:\Users\gao\Desktop\JavaLearn\.idea
C:\Users\gao\Desktop\JavaLearn\bbb
C:\Users\gao\Desktop\JavaLearn\JavaLearn.iml
C:\Users\gao\Desktop\JavaLearn\out
C:\Users\gao\Desktop\JavaLearn\src
小贴士:
调用listFiles方法的File对象,表示的必须是实际存在的目录,否则返回null,无法进行遍历。
其他获取并遍历方法,重点掌握listFiles就可以了,因为其他的都可以使用listFiles实现:
//1.listRoots
File[] files = File.listRoots();
for (File file : files) {
System.out.println(file);
}
System.out.println("======================");
//2.list
File file = new File("C:\\Users\\gao\\Desktop\\JavaLearn");
String[] list = file.list();
for (String s : list) {
System.out.println(s);
}
System.out.println("======================");
//需求,获取文件夹,且b开头的文件夹
//3.list(FilenameFilter filter)
String[] list1 = file.list(new FilenameFilter() {
@Override
//参数一:父级路径
//参数二:子级路径
public boolean accept(File dir, String name) {
File file1 = new File(dir, name);
return file1.isDirectory() && name.startsWith("b");
}
});
for (String s : list1) {
System.out.println(s);
}
System.out.println("======================");
//需求,获取文件夹,且b开头的文件夹
//4.listFiles(FileFilter filter)
File[] files1 = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory() && pathname.getName().startsWith("b");
}
});
for (File file1 : files1) {
System.out.println(file1);
}
System.out.println("======================");
//5.listFiles(FilenameFilter filter)
File[] files2 = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File file1 = new File(dir, name);
return file1.isDirectory() && name.startsWith("b");
}
});
for (File file1 : files2) {
System.out.println(file1);
}
2.5 综合练习
练习1:创建文件夹
在当前模块下的aaa文件夹中创建一个a.txt文件
代码实现:
//1.创建a.txt的父级路径
File file = new File("./aaa");
//2.创建父级路径
//如果aaa是存在的,那么此时创建失败的。
//如果aaa是不存在的,那么此时创建成功的。
file.mkdirs();
//3.拼接父级路径和子级路径
File src = new File(file,"a.txt");
boolean b = src.createNewFile();
if(b){
System.out.println("创建成功");
}else{
System.out.println("创建失败");
}
练习2:查找文件(不考虑子文件夹)
定义一个方法找某一个文件夹中,是否有以avi结尾的电影(暂时不需要考虑子文件夹)
代码示例:
public class Test2 {
public static void main(String[] args) {
/*需求:
定义一个方法找某一个文件夹中,是否有以avi结尾的电影。
(暂时不需要考虑子文件夹)
*/
File file = new File("D:\\aaa\\bbb");
boolean b = haveAVI(file);
System.out.println(b);
}
/*
* 作用:用来找某一个文件夹中,是否有以avi结尾的电影
* 形参:要查找的文件夹
* 返回值:查找的结果 存在true 不存在false
* */
public static boolean haveAVI(File file){// D:\\aaa
//1.进入aaa文件夹,而且要获取里面所有的内容
File[] files = file.listFiles();
//2.遍历数组获取里面的每一个元素
for (File f : files) {
//f:依次表示aaa文件夹里面每一个文件或者文件夹的路径
if(f.isFile() && f.getName().endsWith(".avi")){
return true;
}
}
//3.如果循环结束之后还没有找到,直接返回false
return false;
}
}
练习3:(考虑子文件夹)
找到电脑中所有以avi结尾的电影。(需要考虑子文件夹)
代码示例:
public class Test3 {
public static void main(String[] args) {
/* 需求:
找到电脑中所有以avi结尾的电影。(需要考虑子文件夹)
套路:
1,进入文件夹
2,遍历数组
3,判断
4,判断
*/
findAVI();
}
public static void findAVI(){
//获取本地所有的盘符
File[] arr = File.listRoots();
for (File f : arr) {
findAVI(f);
}
}
public static void findAVI(File src){//"C:\\
//1.进入文件夹src
File[] files = src.listFiles();
//2.遍历数组,依次得到src里面每一个文件或者文件夹
if(files != null){
for (File file : files) {
if(file.isFile()){
//3,判断,如果是文件,就可以执行题目的业务逻辑
String name = file.getName();
if(name.endsWith(".avi")){
System.out.println(file);
}
}else{
//4,判断,如果是文件夹,就可以递归
//细节:再次调用本方法的时候,参数一定要是src的次一级路径
findAVI(file);
}
}
}
}
}
练习4:删除多级文件夹
需求: 如果我们要删除一个有内容的文件夹
1.先删除文件夹里面所有的内容
2.再删除自己
代码示例:
public class Test4 {
public static void main(String[] args) {
/*
删除一个多级文件夹
如果我们要删除一个有内容的文件夹
1.先删除文件夹里面所有的内容
2.再删除自己
*/
File file = new File("D:\\aaa\\src");
delete(file);
}
/*
* 作用:删除src文件夹
* 参数:要删除的文件夹
* */
public static void delete(File src){
//1.先删除文件夹里面所有的内容
//进入src
File[] files = src.listFiles();
//遍历
for (File file : files) {
//判断,如果是文件,删除
if(file.isFile()){
file.delete();
}else {
//判断,如果是文件夹,就递归
delete(file);
}
}
//2.再删除自己
src.delete();
}
}
练习5:统计大小
需求:统计一个文件夹的总大小
代码示例:
public class Test5 {
public static void main(String[] args) {
/*需求:
统计一个文件夹的总大小
*/
File file = new File("D:\\aaa\\src");
long len = getLen(file);
System.out.println(len);//4919189
}
/*
* 作用:
* 统计一个文件夹的总大小
* 参数:
* 表示要统计的那个文件夹
* 返回值:
* 统计之后的结果
*
* 文件夹的总大小:
* 说白了,文件夹里面所有文件的大小
* */
public static long getLen(File src){
//1.定义变量进行累加
long len = 0;
//2.进入src文件夹
File[] files = src.listFiles();
//3.遍历数组
for (File file : files) {
//4.判断
if(file.isFile()){
//我们就把当前文件的大小累加到len当中
len = len + file.length();
}else{
//判断,如果是文件夹就递归
len = len + getLen(file);
}
}
return len;
}
}
练习6:统计文件个数
需求:统计一个文件夹中每种文件的个数并打印。(考虑子文件夹)
打印格式如下:
txt:3个
doc:4个
jpg:6个
代码示例:
public class Test6 {
public static void main(String[] args) throws IOException {
/*
需求:统计一个文件夹中每种文件的个数并打印。(考虑子文件夹)
打印格式如下:
txt:3个
doc:4个
jpg:6个
*/
File file = new File("D:\\aaa\\src");
HashMap<String, Integer> hm = getCount(file);
System.out.println(hm);
}
/*
* 作用:
* 统计一个文件夹中每种文件的个数
* 参数:
* 要统计的那个文件夹
* 返回值:
* 用来统计map集合
* 键:后缀名 值:次数
*
* a.txt
* a.a.txt
* aaa(不需要统计的)
*
*
* */
public static HashMap<String,Integer> getCount(File src){
//1.定义集合用来统计
HashMap<String,Integer> hm = new HashMap<>();
//2.进入src文件夹
File[] files = src.listFiles();
//3.遍历数组
for (File file : files) {
//4.判断,如果是文件,统计
if(file.isFile()){
//a.txt
String name = file.getName();
String[] arr = name.split("\\.");
if(arr.length >= 2){
String endName = arr[arr.length - 1];
if(hm.containsKey(endName)){
//存在
int count = hm.get(endName);
count++;
hm.put(endName,count);
}else{
//不存在
hm.put(endName,1);
}
}
}else{
//5.判断,如果是文件夹,递归
//sonMap里面是子文件中每一种文件的个数
HashMap<String, Integer> sonMap = getCount(file);
//hm: txt=1 jpg=2 doc=3
//sonMap: txt=3 jpg=1
//遍历sonMap把里面的值累加到hm当中
Set<Map.Entry<String, Integer>> entries = sonMap.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
String key = entry.getKey();
int value = entry.getValue();
if(hm.containsKey(key)){
//存在
int count = hm.get(key);
count = count + value;
hm.put(key,count);
}else{
//不存在
hm.put(key,value);
}
}
}
}
return hm;
}
}