IO(Input output)流,分为两种:字节流和字符流
字节流的两个基类:
InputStream(读)OutStream(写)
字符流的两个基类:
Reader(读) Writer(写)
字符流
写(FileWriter)和读(FileReader)
写(FileWriter)
我们知道既然IO流是用于操作数据的,那么数据的最常见的体现形式是:文件
我们使用一个操作文件的Writer子类是FileWriter。我们要查Writer API时注意:后缀名是父类名,前缀名是是该流对象的功能。
我们来首先来看看Writer基类的几种常用方法:
a) publicvoid write(String str) throws IOException:写入字符串
b) publicabstract void flush()throws IOException:刷新该流的缓冲
c) publicabstract void close()throws IOException:关闭该流,但先刷新
对于如何创建一个字符Writer流,我们来详细看看如下代码:
import java.io.*;
class FileWriterDemo
{
publicstatic void main(String[] args) throws IOException
{
//创建一个FileWriter对象,
//该对象一旦被创建就必须明确要被操作的文件
//而且该文件会被创建到指定目录下
//如果该目录下已有同名文件,将被覆盖
//其实改不就是在明确数据要存放的目的地
FileWriterfw=new FileWriter("demo.txt");
//调用write方法,将字符串写入到流中
fw.write("abcde");
//刷新流对象中的缓冲中数据
//将数据刷到目的地中,即将数据保存到目的地
//fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据
//将数据刷到目的地中
//和flush区别:flush刷新后,流可以继续使用,close刷新后,流会关闭
fw.close();
}
}
IO异常(IOException)的处理方式:我们对与Io出现的异常不能抛,我们要自己解决,我们如何去处理一个IO异常呢?我们用代码演示一遍:
import java.io.*;
class IOExceptionDemo
{
publicstatic void main(String[] args)
{
//我们首先创建类型的变量并作用于整个域
FileWriterfw=null;
try{
fw=newFileWriter("k://demo.txt");
fw.write("xixi");
}catch(IOExceptione){
//此处是对异常的处理
System.out.println("catch:"+e);
}finally{
try{
//我们要首先判断对象是否为空,
//此处对象异常不能让用户看到
if(fw!=null)
//close方法必须要单独try,且放在Finally语句中
fw.close();
}catch(IOExceptione){
}
}
}
}
我们如何对数据进行续写?我们就要使用到FileWriter的另一个构造方法。
FileWriter(String fileName, boolean append):根据给定的文件名以及指示是否附加写入数据的 boolean值来构造 FileWriter对象。
创建的时候即new FileWriter(“数据目的地”,“true”);
读(FileReader)
Reader有哪些常用的方法:
a) public int read()throws IOException:读取单个字符
b) public int read(char[] cbuf) throwsIOException:将字符读入数组。
c) public abstract void close()throwsIOException:关闭该流并释放与之关联的所有资源,此处没有刷新。
IO中Reader有两种方式读取:
第一种:使用第一种read方法读取单个字符
import java.io.*;
class ReaderDemo
{
publicstatic void main(String[] args) throws IOException
{
//创建一个文件读取流对象,和指定名称的文件相关联
//要保证该文件是已经存在的。
//如果不存在,会发生FileNotFoundException
FileReaderfr=new FileReader("demo.txt");
//调用读取对象的read方法
//read():一次读一个字符,而且会自动往下读。
//fr.read()
intch=0;;
while((ch=fr.read())!=-1){
System.out.print((char)ch);//强制转换把int转成字符型char。
}
fr.close();
}
}
第二种:使用重载的read(char[] c)方法,读取数组长度的数据
import java.io.*;
class ReaderDemo2
{
publicstatic void main(String[] args) throws IOException
{
FileReaderfr=new FileReader("demo.txt");
//定义一个字符数组。用于存储读到字符
//该read(char[])返回的是读到字符个数
char[]buf=new char[1024];//此处最好设1024
intnum=0;
while((num=fr.read(buf))!=-1){
System.out.println("num:"+num+":"+newString(buf,0,num));
}
fr.close();
}
}
IO读写交互复制
IO流经常涉及到文本复制动作,即读写动作,重点掌握,我们演示下两种文本复制方法:
import java.io.*;
class CopyOfIO
{
publicstatic void main(String[] args) throws IOException
{
//copy_1();
copy_2();
}
publicstatic void copy_1() throws IOException{ //第一种读写
FileWriterfw=new FileWriter("D:\\ReaderDemo_copy.txt");
FileReaderfr=new FileReader("ReaderDemo.java");
intnum=0;
while((num=fr.read())!=-1){
fw.write(num);
}
fw.close();
fr.close();
}
publicstatic void copy_2(){ //第二种读写
FileWriterfw=null;
FileReaderfr=null;
try{
fw=newFileWriter("D:\\ReaderDemo_copy2.txt");
fr=newFileReader("ReaderDemo2.java");
char[]c=new char[1024];
intnum=0;
while((num=fr.read())!=-1){
fw.write(num);
}
}catch(IOExceptione){
thrownew RuntimeException("读写失败");
}finally{
if(fw!=null)
{
try{fw.close();}catch(IOExceptione){}
}
if(fr!=null)
{
try{fr.close();}catch(IOExceptione){}
}
}
}
}
缓冲区读与写
IO流的缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要现有流对象
写(BufferedWrite)
BufferedWrite提供了一个跨平台的换行符方法:newLine();我们结合此方法看看BufferedWriter的代码的演示。
importjava.io.*;
class BufferedWriterDemo
{
publicstatic void main(String[] args) throws IOException
{
//创建一个字符写入流
FileWriterfw=new FileWriter("BufferedWriterDemo.txt");
//将字符写入流的对象存入到缓冲区中构造方法中
//缓冲去是存储文件一种效率的方式
BufferedWriterbw=new BufferedWriter(fw);
for(inti=0;i<5;i++){
bw.write("abcde"+i);
bw.newLine();//平台跨越性换行
}
bw.close();//关闭缓冲流既是关闭流中的字符写入流对象
}
}
读(BufferedReader)
字符读取流缓冲区中提供了一个一次读一行的方法readLine,方便于对文本数据的获取
我们演示下缓冲读的代码:
import java.io.*;
class BufferedReaderDemo
{
publicstatic void main(String[] args) throwsIOException
{
//创建一个字符读取流对象,并构造方法传入已存在关联的文件名称
FileReaderfr=new FileReader("BufferedWriterDemo.java");
//加入了缓冲流技术,提高了读取效率
//将字符读取对象存入到缓冲读的构造方法
BufferedReaderbr=new BufferedReader(fr);
Strings=null;
while((s=br.readLine())!=null){
System.out.println(s);
}
}
}
IO缓冲读写交互复制
import java.io.*;
class BufferCopyDemo
{
publicstatic void main(String[] args)
{
BufferedWriterbw=null;
BufferedReaderbr=null;
try{
bw=new BufferedWriter(newFileWriter("D:\\copyDemo.txt"));
br=newBufferedReader(new FileReader("BufferedReaderDemo.java"));
Strings=null;
while((s=br.readLine())!=null){
bw.write(s);
bw.newLine();//换行
bw.flush();//读取一次刷新一次
}
}catch(IOExceptione){
thrownew RuntimeException("读取失败");
}finally{
if(br!=null)
try{br.close();}catch(IOExceptione){
thrownew RuntimeException("结束流失败");
};
if(bw!=null)
try{bw.close();}catch(IOExceptione){
throw new RuntimeException("结束流失败");
};
}
}
}
在字符流中还有LineNumberReader,可以给读出来的字符加行号。如以下例子:
import java.io.*;
class LineNumberReaderDemo
{
publicstatic void main(String[] args) throws IOException
{
FileReaderfr=new FileReader("ReaderDemo.java");
LineNumberReaderlnr=new LineNumberReader(fr);
Strings=null;
while((s=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+s);
}
}
}
字节流
字节流,分为两个基类,分别是OutStream和InputStream。我们操控文件的两个子类分别是FileOutStream(写)和FileInputStream(读)。
字节流的读与写
FileOutputStream(写)的具体过程是什么样的?其实是和字符流过程差不多,我们做下演示:
import java.io.*;
class FileOutputStreamDemo
{
publicstatic void main(String[] args) throws IOException
{
//创建一个写的字节流,并传入要写入的文件名。
FileOutputStreamfos=new FileOutputStream("fos.txt");
fos.write("abcde".getBytes());//必须把字节转换为字符
fos.close();
}
}
FileInputStream(读)的具体过程也是什么样的呢?
FileInputStream总共有三种读取方式:最后一种使用了特有的方法就是available(),是代表了字节的个数。我们看看这三种读取方式的演示:
import java.io.*;
class FileInputStreamDemo
{
//第一种读取方式:一个个读
publicstatic void fileRead_1()throws IOException
{
//创建一个读取字节流,并关联要读取的文件
FileInputStreamfis=new FileInputStream("fos.txt");
intch=0;
while((ch=fis.read())!=-1){
System.out.println((char)ch);
}
fis.close();
}
//第二种,使用一个字节数组读
publicstatic void fileRead_2()throws IOException
{
FileInputStreamfis=new FileInputStream("fos.txt");
byte[]buf=new byte[1024];//需要是1024的整数倍
intch=0;
while((ch=fis.read(buf))!=-1){
System.out.println(newString(buf,0,ch));
}
fis.close();
}
//第三种,使用FileInputStream特有的方法available();
//这种读取大文件不可取,会造成内存溢出
publicstatic void fileRead_3()throws IOException
{
FileInputStreamfis=new FileInputStream("fos.txt");
byte[]buf=new byte[fis.available()];
fis.read(buf);
System.out.println(newString(buf));
fis.close();
}
public staticvoid main(String[] args) throws IOException
{
//fileRead_1();
//fileRead_2();
fileRead_3();
}
}
字节流的文件复制:我们通过字节流的边读边写来复制一个图片
import java.io.*;
class CopyPic
{
publicstatic void main(String[] args)
{
FileInputStreamfis=null;
FileOutputStreamfos=null;
try{
fis=newFileInputStream("1.bmp");
fos=newFileOutputStream("2.bmp");
byte[]buf=new byte[1024];//定义一个存放容量
intlen=0;
while((len=fis.read(buf))!=-1){//读取容器中字节
fos.write(buf,0,len);//写到容器中的0到len的长度,
}
}catch(IOExceptione){
thrownew RuntimeException("文件复制失败");
}finally{
try
{
if(fis!=null)
fis.close();
}
catch(IOException e)
{
thrownew RuntimeException("读取关闭失败");
}
try
{
if(fos!=null)
fos.close();
}
catch(IOException e)
{
thrownew RuntimeException("写入关闭失败");
}
}
}
}
字节流的缓冲读写
我们知道了字符流的缓冲读与写,知道缓冲可以提高文件复制的效率,字节流的缓冲读与写分别是BufferedInputStream和BufferedOutputStream这两个类。
那么我们来看看一个演示缓冲复制mp3文件的代码:
import java.io.*;
class CopyMp3
{
publicstatic void main(String[] args) throws IOException
{
longstart=System.currentTimeMillis();
copy_1();
longend=System.currentTimeMillis();
System.out.println("复制了"+(end-start)+"毫秒");
}
publicstatic void copy_1()throws IOException
{
//创建一个读取缓冲字节流,并把文件读取字节流对象传入
BufferedInputStreambis=new BufferedInputStream(new FileInputStream("1.mp3"));
//创建一个写入缓冲字节流,并把文件写入字节流对象传入
BufferedOutputStreambos=new BufferedOutputStream(new FileOutputStream("2.mp3"));
intlen=0;
while((len=bis.read())!=-1){
bos.write(len);
}
bis.close();
bos.close();
}
}
字节流的自定义缓冲read()原理:
import java.io.*;
class CopyMp3
{
publicstatic void main(String[] args) throws IOException
{
longstart=System.currentTimeMillis();
copy_1();
longend=System.currentTimeMillis();
System.out.println("复制了"+(end-start)+"毫秒");
}
publicstatic void copy_1()throws IOException
{
//创建一个读取缓冲字节流,并把文件读取字节流对象传入
MyBufferedbis=new MyBuffered(new FileInputStream("1.mp3"));
//创建一个写入缓冲字节流,并把文件写入字节流对象传入
BufferedOutputStreambos=new BufferedOutputStream(new FileOutputStream("2.mp3"));
intlen=0;
while((len=bis.myRead())!=-1){
bos.write(len);
}
bis.myClose();
bos.close();
}
}
class MyBuffered
{
privateInputStream is;
publicMyBuffered(InputStream is){
this.is=is;
}
privatebyte[] buf=new byte[1024];
intcount=0,pos=0;
publicint myRead()throws IOException{
//当字节数为0的时候,即开始读取
if(count==0){
count=is.read(buf);//将byte[]数组里读到的字节数赋给count;
pos=0;//指针,即数组的索引,默认是0
byteb=buf[pos];//把索引处的字节赋给变量b。
//假如已读取1024个字节,每读一次,总1024的字节数减1,
//至到减到0,重新又新开一个byte[]数组,即count=0;又开始循环
count--;
//这里是索引处,每次count-1一次,pos索引读取一次字节
pos++;
returnb&255;//返回读取到的字节,&255的原因是为了不等于-1,有关进制原理
}elseif(count>0){
byteb=buf[pos];
count--;
pos++;
returnb&0xff;
}
return-1;
}
publicvoid myClose()throws IOException{
is.close();
}
}
字节流与字符流的交互
读取转换流
我们使用字节流的读取来读取键盘的录入,这里我们涉及到System的输入输出情况
a) System.out:对应的是标准的输出设备,控制台。
b) System.in:对应的标准输入设计。
那么我们根据System这两种来看看IO流的读取键盘录入的两种方法:
第一种:
import java.io.*;
class ReadIn
{
publicstatic void main(String[] args) throws IOException
{
InputStreamis=System.in;
StringBuildersb=new StringBuilder();//使用可变的容器,来存放输入的字符
while(true){
intch=is.read();//键盘输入读取的字节赋给ch
if(ch=='\r')
continue;
if(ch=='\n'){
Strings=sb.toString();
if("over".equals(s))//自定义结束程序
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());//读取完以后我们清空一次StringBuffer。
}else
sb.append((char)ch);
}
第二种:我们使用的是字符流中的InputStreamReader将字节流转换成字符流,再使用装饰缓冲BufferedReader类里的readLine方法来读取键盘的输入。如:
import java.io.*;
class ReadIn2
{
publicstatic void main(String[] args) throws IOException
{
InputStream is=System.in;
InputStreamReader isr=newInputStreamReader(is);//将字节流转换为字符流
BufferedReader br=newBufferedReader(isr);
Strings=null;
while((s=br.readLine())!=null){
if("over".equals(s)){//自定义退出标签
break;
}
System.out.println(s.toUpperCase());//将输入的字符转换为大写
}
}
}
上面的蓝色字体我们以后可以简写为:
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
这段代码以后是我们经常使用键盘录入的一段代码,要记熟
输出转换流
在字节流输出转换为字符流输出,我们就要用到OutputStreamWriter类来转换
importjava.io.*;
classReadIn2
{
public static void main(String[] args)throws IOException
{
/*
键盘录入
*/
InputStream is=System.in;
InputStreamReader isr=newInputStreamReader(is);//将字节流转换为字符流
BufferedReader br=newBufferedReader(isr);
/*
录入输出
*/
OutputStreamos=System.out;
OutputStreamWriterosw=new OutputStreamWriter(os);
BufferedWriterbw=new BufferedWriter(osw);
String s=null;
while((s=br.readLine())!=null){
if("over".equals(s)){//自定义退出标签
break;
}
//System.out.println(s.toUpperCase());//将输入的字符转换为大写
bw.write(s.toUpperCase());//将读取到字符的输出到控制台上
bw.newLine();//换行
bw.flush();//必须刷新,否则无法看不到控制台上输出的字符
}
}
}
我们也可以将上面蓝色字体简写为:
BufferedWriter bw=new BufferedWriter(new OutStreamWriter(System.in));
流操作的基本规律
我们常常很痛苦就是流对象有很多,不知道该用哪一个?
那么我们可以通过三个明确来完成
a) 明确源和目的。
源:输入流。 InputStream Reader
目的:输出流。Outpustream Writer
b) 操作的数据是否是纯文本。
i. 是:字符流
ii. 不是:字节流
c) 当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
i. 源设备:内存,硬盘,键盘
ii. 目的设备:内存,硬盘,控制台
举一个例子,比如我们将一个文本文件中数据存储到另一个文件中,即复制文件。
源:因为是源,所以使用读取流。InputStream Reader,是不是操作文本文件呢?我们是操作文本文件的,那么我们就可以选择Reader,这样体系就明确了。接下来我们要明确要使用该体系中的哪个对象?明确设备是硬盘上的一个文本文件,Reader体系中可以操作文件的对象是FileReader。即FileReader fr =new FileReader(“a.txt”);是否提高效率,是的话我们加入Reader体系中缓冲区BufferedReader,即BufferedReader br=newBufferedReader(fr);
目的:OutputStream Writer,是不是纯文本?是的话我们就使用writer,设备是什么?也是硬盘上的一个文件,那么Writer体系中可以操作文件的对象FileWriter。即
FileWriterfw=newFileWriter(“a_copy.txt”);是否需要提高效率呢?是的话,我们加入Writer体系中缓冲区BufferedWriter,即BufferedWriter bw=new BufferedWriter(fw);
注意:
a) 当源设备是键盘录入的时候,它所对应的对象是System.in,而该对象对应的却是字节流,我们常常为了键盘的文本数据方便要转成字符流,所以我们就要用到Reader体系中的转换流,InputStreamReader,即InputStreamReader isr=newInputStreamReader(System.in),同时我们需要提高效率的话,就用BufferedReader fr=new BufferedReader(isr);
b) 同样的,当我们目的设备是控制台输出的话,就使用的是对象是System.out,它所对应的字节流也需要进行转换,这时我们也要用到转换流OutputStreamWriter,即OutputStreamWriter osw=newOutputStreamWriter(System.out);同时我们需要提高效率的话就使用BufferedWriter bw=new BufferedWriter(osw);
c) 还有一种特殊的情况就是当涉及到字符编码转换的时,我们就要使用到转换流,即字符和字节之间的桥梁。因为只有转换流才有指定的编码格式,。即OutputStreamWriter osw=newOutputStreamWriter(new FileOutputStream(“d.txt”)),如果需要高效的话,就需要使用BufferedWriterbw=newBufferedWriter(osw);
我们演示下编码转换的代码:
import java.io.*;
class Test
{
publicstatic void main(String[] args) throws IOException
{
//我们要把键盘输入的对应字节流转换为字符流
BufferedReaderbr=new BufferedReader(new InputStreamReader(System.in));
//我们把涉及到的字符编码使用转换流来指定编码格式
BufferedWriterbw=new BufferedWriter(new OutputStreamWriter(newFileOutputStream("d1.txt"),"UTF-8"));
Strings=null;
while((s=br.readLine())!=null){
if("over".equals(s)){
break;
}
bw.write(s.toUpperCase());
bw.newLine();
bw.flush();
}
}
}
总结:对于流的源和目的我们基本了解如下
源设备:
a) 键盘:System.in
b) 硬盘:FileStream
c) 内存:ArrayStream(后面ArrayStream功能讲解)
目的:
a) 控制台:System.out
b) 硬盘:FileStream
c) 内存:ArrayStream(后面ArrayStream功能讲解)
字节流和字符流的扩展应用
第一个应用:我们之前学的字节流和字符流复制文本,现在我们使用System类的两个方法来达到复制文本的效果;
a) public static voidsetIn(InputStream in):重新分配“标准”输入流。
b) public static voidsetOut(PrintStream out):重新分配“标准”输出流。
我们根据这两种方法来演示下复制文本的效果:
import java.io.*;
class Test
{
public static void main(String[] args)throws IOException
{
System.setIn(newFileInputStream("ReadIn.java"));//指定源文件
System.setOut(newPrintStream("ReadIn_copy.txt"));//指定目的文件。即要复制
//我们要把键盘输入的对应字节流转换为字符流
BufferedReader br=new BufferedReader(newInputStreamReader(System.in));
//我们要把控制台输出的对应字节流转换为字符流
BufferedWriter bw=newBufferedWriter(new OutputStreamWriter(System.out));
String s=null;
while((s=br.readLine())!=null){
if("over".equals(s)){
break;
}
bw.write(s.toUpperCase());
bw.newLine();
bw.flush();
}
}
}
第二个应用是将异常信息日志保存到文本文件中,并且添加出现异常的时间:
这里我们就要使用到异常打印信息设计IO流的方法;
public void printStackTrace(PrintStream s):将此 throwable 及其追踪输出到指定的 PrintWriter。
而PrintStream有一个构造函数是
public PrintStream(String fileName) throws FileNotFoundException:创建具有指定文件名称且不带自动行刷新的新打印流
以及一个打印到给定文件的方法:
public void println(String x):打印 String,然后终止该行
现在我们演示下异常日志存储到文件中:
import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionDemo
{
public static void main(String[] args)
{
try
{
int[] i=new int[2];
System.out.println(i[3]);
}
catch (ArrayIndexOutOfBoundsException e)
{
Date d=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
String s=sdf.format(d);
try
{
PrintStream ps=new PrintStream("exception.log");
ps.println(s);//将时间打印到指定文本
System.setOut(ps);//把异常信息打印到指定的文本
}
catch (IOException ex)
{
throw new RuntimeException("创建日志失败");
}
e.printStackTrace(System.out);
}
}
}
第三个应用:我们将系统信息存储到指定的文件中。
这里我们就涉及到System的一个方法
public static Properties getProperties():确定当前的系统属性。
我们还需要用Properties来接受,而且该类还有一个方法是将系统信息存储到指定的文件中。
public void list(PrintStream out): 将属性列表输出到指定的输出流
代码演示如下:
import java.util.*;
import java.io.*;
class SystemInfo
{
public static void main(String[] args) throws IOException
{
Properties p=System.getProperties();
p.list(new PrintStream("syetem.log"));
}
}