IO流
概述:IO流就是用来进行数据的输入输出操作的一个缓冲区。
按操作的对象不同可分为:
1、对字符操作
Reader和Writer
2、对字节操作
InputStream和OutputStream
字符流:
FileWriter:是Writer的一个子类,用于进行文件的数据写入,以字符为操作单位。
首先要创建一个FileWriter对象,构造方法的参数要指定一个文件名,用来明确数据写入的位置。如:FileWriter fw = new FileWriter("demo.txt");
常用方法:
write() //将数据存储到字符流中,可以以字符串,字符,字符数组等为参数。
flush() //刷新字符流,将数据写入到目的地。
close() //刷新字符流,同时关闭字符流,调用后Writer对象不能使用。
示例:
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。
//没有此文件 将创建文件,如有同名文件,将被覆盖。
FileWriter fw = new FileWriter("demo.txt");
//write方法将字符串写入到流中。
fw.write("hello");
//刷新缓冲区中数据,写入目的地中。
//fw.flush();
//关闭流,并刷新一次缓冲中的数据。
fw.close();
}
}
字符流读取方法
以FileReader为例:
常用方法:
read() //读取一个字符,返回字符对应的ACSII码
close() //关闭读取流
读取文件示例:
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
//第一种读取方式:单个字符读取
FileReader fr = new FileReader("demo.txt");
while(true)
{
int c = fr.read();
if(c == -1)
break;
System.out.print((char)c);
}
fr.close();
//第二种方式:通过字符数组进行读取。
//read(char[])返回的是读到的字符个数
FileReader fr2 = new FileReader("demo.txt");
char[] buf = new char[1024];
int num = 0;
while((num = fr2.read(buf)) != -1)
System.out.print(new String(buf, 0, num));
fr2.close();
}
}
带缓冲区的字符读取和写入流:
BufferedReader和BufferedWriter:
这两个类封装了定义缓冲区的操作,在读取和写入时定义了字符数组作为缓冲区,避免了单个字符操作的低效率。建立对象时,需要传入对应的Reader和Writer对象作为参数。
BufferedWriter:
建立对象时格式应如:
FileWriter fw = new FileWriter("test.txt");
BufferedWriter bufw = new BufferedWriter(fw);
特有方法:
newLine() //添加一个换行符,自动通过系统获取,可以跨平台。
BufferedReader:
建立对象和上面方法类似。
特有方法:
readLine() //返回一个字符串,内容是读取一行字符的内容,到文本末尾返回null。
示例:
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("test.java");
BufferedReader bufr = new BufferedReader(fr);
String s = null;
while((s = bufr.readLine()) != null)
{
System.out.println(s);
}
}
}
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
FileWriter fw = new FileWriter("buf.txt");
BufferedWriter bufw = new BufferedWriter(fw);
bufw.write("abcde");
bufw.flush();
bufw.close();//关闭缓冲区也直接关闭了FileWriter对象;
}
}
装饰模式:
这种对原有Reader和Writer功能进行增强的设计模式成为装饰设计模式。
方法是在遇到一个定义好的类,并需要对其进行改进时,定义一个新类,将其作为新类的成员,并定义新方法,内部使用原有需要改进的方法,并加入新的代码。
这种设计模式的好处在于:
装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。
自定义BufferedReader:
下面用这种设计模式 自定义一个BufferedReader,主要以自定义的readLine()方法为例:
import java.io.*;
//自定义BufferedReader类
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Reader r)
{
this.r = r;
}
//一次读一行数据的方法
public String myReadLine()throws IOException
{
//定义一个临时容器,原BufferedReader使用的是字符数组,
//这里为了方便使用StringBuilder,
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = r.read()) != -1)
{
//判断是否为行终止符
if(ch == '\r')
continue;
if(ch == '\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length() != 0)
{
return sb.toString();
}
return null;
}
public void close()throws IOException
{
r.close();
}
public int read(char[] cbuf, int off, int len)throws IOException
{
return r.read(cbuf, off, len);
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("test.java");
MyBufferedReader mbufr = new MyBufferedReader(fr);
FileWriter fw = new FileWriter("buf.txt");
BufferedWriter bufw = new BufferedWriter(fw);
String buf = null;
while((buf = mbufr.myReadLine()) != null)
{
fw.write(buf);
bufw.newLine();
bufw.flush();
}
mbufr.close();
fw.close();
}
}
字节流:
字节流是直接针对字节进行操作的,所以字节流可以直接对非文本文件进行读写。
读写方法与字符流类似,只是自定义的缓冲区缓存byte数组就可以了。
以拷贝图片为例:
import java.io.*;
class CopyPic
{
public static void picCopy()
{
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
fos = new FileOutputStream("buf.jpg");
fis = new FileInputStream("logo.jpg");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf)) != -1)
{
fos.write(buf, 0, len);
}
}
catch(IOException e)
{
System.out.println("pic copy failed");
}
finally
{
try
{
if(fos != null)
fos.close();
}
catch(IOException e)
{
System.out.println("output close failed");
}
try
{
if(fos != null)
fos.close();
}
catch(IOException e)
{
System.out.println("input close failed");
}
}
}
public static void main(String[] args)
{
picCopy();
}
}
字节流也有对应的带有缓冲区的加强类:BufferedInputStream和BufferedOutputStream
调用read()方法时应注意,它会把读到的byte类型数据提升为int类型并返回,数据前自动补齐-1,这样在遇到连续8位是1的数据时,再补齐-1就会被当做结束标记,
自定义read()方法时,应注意在提升byte为int时,要在前面补0,也就是将数据和255做与运算。
自定义BufferedInputStream如下:
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024];//缓冲区
private int pos = 0, count = 0;//定义指针,计数器
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//一次读一个字节,从缓冲区——字节数组中读
public int myRead()throws IOException
{
if(count == 0)//数组是否取空
{
count = in.read(buf);//通过in对象读取硬盘上的数据 并存储到缓冲区buf中,
if(count < 0)
{
return -1;
}
pos = 0;
byte b = buf[pos];//第一次从缓冲区中取一个字节
count --;
pos ++;
return b & 0xff;//补0
}else if(count > 0)
{
byte b = buf[pos];//一次从缓冲区中取一个字节
count --;
pos ++;
return b & 255;//补0
}
return -1;
}
public void close()throws IOException
{
in.close();
}
}
/* byte型提升为int型后,8位数据提升为32位,前面系统自动补1,如果独到8个1,补位后
* 还是-1,就会中断读数据循环,所以要手动补0。就和255与运算。
*
*/
字符流与字节流转换流:
最简单的应用例子就是,在获取键盘录入时,希望提高效率,避免输入一个读取一个,改为输入一行,然后整行读取到缓冲区,而readLine()方法又是字符流的方法,
这时就可以使用转换流,将InputStream对象传入InputStreamReader构造函数中,再通过BufferedReader对象来使用readLine()方法。
代码如下:
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
//获取键盘录入对象
// InputStream in = System.in;
// //将字节流转换成字符流,使用转换流
// InputStreamReader isr = new InputStreamReader(in);
// BufferedReader br = new BufferedReader(isr);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// OutputStream out = System.out;
// OutputStreamWriter osw = new OutputStreamWriter(out);
// BufferedWriter bw = new BufferedWriter(osw);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String buf = null;
while((buf = br.readLine()) != null)
{
if("over".equals(buf))
break;
bw.write(buf.toUpperCase());
bw.newLine();
bw.flush();
}
br.close();
br.close();
bw.close();
}
}
流操作规律:
下面总结一下流操作规律,以便明确数据操作时用哪个流对象。
通过明确以下几点就可以确定:
1、 源:输入流:InputStream Reader
目的:输出流:OutputStream Writer
2、操作的是否为纯文本:
是:字符流对象
否:字节流对象
3、通过设备来确定具体对象:
硬盘上文件:Filexxxx
标准输入输出设备(键盘,控制台):System.xx 这里可以通过System.setxx()来设置默认设备。
再通过是否需要提高效率决定是否用Bufferedxxx。
示例:
需求:想要将一个文件的数据打印在控制台上。
源:文件
目的:控制台
代码如下:
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
//获取键盘录入对象
// InputStream in = System.in;
// //将字节流转换成字符流,使用转换流
// InputStreamReader isr = new InputStreamReader(in);
// BufferedReader br = new BufferedReader(isr);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// OutputStream out = System.out;
// OutputStreamWriter osw = new OutputStreamWriter(out);
// BufferedWriter bw = new BufferedWriter(osw);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String buf = null;
while((buf = br.readLine()) != null)
{
if("over".equals(buf))
break;
bw.write(buf.toUpperCase());
bw.newLine();
bw.flush();
}
br.close();
br.close();
bw.close();
}
}