IO流
概述
Java中采用IO流的方式来进行数据传输,IO流分为两种:
1)字节流的抽象基流:InputStream和OutputStream
2)字符流的抽象基流:Reader和Writer
P.S.
此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;如InputStream子类FileInputStream;Reader子类FileReader
记住:如果要操作文字数据,建议优先考虑字符流。
而且要将数据从内存写到硬盘上,要使用字符流中的输出流:Writer。
硬盘的数据基本体现是文件,希望找到一个可以操作文件的Writer:FileWriter
字符流
字符流输入:
用例:
public class Test1 {
public static void main(String[] args) throws IOException {
File file = new File("d://IO");
if(!file.exists()){
//不存在则创建路径
file.mkdirs();
}
FileWriter fw = new FileWriter(file+"/123.txt");
fw.write("你好,字符流输入!");
//刷新缓存到指定文件
fw.flush();
fw.close();
}
}
若更改为:FileWriter fw = new FileWriter(file+”/123.txt”,true);
表示: 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处 。
加ture常用来进行文件续写。
字符流输出
1.单个字符读取
public class Test2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("d://IO/123.txt");
int c = 0;
while((c= fr.read())!=-1){
//这里read的返回值是char值的int型,可以转型为char类型
System.out.print((char)c);
}
}
}
2.字符数组读取
public class Test3 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("d://IO/123.txt");
char []c = new char[1024];
//返回值是读取的长度
int len = fr.read(c);
System.out.println(new String(c, 0, len));
}
}
练习:
需求:
将d盘一个文本文件复制到f盘、
复制的原理:
其实就是将c盘下的文件数据存储到e盘的一个文件中。
步骤:
1、在e盘创建一个文件。用于存储c盘文件中的数据。
2、定义读取流和c盘文件关联。
3、通过不断的读写完成数据存储。
4、关闭资源。
/**
单字符传输
*/
public class Test4 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\IO\\123.txt");
FileWriter fw = new FileWriter("f:\\copy123.txt");
//单个字符传送
int c ;
while((c=fr.read())!=-1){
/**public void write(int c)
* c - 指定要写入字符的 int。
*/
fw.write(c);
}
fr.close();
fw.close();
}
}
public class Test5 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\IO\\123.txt");
FileWriter fw = new FileWriter("f:\\copy123.txt");
// 字符数组传送
char[] c = new char[1024];
String buf = null;
int len = 0;
while ((len = fr.read(c)) != -1) {
buf = new String(c, 0, len);
fw.write(buf);
}
fw.close();
fr.close();
}
}
字符流的缓冲区——BufferedReader和BufferedWriter
使用注意:
1.使用缓冲区技术是为了解决性能问题,提高效率
2.需要先建立流对象,再将流对象交给缓冲区构造函数去处理
3. 记住,只要用到缓冲区,就要记得刷新。(关闭流同样会刷新,但为了排除意外事故,保证数据存在,建议写入一次就刷新一次)
如:bufw.flush();
4.小知识:BufferedWriter缓冲区中提供了一个跨平台的换行符:newLine();可以在不同操作系统上调用,用作数据换行。
如:bufw.newLine();
5.读取流缓冲区BufferedReader
BufferedReader.readLine():另外开辟了一个缓冲区,存储的是原缓冲区一行的数据,不包含换行符。所以实际使用中常使用BufferedReader.newLine();方法换行
练习:使用字符缓冲区复制文本
public class Test6 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("D:\\IO\\Hello.java"));
BufferedWriter bw = new BufferedWriter(new FileWriter("f:\\CopyHello.java"));
String buf = null;
while((buf = br.readLine())!=null){
bw.write(buf);
//添加换行,否则输出文本没有格式
bw.newLine();
// //使用缓冲区的刷新方法将数据刷目的地中
}
bw.close();
br.close();
}
}
练习:模仿一个BufferedReader的readLine方法
/*
需求:根据readLine方法原理,模拟BufferedReader写一个自己的MyBufferedReader
*/
import java.io.*;
//自定义缓冲类
class MyBufferedReader extends Reader
{
private Reader r;//定义接收的流对象
MyBufferedReader(Reader r)
{
this.r=r;
}
//自定义整行读取
public String myReadLine()throws IOException
{
//创建一个容器,用来存储一行的字符
StringBuilder sb =new StringBuilder();
//一个字符一个字符读取
for (int ch=0;(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 int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len);
}
//复写父类的close方法
public void close()throws IOException
{
r.close();
}
}
//测试MyBufferedReader
public class Test7
{
public static void main(String[] args)
{
MyBufferedReader mbr=null;
try
{
mbr=new MyBufferedReader(new FileReader("D:\\IO\\Hello.java"));
for (String line=null;(line=mbr.myReadLine())!=null ; )
{
System.out.println(line);//显示效果
}
}
catch (IOException e)
{
throw new RuntimeException("读取数据失败");
}
finally
{
try
{
if(mbr!=null)
mbr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取流关闭失败");
}
}
}
}
LineNumberReader
此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
换行符('\n')、回车符('\r')
/*
需求:利用LineNumberReader的特有方法去设置和获取文件中数据的行号
*/
public class Test8 {
public static void main(String[] args) {
LineNumberReader lnr = null;
try {
// 将读取流对象传入
lnr = new LineNumberReader(new FileReader("D:\\IO\\Hello.java"));
lnr.setLineNumber(10);// 设置开始行号
for (String line = null; (line = lnr.readLine()) != null;) {
System.out.println(lnr.getLineNumber() + ":" + line);// 打印每行行号和字符
}
} catch (IOException e) {
throw new RuntimeException("读取数据失败");
} finally {
try {
if (lnr != null)
lnr.close();
} catch (IOException e) {
throw new RuntimeException("读取流关闭失败");
}
}
}
}
装饰设计模式
通过多态进行一个功能的增强,不要仅仅使用继承,而应多使用多态。
字节流
1.基本操作与字符流类相同。但它不仅可以操作字符,还可以操作其他媒体文件。
2.由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。
3.读写字节流:InputStream 输入流(读)
OutputStream 输出流(写)
练习:复制图片等媒体资源
public class Test1 {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream("d:\\IO\\ME.JPG");
OutputStream out = new FileOutputStream("f:\\CopyME.JPG");
/**
* 另种写法:不推荐
* byte []b= new byte[in.available()];
* 若in.available()值过大,会导致内存溢出
* p.s.
* in.available():返回文件中的字节个数
*/
// 推荐写法,自定义字符缓冲区
byte[] b = new byte[1024];
int len = 0;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
out.close();
in.close();
}
}
字节流缓冲区
练习:自定义字节流读取缓冲区
/*
自定义字节流读取缓冲区
思路:
1、定义一个固定长度的数组
2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为0
3、每次将字节数据存入元素要先将数组中的元素取完
*/
/*
自定义字节流读取缓冲区
思路:
1、定义一个固定长度的数组
2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为0
3、每次将字节数据存入元素要先将数组中的元素取完
*/
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] by=new byte[1024];
private int count=0,pos=0;
MyBufferedInputStream(InputStream in)
{
this.in=in;
}
//自定义读方法,一次读一个字节
public int myRead()throws IOException
{
//通过in对象读取硬盘上数据,并存储by中。
//存储在数组中的数据被读取完,再通过in对象从硬盘上读取数据
if(count==0)
{
count=in.read(by);
if(count<0)//文件数据全部被读取出来了
return -1;
pos=0;//初始化指针
byte b=by[pos];
count--;//每被读一个字节,表示数组中的字节数少一个
pos++;//指针加1
return b&255;//返回的byte类型提升为int类型,字节数增加,且高24位被补1,原字节数据改变。
//通过与上255,主动将byte类型提升为int类型,将高24位补0,原字节数据不变。
//而在输出字节流写入数据时,只写该int类型数据的最低8位。
}
else if(count>0)//如果数组中的数据没被读取完,则继续读取
{
byte b=by[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
//自定义关闭资源方法
public void close()throws IOException
{
in.close();
}
}
//测试自定义输入字节流缓冲区
public class Test2
{
public static void main(String[] args)
{
long start=System.currentTimeMillis();
//利用字节流的缓冲区进行复制
copy_2();
long end=System.currentTimeMillis();
System.out.println("复制共用时:"+(end-start)+"毫秒");
}
//使用字节流的缓冲区进行复制
public static void copy_2()
{
BufferedOutputStream bout=null;
MyBufferedInputStream bin=null;
try
{
//关联复制文件输入流对象到缓冲区
bin=new MyBufferedInputStream(new FileInputStream("d:\\IO\\ME.JPG"));
//指定文件粘贴位置的输出流对象到缓冲区
bout=new BufferedOutputStream(new FileOutputStream("f:\\CopyME.JPG"));
int by=0;
while((by=bin.myRead())!=-1)
{
bout.write(by);//将缓冲区中的数据写入指定文件中
}
}
catch(IOException e)
{
throw new RuntimeException("MP3复制失败");
}
finally
{
try
{
if(bin!=null)
bin.close();//关闭输入字节流
}
catch(IOException e)
{
throw new RuntimeException("读取字节流关闭失败");
}
try
{
if(bout!=null)
bout.close();//关闭输出字节流
}
catch(IOException e)
{
throw new RuntimeException("写入字节流关闭失败");
}
}
}
}
流操作
键盘录入
一、键盘录入
1.标准输入输出流
System.in:对应的标准输入设备,键盘。
Ssytem.out:对应的是标准的输出设备,控制台。
System.in的类型是InputStream.
System.out的类型是PrintStream是OutputStream的子类FilterOutputStream的子类。
2.改进
由于键盘录入是字节流,效率较低。可不可以使用整行读取,那么需要借助readLine方法,但是这个是字符流的方法。所以,需要将字节流转换成字符流。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
练习:
/**
* 需求:将键盘录入的数据转换成大写输出,显示在控制台,当输入over时,表示结束
源:键盘录入。
目的:控制台。
*/
public class Test1 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String s = null;
while((s = br.readLine())!=null){
if(s.equals("over"))
break;
bw.write(s.toUpperCase());
bw.flush();
}
}
}
练习:
/**
* 需求:想把键盘录入的数据存储到一个文件中。
* 源:键盘
* 目的:文件
* 把录入的数据按照指定的编码表(UTF-8),将数据存到文件中。
* 需求:想要将一个文件的数据打印在控制台上。
* 源:文件
* 目的:控制台
*/
public class Test2 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new FileWriter("f:\\MyTest.txt"));
String s = null;
while((s = br.readLine())!=null){
if(s.equals("over"))
break;
bw.write(s);
bw.flush();
bw.newLine();
}
bw.close();
br.close();
}
}
流的操作规律
1、
源:键盘录入。
目的:控制台。
2、需求:想把键盘录入的数据存储到一个文件中。
源:键盘
目的:文件。
使用字节流通向字符流的转换流(桥梁):InputStreamReader
3、需求:想要将一个文件的数据打印在控制台上。
源:文件
目的:控制台
使用字符流通向字节流的转换流(桥梁):OutputStreamWriter
4、流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成:
4.1 明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer
4.2 操作的数据是否是纯文本。
是:字符流
否:字节流
4.3 当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
5、规律体现
5.1 将一个文本文件中数据存储到另一个文件中。复制文件。
1)源:因为是源,所以使用读取流:InputStream和Reader
明确体系:是否操作文本:是,Reader
明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader
是否需要提高效率:是,加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
2)目的:输出流:OutputStream和Writer
明确体系:是否操作文本:是,Writer
明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是,加入Writer体系中缓冲区 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
1)源:输入流,InputStream和Reader
是否是文本?否,InputStream
源设备:硬盘上的一个文件。InputSteam体系中可以操作文件的对象是FileInputSteam
是否需要提供效率:是,BufferedInputStream
BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));
2)目的:输出流,OutputStream和Writer
是否是文本?否,OutputStream
源设备:硬盘上的文件,FileOutputStream
是否需要提高效率:是,加入BufferedOutputStream
BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));
5.2 需求:将键盘录入的数据保存到一个文件中。
1)源:InputStream和Reader
是不是纯文本?是,Reader
设备:键盘。对应的对象是System.in。——为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成Reader。用Reader体系中转换流,InputStreamReader
InputStreamReaderisr = new InputStreamReader(System.in);
需要提高效率吗?需要,BufferedReader
BufferedReaderbufr = new BufferedReader(isr);
2)目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = newFileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
5.3 扩展:想要把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘上的一个文件。使用 FileWriter。——但是FileWriter是使用的默认编码表:GBK。而存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。
该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream
OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要,BufferedWriter
BufferedWriter bufw = new BufferedWriter(osw);
记住:
转换流什么使用?
字符和字节之间的桥梁。通常,涉及到字符编码转换时,需要用到转换流。
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
1)源:InputStreamReader
是文本?是:Reader
设备:硬盘。上的文件:FileReader
是否需要提高效率?是:BufferedReader
BufferedReader br=new BufferedReader(newFileReader("1.txt"));
2)目的:OutputStream Writer
是文本?是:Writer
设备:控制台。对应对象System.out。由于System.out对应的是字节流,所以利用OutputSteamWriter转换流
是否提高效率?是:BufferedWriter
BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));
练习:带编码的流操作
/**
* 带编码集的操作
* @author LQX
*
*/
public class Test3 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
//指定UTF-8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("f://MyTest.java"), "UTF-8");
String s = null;
while((s = br.readLine())!=null){
if(s.equals("over"))
break;
osw.write(s+"\r\n");
osw.flush();
}
}
}
public class Test4 {
public static void main(String[] args) throws IOException {
/*BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("f:\\MyTest.java")));
String s = null;
while((s = br.readLine())!=null){
System.out.println(s);
}*/
//该文件在创建时我指定了编码为UTF-8,所以多出来是乱码
FileReader fr = new FileReader("f:\\MyTest.java");
char []c = new char[1024];
/* int len = 0;
while((len = fr.read(c))!=-1){
System.out.println(new String(c, 0, len));
}*/
//修改后,设定了编码为UTF-8,能正确读出来
InputStreamReader isr = new InputStreamReader(new FileInputStream("f:\\MyTest.java"), "UTF-8");
int len = 0;
while((len = isr.read(c))!=-1){
System.out.println(new String(c, 0, len));
}
}
}
什么时候使用流转换?
1.目标设备是字节流,但操作是字符流,使用转换流作为桥梁,提高效率。
2.涉及文本字符编码表时,必须使用转换流,因为只有它才提供自定义编码。