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


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类、递归)_第1张图片

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("清理运行后垃圾");
		}
		
	}
}

    运行结果:


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类、递归)_第2张图片

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;
	}
}

    运行结果:


    由此可以看出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类、递归)_第3张图片

JavaSE---IO流基础(异常、File类、递归)_第4张图片

    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));
	}
}

    程序运行前:


    程序运行后:


    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类、递归)_第5张图片

   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类、递归)_第6张图片

    练习:判断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());
				}
			}
		}
	}
}

    运行结果:


  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);
		}
	}
}

    运行结果:



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类、递归)_第7张图片

    程序运行后:

JavaSE---IO流基础(异常、File类、递归)_第8张图片

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类、递归)_第9张图片

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

    运行结果:

JavaSE---IO流基础(异常、File类、递归)_第10张图片

    练习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类、递归)_第11张图片

    练习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类、递归)_第12张图片

    程序运行后:

JavaSE---IO流基础(异常、File类、递归)_第13张图片

JavaSE---IO流基础(异常、File类、递归)_第14张图片

你可能感兴趣的:(Java)