6、IO流基础
异常:就是程序在运行时出现不正常情况。 在Java中,将异常封装成了一个类:Throwable。Throwable下分为两大体系:Error和Exception。
Throwable
|--Error:严重错误,如内存溢出
|--Exception
|--RuntimeException:不需要程序员处理,因为发生这类异常一般都是程序不严谨造成的,需要修改程序。//特殊异常类,抛时不需要声明
|--其他异常类:必须由程序员处理,因为这些异常在编译时会检测,如果不处理,则编译不通过。
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);
}
}
}
运行结果:
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("清理运行后垃圾");
}
}
}
有时候我们需要自己定义异常类,那该如何处理呢?
首先需要自己建立一个类,并继承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();
}
}
}
异常在子类复写父类方法时的注意事项
1)子类抛出的异常必须是父类的异常的子类或者子集,不能抛出父类没有的异常。
2)父类或者接口没有异常抛出时,而子类方法内如果有异常发生,只能使用try,不能使用throws。
面试题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语句之前执行。
File:文件和目录(文件夹)路径名的抽象表示形式
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");
// 以上三种方式其实效果一样
}
}
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());
}
}
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
}
}
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());
}
}
}
练习:判断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);
}
}
}
需求:把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);
}
}
}
程序运行后:
递归就是方法定义中调用方法本身的现象。例如:
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);
}
}
}
练习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
练习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());
}
}
}
}
}
练习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());
}
}
程序运行后: