Java基础——IO流(一)

一、概述

  • IO流用来处理设备之间的数据传输。(设备:如内存到硬盘、U盘、光盘等)
  • Java对数据的操作是通过流的方式。
  • Java用于操作流的对象都在IO包中。
  • 流按操作数分两种:字节流和字符流。
  • 流按流向分为:输入流和输出流。

IO流常用基类:

  • 字节流的抽象顶层基类(顶层父类):InputStream,OutputStream
  • 字符流的顶层基类:Reader, Writer
  •  

二、输入流&输出流

输入与输出是相对内存而言的。

将外设中的数据读取到内存中,就是输入

将内存的数据写入到外设中,就是输出

三、字节流&字符流

字节流可以处理所有数据。为什么呢?

Unicode码表是一个国际组织将各种各样码表统一起来的码表,包括中文和英文。

字符流怎么来的?其实就是字节流读取文字字节数据后不直接操作,而是先查指定的编码表 ,获取对应的文字,然后再对文字进行操作。
简单说:字节流+编码表

四、字符流FileWriter

需求:将一些文字存储到硬盘一个文件中。

注意:如果要操作文字数据,建议优先考虑字符流。而且要将数据从内存写到硬盘上,要使用字符流中的输出流。

FileWriter:位于IO->Writer->OutputStreamWriter里面

硬盘的数据基本实现是文件。希望找到一个可以操作文件的Writer。

使用FileWriter的例子(使用构造函数FileWriter(String FilePath)):

import java.io.FileWriter;
import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        try {
            /*调用构造函数FileWriter(String File)写入数据*/
            FileWriter fw = new FileWriter("demo.txt");
            fw.write("abcde---");//数据被临时存储到流里面了
//            fw.flush();//进行刷新,将数据直接写入到目的地中
            fw.close();//关闭此流,关闭之前自动刷新缓冲区到目的地
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            /*调用构造函数FileWriter(String fileName, boolean append)写入数据*/
            FileWriter fw2 = new FileWriter("demo.txt", true);
            fw2.write("abcde"+SEPARATOR+"---");//数据被临时存储到流里面了
            fw2.write(SEPARATOR+"JQK");
//            fw.flush();//进行刷新,将数据直接写入到目的地中
            fw2.close();//关闭此流,关闭之前自动刷新缓冲区到目的地
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

异常处理:IOException

import java.io.FileWriter;
import java.io.IOException;

public class Main {
    private static final String SEPARATOR = System.getProperty("line.separator");
    public static void main(String[] args) {
        FileWriter fw = null;//初始化为空
        try {
            fw = new FileWriter("demo.txt");
            fw.write("abcde"+SEPARATOR+"---");//数据被临时存储到流里面了

        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {//无论成功与否,关闭流
            try {//当流不为空,关闭。try..catch的原因和上面一样
                if(fw!=null) fw.close();//关闭此流,关闭之前自动刷新缓冲区到目的地
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

五、字符流FileReader

需求:读取一个文本文件。将读取到的字符打印到控制台。

方式一:read()方法,一个一个读取

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ReadDemo {
    public static void main(String[] args) {

        FileReader fr=null;
        try {
            fr = new FileReader("demo.txt");
            int p;
            while((p=fr.read()) != -1){//当读到的是-1表示读取结束。
                System.out.print((char)p);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

方式二:使用read(char[] )读取文本文件数据

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ReadDemo {
    public static void main(String[] args) {
        long time1 = System.currentTimeMillis();
        FileReader fr=null;
        try {
            fr = new FileReader("demo.txt");
            char[] ch = new char[3];//可以尝试不同数组长度的运行速度,建议值1024整数倍
            int len = 0;
            while((len=fr.read(ch))!=-1){
                System.out.print(new String(ch, 0, len));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fr.close();
                System.out.println();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long time2 = System.currentTimeMillis();
        System.out.println("总耗时:"+(time2-time1)+"ms");
    }
}

练习:将C盘一个文本文件复制到d盘。
分析:


复制原理:读取C盘文件中的数据,将这些数据写入到D盘中。(连读带写)

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyTextTest_2 {
	private static final int BUFFER_SIZE = 1024;
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		FileReader fr = null;
		FileWriter fw = null;
		try {
			fr = new FileReader("IO流_2.txt");
			fw = new FileWriter("copytest_2.txt");
			
			//创建一个临时容器,用于缓存读取到的字符。
			char[] buf = new char[BUFFER_SIZE];//这就是缓冲区。 
			
			//定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数 )
			int len = 0;
			
			while((len=fr.read(buf))!=-1){
				fw.write(buf, 0, len);
			}		
		} catch (Exception e) {
//			System.out.println("读写失败");
			throw new RuntimeException("读写失败");
		}finally{
			if(fw!=null)
				try {
					fw.close();
				} catch (IOException e) {				
					e.printStackTrace();
				}
			if(fr!=null)
				try {
					fr.close();
				} catch (IOException e) {				
					e.printStackTrace();
				}
		}
	}
}

六、字符流缓冲区

缓冲区的出现提供了对数据的读写效率。

对应的类有BufferedWriterBufferedReader。同时注意,缓冲区要结合流才可以使用在流的基础上对流的功能惊醒了增强。

优化性能比较有效的方法之一就是建立缓冲区。

1.BufferedWriter

构造函数
BufferedWriter(Writer w);
BufferedWriter(Writer w, int sz);
import java.io.*;

public class Main {
    private static final String SEPARATOR = System.getProperty("line.separator");
    public static void main(String[] args) {
        FileWriter fw = null;
        try {
            /*调用Writer写入数据*/
            fw = new FileWriter("demo.txt");
            //建立缓冲区
            BufferedWriter buffw = new BufferedWriter(fw);
            //使用缓冲区的写入方法
            buffw.write("abcde"+SEPARATOR+"---接口");
            //刷新并关闭缓冲区,注意:此时本质是关闭了被缓冲的流对象
            buffw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if(fw!=null) fw.close();//关闭此流,关闭之前自动刷新缓冲区到目的地
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2.BufferedReader

注意其中的特有方法readline()

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class ReadDemo {
    public static void main(String[] args) {
        long time1 = System.currentTimeMillis();
        FileReader fr=null;
        try {
            fr = new FileReader("demo.txt");
            BufferedReader bufr = new BufferedReader(fr);
            String line=null;
            while((line=bufr.readLine())!=null){//注意这里的readline方法的使用,读取一整行
                System.out.println(line);
            }
            bufr.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fr.close();
                System.out.println();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long time2 = System.currentTimeMillis();
        System.out.println("总耗时:"+(time2-time1)+"ms");
    }
}

重写读写练习:将C盘一个文本文件复制到d盘。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyTextByBufTest {
	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {

		FileReader fr = new FileReader("buf.txt");		
		BufferedReader bufr = new BufferedReader(fr);
		
		FileWriter fw = new FileWriter("buf_copy.txt");
		BufferedWriter bufw = new BufferedWriter(fw);

		String line = null;
		while((line=bufr.readLine())!=null){
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}
		bufw.close();
		bufr.close();
	}
}

3.自定义MyBufferedReader的read、readLine方法

可以试着自己来做一遍,与BufferedReader功能相同的类,以便对缓冲区有深刻的理解。

要求:

  • 从硬盘(源)读取文件所有信息(当文件小于Buffer大小时)到内存的缓冲区中;
  • read方法实现对内存中数组进行访问(一个一个的访问)的方法;
  • 此次取完后,再从源中继续取一批数据,取完用-1作为标记;
  • readLine方法实现对回车换行的识别;
import java.io.FileReader;
import java.io.IOException;

public class MyBufferedReader {
    private FileReader r;

    private char[] buf = new char[1024];
    //定义操作这个数组中的元素
    private int pointer = 0;
    //记录缓冲区中数据个数
    private int counter = 0;

    MyBufferedReader(FileReader r){
        this.r=r;
    }

    /**
     *
     * @return
     * @throws IOException
     */
    public int myRead() throws IOException {
        //从源中获取一批数据到缓冲区
        if (counter==0) {//等于0才需要从源中获取
            counter = r.read(buf);
            pointer = 0;//因为从pointer已经到头了,需要从头来做
        }
        if(counter<0) return -1;
        //取出要返回的字符(单个)
        char ch = buf[pointer++];
        counter--;

        return ch;
    }

    public String myReadLine() throws IOException {
        StringBuilder sb = new StringBuilder();
        int ch = 0;
        while((ch = myRead()) != -1){
            //读到\n停止并返回
            if(ch != '\n'){
                if(ch=='\r') continue;//读到\r既不存也不停止
                //将读到的字符存储到缓存行数据的缓冲区中
                sb.append((char) ch);
            }
            else break;
        }

        if (sb.length()!=0) {
            return sb.toString();
        }
        return null;//注意别少了return null否则停不下来
    }

    public void myClose() throws IOException {
        if(r!=null) {
            r.close();
        }
    }
}

七、装饰设计模式

我们注意到,在上面讲到的BufferedReader中的read()方法对原本的FileReader中的read()方法进行了功能上的增强(将数据一次性大量加载到缓冲区,提升了运行效率)。这样的基本功能不变,运行效率增强的做法是一种新的设计模式——装饰设计模式。

1.定义:

对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。

2.装饰和继承

装饰和继承都能实现一样的功能:进行功能的拓展增强。

那么他们之间有什么区别呢?
继承--->如果这个体系进行功能拓展,有了好多流对象。那么这个流要提高效率,是不是也要产生子类呢?是。这时就会发现职位提高功能,而进行的继承,导致继承体系越来越臃肿。不够灵活。(不能做到不想用某个功能就不用了)

那我们重新思考这个问题。装饰的思考方式是怎样的呢?
既然加入的都是同一种技术——缓冲。前一种是让缓冲和具体的对象相结合。可不可以将缓冲进行单独的封装,哪个对象需要缓冲,就将哪个对象和缓冲关联。

3.特点

装饰类和被装饰类都必须所属同一个接口或父类。

八、补充

LineNumberReader继承自 java.io.BufferedReader ,与其父类功能相似。

特点:一个缓冲字符输入流跟踪行号。这个类定义方法 setLineNumber(int)和 getLineNumber()分别设置和获取当前行号。

方法:

int getLineNumber()    获取当前行号。

void setLineNumber(int lineNumber)    设置当前行号。

 

我最近也是在不断的学习中,水平有限,如果有什么意见或建议请下方评论哦,我将十分感谢。

如果觉得有收获可以关注一波~~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值