IO流(上)
一、概述:
IO流:即Input、Output流,输入、输出流。在java中,对数据的操作通过流的形式完成;流可以按照操作对象的不同分为字符流(专门用于操作纯文本文件)和字节流(非纯文本文件)两种,也可以按照流向的不同分为输入流和输出流,这里的输入和输出都是相对于内存而言的。
总之:IO流专门用来处理设备之间的数据传输;字符流=字节流+默认的编码。
本章要介绍的字符流类如下图所示:
本章要介绍的字节流类如下图所示:
二、字符流
字符流有默认的编码,一般用于操作纯文本的数据。
字符流的两个抽象基类:Reader(读取数据),Writer(写出数据,内存写到外设中);这两个抽象基类提供了对于字符流操作的基本方法,但有些方法是抽象的,必须由子类去覆盖。一切操作字符流的类都直接或者间接继承Reader、Writer抽象基类;这些子类的后缀是父类名称,前缀是具体的功能,例如:FileReader、BufferedReader、FileWriter、BufferedWriter等等。
既然IO流用于操作数据,那么数据的最常见体现形式就是:文件。在IO流中,有专门的类作文件数据的流对象,这两个类就是FileReader、FileWriter。
flush()方法:调用write方法,只是把数据写入到流中;而flush方法用于将流中的数据刷到目的文件中;注意:读取流没有刷新这种说法。
为了能更加清晰、更加深刻的理解,下面主要以代码的形式介绍:
往磁盘文件中写入数据:
package itheima.day18;
import java.io.FileWriter;
import java.io.IOException;
//需求:在硬盘上,创建一个文件并写入一些文字数据
public class FileWriterDemo {
public static void main(String[] args) {
FileWriter fw = null;
try {
// append为true时,不覆盖原文件
fw = new FileWriter("D:\\java\\IODemoFile\\demo.txt"/*,true*/);
fw.write("坚韧的民族主义者---蒋中正;");
fw.flush();
fw.write("抗日英雄----李宗仁;");
fw.write("当代小诸葛---白崇禧;");
fw.flush();
} catch (IOException e) {
throw new RuntimeException("文件读写失败");
}finally{
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException("文件关闭失败");
}
}
}
}
1、读取一个.java文件,并打印到控制台上:
package itheima.day18;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\FileReaderTest.java");
char[] buf = new char[1024];
int num=0;
while((num=fr.read(buf))!=-1){
System.out.print(new String(buf,0,num));
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到异常!");
} catch (IOException e) {
throw new RuntimeException("IO读写异常!");
}finally{
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException("文件关闭失败!");
}
}
}
}
2、复制一个文件:
package itheima.day18;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//需求:将c盘一个文件复制到D盘(主要是演示文件的复制)
// 复制原理:其实就是将C盘下的文件数据存储到D盘的一个文件中。
// 步骤:
// 1、在D盘创建一个文件,用于存储c盘文件中的数据
// 2、定义读取流和C盘文件关联
// 3、通过不断的读写完成数据的存储
public class CopyTest {
public static void main(String[] args) {
copy_2();
}
public static void copy_1(){
FileWriter fw =null;
FileReader fr = null;
try {
fw = new FileWriter("D:\\java\\IODemoFile\\CopyTest.java");
fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\CopyTest.java");
int ch =0;
while((ch =fr.read())!=-1){
fw.write((char)ch);
fw.flush();
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到异常!");
} catch (IOException e) {
throw new RuntimeException("IO读取异常!");
}finally{
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException("写入文件关闭失败!");
}
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException("读取文件关闭异常!");
}
}
}
public static void copy_2(){
FileWriter fw =null;
FileReader fr = null;
try {
fw = new FileWriter("D:\\java\\IODemoFile\\CopyTest.java");
fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\CopyTest.java");
char[] buf = new char[1024];
int len =0;
while((len =fr.read(buf))!=-1){
fw.write(buf,0,len);
fw.flush();
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到异常!");
} catch (IOException e) {
throw new RuntimeException("IO读写异常!");
}finally{
try {
if(fw!=null)
fw.close();
} catch (IOException e) {
throw new RuntimeException("写入文件关闭失败!");
}
try {
if(fr!=null)
fr.close();
} catch (IOException e) {
throw new RuntimeException("读取文件关闭失败!");
}
}
}
}
字符流的缓冲区:缓冲区的出现是为了提高对数据的读写效率;对应类:BufferedReader、BufferedWriter,注意:缓冲区只是在流的基础上对流的功能进行增强,所以必须首先有流对象。
BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入,最重要的增加了newLine()方法。
BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取,最重要的是增加了readLine()方法。
void newLine(): 写入一个行分隔符。
String readLine():读取一个文本行,只返回数据,不返回回车符。
readLine()方法的原理:无论是读一行、获取多个字符,还是读取一个字符,其实最终都是在硬盘上一个一个的读取。所以readLine()方法最终使用的还是read()一次读一个的方法。
3、通过缓冲区复制一个.java文件:
package itheima.day19;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//通过缓冲区复制一个.java文件
public class CopyTextByBuffered {
public static void main(String[] args) {
BufferedReader bufr = null;
BufferedWriter bufw = null;
try {
bufr =new BufferedReader(
new FileReader(
"D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day19\\CopyTextByBuffered.java"));
bufw = new BufferedWriter(
new FileWriter("D:\\java\\IODemoFile\\CopyTestByBuf.java"));
String line =null;
while((line = bufr.readLine())!=null){
System.out.println(line);
bufw.write(line);
bufw.newLine();
bufw.flush();
}
} catch (IOException e) {
throw new RuntimeException("文件读取失败!");
}finally{
try {
if(bufr!=null)
bufr.close();
}catch (IOException e) {
throw new RuntimeException("文件读取关闭失败!");
}
try {
if(bufw!=null)
bufw.close();
}catch (IOException e) {
throw new RuntimeException("文件写出关闭失败!");
}
}
System.out.println("复制完成!!!");
}
}
装饰设计模式:当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有的功能,并提供加强功能;那么自定义的该类成为装饰类。装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰对象的功能,提供更强的功能。
装饰设计模式比继承更加灵活,不仅避免了继承体系的臃肿,还降低了类与类之间的耦合关系。装饰类因为只是增强已有对象,具备的功能和已有对象的功能是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于同一个体系中。
4、模拟带有ReadLine功能的BufferedReader:
package itheima.day19;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
// 定义MyBufferedReader,myReadLine方法
public class MyBufferedReaderDemo
{
public static void main(String[] args) {
MyBufferedReader myBuf = null;
try {
FileReader fr = new FileReader("D:\\java\\IODemoFile\\CopyTestByBuf.java");
myBuf = new MyBufferedReader(fr);
String line = null;
while((line = myBuf.myReadLine())!=null){
System.out.println(line);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("要读取的文件不存在!");
} catch (IOException e) {
throw new RuntimeException("文件读写失败!");
}finally{
try {
if(myBuf!=null)
myBuf.myClose();
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Reader r){
this.r = r;
}
public String myReadLine() throws IOException{
.
StringBuilder sb = new StringBuilder();
int ch =0;
while((ch =r.read())!= -1){
if(ch =='\r')
continue;
if(ch =='\n')
return sb.toString();
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void myClose() throws IOException{
r.close();
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return r.read(cbuf,off,len);
}
@Override
public void close() throws IOException {
r.close();
}
}
5、模拟带有行号功能的LineNumberReader:
package itheima.day19;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import itheima.day19.MyBufferedReader;
public class MyLineNumberReaderDemo {
public static void main(String[] args) {
MyLineNumberReader mylnr = null;
try {
FileReader fr = new FileReader("src\\itheima\\day19\\LineNumberReaderDemo.java");
mylnr = new MyLineNumberReader(fr);
String line = null;
mylnr.setLineNumber(200);
while((line = mylnr.myReadLine())!=null){
System.out.println(mylnr.getLineNumber()+"::"+line);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到异常!");
} catch (IOException e) {
throw new RuntimeException("文件读写异常!");
}finally{
try {
if(mylnr!=null)
mylnr.myClose();
} catch (IOException e) {
throw new RuntimeException("文件关闭失败!");
}
}
}
}
class MyLineNumberReader extends MyBufferedReader
{
private int lineNumber;
MyLineNumberReader(Reader r){
super(r);
}
public String myReadLine() throws IOException{
lineNumber++;
return super.myReadLine();
}
public void setLineNumber(int lineNumber){
this.lineNumber = lineNumber;
}
public int getLineNumber(){
return lineNumber;
}
}
三、字节流
字符流用于操作纯文本文件,但在计算机中有些文件不是文本类型的,比如:图片、mp3、电影等等,这时就要用到字节流操作。字节流的基本操作与字符流相同,但它不仅可以操作字符,还可以操作其他媒体文件。
字节流的两个抽象基类:InputStream:,输入流,相对于内存而言,数据流入到内存中,读取;OutputStream:输出流,数据从内存流出到外设中,写出。
OutputStream类中也有write方法,可是使用的是字节数组,而字符流用的是字符数组。
带缓冲区的字节流的读取原理如下图:
6、复制图片:
package itheima.day19;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
//复制一个图片
public class CopyPic {
public static void main(String[] args) {
copyPicture();
}
public static void copyPicture(){
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream("抗日英雄:李宗仁.jpg");
fos = new FileOutputStream("D:\\java\\IODemoFile\\李宗仁:台儿庄.jpg");
fos.write("abcd".getBytes());
byte[] buf = new byte[1024];
int len =0;
while((len = fis.read(buf))!=-1){
fos.write(buf,0,len);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件未找到异常!");
} catch (IOException e) {
throw new RuntimeException("文件读写异常!");
}finally{
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
throw new RuntimeException("写入文件关闭失败!");
}
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
throw new RuntimeException("读取文件关闭失败!");
}
}
}
}
7、复制mp3:
package itheima.day19;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
//复制mp3,通过缓冲区
public class CopyMP3 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
copy_1();
long end = System.currentTimeMillis();
System.out.println(end - start+"毫秒");
}
public static void copy_1(){
BufferedInputStream bufis = null;
BufferedOutputStream bufos = null;
try {
bufis =
new BufferedInputStream(
new FileInputStream(
"D:\\静心--佛家歌曲\\王菲《心经》第三届世界佛教论坛.mp3"));
bufos =
new BufferedOutputStream(
new FileOutputStream("D:\\java\\IODemoFile\\心经.mp3"));
int by =0;
while((by = bufis.read())!=-1){
bufos.write(by);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bufos!=null)
bufos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bufis!=null)
bufis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void copy_2(){
MyBufferedInputStream bufis = null;
BufferedOutputStream bufos = null;
try {
bufis = new MyBufferedInputStream(
new FileInputStream("D:\\静心--佛家歌曲\\王菲《心经》第三届世界佛教论坛.mp3"));
bufos = new BufferedOutputStream(
new FileOutputStream("D:\\java\\IODemoFile\\心经11.mp3"));
int by =0;
while((by = bufis.myRead())!=-1){
bufos.write(by);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(bufos!=null)
bufos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bufis!=null)
bufis.myClose();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//模拟BufferedInputStream类
//字节流缓冲区的基本原理:
class MyBufferedInputStream{
private InputStream in;
private byte[] buf = new byte[1024];
private int pos =0 ,count = 0;
MyBufferedInputStream(InputStream in){
this.in = in;
}
// 为何返回int 而不是byte?
// byte -1 ---> int -1(自动类型提升)
// 1111 1111 --> 1111....1111(32)位
// 很有可能流会读到连续八个1,返回byte类型的-1,会与结束标识混淆!
public int myRead() throws IOException{
// 先将数据读取到字节数组buf中
if(count ==0){
count = in.read(buf);
if(count<0)
<span style="white-space:pre"> </span> return -1;
// 一个一个读取,指针向前,数量减少
pos =0;
byte b= buf[pos];
count--;
pos++;
return b&0xff;
}
else if(count>0){
byte b = buf[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
public void myClose() throws IOException{
in.close();
}
}
四、转换流
当字节流中的数据都是字符时,转成字符流操作更加高效。转换流就是字符流与字节流之间的桥梁,主要是方便字符流与字节流之间的操作。因为有了字符流之后才需要转换,所以转换流定义在字符流体系中。
InputStreamReader:字节流通向字符流的桥梁;
OutputStreamWriter:字符流通向字节流的桥梁。
标准输入输出流:在System类中,有两个字段:in、out,它们分别代表系统标准的输入和输出设备;默认的输入设备对应的是键盘,输出设备对应的是控制台。System.in的类型是InputStream,System.out的类型是PrintStream;PrintStream是OutputStream的子类。可以通过System.setIn();System.setOut()改变标准输入输出设备。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
8、读取键盘录入:
package itheima.day19;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
//读取键盘录入。
// System.out:对应的设备是标准输出设备,控制台。
// System.in:对应的设备是标准输入设备,键盘。
//需求:通过键盘录入数据,
// 当录入一行数据后,就将改行数据打印
// over,停止录入。
public class ReadIn {
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream("src\\itheima\\day19\\ReadIn.java"));
<span style="white-space:pre"> </span> System.setOut(new PrintStream("zz.txt"));
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader bufr = new BufferedReader(isr);
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
String line = null;
while((line = bufr.readLine())!=null){
if("over".equalsIgnoreCase(line))
break;
// System.out.println(line.toUpperCase());
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
public static void method_1() throws IOException{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while(true){
int ch = in.read();
if(ch=='\r')
continue;
if(ch=='\n'){
String s = sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0, sb.length());
}
else{
sb.append((char)ch);
}
}
}
}
9、异常信息的日志存储:
package itheima.day19;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;
//异常的日志信息
public class ExceptionInfoDemo {
public static void main(String[] args) {
try {
int[] arr = new int[2];
System.out.println(arr[3]);
} catch (Exception e) {
try {
// e.printStackTrace(new PrintStream("a.txt"));
// log4j 专门处理异常日志的工具包
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
String s = sdf.format(d);
PrintStream ps =new PrintStream("exception.log");
ps.println(s);
System.setOut(ps);
e.printStackTrace(System.out);
} catch (FileNotFoundException e1) {
throw new RuntimeException("日志文件创建失败!");
}
}
}
}
流操作的基本规律:流对象很多,不知道用哪个;可以通过下面三个明确来完成。
1、明确源和目的,源:输入流,InputStream、Reader;目的:输出流,OutputStream
Writer。
2、操作的是否是纯文本?是:字符流;否:字节流。
3、当体系明确以后,再明确使用哪个具体的对象,通过设备来进行区分:
源:内存、硬盘、键盘;目的:内存、硬盘、控制台。
例如:1、将一个文本文件中的数据存储到另一个文件中:复制文件。
源:因为是源,所以使用读取流。InputStream Reader;是否操作文本文件?是:这时就可以选择Reader,这样体系就明确了。
接下来明确要使用该体系中的那个对象?明确设备:硬盘,上的一个文件。
Reader体系中可以操作文件的对象是FileReader
是否需要提高效率?
是,加入Reader体系中的缓冲区BufferedReader
FileReaderfr = new FileReader(“a.txt”)
BufferedReaderbufr = new BufferedReader(fr);
目的:OutputStream Writer ;是否是纯文本?是:Writer
设备:硬盘,一个文件,Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率?是!加入Writer体系中缓冲区BUfferedWriter
FileWriterfw = new FileWriter(“b.txt”);
BufferedWriterbufw = new BufferedWriter(fw);