学习IO流之前,先介绍异常,以后我们会经常遇到各种各样的异常,那么遇到异常我们该怎么处理呢?
一:异常
1.1 异常:就是程序出现的不正常的情况。
异常大致分类:
①错误(Error):这是非常严重的问题,一般我们处理不了,一般在这里指的是硬件问题。
②异常(Exception):
a.编译时期异常 开始就必须要处理的,如果不处理,后面就走不了。
b.运行时期异常 开始可以不用处理。这种问题一旦发生,就是我们的程序问题,需要我们修改程序。
1.1.1 异常体系结构图:
1.1.2 针对异常,JVM默认的处理方案:
先举一个例子,除数为0的异常:
package com.edu_01;
public class ExceptionDemo2 {
public static void main(String[] args) {
System.out.println("start");
int a = 10;
int b = 0;
System.out.println(a/b);
System.out.println("end");
}
}
//运行结果:
/** start
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.edu_01.ExceptionDemo2.main(ExceptionDemo2.java:23)
*/
并没有输出 end,且抛出了java.lang.ArithmeticException: / by zero异常。
我们发现,一旦遇到程序出现了问题,就会把问题的类名,错误原因,错误的位置等信息打印在控制台,以便我们观察。并且,会自动从当前出问题的地方停止掉。
然而JVM默认的处理方案虽然可以,但是不够好。
即便程序出问题,也不应该直接停止,因为我们的程序可能是由多部分组成的,当其中一个部分出问题时,不应该影响其他部分的执行。所以,我们应该想办法让其他的部分能够执行下去。
1.2 如何处理异常,保证各个部分不影响的呢?
两种方案:
A:try…catch…finally
B:throws
方案一: try…catch…finally:
try{
可能出现异常的代码
}catch(异常类名 变量名) {
针对异常的代码处理
}finally {
释放资源的地方
}
先不看finally,我们简化一下:
try{
可能出现异常的代码
}catch(异常类名 变量名) {
针对异常的代码处理
}
package com.edu_01;
public class ExceptionDemo3 {
public static void main(String[] args) {
System.out.println("start");
int a= 10;
int b = 0;
try{
//可能出现异常的代码
System.out.println(a/b);//当除数为0的时候会抛出ArithmeticException这个异常
//接着程序会拿着这个异常和catch里面的异常类已经对比
}catch(ArithmeticException e){
//当程序抛出ArithmeticException这个异常之后给出的具体的处理方法
System.out.println("你的除数不能为0");
}
System.out.println("end");
}
}
/**
* start
你的除数不能为0
end
*/
1.3多个异常的处理
A:针对每一个出现问题的地方写一个try…catch语句
B:针对多个异常,采用一个try,多个catch的情况。try…catch…catch…
遇到try里面的问题,就自动和catch里面进行匹配。一旦匹配就执行catch里面的内容,执行完毕后,接着执行后面的代码。
注意:如果异常间有子父关系,父必须在最后。
package com.edu_01;
public class ExceptionDemo4 {
public static void main(String[] args) {
int[] arr = {1,2,3};
try{
System.out.println(arr[3]);//抛出IndexOutOfBoundsException
System.out.println(10/0);//抛出ArithmeticException
arr = null;
System.out.println(arr[2]);//抛出空指针异常NullpointerException
}catch(IndexOutOfBoundsException e){//Exception e = new IndexOutOfBoundsException();
System.out.println("数组越界了");
}catch(ArithmeticException e){
System.out.println("除数不能为0");
}catch(Exception e){//当前面的异常类都没有匹配到之后,会自动匹配这个异常
System.out.println("出现了其他异常");
}
}
}
//输出结果:
//数组越界了
1.4 编译时期异常和运行时期异常的区别:
编译时期异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译。
运行时期异常:无需显示处理,也可以和编译时异常一样处理。
1.5 Throwable中的方法:(演示除数为0异常)
printStackTrace():打印异常信息,程序从出问题的地方开始就会打印创建一个该异常对应的对象, 该对象直接调用打印方法
package com.edu_01;
public class ExceptionDemo6 {
public static void main(String[] args) {
try{
System.out.println(10/0);//当程序出问题了之后会抛出一个ArithmeticException的对象
//new ArithmeticException()
}catch(ArithmeticException e){//ArithmeticException e = new ArithmeticException();
e.printStackTrace();//打印异常信息
}
}
}
//输出结果:
/**
* java.lang.ArithmeticException: / by zero
at com.edu_01.ExceptionDemo6.main(ExceptionDemo6.java:12)
*/
1.6 try…catch 与 throws的区别:
try…catch 是直接进行了处理。而throws则是把异常处理的事情交给了调用者。
throws用在方法上,声明方法有异常,交给调用者处理。但是如果是编译时期异常,调用者就必须处理。如果是运行时期异常,调用者可以处理,也可以不处理。
1.7 throws:(演示文件未找到异常,除数为0异常)
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用,号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能,并不一定会发生这些异常
package com.edu_01;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ExceptionDemo7 {
public static void main(String[] args){
method();
//method2();//将这个异常交给了调用者去进行处理
//1.可以继续往上throws,将异常继续向上进行抛出了
//2.自己try..catch...,相当与自己处理了这个异常
try{
method2();
}catch(FileNotFoundException e){
System.out.println("文件未找到");
}
/**
* 我们以后遇到异常是抛还是抓呢?
* 答:原则上能抛就抛,等到已经抛到了程序最底层的时候,最好就不要抛了,自己抓取。
*/
}
private static void method2() throws FileNotFoundException {
//此时会抛出一个编译时期的异常,
//我们必须在方法上进行声明,如果不声明的话,会一直编译报错
FileInputStream fis = new FileInputStream("D://a.txt");
}
private static void method() throws ArithmeticException {
//在方法声明上声明这个方法可能出现的异常,不代表这个异常一定会出现
//此时仅仅是告诉我的调用者我的这个方法可能会出现异常,并不做具体的处理,交给
//调用者自己去处理这个异常
//此时抛出的 出数为0的异常,属于运行时期异常
System.out.println(10/5);
}
}
1.8 异常处理 try…catch…finally
finally:一般用于释放资源。在数据库操作或者IO流比较常见。
特点:被finally控制的语句体一定会执行
特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))
1.9 final,finally的区别?
final:最终的意思。可以修饰类,方法,变量。
修饰类,类不能被继承
修饰方法,方法不能被重写
修饰变量,变量是常量
finally:
异常处理的一部分。被finally控制的代码一定会执行。
特殊情况:在执行到finally之前,jvm退出了。
1.9.1异常处理时应注意:
A:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
B:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
C:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
上述仅仅针对编译时期异常,与运行时期异常无关。
1.9.2 throw和throws的区别?
throws:
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
throw:
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常?
注意:如果throw的是编译时期异常,在方法声明上必须用throws进行标记
如果throw的是运行时期异常,在方法声明上可以用throws进行标记,也可以不用。
二:IO流
学习完异常,现在开始正式学习IO流,IO流部分是java中非常重要的一部分。
(一) I/O流的分类
一般我们在讨论IO的分类时,默认是按照数据类型分的。
(二) File
File类的使用和普通类基本一样,着重学习构造方法和成员方法。
File:文件和目录(文件夹)路径名的抽象表示形式。
2.1 File的构造方法:
File(String pathname):把一个路径名称封装成File对象
File(String parent, String child):把一个父路径和一个子路径封装成一个File对象
File(File parent, String child):把一个父路径File对象和一个子路径封装成一个File对象
package com.edu_01;
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args) throws IOException {
//File(String pathname):把一个路径名称封装成File对象
//File file = new File("D://a.txt");
//System.out.println(file.createNewFile());//当文件不存在的时候,创建文件,如果文件存在,不创建
System.out.println("------------");
//File(String parent, String child):把一个父路径和一个子路径封装成一个File对象
//File file = new File("D://test", "a.txt");
//java.io.IOException: 系统找不到指定的路径,
//创建文件的时候一定要保证路径存在
//System.out.println(file.createNewFile());
System.out.println("-----------------");
//File(File parent, String child):把一个父路径File对象和一个子路径封装成一个File对象
File file = new File("D://test");
File file2 = new File(file, "a.txt");
System.out.println(file2.createNewFile());
}
}
2.2 成员方法:
创建功能
A:创建文件
public boolean createNewFile():如果文件不存在,就创建。否则,不创建。
B:创建目录
public boolean mkdir():如果目录不存在,就创建。否则,不创建。
public boolean mkdirs():如果目录不存在,就创建。否则,不创建。即时父目录不存在,也可以连父目录一起创建。
注意事项:需要造什么东西,就应该用对应的方法。
删除功能:
public boolean delete():既可以删除文件,又可以删除目录。
路径问题:
A:绝对路径 就是以盘符开始的路径(d:\test\aaa\b.txt)
B:相对路径 就是不以盘符开始的路径(a.txt),一般都是相对应当前的项目而言的。
注意事项:
A:Java程序的删除不走回收站。
B:如果目录内还有内容就不能删除。
判断功能
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():获取名称
(三) 字节流及高效字节流
字节流比较通用,因为现在所有东西都是建立在字节的基础上的。
3.1 字节输出流 顾名思义是往文件里录入东西。
类OutputStream是一个抽象类,我们所用的是其子类FileOutputStream
有了这个类之后,我们如何往一个文件写数据呢(比如向文件中录入helloworld)?或者说就是字节输出流的操作步骤是什么呢?
A:创建字节输出流对象
B:调用写数据的方法
C:释放资源
具体做法如下:
A: 构造(两种方法)
FileOutputStream(File file)
FileOutputStream(String name)
FileOutputStream fos = new FileOutputStream("fos.txt");
/**请问上面这个操作做了哪几件事情?
1.创建了一个文件输出流fos,指向文件a.txt
2.创建了a.txt这个文件
*/
B: FileOutputStream写数据的方法
write(byte[] b)
write(int b) :一次写一个字节
write(byte[] b, int off, int len) :一次写一个字节数组的一部分
fos.write("helloworld".getBytes());//FileOutputStream对象fos调用write方法
C: fos.close(); 关流
关流之后就不能再往里录入了。
package cokm.edu_01;
//向文件中录入helloworld
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputStreamDemo {
public static void main(String[] args) throws IOException {
//1.创建文件输出流对象
FileOutputStream fos = new FileOutputStream("a.txt");
//或者 File file = new File("a.txt");
//FileOutputStream fos = new FileOutputStream(file);
//2.调用输出流的写数据的方法给文件中写数据
byte[] byf = "helloworld".getBytes();
fos.write(byf);
//3.释放资源,关流操作
fos.close();
}
}
3.2 字节输入流: 读取文件
我们现在会写了,那么怎么读取文件呢?这里用到了字节输入流。
字节输入流操作步骤(类似于字节输出流):
A: 创建字节输入流对象
FileInputStream fis = new FileInputStream(“a.txt”);
构造方法:
FileOutputStream(File file)
FileOutputStream(String name)
B: 调用方法读取数据,read方法,当文件读取到末尾后如果继续读取,将返回,-1
read(),一次读取一个字节,返回的是int型,是读取字节对应的ASCII码
read(byte[] b) 一次读取一个字节数组的长度,返回int,是实际读取的长度,并将读取的内容返回到数组中
read(byte[] b, int off, int len) 一次读取一个字节数组的一部分,返回int,是实际读取的长度,并将读取的内容返回到数组中
C: 释放资源
fis.close();
package cokm.edu_02;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class InputStreamDmeo {
public static void main(String[] args) throws IOException {
//读取文件a.txt
//1.创建文件输入流对象,并关联文件
FileInputStream fis = new FileInputStream("a.txt");
//2.调用输入流的读取文件的方法,读取文件
int by;
while ((by=fis.read())!=-1) {
System.out.println((char)by);
}
//3.释放资源
fis.close();
}
}
现在我们会读取一个文件,又会写文件,那么复制一个文件呢?边读边写就行了 。
练习:A:把a.txt的内容复制到b.txt中
分析:
A. 封装数据源和目的地
B. 利用循环,边读边写
C. 释放资源,关流
package cokm.edu_03;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFile {
public static void main(String[] args) throws IOException {
//A:把a.txt的内容复制到b.txt中
//封装数据源和目的地
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt");
//2.读取数据源中的数据,将读取到的数据写入目的地中
int by;
while ((by=fis.read())!=-1) {
//将读取到的字节写入fos中
fos.write(by);
}
//3.释放资源
fos.close();
fis.close();
}
}
3.3 字节缓冲区流(也叫高效流):
BufferedInputStream(read() 一次读取一个字节, public int read(byte[] b):返回实际读取长度,数据被读取到数组中。)
BufferedOutputStream(write(byte[] b))
字节流分为:
低级流: 基本的流,可以直接操作文件。
高级流:是操作基本流的流。
字节流综合练习:
字节流复制文件(视频文件,并测试所用时间):
A:基本字节流一次读写一个字节
B:基本字节流一次读写一个字节数组(字节流中的最重要部分)
C:高效字节流一次读写一个字节
D:高效字节流一次读写一个字节数组
package cokm.edu_06;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
//复制文件开始之前的时间
long startTime = System.currentTimeMillis();
method1();//15099
method2();//31(这种处理方案使用的是最多的)
method3();//234
method4();//16
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
}
private static void method4() throws IOException {
// TODO Auto-generated method stub
//C:高效字节流一次读写一个字节
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D://b.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.mp4"));
//高校字节流一次读写一个字节
byte[] byf = new byte[1024];
int len;
while ((len = bis.read(byf))!=-1) {
bos.write(byf, 0, len);
}
//3.关流
bos.close();
bis.close();
}
private static void method3() throws IOException {
//C:高效字节流一次读写一个字节
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D://b.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.mp4"));
//高校字节流一次读写一个字节
int by;
while ((by = bis.read())!=-1) {
bos.write(by);
}
//3.关流
bos.close();
bis.close();
}
private static void method2() throws IOException {
// B:基本字节流一次读写一个字节数组
//疯转数据剧院和目的地
FileInputStream fis = new FileInputStream("D://b.mp4");
FileOutputStream fos = new FileOutputStream("a.mp4");
//一次读写一个字节
byte[] byf =new byte[1024];
int len;
while ((len=fis.read(byf))!=-1) {
fos.write(byf, 0, len);
}
//3.释放资源
fos.close();
fis.close();
}
private static void method1() throws IOException {
//A:基本字节流一次读写一个字节
//疯转数据剧院和目的地
FileInputStream fis = new FileInputStream("D://b.mp4");
FileOutputStream fos = new FileOutputStream("a.mp4");
//一次读写一个字节
int by;
while ((by=fis.read())!=-1) {
fos.write(by);
}
//3.释放资源
fos.close();
fis.close();
}
}
到此,字节流基本上学完了。
=====================以下为知识补充=====================
编码、解码问题
public byte[] getBytes(String charsetName) 按照给定的编码方式,编码字节数组(gbk,utf-8)
String(byte[] bytes, String charsetName) 按照给定的编码方式解码字符数组
package cokm.edu_07;
//编解码练习
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class EncodingDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
//创建一个字符串
String s = "中国好";
byte[] bytes1 = s.getBytes();
//使用gbk去进行编码的时候,我们是将一个汉字编码成两个负数的字节。
//当我们解码的时候,如果字节数组中遇见负数的话,会将前面和后面的负数进行拼接,去gbk码表中找到相应的明文
System.out.println(Arrays.toString(bytes1));//[-42, -48, -71, -6, -70, -61]
System.out.println("----------------");
byte[] bytes2 = s.getBytes("gbk");
System.out.println(Arrays.toString(bytes2));//[-42, -48, -71, -6, -70, -61]
System.out.println("------------");
byte[] bytes3 = s.getBytes("utf-8");
//当使用uft-8进行编码的时候,一个汉字会被编码成3个负数的字节
//解码的时候,一旦遇见负数,三个拼接,去码表中进行解码
System.out.println(Arrays.toString(bytes3));//[-28, -72, -83, -27, -101, -67, -27, -91, -67]
System.out.println("--------------");
System.out.println(new String(bytes3));//涓浗濂?
System.out.println(new String(bytes3, "utf-8"));//中国好
//结论:以后使用哪种编码就使用哪种解码(编解码方式必须一致,不然会出现乱码)
//我们以后编解码方式一般来说不需要指定特定的码表,一般来说使用默认的就可以
}
}