Java基础_IO流(上)

IO流(上)

一、概述:

    IO流:即Input、Output流,输入、输出流。在java中,对数据的操作通过流的形式完成;流可以按照操作对象的不同分为字符流(专门用于操作纯文本文件)和字节流(非纯文本文件)两种,也可以按照流向的不同分为输入流和输出流,这里的输入和输出都是相对于内存而言的。

    总之:IO流专门用来处理设备之间的数据传输;字符流=字节流+默认的编码。

本章要介绍的字符流类如下图所示:

本章要介绍的字节流类如下图所示:


二、字符流

    字符流有默认的编码,一般用于操作纯文本的数据

    字符流的两个抽象基类:Reader(读取数据),Writer(写出数据,内存写到外设中);这两个抽象基类提供了对于字符流操作的基本方法,但有些方法是抽象的,必须由子类去覆盖。一切操作字符流的类都直接或者间接继承Reader、Writer抽象基类;这些子类的后缀是父类名称,前缀是具体的功能,例如:FileReader、BufferedReader、FileWriter、BufferedWriter等等。

    既然IO流用于操作数据,那么数据的最常见体现形式就是:文件。在IO流中,有专门的类作文件数据的流对象,这两个类就是FileReader、FileWriter

    flush()方法:调用write方法,只是把数据写入到流中;而flush方法用于将流中的数据刷到目的文件中;注意:读取流没有刷新这种说法。

    为了能更加清晰、更加深刻的理解,下面主要以代码的形式介绍:

往磁盘文件中写入数据:

package itheima.day18;

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

//需求:在硬盘上,创建一个文件并写入一些文字数据
public class FileWriterDemo {

	public static void main(String[] args)  {
		FileWriter fw = null;
		try {
//			append为true时,不覆盖原文件
			fw = new FileWriter("D:\\java\\IODemoFile\\demo.txt"/*,true*/);
			fw.write("坚韧的民族主义者---蒋中正;");
			fw.flush();
			fw.write("抗日英雄----李宗仁;");
			fw.write("当代小诸葛---白崇禧;");
			fw.flush();
		} catch (IOException e) {
			throw new RuntimeException("文件读写失败");
		}finally{
			try {
				if(fw!=null)
					fw.close();
			} catch (IOException e) {
				throw new RuntimeException("文件关闭失败");
			}
		}
	}

}

1、读取一个.java文件,并打印到控制台上:

package itheima.day18;

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

public class FileReaderTest {
	public static void main(String[] args) {
		FileReader fr = null;
		try {
			fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\FileReaderTest.java");
			char[] buf = new char[1024];
			int num=0;
			while((num=fr.read(buf))!=-1){
				System.out.print(new String(buf,0,num));
			}
		} catch (FileNotFoundException e) {
			throw new RuntimeException("文件未找到异常!");
		} catch (IOException e) {
			throw new RuntimeException("IO读写异常!");
		}finally{
			try {
				if(fr!=null)
					fr.close();
			} catch (IOException e) {
				throw new RuntimeException("文件关闭失败!");
			}
		}
	}
}

2、复制一个文件:

package itheima.day18;

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

//需求:将c盘一个文件复制到D盘(主要是演示文件的复制)
//	复制原理:其实就是将C盘下的文件数据存储到D盘的一个文件中。
//	 步骤:
// 		1、在D盘创建一个文件,用于存储c盘文件中的数据
// 		2、定义读取流和C盘文件关联
//  	3、通过不断的读写完成数据的存储
public class CopyTest {

	public static void main(String[] args) {
		copy_2();	
	}
	public static void copy_1(){
		
		FileWriter fw =null;
		FileReader fr = null;
		try {
			fw = new FileWriter("D:\\java\\IODemoFile\\CopyTest.java");
			fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\CopyTest.java");
			int ch =0;
			while((ch =fr.read())!=-1){
				fw.write((char)ch);
				fw.flush();
			}
		} catch (FileNotFoundException e) {
			throw new RuntimeException("文件未找到异常!");
		} catch (IOException e) {
			throw new RuntimeException("IO读取异常!");
		}finally{
			try {
				if(fw!=null)
					fw.close();
			} catch (IOException e) {
				throw new RuntimeException("写入文件关闭失败!");
			}
			try {
				if(fr!=null)
					fr.close();
			} catch (IOException e) {
				throw new RuntimeException("读取文件关闭异常!");
			}
		}
	}
	public static void copy_2(){
		FileWriter fw =null;
		FileReader fr = null;
		try {
			fw = new FileWriter("D:\\java\\IODemoFile\\CopyTest.java");
			fr = new FileReader("D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day18\\CopyTest.java");
			char[] buf = new char[1024];
			int  len =0;
			while((len =fr.read(buf))!=-1){
				fw.write(buf,0,len);
				fw.flush();
			}
		} catch (FileNotFoundException e) {
			throw new RuntimeException("文件未找到异常!");
		} catch (IOException e) {
			throw new RuntimeException("IO读写异常!");	
		}finally{
			try {
				if(fw!=null)
					fw.close();
			} catch (IOException e) {
				throw new RuntimeException("写入文件关闭失败!");
			}
			try {
				if(fr!=null)
					fr.close();
			} catch (IOException e) {
				throw new RuntimeException("读取文件关闭失败!");
			}
		}
	}
}

    字符流的缓冲区:缓冲区的出现是为了提高对数据的读写效率;对应类:BufferedReader、BufferedWriter,注意:缓冲区只是在流的基础上对流的功能进行增强,所以必须首先有流对象。

    BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入,最重要的增加了newLine()方法。

    BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取,最重要的是增加了readLine()方法。

    void  newLine(): 写入一个行分隔符。

    String  readLine():读取一个文本行,只返回数据,不返回回车符。

    readLine()方法的原理:无论是读一行、获取多个字符,还是读取一个字符,其实最终都是在硬盘上一个一个的读取。所以readLine()方法最终使用的还是read()一次读一个的方法。

3、通过缓冲区复制一个.java文件:

package itheima.day19;

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

//通过缓冲区复制一个.java文件
public class CopyTextByBuffered {

	public static void main(String[] args) {
		
		BufferedReader bufr = null;
		BufferedWriter bufw = null;
		try {
			bufr =new BufferedReader(
					new FileReader(
							"D:\\java\\workspace1\\JavaFoundation\\src\\itheima\\day19\\CopyTextByBuffered.java"));
			bufw = new BufferedWriter(
					new FileWriter("D:\\java\\IODemoFile\\CopyTestByBuf.java"));
			String line =null;
			while((line = bufr.readLine())!=null){
				System.out.println(line);
				bufw.write(line);
				bufw.newLine();
				bufw.flush();
			}
		} catch (IOException e) {
			throw new RuntimeException("文件读取失败!");
		}finally{
			try {
				if(bufr!=null)
					bufr.close();
			}catch (IOException e) {
				throw new RuntimeException("文件读取关闭失败!");
			}
			try {
				if(bufw!=null)
					bufw.close();
			}catch (IOException e) {
				throw new RuntimeException("文件写出关闭失败!");
			}
		}
		System.out.println("复制完成!!!");
	}
}

    装饰设计模式:当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有的功能,并提供加强功能;那么自定义的该类成为装饰类。装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰对象的功能,提供更强的功能。

    装饰设计模式比继承更加灵活,不仅避免了继承体系的臃肿,还降低了类与类之间的耦合关系。装饰类因为只是增强已有对象,具备的功能和已有对象的功能是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于同一个体系中。

4、模拟带有ReadLine功能的BufferedReader:

package itheima.day19;

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

// 定义MyBufferedReader,myReadLine方法
public class MyBufferedReaderDemo 
{
	public static void main(String[] args) {
		
		MyBufferedReader myBuf = null;
		try {
			FileReader fr = new FileReader("D:\\java\\IODemoFile\\CopyTestByBuf.java");
			myBuf = new MyBufferedReader(fr);
			String line = null;
			while((line = myBuf.myReadLine())!=null){
				System.out.println(line);
			}
		} catch (FileNotFoundException e) {
			throw new RuntimeException("要读取的文件不存在!");
		} catch (IOException e) {
			throw new RuntimeException("文件读写失败!");
		}finally{
			try {
				if(myBuf!=null)
					myBuf.myClose();
			} catch (IOException e) {
				System.out.println(e.toString());
			}
		}
	}
}
class MyBufferedReader extends Reader
{
	private Reader r;
	MyBufferedReader(Reader r){
		this.r  = r;
	}
	public String myReadLine() throws IOException{
		.
		StringBuilder sb = new StringBuilder();
		int ch =0;
		while((ch =r.read())!= -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 r.read(cbuf,off,len);
	}
	@Override
	public void close() throws IOException {
		r.close();
	}	
}

5、模拟带有行号功能的LineNumberReader:

package itheima.day19;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import itheima.day19.MyBufferedReader;

public class MyLineNumberReaderDemo {

	public static void main(String[] args) {
		
		 MyLineNumberReader mylnr = null;
		try {
			FileReader fr = new FileReader("src\\itheima\\day19\\LineNumberReaderDemo.java");
			 mylnr = new MyLineNumberReader(fr);
			 String line = null;
			 mylnr.setLineNumber(200);
			 while((line = mylnr.myReadLine())!=null){
				 System.out.println(mylnr.getLineNumber()+"::"+line);
			 }
		} catch (FileNotFoundException e) {
			throw new RuntimeException("文件未找到异常!");
		} catch (IOException e) {
			throw new RuntimeException("文件读写异常!");
		}finally{
			try {
				if(mylnr!=null)
					mylnr.myClose();
			} catch (IOException e) {
				throw new RuntimeException("文件关闭失败!");
			}
		}
	}
}

class MyLineNumberReader extends MyBufferedReader
{
	private int lineNumber;
	MyLineNumberReader(Reader r){
		super(r);
	}
	
	public String myReadLine() throws IOException{
		lineNumber++;
		return super.myReadLine();
	}
	
	public void setLineNumber(int lineNumber){
		this.lineNumber = lineNumber;
	}
	
	public int getLineNumber(){
		return lineNumber;
	}
}

三、字节流

    字符流用于操作纯文本文件,但在计算机中有些文件不是文本类型的,比如:图片、mp3、电影等等,这时就要用到字节流操作。字节流的基本操作与字符流相同,但它不仅可以操作字符,还可以操作其他媒体文件。

    字节流的两个抽象基类:InputStream:,输入流,相对于内存而言,数据流入到内存中,读取;OutputStream:输出流,数据从内存流出到外设中,写出

    OutputStream类中也有write方法,可是使用的是字节数组,而字符流用的是字符数组

    带缓冲区的字节流的读取原理如下图:


6、复制图片:

package itheima.day19;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

//复制一个图片
public class CopyPic {

	public static void main(String[] args) {
		copyPicture();
	}
	public static void copyPicture(){
		
		FileOutputStream fos = null;
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("抗日英雄:李宗仁.jpg");
			fos = new FileOutputStream("D:\\java\\IODemoFile\\李宗仁:台儿庄.jpg");
			fos.write("abcd".getBytes());
			
			byte[] buf = new byte[1024];
			int len =0;
			while((len = fis.read(buf))!=-1){
				fos.write(buf,0,len);
			}
		} catch (FileNotFoundException e) {
			throw new RuntimeException("文件未找到异常!");
		} catch (IOException e) {
			throw new RuntimeException("文件读写异常!");
		}finally{
			try {
				if(fos!=null)
					fos.close();
			} catch (IOException e) {
				throw new RuntimeException("写入文件关闭失败!");
			}
			try {
				if(fis!=null)
					fis.close();
			} catch (IOException e) {
				throw new RuntimeException("读取文件关闭失败!");
			}
		}
	}
}

7、复制mp3:

package itheima.day19;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

//复制mp3,通过缓冲区
public class CopyMP3 {

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		copy_1();
		long end = System.currentTimeMillis();
		System.out.println(end - start+"毫秒");
	}
	public static void copy_1(){
		BufferedInputStream bufis = null;
		BufferedOutputStream bufos = null;
		try {
			bufis = 
					new BufferedInputStream(
							new FileInputStream(
									"D:\\静心--佛家歌曲\\王菲《心经》第三届世界佛教论坛.mp3"));
			bufos = 
					new BufferedOutputStream(
							new FileOutputStream("D:\\java\\IODemoFile\\心经.mp3"));
			int by =0;
			while((by = bufis.read())!=-1){
				bufos.write(by);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			
			try {
				if(bufos!=null)
					bufos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			try {
				if(bufis!=null)
					bufis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void copy_2(){
		
		MyBufferedInputStream bufis = null;
		BufferedOutputStream bufos = null;
		try {
			bufis = new MyBufferedInputStream(
						new FileInputStream("D:\\静心--佛家歌曲\\王菲《心经》第三届世界佛教论坛.mp3"));
			bufos = new BufferedOutputStream(
						new FileOutputStream("D:\\java\\IODemoFile\\心经11.mp3"));
			int by =0;
			while((by = bufis.myRead())!=-1){
				bufos.write(by);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			
			try {
				if(bufos!=null)
					bufos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			try {
				if(bufis!=null)
					bufis.myClose();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

//模拟BufferedInputStream类
//字节流缓冲区的基本原理:
class MyBufferedInputStream{
	
	private InputStream in;
	private byte[] buf = new byte[1024];
	private int pos =0 ,count = 0;
	MyBufferedInputStream(InputStream in){
		this.in = in;
	}
	
//	为何返回int 而不是byte?
//	byte -1 ---> int -1(自动类型提升)
// 	1111 1111 --> 1111....1111(32)位
//	很有可能流会读到连续八个1,返回byte类型的-1,会与结束标识混淆!
	public int myRead() throws IOException{
//		先将数据读取到字节数组buf中
		if(count ==0){
			count = in.read(buf);
			if(count<0)
	<span style="white-space:pre">	</span>		return -1;
//			一个一个读取,指针向前,数量减少
			pos =0;
			byte b= buf[pos];
			count--;
			pos++;
			return b&0xff;
		}
		else if(count>0){
			byte b = buf[pos];
			count--;
			pos++;
			return b&0xff;
		}
		return -1;
	}
	
	public void myClose() throws IOException{
		in.close();
	}
}

四、转换流

    当字节流中的数据都是字符时,转成字符流操作更加高效。转换流就是字符流与字节流之间的桥梁,主要是方便字符流与字节流之间的操作。因为有了字符流之后才需要转换,所以转换流定义在字符流体系中。

    InputStreamReader:字节流通向字符流的桥梁;

    OutputStreamWriter:字符流通向字节流的桥梁。

    标准输入输出流:在System类中,有两个字段:in、out,它们分别代表系统标准的输入和输出设备;默认的输入设备对应的是键盘,输出设备对应的是控制台。System.in的类型是InputStream,System.out的类型是PrintStream;PrintStream是OutputStream的子类。可以通过System.setIn();System.setOut()改变标准输入输出设备。

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

8、读取键盘录入:

package itheima.day19;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;

//读取键盘录入。
// 		 System.out:对应的设备是标准输出设备,控制台。
//		 System.in:对应的设备是标准输入设备,键盘。
//需求:通过键盘录入数据,
//		 当录入一行数据后,就将改行数据打印
// 		 over,停止录入。
public class ReadIn {

	public static void main(String[] args) throws IOException {
		
		System.setIn(new FileInputStream("src\\itheima\\day19\\ReadIn.java"));
<span style="white-space:pre">	</span>	System.setOut(new PrintStream("zz.txt"));
		InputStream in = System.in;
		InputStreamReader isr = new InputStreamReader(in);
		BufferedReader bufr = new BufferedReader(isr);
		OutputStream out = System.out;
		OutputStreamWriter osw = new OutputStreamWriter(out);
		BufferedWriter bufw = new BufferedWriter(osw);
		
		String line = null;
		while((line = bufr.readLine())!=null){
			if("over".equalsIgnoreCase(line))
				break;
//			System.out.println(line.toUpperCase());
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
	}
	public static void method_1() throws IOException{
		InputStream in = System.in;
		StringBuilder sb = new StringBuilder();
		
		while(true){
			int ch = in.read();
			if(ch=='\r')
				continue;
			if(ch=='\n'){
				String s = sb.toString();
				if("over".equals(s))
					break;
				System.out.println(s.toUpperCase());
				sb.delete(0, sb.length());
			}
			else{
				sb.append((char)ch);
			}
		}
	}
}

9、异常信息的日志存储:

package itheima.day19;

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

//异常的日志信息
public class ExceptionInfoDemo {

	public static void main(String[] args) {
		
		try {
			int[] arr = new int[2];
			System.out.println(arr[3]);
		} catch (Exception e) {
			try {
//				e.printStackTrace(new PrintStream("a.txt"));
//				log4j 专门处理异常日志的工具包
				
				Date d = new Date();
				SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
				String s = sdf.format(d);
				
				PrintStream ps =new PrintStream("exception.log");
				ps.println(s);
				System.setOut(ps);
				e.printStackTrace(System.out);	
			} catch (FileNotFoundException e1) {
				throw new RuntimeException("日志文件创建失败!");
			}
		}
	}
}

    流操作的基本规律:流对象很多,不知道用哪个;可以通过下面三个明确来完成。

    1、明确源和目的,源:输入流,InputStream、Reader;目的:输出流,OutputStream

        Writer。

    2、操作的是否是纯文本?是:字符流;否:字节流。

    3、当体系明确以后,再明确使用哪个具体的对象,通过设备来进行区分

        源:内存、硬盘、键盘;目的:内存、硬盘、控制台。

    例如:1、将一个文本文件中的数据存储到另一个文件中:复制文件。

    源:因为是源,所以使用读取流。InputStream Reader;是否操作文本文件?是:这时就可以选择Reader,这样体系就明确了。

    接下来明确要使用该体系中的那个对象?明确设备:硬盘,上的一个文件。

Reader体系中可以操作文件的对象是FileReader

    是否需要提高效率?

    是,加入Reader体系中的缓冲区BufferedReader

    FileReaderfr = new FileReader(“a.txt”)

    BufferedReaderbufr = new BufferedReader(fr);

    目的:OutputStream Writer ;是否是纯文本?是:Writer

    设备:硬盘,一个文件,Writer体系中可以操作文件的对象FileWriter。

    是否需要提高效率?是!加入Writer体系中缓冲区BUfferedWriter

    FileWriterfw = new FileWriter(“b.txt”);

    BufferedWriterbufw = new BufferedWriter(fw);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章