异常、File类、I\O流
异常
异常就是Java程序在运行过程中出现的错误。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。

从大体来分异常为两块:
1、error---错误 : 是指程序无法处理的错误,表示应用程序运行时出现的重大错误。例如jvm运行时出现的OutOfMemoryError以及Socket编程时出现的端口占用等程序无法处理的错误。
2、Exception --- 异常 :异常可分为运行时异常跟编译异常
1)运行时异常:即RuntimeException及其子类的异常。这类异常在代码编写的时候不会被编译器所检测出来,是可以不需要被捕获,但是程序员也可以根据需要进行捕获抛出。
2)编译异常:RuntimeException以外的异常。这类异常在编译时编译器会提示需要捕获,如果不进行捕获则编译错误。
- 捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
/*
以上代码编译运行输出结果如下:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
*/
- 多重捕获块
一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}
- finally关键字
finally 关键字用来创建在 try 代码块后面执行的代码块。
无论是否发生异常,finally 代码块中的代码总会被执行。
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
注意下面事项:
1.catch 不能独立于 try 存在。
2.在 try/catch 后面添加 finally 块并非强制性要求的。
3.try 代码后不能既没 catch 块也没 finally 块。
4.try, catch, finally 块之间不能添加任何代码。
- throws/throw 关键字
如果一个方法没有捕获一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
throws的方式处理异常:
定义功能方法时,需要把出现的问题暴露出来让调用者去处理。
那么就通过throws在方法上标识.
throw的方式处理异常:
在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
throws和throw的区别:
1.throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
2.throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
public static void main(String[] args){
try {
str2int("a");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void str2int(String str) throws Exception { //这里将得到的异常向外抛出
try {
System.out.println(Integer.parseInt(str));
} catch(NumberFormatException e) {
//TODO 这里可以做一些处理,处理完成后将异常报出,让外层可以得到异常信息
throw new Exception("格式化异常");
}
}
/*
输出结果
java.lang.Exception: 格式化异常
at Exception.ExceptionDemo.str2int(ExceptionDemo.java:37)
at Exception.ExceptionDemo.main(ExceptionDemo.java:25)
*/
- 声明自定义异常
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
public class Person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws AgeOutOfBoundsException{
if (age <= 150 && age > 0){
this.age = age;
}else {
throw new AgeOutOfBoundsException("年龄非法");
}
}
}
public class ExceptionDemo {
public static void main(String[] args){
customExceptionDemo();
}
private static void customExceptionDemo(){
Person person = new Person();
try {
person.setAge(-1);
} catch (AgeOutOfBoundsException e) {
e.printStackTrace();
}
}
}
class AgeOutOfBoundsException extends Exception{
public AgeOutOfBoundsException() {
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}
File类
-
构造方法
File(String pathname):根据一个路径得到File对象
File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象(更强大) -
创建功能
public boolean createNewFile(): 创建文件 如果存在这样的文件,就不创建了
public boolean mkdir(): 创建文件夹 如果存在这样的文件夹,就不创建了
public boolean mkdirs(): 创建文件夹,如果父文件夹不存在,会帮你创建出来 -
重命名和删除功能
public boolean renameTo(File dest):把文件重命名为指定的文件路径
public boolean delete():删除文件或者文件夹
重命名注意事项:
1.如果路径名相同,就是改名。
2.如果路径名不同,就是改名并剪切。
删除注意事项:
Java中的删除不走回收站。
要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹. -
判断功能
public boolean isDirectory() 判断是否是目录
public boolean isFile() 判断是否是文件
public boolean exists() 判断是否存在
public boolean canRead() 判断是否可读
public boolean canWrite() 判断是否可写
public boolean isHidden() 判断是否隐藏 -
获取功能
public String getAbsolutePath() 获取绝对路径
public String getPath() 获取路径(构造方法中的路径)
public String getName() 获取名称
public long length() 获取长度。字节数
public long lastModified() 获取最后一次的修改时间,毫秒值
public String[] list() 获取指定目录下的所有文件或者文件夹的名称数组
public File[] listFiles() 获取指定目录下的所有文件或者文件夹的File数组 -
文件名称过滤器
public String[] list(FilenameFilter filter)
public File[] listFiles(FileFilter filter)
I/O流
IO流用来处理设备之间的数据传输。
Java对数据的操作是通过流的方式。
流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
字符流 : 字符流只能操作纯字符数据,比较方便。
IO流常用父类
字节流的抽象父类:
InputStream
OutputStream
字符流的抽象父类:
Reader
Writer
- FileInputStream
read()一次读入一个字节
FileInputStream fileInputStream = new FileInputStream("rr.txt");
int read;
// 每次读一个字节 -1表示结束
while ((read = fileInputStream.read()) != -1){
System.out.println(read);
}
fileInputStream.close(); // 关闭流释放资源
- FileOutputStream
write()一次写出一个字节
// 拷贝方式一 不推荐使用
// 如果t.txt文件不存在,就创建一个,否则,在将原文件清空
// FileOutputStream fileOutputStream = new FileOutputStream("/Users/zhangxiaohan/Desktop/t.txt");
// 如果t.txt文件不存在,就创建一个,否则,在原文件基础上增加内容
FileOutputStream fileOutputStream = new FileOutputStream("/Users/zhangxiaohan/Desktop/t.txt", true);
fileOutputStream.write(65);
fileOutputStream.write(66);
fileOutputStream.write(67);
fileOutputStream.close();
字节数组拷贝之available()方法:
int read(byte[] b) 一次读取一个字节数组
write(byte[] b) 一次写出一个字节数组
available() 获取读的文件所有的字节个数
弊端:有可能会内存溢出 不推荐
// 拷贝方式二 不推荐使用
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
byte[] arr = new byte[fis.available()]; //根据文件大小做一个字节数组
fis.read(arr); //将文件上的所有字节读取到数组中
fos.write(arr); //将数组中的所有字节一次写到了文件上
fis.close();
fos.close();
// 第三种拷贝
FileInputStream fis = new FileInputStream("致青春.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
int len;
byte[] arr = new byte[1024 * 8]; //自定义字节数组
while((len = fis.read(arr)) != -1) {
fos.write(arr, 0, len); //写出字节数组写出有效个字节个数
}
fis.close();
fos.close();
- BufferedInputStream和BufferOutputStream拷贝
BufferedInputStream:
BufferedInputStream内置了一个缓冲区(数组)
从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个,程序再次读取时, 就不用找文件了, 直接从缓冲区中获取,直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个。
BufferedOutputStream:
BufferedOutputStream也内置了一个缓冲区(数组)
程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中,直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。
FileInputStream fis = new FileInputStream("致青春.mp3"); //创建文件输入流对象,关联致青春.mp3
BufferedInputStream bis = new BufferedInputStream(fis);
//创建缓冲区对fis 包装
FileOutputStream fos = new FileOutputStream("copy.mp3"); //创建输出流对象,关联copy.mp3
BufferedOutputStream bos = new BufferedOutputStream(fos); //创建缓冲区对fos 包装
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close(); //只关包装后的对象即可
bos.close();
- flush和close方法的区别
flush()方法:
用来刷新缓冲区的,刷新后可以再次写出
需要实时写出的用flush刷新
close()方法:
用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出 。
字符流
字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
读取的时候是按照字符的大小读取的,不会出现半个中文
写出的时候可以直接将字符串写出,不用转换为字节数组
- FileReader
FileReader类的read()方法可以按照字符大小读取。
FileReader fr = new FileReader("aaa.txt");
int ch;
while((ch = fr.read()) != -1) { //将读到的字符赋值给ch
System.out.println((char)ch); //将读到的字符强转后打印
}
fr.close();
- FileWriter
FileWriter类的write()方法可以自动把字符转为字节写出.
FileWriter fw = new FileWriter("aaa.txt");
fw.write("aaa");
fw.close();
- 字符流的拷贝
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
int ch;
while((ch = fr.read()) != -1) {
fw.write(ch);
}
fr.close();
fw.close();
- 带缓冲的字符流
BufferedReader 的 read() 方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率。
BufferedWriter 的 write() 方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率。
BufferedReader br = new BufferedReader(new FileReader("aaa.txt")); //创建字符输入流对象,关联aaa.txt
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt")); //创建字符输出流对象,关联bbb.txt
int ch;
while((ch = br.read()) != -1) { //read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch
bw.write(ch); //write一次,是将数据装到字符数组,装满后再一起写出去
}
br.close(); //关流
bw.close();
- readLine() 和 newLine() 方法
BufferedReader 的readLine() 方法可以读取一行字符(不包含换行符号)
BufferedWriter 的newLine() 可以输出一个跨平台的换行符号"\r\n"
BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));
String line;
while((line = br.readLine()) != null) {
bw.write(line);
//bw.write("\r\n"); //只支持windows系统
bw.newLine(); //跨平台的
}
br.close();
bw.close();
- LineNumberReader
LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号
调用getLineNumber()方法可以获取当前行号
调用setLineNumber()方法可以设置当前行号
LineNumberReader lnr = new LineNumberReader(new FileReader("aaa.txt"));
lnr.setLineNumber(100); //设置行号,行号从101开始,默认从0开始
while((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + ":" + line);//获取行号
}
lnr.close();
- 装饰设计模式
好处:耦合性不强,被装饰的类的变化与装饰类的变化无关。
interface Coder {
public void code();
}
class Student implements Coder {
@Override
public void code() {
System.out.println("javase");
System.out.println("javaweb");
}
}
class SuperStudent implements Coder {
private Student s; //获取到被包装的类的引用
public ItcastStudent(Student s) { //通过构造函数创建对象的时候,传入被包装的对象
this.s = s;
}
@Override
public void code() { //对其原有功能进行升级
s.code();
System.out.println("数据库");
System.out.println("ssh");
System.out.println(".....");
}
}
- 使用指定的码表读写字符
FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)。
FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)。
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8")); //高效的用指定的编码表读
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("GBK.txt"), "GBK")); //高效的用指定的编码表写
int ch;
while((ch = br.read()) != -1) {
bw.write(ch);
}
br.close();
bw.close();
递归
方法自己调用自己。
构造方法不能使用递归调用。
好处:不用知道循环次数。
弊端:不能调研次数过多,容易导致栈内存溢出。
private static int test(int num){
if (num > 1){
return num * test(num - 1) ;
}else {
return num;
}
}
- 序列流(了解)
序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推.
SequenceInputStream(InputStream, InputStream)
FileInputStream fis1 = new FileInputStream("a.txt"); //创建输入流对象,关联a.txt
FileInputStream fis2 = new FileInputStream("b.txt"); //创建输入流对象,关联b.txt
SequenceInputStream sis = new SequenceInputStream(fis1, fis2); //将两个流整合成一个流
FileOutputStream fos = new FileOutputStream("c.txt"); //创建输出流对象,关联c.txt
int b;
while((b = sis.read()) != -1) { //用整合后的读
fos.write(b); //写到指定文件上
}
sis.close();
fos.close();
- 序列流整合多个 (了解)
SequenceInputStream(Enumeration)
FileInputStream fis1 = new FileInputStream("a.txt"); //创建输入流对象,关联a.txt
FileInputStream fis2 = new FileInputStream("b.txt"); //创建输入流对象,关联b.txt
FileInputStream fis3 = new FileInputStream("c.txt"); //创建输入流对象,关联c.txt
Vector<InputStream> v = new Vector<>(); //创建vector集合对象
v.add(fis1); //将流对象添加
v.add(fis2);
v.add(fis3);
Enumeration<InputStream> en = v.elements(); //获取枚举引用
SequenceInputStream sis = new SequenceInputStream(en); //传递给SequenceInputStream构造
FileOutputStream fos = new FileOutputStream("d.txt");
int b;
while((b = sis.read()) != -1) {
fos.write(b);
}
sis.close();
fos.close();
- 内存输出流
该输出流可以向内存中写数据, 把内存当作一个缓冲区, 写出之后可以一次性获取出所有数据。
创建对象: new ByteArrayOutputStream()
写出数据: write(int), write(byte[])
获取数据: toByteArray()
FileInputStream fis = new FileInputStream("a.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b;
while((b = fis.read()) != -1) {
baos.write(b);
}
//byte[] newArr = baos.toByteArray(); //将内存缓冲区中所有的字节存储在newArr中
//System.out.println(new String(newArr));
System.out.println(baos);
fis.close();
- 对象操作流ObjecOutputStream)(了解)
该流可以将一个对象写出, 或者读取一个对象到程序中. 也就是执行了序列化和反序列化的操作.
// Person
public class Person implements Serializable {
}
public class Demo3_ObjectOutputStream {
/**
* @param args
* @throws IOException
* 将对象写出,序列化
*/
public static void main(String[] args) throws IOException {
Person p1 = new Person("张三", 23);
Person p2 = new Person("李四", 24);
// FileOutputStream fos = new FileOutputStream("e.txt");
// fos.write(p1);
// FileWriter fw = new FileWriter("e.txt");
// fw.write(p1);
//无论是字节输出流,还是字符输出流都不能直接写出对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));//创建对象输出流
oos.writeObject(p1);
oos.writeObject(p2);
oos.close();
}
}
- 对象操作流ObjectInputStream)(了解)
public class Demo3_ObjectInputStream {
/**
* @param args
* @throws IOException
* @throws ClassNotFoundException
* @throws FileNotFoundException
* 读取对象,反序列化
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
Person p1 = (Person) ois.readObject();
Person p2 = (Person) ois.readObject();
System.out.println(p1);
System.out.println(p2);
ois.close();
}
}
- 对象操作流优化(了解)
// 将对象存储在集合中写出
Person p1 = new Person("张三", 23);
Person p2 = new Person("李四", 24);
Person p3 = new Person("马哥", 18);
Person p4 = new Person("辉哥", 20);
ArrayList<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("f.txt"));
oos.writeObject(list); //写出集合对象
oos.close();
// 读取到的是一个集合对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("f.txt"));
ArrayList<Person> list = (ArrayList<Person>)ois.readObject(); //泛型在运行期会被擦除,索引运行期相当于没有泛型
//想去掉黄色可以加注解 @SuppressWarnings("unchecked")
for (Person person : list) {
System.out.println(person);
}
ois.close();
- 打印流的概述和特点
该流可以很方便的将对象的toString()结果输出, 并且自动加上换行, 而且可以使用自动刷出的模式。
System.out就是一个PrintStream, 其默认向控制台输出信息。
PrintStream ps = System.out;
ps.println(97); //其实底层用的是Integer.toString(x),将x转换为数字字符串打印
ps.println("xxx");
ps.println(new Person("张三", 23));
Person p = null;
ps.println(p); //如果是null,就返回null,如果不是null,就调用对象的toString()
- 标准输入输出流概述和输出语句
标准输入输出流:
System.in是InputStream, 标准输入流, 默认可以从键盘输入读取字节数据。
System.out是PrintStream, 标准输出流, 默认可以向Console中输出字符和字节数据。
修改标准输入输出流(了解):
修改输入流: System.setIn(InputStream)
修改输出流: System.setOut(PrintStream)
System.setIn(new FileInputStream("a.txt")); //修改标准输入流
System.setOut(new PrintStream("b.txt")); //修改标准输出流
InputStream in = System.in; //获取标准输入流
PrintStream ps = System.out; //获取标准输出流
int b;
while((b = in.read()) != -1) { //从a.txt上读取数据
ps.write(b); //将数据写到b.txt上
}
in.close();
ps.close();
网友评论