Java基础--I/O 第一部分

本文深入解析Java中的I/O流概念,包括字节流、字符流及其子类的使用,如FileWriter、FileReader、BufferedWriter、BufferedReader等。同时,文章探讨了流操作的规律,帮助开发者在不同场景下选择合适的流对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.输入输出

  • 输入流和输出流相对于内存设备而言.

              将外设中的数据读取到内存中:输入
              将内存的数写入到外设中:输出。

  •  字符流的由来:

             其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表。获取对应的文字。在对这个文字进行操作。简单说:字节流+编码表

2.字节流和字符流的基类

  • 字节流的两个顶层父类:
  1. InputStream  
  2. OutputStream.
  • 字符流的两个顶层父类:
  1. Reader
  2. Writer

     这些体系的子类都以父类名作为后缀。 而且子类名的前缀就是该对象的功能。

3.字符流

3.1  FilterWriter的使用

  • 需求:将一些文字存储到硬盘一个文件中。
public class FileWriterDemo {

	private static final String LINE_SEPARATOR = System.getProperty("line.separator");
	
	public static void main(String[] args) throws IOException {

		//创建一个可以往文件中写入字符数据的字符输出流对象。
		/*
		 * 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)。
		 * 
		 * 如果文件不存在,则会自动创建。
		 * 如果文件存在,则会被覆盖。
		 * 
		 * 如果构造函数中加入true,可以实现对文件进行续写!
		 */
		FileWriter fw = new FileWriter("demo.txt",true);		
		/*
		 * 调用Writer对象中的write(string)方法,写入数据。 
		 * 
		 * 其实数据写入到临时存储缓冲区中。
		 * 
		 */
		fw.write("abcde"+LINE_SEPARATOR+"hahaha");
//		fw.write("xixi");
		
		/*
		 * 进行刷新,将数据直接写到目的地中。
		 */	
//		fw.flush();		
		/*
		 * 关闭流,关闭资源。在关闭前会先调用flush刷新缓冲中的数据到目的地。
		 */
		fw.close();
		
//		fw.write("haha");// java.io.IOException: Stream closed
	}
}
  • 字符输出的 I/O 异常
public class IOExceptionDemo {

	private static final String LINE_SEPARATOR = System.getProperty("line.separator");
	public static void main(String[] args) {

		FileWriter fw = null;
		try {

			fw = new FileWriter("k:\\demo.txt");

			fw.write("abcde" + LINE_SEPARATOR + "hahaha");

		} catch (IOException e) {
			System.out.println(e.toString());
		} finally {
			if (fw != null)
				try {
					fw.close();
				} catch (IOException e) {
					// code....
					throw new RuntimeException("¹Ø±Õʧ°Ü");
				}
		}
	}
}

3.2  FilterReader的使用

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

一次读取一个字节 

public class FileReaderDemo {

	public static void main(String[] args) throws IOException {
	
		//1,创建读取字符数据的流对象。
		/*
		 * 在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。 
		 * 
		 * 用一个读取流关联一个已存在文件。 
		 */
		FileReader fr = new FileReader("demo.txt");
		
		int ch = 0;
		
		while((ch=fr.read())!=-1){
			System.out.println((char)ch);
		}
		fr.close();
	}
}

一次读取一个字节数组

public class FileReaderDemo2 {

	public static void main(String[] args) throws IOException {

		FileReader fr = new FileReader("demo.txt");
		
		/*
		 * 使用read(char[])读取文本文件数据。
		 *  
		 * 先创建字符数组。
		 */
		char[] buf = new char[1024];
		
		int len = 0;
		
		while((len=fr.read(buf))!=-1){
			System.out.println(new String(buf,0,len));
		}
		fr.close();
	}
}
  • 文本复制案例:

/**
 * 文本文件复制:
 * 1.一次读取一个字节
 * 2.一次读取一个数组
 */
public class CopyTextTest1 {
	
	private static final int BUF_SIZE = 1024;

	//文本文件复制:1一次读取一个字节
	@Test
	public void demo1() throws IOException {
		//输入源
		FileReader fr=new FileReader("IO_字符文件.txt");
		//输出目的
		FileWriter fw=new FileWriter("copy_text1.txt");
		int ch=0;
		while((ch=fr.read())!=-1) {
			fw.write(ch);
		}
		//关流
		fr.close();
		fw.close();
	}
	
	
	// 2.一次读取一个数组(带异常处理的)
	@Test
	public void demo2() {
		
		//输入源
		FileReader fr=null;
		//输出目的
		FileWriter fw=null;
		try {
			fr=new FileReader("IO_字符文件.txt");
			fw=new FileWriter("copy_text2.txt");
			char[]cbuf=new char[BUF_SIZE];
			int len=0;
			while((len=fr.read(cbuf))!=-1) {
				fw.write(cbuf, 0, len);
			}
		} catch (Exception e) {
			throw new RuntimeException("读写错误");
		}finally {
			if (fr!=null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (fw!=null) {
				try {
					fw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

3.3  字符流缓冲区

BufferedWriter 
          :newLine();
BufferedReader
            : readLine();

 

 

 

 

 

3.3.1 BufferedWriter :实例

public class BufferedWriterDemo {

	private static final String LINE_SEPARATOR = System.getProperty("line.separator");

	public static void main(String[] args) throws IOException {

		FileWriter fw = new FileWriter("buf.txt");
		
		//为了提高写入的效率。使用了字符流的缓冲区。
		//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
		BufferedWriter bufw = new BufferedWriter(fw);
		
		//使用缓冲区的写入方法将数据先写入到缓冲区中。
//		bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
//		bufw.write("xixiixii");
//		bufw.newLine();
//		bufw.write("heheheheh");
		
		for(int x=1; x<=4; x++){
			bufw.write("abcdef"+x);
			bufw.newLine();
			bufw.flush();
		}
		//使用缓冲区的刷新方法将数据刷目的地中。
//		bufw.flush();
			
		//关闭缓冲区。其实关闭的就是被缓冲的流对象。
		bufw.close();
	}
}

3.3.2  BufferedReader :实例

	public static void main(String[] args) throws IOException {

		FileReader fr = new FileReader("buf.txt");
		
		BufferedReader bufr = new BufferedReader(fr);
		
		String line = null;
		
		while((line=bufr.readLine())!=null){
			System.out.println(line);
		}
			
		bufr.close();	
	}
  • 使用缓冲区进行文本复制

/**
 * 文本复制:使用缓冲区
 */
public class CopyTestByBuffer {

	@Test
	public void demo1() throws IOException {
		//输入
		BufferedReader br=new BufferedReader(new FileReader("IO_字符文件.txt"));
		//输出
		BufferedWriter bw=new BufferedWriter(new FileWriter("copy_text3.txt"));
		
		String line=null;
		while((line=br.readLine())!=null) {
			bw.write(line);
			bw.newLine();//换行
			bw.flush();//刷新
		}
		//关流
		bw.close();
		br.close();
	}
}

自定义的读取缓冲区

其实就是模拟一个BufferedReader.

 * 分析:
 * 缓冲区中无非就是封装了一个数组,
 * 并对外提供了更多的方法对数组进行访问。
 * 其实这些方法最终操作的都是数组的角标。
 * 
 * 缓冲的原理:
 * 其实就是从源中获取一批数据装进缓冲区中。
 * 在从缓冲区中不断的取出一个一个数据。
 * 
 * 在此次取完后,在从源中继续取一批数据进缓冲区。
 * 当源中的数据取光时,用-1作为结束标记。 

public class MyBufferedReader extends Reader {

	private Reader r;
	
	//定义一个数组作为缓冲区。
	private char[] buf = new char[1024];
	
	//定义一个指针用于操作这个数组中的元素。当操作到最后一个元素后,指针应该归零。	
	private int pos = 0;	
	
	
	//定义一个计数器用于记录缓冲区中的数据个数。 当该数据减到0,就从源中继续获取数据到缓冲区中。
	private int count = 0;
	
	
	MyBufferedReader(Reader r){
		this.r = r;
	}
	
	/**
	 * 该方法从缓冲区中一次取一个字符。 
	 * @return
	 * @throws IOException
	 */
	public int myRead() throws IOException{
		
		if(count==0){
			count = r.read(buf);
			pos = 0;
		}
		if(count<0)
			return -1;
		
		char ch = buf[pos++];
		
		count--;
		
		return ch;
				
	}
	
	public String myReadLine() throws IOException{
		
		StringBuilder sb = new StringBuilder();
		
		int ch = 0;
		while((ch = myRead())!=-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 0;
	}

	@Override
	public void close() throws IOException {
	}
}

4. 字节流

4.1 InputStream

  • 使用缓冲区   BufferedInputStream

4.2 OutputStream

  • 使用缓冲   BufferedOutputStream

4.3 文件复制案例

4.4 数据回显

需求:从键盘读取数据,转换成大写,打印到控制台,当遇到  over 时停止打印

4.4.1 仅仅使用缓冲区

4.4.2 加上转换流

  1.     InputStreamReader:字节到字符的转换桥梁,解码
  2.     OutputStreamWriter:字符到字节的桥梁,编码。

  • 转换流详解图:

5. 流的操作规律

之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。

想要知道开发时用到哪些对象。只要通过四个明确即可。

1,明确源和目的(汇)
    源:InputStream  Reader
    目的:OutputStream  Writer

2,明确数据是否是纯文本数据。
    源:是纯文本:Reader
        否:InputStream
    目的:是纯文本 Writer
        否:OutputStream
    
    到这里,就可以明确需求中具体要使用哪个体系。
    
3,明确具体的设备。
    源设备:
        硬盘:File
        键盘:System.in
        内存:数组
        网络:Socket流
        
    目的设备:
        硬盘:File
        控制台:System.out
        内存:数组
        网络:Socket流

4,是否需要其他额外功能。
    1,是否需要高效(缓冲区);
        是,就加上buffer.
    2,转换。

实例分析:

需求1:复制一个文本文件。
    1,明确源和目的。
        源:InputStream Reader
        目的:OutputStream  Writer
    2,是否是纯文本?
        是!
        源:Reader
        目的:Writer
        
    3,明确具体设备。
        源:
            硬盘:File
        目的:
            硬盘:File
    
        FileReader fr = new FileReader("a.txt");
        FileWriter fw = new FileWriter("b.txt");
        
    4,需要额外功能吗?
        需要,需要高效。
        BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
        BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
        
=============================================================

需求2:读取键盘录入信息,并写入到一个文件中。
        
    1,明确源和目的。
        源:InputStream Reader
        目的:OutputStream  Writer
    2,是否是纯文本呢?
        是,
        源:Reader
        目的:Writer
    3,明确设备
        源:
            键盘。System.in
        目的:
            硬盘。File
            
        InputStream in = System.in;
        FileWriter fw = new FileWriter("b.txt");
        这样做可以完成,但是麻烦。将读取的字节数据转成字符串。再由字符流操作。
    4,需要额外功能吗?
        需要。转换。    将字节流转成字符流。因为名确的源是Reader,这样操作文本数据做便捷。
            所以要将已有的字节流转成字符流。使用字节-->字符 。InputStreamReader
        InputStreamReader isr = new InputStreamReader(System.in);
        FileWriter fw = new FileWriter("b.txt");
        
        还需要功能吗?
        需要:想高效。
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
        
======================================================================
    
需求3:将一个文本文件数据显示在控制台上。
    1,明确源和目的。
        源:InputStream Reader
        目的:OutputStream  Writer
    2,是否是纯文本呢?
        是,
        源:Reader
        目的:Writer
    3,明确具体设备
        源:
            硬盘:File
        目的:
            控制台:System.out
            
        FileReader fr = new FileReader("a.txt");
        OutputStream out = System.out;//PrintStream
    4,需要额外功能吗?
        需要,转换。
        FileReader fr= new FileReader("a.txt");
        OutputStreamWriter osw = new OutputStreamWriter(System.out);
        需要,高效。 
        BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
        BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
        
======================================================================

需求4:读取键盘录入数据,显示在控制台上。
    1,明确源和目的。
        源:InputStream Reader
        目的:OutputStream  Writer
    2,是否是纯文本呢?
        是,
        源:Reader
        目的:Writer
    3,明确设备。
        源:
            键盘:System.in
        目的:
            控制台:System.out
        
        InputStream in = System.in;
        OutputStream out = System.out;
        
    4,明确额外功能?
        需要转换,因为都是字节流,但是操作的却是文本数据。
        所以使用字符流操作起来更为便捷。
        InputStreamReader isr = new InputStreamReader(System.in);
        OutputStreamWriter osw = new OutputStreamWriter(System.out);
        
        为了将其高效。
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
        

  •    使用指定的码表将文本写入到磁盘中(指定了编码表,一定需要用到转换流
	public static void writeText_2() throws IOException {
		
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_3.txt"),"GBK");
		
//		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_3.txt"),"GBK");
//		FileWriter fw = new FileWriter("gbk_1.txt");
		
		/*
		 * 这两句代码的功能是等同的。 
		 * FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象,可以方便操作文本文件。
		 *             简单说:操作文件的字节流+本机默认的编码表。
		 * 				这是按照默认码表来操作文件的便捷类。
		 * 
		 * 如果操作文本文件需要明确具体的编码。FileWriter就不行了。必须用转换流。 
		 * 
		 */
				
		osw.write("你好");
		
		osw.close();				
	}
  • 根据指定的编码从文件中读取文本(指定了编码表,一定需要用到转换流
	public static void readText_2() throws IOException, FileNotFoundException {
	
		InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk_1.txt"),"utf-8");
		char[] buf = new char[10];
		int len = isr.read(buf);
		String str = new String(buf,0,len);
		System.out.println(str);
		
		isr.close();
	}


5,将一个中文字符串数据按照指定的编码表写入到一个文本文件中.
    
    1,目的。OutputStream,Writer
    2,是纯文本,Writer。
    3,设备:硬盘File 
    FileWriter fw = new FileWriter("a.txt");
    fw.write("你好"); 
    
    注意:既然需求中已经明确了指定编码表的动作。
    那就不可以使用FileWriter,因为FileWriter内部是使用默认的本地码表。
    只能使用其父类。OutputStreamWriter.
    OutputStreamWriter接收一个字节输出流对象,既然是操作文件,那么该对象应该是FileOutputStream
    
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName);
    
    需要高效吗?
    BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charsetName));

什么时候使用转换流呢?

    1,源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。
        提高对文本操作的便捷。
    2,一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值