JAVA初步学习———第十一章 IO流

  • 数据以二进制的形式在程序与设备之间流动传输,就像水在管道里流动一样,所以就把这种数据传输的方式称之为输入流、输出流。
  • 流具有方向性,可以分为输入和输出。

一、流的分类

(1)根据数据的流向分为:

  • 输入流 :把数据从其他设备上读取到程序中的流
  • 输出流 :把数据从程序中写出到其他设备上的流
    (2)根据数据的类型分为:字节流和字符流
  • 字节流 :以字节为单位(byte),读写数据的流
  • 字符流 :以字符为单位(char),读写数据的流

字节输入流,在程序中,以字节的方式,将设备(文件、内存、网络等)中的数据读进来
字节输出流,在程序中,以字节的方式,将数据写入到设备(文件、内存、网络等)中
字符输入流,在程序中,以字符的方式,将设备(文件、内存、网络等)中的数据读进来
字符输出流,在程序中,以字符的方式,将数据写入到设备(文件、内存、网络等)中
字节指的是byte,字符指的的是char

二、流的结构

和IO流相关的类,主要是在 java.io 包下的定义的

  • 几乎所有的流,都是派生自四个抽象的父类型:
    • InputStream ,代表字节输入流类型
      它的子类型一定是以字节的方式,从某个地方把数据取到程序中,只是不同的子类型,它们的目的地不一样,并且一般可以通过子类型的名字看出它的的目的地是哪里
      • ByteArrayInputStream ,它是以字节的形式,去读取某个地方的数据,通过名字可以看出,它的目的地是字节数组中,也就是以字节的形式从byte数组中读取数据
      • FileInputStream ,它是以字节的形式,去读取某个地方的数据,通过名字可以看出,它的目的地是文件,也就是以字节的形式从文件中读取数据
    • OutputStream ,代表字节输出流类型
    • Reader ,代表字符输入流类型
    • Writer ,代表字符输出流类型
  • 一般情况下,一个流,会具备最起码的三个特点:
    • 是输入还是输出
    • 是字节还是字符
    • 流的目的地

三、字节流

1.概述

(1)InputStream 中最核心的三个read方法:

//每次读一个字节,返回值是本次读取的字节值 
public abstract int read() throws IOException; 


//每次读多个字节,并存放到指定的字节数组中,返回值是本次一共读取了多个字节(字节数) 
public int read(byte b[]) throws IOException { 
	return read(b, 0, b.length); 
}


//每次读多个字节,并存放到指定的字节数组中,返回值是本次一共读取了多个字节(字节数) 
//同时可以指定从数组的什么位置开始存放,以及在数组中最多存放多个字节 
public int read(byte b[], int off, int len) throws IOException { 
	if (b == null) { 
		throw new NullPointerException(); 
	} else if (off < 0 || len < 0 || len > b.length - off) { 
		throw new IndexOutOfBoundsException(); 
	} else if (len == 0) { 
		return 0; 
	}
	

	int c = read(); 
	if (c == -1) { 
		return -1;
	}
	b[off] = (byte)c; 
	int i = 1; 
	try {
		for (; i < len ; i++) { 
			c = read(); 
			if (c == -1) { 
				break; 
			}
			b[off + i] = (byte)c; 
		} 
	} catch (IOException ee) { }
	return i; 
}

(2)OutputStream 中最核心的三个write方法:

//写出去一个字节值 
public abstract void write(int b) throws IOException; 

//把一个自己数组中的值全部写出去 
public void write(byte b[]) throws IOException { 
	write(b, 0, b.length); 
}

//写出字节数组,指定开始位置,以及写出的字节数量 
public void write(byte b[], int off, int len) throws IOException { 
	if (b == null) { 
		throw new NullPointerException(); 
	} else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) 	
	{ 
		throw new IndexOutOfBoundsException(); 
	} else if (len == 0) { 
		return; 
	}
	for (int i = 0 ; i < len ; i++) { 
		write(b[off + i]); 
	}
}

2.控制台

在代码中,使用流操作数据的的基本步骤是:

  1. 声明流
  2. 创建流
  3. 使用流
  4. 关闭流
  • 使用字节流,从控制台读取数据,以及向控制台中写数据。java.lang.System
  • PrintStreamOutputStream 的子类型
  • 这个子类中重写的read方法,会让线程阻塞,等待用户在控制台中的输入,用户输入并按下回车,程序中的read方法就从阻塞状态恢复过来,从而读取到用户输入的内容
  • 运行一次后,程序就结束了,如果想让程序一直读取和写出,那么可以加入while循环
public class Test { 
	public static void main(String[] args) { 
		//1.声明流 
		InputStream in = null; 
		OutputStream out = null; 
		
		
		//2.创建流,这里使用System中已经创建好的流对象 
		in = System.in; 
		out = System.out; 
		
		
		//3.使用流 
		int len = -1; 
		byte[] buf = new byte[1024]; 
		try {
			//循环,一个流不停的读数据,一个流不同的写数据 
			while((len=in.read(buf))!=-1){ 
				len=in.read(buf);
				out.write(buf,0,len);
				//本次读了多几个字节,那么就写出几个字节 
				out.flush(); 
			} 
		} catch (IOException e) { 
			e.printStackTrace(); 
		}finally { 
			

			//4.关闭流 
			if(in!=null) { 
				try {
					in.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			}
			if(out!=null) { 
				try {
					out.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
}

3.字节数组

  • 使用字节流,从字节数组中读取数据,以及向字节数组中写数据。
    • java.io.ByteArrayInputStream 负责从字节数组中读取数据
    • java.io.ByteArrayOutputStream 负责把数据写入到字节数组中
public class Test { 
	public static void main(String[] args) { 
		//1.声明流 
		InputStream in = null; 
		OutputStream out = null; 
		

		//2.创建流 
		byte[] arr = "hello".getBytes(); 
		in = new ByteArrayInputStream(arr);
		 out = new ByteArrayOutputStream(); 
		

		 //3.使用流int 
		 len = -1; 
		 byte[] buf = new byte[1024]; 
		 try {
		 	len = in.read(buf); 
		 	//将数据写入到了out对象中的属性里面,该属性是一个字节数组 
		 	out.write(buf,0,len); 
		 	out.flush(); 
		 	//ByteArrayOutputStream中的toByteArray方法,可以将写入到out对象中的数据返回 
		 	byte[] toByteArray = ((ByteArrayOutputStream) out).toByteArray(); 
		 	System.out.println(Arrays.toString(toByteArray)); 
		 } catch (IOException e) { 
		 	e.printStackTrace(); 
		 }finally { 
		 	

			//4.关闭流 
		 	if(in!=null) { 
		 		try {
		 			in.close(); 
		 		} catch (IOException e) { 
		 			e.printStackTrace(); 
		 		} 
		 	}
		 	if(out!=null) { 
		 		try {
		 			out.close(); 
		 		} catch (IOException e) { 
		 			e.printStackTrace(); 
		 		} 
		 	} 
		 } 
	} 
}

4.管道

  • 使用字节流,可以从管道中读取数据,以及向管道中写数据。
    • java.io.PipedInputStream 负责从管道中读取数据
    • java.io.PipedOutputStream 负责将数据写入到管道中

一般可以在一个线程中,使用管道输出流,将数据写入到管道中,在另一个线程中,读取管道中的数据。

public class Test { 
	public static void main(String[] args) { 
		PipedInputStream in = null; 
		PipedOutputStream out = null; 
		in = new PipedInputStream(); 
		out = new PipedOutputStream(); 
		
		try {
			//管道对接 
			in.connect(out); 
			Thread t1 = new WriteThread(out); 
			Thread t2 = new ReadThread(in); 
			t1.start(); 
			t2.start(); 
			t1.join();
			t2.join(); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} catch (InterruptedException e) { 
			e.printStackTrace(); 
		}
		System.out.println(); 
		System.out.println("程序运行结束!"); 
	} 
}

class WriteThread extends Thread{ 
	private OutputStream out; 
	public WriteThread(OutputStream out){ 
		this.out = out; 
	}
	@Override 
	public void run() { 
		byte[] arr = "hello world briup".getBytes(); 
		try {
			for(int i=0;i<arr.length;i++){ 
				out.write(arr[i]); 
				out.flush(); 
				Thread.sleep(1000); 
			} 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} catch (InterruptedException e) { 
			e.printStackTrace(); 
		} finally { 
			if(out!=null) { 
				try {
					out.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
}

class ReadThread extends Thread{ 
	private InputStream in; 
	public ReadThread(InputStream in){ 
		this.in = in; 
	}
	@Override 
	public void run() { 
		int data = -1; 
		try {
			while((data=in.read())!=-1){ 
				// System.out.print(data); 
				System.out.write(data); 
				System.out.flush(); 
			}
			System.out.flush(); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} finally { 
			if(in!=null) { 
				try {
					in.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		}
	} 
}

5.文件

java.io.File 类,是java中对文件和目录的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
使用File既可以表示一个文件,也可以表示一个目录。
常用方法
public String getAbsolutePath() ,返回file的绝对路径
public String getPath() ,返回创建file对象时传入的路径参数(有可能是相对路径)
public String getName() ,返回file的名字
public long length() ,file如果表示文件,则返回文件内容的长度(字节个数)
public boolean exists() ,判断此文件或目录是否真的存在
public boolean isDirectory() ,判断File表示的是否是一个目录
public boolean isFile() ,判断file表示的是否是一个文件
public boolean createNewFile() ,创建一个文新文件
public boolean delete() 删除文件或目录
public boolean mkdir() 创建一级目录
public boolean mkdirs() 创建多级目录
public String[] list() ,返回目录中所有的子文件或子目录,返回值是String类型数组
public File[] listFiles() ,返回目录中所有的子文件或子目录,返回值是File类型数组
相对路径

import java.io.File; 
public class Test{ 
	public static void main(String[] args){ 
		//直接使用相对路径 
		String pathname = "Hello.java"; 
		File file = new File(pathname); 
		System.out.println("-------------------------"); 
		System.out.println(file.getAbsoluteFile());//所表示文件或目录的绝对路径 
		System.out.println(file.getPath());//创建file对象的时候所传的路径 
		System.out.println("-------------------------"); 
	}
}

四、字符流

1.概述

字符流可以用字符的形式,读写数据,专门用于处理文本数据。
java.io.Reader 是所有字符输入流的抽象父类型
java.io.Writer 是所有字符输出流的抽象父类型

(1)Reader 中最核心的三个read方法:

//每次读取一个字符,返回这个字符的编码值 
public int read() throws IOException { 
	char cb[] = new char[1]; 
	if (read(cb, 0, 1) == -1) 
		return -1; 
	else
		return cb[0]; 
}

//每次读取多个字符,并存放到指定字符数组中,返回值是本次读取到的字符个数 
public int read(char cbuf[]) throws IOException { 
	return read(cbuf, 0, cbuf.length); 
}

//每次读取多个字符,并存放到指定字符数组中,返回值是本次读取到的字符个数 
//同时可以指定从数组的什么位置开始存放,以及在数组中最多存放多个字符 
abstract public int read(char cbuf[], int off, int len) throws IOException;

(2)Writer 中最核心的三个write方法(前三个):

//写出去一个字符,注意字符可以使用int值来表示 
public void write(int c) throws IOException { 
	synchronized (lock) { 
		if (writeBuffer == null){ 
			writeBuffer = new char[WRITE_BUFFER_SIZE]; 
		}
		writeBuffer[0] = (char) c; 
		write(writeBuffer, 0, 1); 
	} 
}

//写出去数组中的多个字符 
public void write(char cbuf[]) throws IOException { 
	write(cbuf, 0, cbuf.length); 
}

//写出去数组中的多个字符 
//可以指定从数组的什么位置开始写,以及多少个字符 
abstract public void write(char cbuf[], int off, int len) throws IOException; 

//写出去一个字符串 
public void write(String str) throws IOException { 
	write(str, 0, str.length()); 
}

//写出去一个字符串 
//可以指定从字符串的什么位置开始写,以及多少个字符 
public void write(String str, int off, int len) throws IOException { 
	synchronized (lock) { 
		char cbuf[]; 
		if (len <= WRITE_BUFFER_SIZE) { 
			if (writeBuffer == null) { 
				writeBuffer = new char[WRITE_BUFFER_SIZE]; 
			}
			cbuf = writeBuffer; 
		} else { 
			cbuf = new char[len]; 
		}
		str.getChars(off, (off + len), cbuf, 0); 
		write(cbuf, 0, len); 
	} 
}

2.字符数组

使用字符流,从字符数组中读取数据,以及向字符数组中写数据。
java.io.CharArrayReader 负责从字符数组中读取数据
java.io.CharArrayWriter 负责把数据写入到字符数组中

public class Test { 
	public static void main(String[] args) { 
		//1.声明流 
		Reader in = null; 
		Writer out = null; 
		
		//2.创建流 
		char[] arr = "hello 中国".toCharArray(); 
		in = new CharArrayReader(arr); 
		out = new CharArrayWriter(); 
		
		//3.使用流 
		int len = -1; 
		char[] buf = new char[1024]; 
		try {
			//返回本次一共读取了多少个字符 
			len = in.read(buf); 
			//将数据写入到了out对象中的属性里面,该属性是一个字符数组 
			out.write(buf,0,len); 
			out.flush(); 
			//CharArrayWriter中的toCharArray方法,可以将写入到out对象中的数据返回 
			char[] toCharArray = ((CharArrayWriter) out).toCharArray(); 
			System.out.println(Arrays.toString(toCharArray)); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		}finally { 
			
			//4.关闭流 
			if(in!=null) { 
				try {
					in.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			}if(out!=null) { 
				try {
					out.close(); 
				} catch (IOException e) { 
				e.printStackTrace(); 
				}
			} 
		} 
	} 
}

3.管道

  • 使用字符流,可以从管道中读取数据,以及向管道中写数据。
    • java.io.PipedReader 负责从管道中读取数据
    • java.io.PipedWriter 负责将数据写入到管道中
public class Test { 
	public static void main(String[] args) { 
		PipedReader in = null; 
		PipedWriter out = null; 
		in = new PipedReader(); 
		out = new PipedWriter(); 
		try {
			//管道对接 
			in.connect(out); 
			Thread t1 = new WriteThread(out); 
			Thread t2 = new ReadThread(in); 
			t1.start(); 
			t2.start();
			t1.join(); 
			t2.join(); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} catch (InterruptedException e) { 
			e.printStackTrace(); 
		}
		System.out.println(); 
		System.out.println("程序运行结束!"); 
	} 
}


class WriteThread extends Thread{ 
	private Writer out; 
	public WriteThread(Writer out){ 
		this.out = out; 
	}
	@Override 
	public void run() { 
		char[] arr = "hello world briup".toCharArray(); 
		try {
			for(int i=0;i<arr.length;i++){ 
				out.write(arr[i]); 
				out.flush(); 
				Thread.sleep(1000); 
			} 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} catch (InterruptedException e) { 
			e.printStackTrace(); 
		} finally { 
			if(out!=null) { 
				try {
					out.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		
		} 
	} 
}


class ReadThread extends Thread{ 
	private PipedReader in; 
	public ReadThread(PipedReader in){ 
		this.in = in; 
	}
	@Override 
	public void run() { 
		int data = -1; 
		try {
			while((data=in.read())!=-1){ 
				//System.out.print(data); 
				System.out.write(data); 
				System.out.flush(); 
			}
			System.out.flush(); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		} finally { 
			if(in!=null) { 
				try {
					in.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
}

4.文件

  • 使用字符流,可以从文件中读取数据,以及向文件中写数据。
    • java.io.FileReader ,负责从文件中读取数据
    • java.io.FileWriter ,负责把数据写入到文件中

当读取文件的末尾没有数据的时候,read方法会返回-1,表示读完了

public class Test { 
	public static void main(String[] args) { 
		
		//1.声明流 
		Reader in = null; 
		Writer out = null; 
		try {
			
			//2.创建流
			File file1 = new File("src/main/java/com/briup/demo/a.txt"); 
			File file2 = new File("src/main/java/com/briup/demo/b.txt"); 
			in = new FileReader(file1); 
			out = new FileWriter(file2); 
			
			//3.使用流 
			int len = -1; 
			char[] buf = new char[1024]; 
			while((len=in.read(buf))!=-1){ 
				out.write(buf,0,len); 
			}
			out.flush(); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		}finally { 
			
			//4.关闭流 
			if(in!=null) { 
				try {
					in.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			}
			if(out!=null) { 
				try {
					out.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
}

五、数据流

  • 字节流操作数据的时候,以字节文件单位,一个个的进行读写,很多时候这样并不方便。我们希望读出来的若干个字节,自动转换为指定类型的数据,例如int、float、char等。
  • 类似的,我们也希望每次能直接把一个数据,自动转成字节再写出去。

1.输出

  • java.io.DataOutputStream ,可以将指定类型的数据转换为字节,并写出去
  • DataOutputStream 必须要“包裹”一个字节流,增强这个字节流的功能,一次可以写出去一个具体类型的数据 通过
  • writeLong 方法可以看出,内部是通过移位操作,拿到每一个字节的值,放到数组中,再写出去
public class Test { 
	public static void main(String[] args) { 
		
		//1.声明流 
		DataOutputStream out = null;
		ByteArrayOutputStream byteArrayOutputStream = null; 
		try {
			
			//2.创建流 
			//先创建出字节数组输出流,将最终的结果输出到对象内部的属性中,其属性是一个字节数组 
			byteArrayOutputStream = new ByteArrayOutputStream(); 
			//“包裹”字节数组输出流,增强它的功能,可以一次写到数组中一个指定类型的数据 
			out = new DataOutputStream(byteArrayOutputStream); 
			
			//3.使用流 
			out.writeLong(1000L); 
			out.flush(); 
			//将写入的数据转为字节数组并返回 
			byte[] toByteArray = byteArrayOutputStream.toByteArray(); 
			System.out.println(Arrays.toString(toByteArray)); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		}finally { 
			
			//4.关闭流 
			if(byteArrayOutputStream!=null) { 
				try {
					byteArrayOutputStream.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			}
			if(out!=null) { 
				try {
					out.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
}

//运行结果: 
		[0, 0, 0, 0, 0, 0, 3, -24]

2.输入

数据读出来的顺序要,和之前写进去的顺序一致

  • java.io.DataInputStream 可以将读取到的字节自动转化为指定类型的数据,但一般都需要和 DataOutputStream 配合使用。
    • DataOutputStream 负责把指定类型的数据,转化为字节并写出去
    • DataInputStream 负责把读取到的若干个字节,转化为指定类型的数据
      (1)先使用 DataOutputStream 将数据写入到文件中
public static void main(String[] args) { 
	
	//1.声明流 
	DataOutputStream out = null; 
	try {
		
		//2.创建流 
		File file = new File("src/main/java/com/briup/demo/a.txt"); 
		//“包裹”文件字节输出流,增强数据写出功能 
		out = new DataOutputStream(new FileOutputStream(file)); 
		
		//3.使用流 
		out.writeLong(1000L); 
		out.writeInt(5); 
		out.writeDouble(10.5D); 
		out.writeChar('a'); 
		out.writeUTF("hello,中国"); 
		out.flush(); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		
		//4.关闭流 
		if(out!=null) { 
			try {
				out.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}

(2)使用 DataInputStream 将数据从文件中读出来

public static void main(String[] args) { 
	
	//1.声明流 
	DataInputStream in = null;
	try {
		
		//2.创建流 
		File file = new File("src/main/java/com/briup/demo/a.txt"); 
		//“包裹”文件字节输入流,增强读取数据功能 
		in = new DataInputStream(new FileInputStream(file)); 
		
		//3.使用流 
		//注意,数据读出来的顺序要,和之前写进去的顺序一致 
		System.out.println(in.readLong()); 
		System.out.println(in.readInt()); 
		System.out.println(in.readDouble()); 
		System.out.println(in.readChar()); 
		System.out.println(in.readUTF()); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		
		//4.关闭流 
		if(in!=null) { 
			try {
				in.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}

六、缓冲流

缓冲流,可以在创建流对象时,设置一个默认大小的缓冲区数组,通过缓冲区进行读写,减少系统磁盘的IO次数,从而提高读写的效率。

1.字节缓冲流

java.io.BufferedInputStream ,负责给字节输入流提供缓冲功能
java.io.BufferedOutputStream ,负责给字节输出流提供缓冲功能

字节缓冲流的构造器,要求一定要传入一个字节流对象,然后缓冲流就可以对这个字节流的功能进行增强,提供缓冲数据的功能,从而提高读写的效率

public static void main(String[] args) { 
	
	//1.声明流 
	InputStream in = null; 
	OutputStream out = null; 
	try {
		
		//2.创建流 
		File file1 = new File("src/main/java/com/briup/demo/背影.txt"); 
		File file2 = new File("src/main/java/com/briup/demo/背影copy.txt"); 
		// in = new FileInputStream(file1); 
		// out = new FileOutputStream(file2); 
		//使用缓冲流增强文件字节流的功能,提供读写效率 
		//使用缓存流“包裹”其他的字节流即可 
		in = new BufferedInputStream(new FileInputStream(file1)); 
		out = new BufferedOutputStream(new FileOutputStream(file2)); 
		
		//3.使用流 
		int data = -1; 
		long start = System.currentTimeMillis();
		while((data=in.read())!=-1){ 
			out.write(data); 
		}
		out.flush(); 
		long end = System.currentTimeMillis(); 
		System.out.println("使用缓冲流完成读写操作的时间为:"+(end-start)+"毫秒"); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		
		//4.关闭流 
		if(in!=null) { 
			try {
				in.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		}
		if(out!=null) { 
			try {
				out.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}
//运行结果:每次运行结果稍有差异 
		使用字节流完成读写操作的时间为:22毫秒

2.字符缓冲流

java.io.BufferedReader,负责给字符输入流提供缓冲功能
java.io.BufferedWriter ,负责给字符输出流提供缓冲功能

字符缓冲流的构造器,要求一定要传入一个字符流对象,然后缓冲流就可以对这个字符流的功能进行增强,提供缓冲数据的功能,从而提高读写的效率

public class Test { 
	public static void main(String[] args) { 
		
		//1.声明流 
		Reader in = null; 
		Writer out = null; 
		try {
			
			//2.创建流 
			File file1 = new File("src/main/java/com/briup/demo/背影.txt"); 
			File file2 = new File("src/main/java/com/briup/demo/背影copy.txt"); 
			// in = new FileReader(file1); 
			// out = new FileWriter(file2); 
			//使用缓冲流增强文件字符流的功能,提高读写效率 
			in = new BufferedReader(new FileReader(file1)); 
			out = new BufferedWriter(new FileWriter(file2)); 
			
			//3.使用流 
			int data = -1; 
			long start = System.currentTimeMillis();
			while((data=in.read())!=-1){ 
				out.write(data); 
			}
			out.flush(); 
			long end = System.currentTimeMillis(); 
			System.out.println("使用字符流完成读写操作的时间为:"+(end-start)+"毫秒"); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		}finally { 
			
			//4.关闭流 
			if(in!=null) { 
				try {
					in.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			}
			if(out!=null) { 
				try {
					out.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
}
//运行结果:每次运行结果稍有差异 
		使用字符流完成读写操作的时间为:30毫秒

3.特殊方法

(1)BufferedReader——进行数据的操作,刚好可以一次读取一行数据

  • BufferedReader 中有一个方法 readLine,会使用的比较多
  • 如果下一行是空行, readLine 方法返回空字符串,也就是 String line = ""
  • 如果没有下一行数据了, readLine 方法返回 null

(2) PrintWriter——进行数据的操作,可以一次写出一行数据

  • 一次写一行,写完之后自动加换行
  • PrintWriter 中重载了很次构造器,它可以“包裹”一个字符流对象、字节流对象、文件对象等
  • PrintWriter 中重载了很次 println 方法,该方法可以再输出数据后,添加一个新行,【默认】情况下在Windows系统中添加的是回车换行(\r\n),在Linux系统中添加的时候换行(\n)
public class Test { 
	public static void main(String[] args) { 
		
		//1.声明流 
		BufferedReader in = null; 
		PrintWriter out = null; 
		try {
			
			//2.创建流 
			File file1 = new File("src/main/java/com/briup/demo/背影.txt"); 
			File file2 = new File("src/main/java/com/briup/demo/背影copy.txt"); 
			in = new BufferedReader(new FileReader(file1)); 
			out = new PrintWriter(new FileWriter(file2)); 
			
			//3.使用流 
			String line = null;
			while((line=in.readLine())!=null){ 
				out.println(line); 
			}
			out.flush(); 
		} catch (IOException e) { 
			e.printStackTrace(); 
		}finally { 
			
			//4.关闭流 
			if(in!=null) { 
				try {
					in.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			}
			if(out!=null) { 
				//PrintWriter中重写的close方法没有声明抛出异常 
				//重写的语法要求,抛出异常可以缩小,但是不能扩大 
				out.close(); 
			} 
		} 
	} 
}

七、转换流

转换流可以在把一个字节流转换为字符的同时,并指定转换的字符编码

1.输出

java.io.OutputStreamWriter ,可以将字节输出流转换为字符输出流,并指定编码
它的构造器参数,要求传入一个需要转换的字节输出流,和一个指定的字符编码
OutputStreamWriter 本身就是一个字符输出流,它继承了 Writer

public static void main(String[] args) { 
	
	//1.声明流 
	PrintWriter out = null; 
	//转换流,同时也是一个字符流 
	OutputStreamWriter outputStreamWriter = null; 
	try {
		
		//2.创建流 
		//创建文件对象 
		File file = new File("src/main/java/com/briup/demo/a.txt"); 
		//将字节流转换为字符流 
		outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file),"UTF-8");
		//“包裹”转换流,增强这个字符流的功能,可以一次写出一行字符串,并自动换行 
		//注意,转换流同时也是一个字符流 
		out = new PrintWriter(outputStreamWriter); 
		
		//3.使用流 
		out.println("你好,程序员"); 
		out.flush(); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		
		//4.关闭流 
		if(outputStreamWriter!=null){ 
			try {
				outputStreamWriter.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		}
		if(out!=null) { 
			out.close(); 
		} 
	} 
}

2.输入

java.io.InputStreamReader ,可以将字节输入流转换为字符输入流,并指定编码
它的构造器参数,要求传入一个需要转换的字节输入流,和一个指定的字符编码
InputStreamReader 本身就是一个字符输出流,它继承了 Reader

public static void main(String[] args) { 
	
	//1.声明流 
	BufferedReader in = null; 
	//转换流,同时也是一个字符流 
	InputStreamReader inputStreamReader = null; 
	try {
		
		//2.创建流 
		//创建文件对象 
		File file = new File("src/main/java/com/briup/demo/a.txt"); 
		//将字节流转换为字符流 
		inputStreamReader = new InputStreamReader(new FileInputStream(file),"UTF- 8"); 
		//“包裹”转换流,增强这个字符流的功能,可以一次读出一行字符串数据 
		//注意,转换流同时也是一个字符流 
		in = new BufferedReader(inputStreamReader); 
		
		//3.使用流 
		String line = null; 
		while((line=in.readLine())!=null){ 
			System.out.println(line); 
		} 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		
		//4.关闭流 
		if(inputStreamReader!=null){ 
			try {
				inputStreamReader.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		}
		if(in!=null) { 
			try {
				in.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}

八、对象流

完成对象的序列化和反序列化,就需要用到对象流了

  • Java 提供了一种对象序列化的机制,可以将对象和字节序列之间进行转换:
    • 序列化
      程序中,可以用一个字节序列来表示一个对象,该字节序列包含了对象的类型、对象中的数据等。如果这个字节序列写出到文件中,就相当于在文件中持久保存了这个对象的信息
    • 反序列化
      相反的过程**,从文件中将这个字节序列读取回来**,在内存中重新生成这个对象,对象的类型、对象中的数据等,都和之前的那个对象保持一致。(注意,这时候的对象和之前的对象,内存地址可能是不同的)
  • 在java中,并非所有对象都可以进行序列化和反序列化,而是只有实现了指定接口的对象才可以进行。
  • java.io.Serializable接口中没有抽象方法,这只是一个“标识”接口,实现它的的对象才可以进行序列化和反序列化操作

1.对象输出流

java.io.ObjectOutputStream ,将Java对象转换为字节序列,并输出到内存、文件、网络等地方
ObjectOutputStream 中需要“包裹”一个字节流,表示对象转换的字节序列要输出到什么地方

public static void main(String[] args) { 
	
	//1.声明流 
	ObjectOutputStream out = null; 
	try {
		
		//2.创建流 
		//创建文件对象 
		File file = new File("src/main/java/com/briup/demo/a.txt"); 
		//"包裹"一个文件字节输出流 
		out = new ObjectOutputStream(new FileOutputStream(file)); 
		
		//3.使用流 
		Student stu = new Student("tom",20); 
		//序列化:对象->字节 
		out.writeObject(stu); 
		out.flush(); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		
		//4.关闭流 
		if(out!=null) { 
			try {
				out.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}

2.对象输入流

java.io.ObjectInputStream ,从某一个地方读取出对象的字节序列,并生成对应的对象。
ObjectInputStream 中需要“包裹”一个字节流,表示从什么地方读取对象的字节序列

public static void main(String[] args) { 
	
	//1.声明流 
	ObjectInputStream in = null; 
	try {
		
		//2.创建流 
		//创建文件对象 
		File file = new File("src/main/java/com/briup/demo/a.txt"); 
		//"包裹"一个文件字节输出流 
		in = new ObjectInputStream(new FileInputStream(file)); 
		
		//3.使用流 
		Object obj = in.readObject(); 
		//反序列化:字节->对象 
		System.out.println(obj); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	} catch (ClassNotFoundException e) { 
		e.printStackTrace(); 
	} finally { 
		
		//4.关闭流 
		if(in!=null) { 
			try {
				in.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}

3. transient

  • transient 可以修饰类中的属性,它是让对象在进行序列化的时候,忽略掉指定的属性。
  • 常用在一些敏感属性的修饰,例如对象中的password属性,我们并不想将这个敏感属性的值进行序列化保存,那么就可以使用 transient 来对他进行修饰
  • transient 的单词含义就是短暂的、转瞬即逝。

九、随机访问流

1.概述

  • java.io.RandomAccessFile 是JavaAPI中提供的对文件进行随机访问的流,它并没有继承之前介绍到的那四个抽象父类型
  • 这个随机访问流,它的对象即可用作文件内容的读,又可以用作文件内容的写,同时它还可以任意定位到文件的某一个位置进行读或者写操作
    • 对象既可读也可以写
    • 随机定位文件中的任意字节位置进行读或写,并且可以前后反复定位
  • 创建该类的对象时,需要指定要操作的文件和操作的模式:
    • “r” 模式,以只读方式来打开指定文件夹。如果试图对该RandomAccessFile执行写入方法,都将抛出IOException异常。
    • “rw” 模式,以读写方式打开指定文件。如果该文件尚不存在,则试图创建该文件。
    • “rws” 模式,以读写方式打开指定文件。相对于”rw” 模式,还要求对文件内容或元数据的每个更新都同步写入到底层设备。
    • “rwd” 默认,以读写方式打开指定文件。相对于”rw” 模式,还要求对文件内容每个更新都同步写入到底层设备。
      程序中,以r和rw模式最为常用,r模式表示只读,rw模式表示既能读又能写

2.使用

(1)使用随机访问流进行内容替换

public static void main(String[] args) { 
	
	//randomAccessFile负责读取文件内容以及向文件中写内容 
	RandomAccessFile randomAccessFile = null;
	try {
		File file = new File("src/main/java/com/briup/demo/a.txt"); 
		
		//文件中要替换数据的位置 
		int replacePos = 6; 
		
		//文件中要插入的内容 
		String replaceContent = "yue"; 
		
		//设置randomAccessFile为读写模式 
		randomAccessFile = new RandomAccessFile(file,"rw"); 
		byte[] buf = new byte[1024]; 
		int len = -1; 
		
		//randomAccessFile定位到要替换数据的位置,准备去写要替换的内容 
		//注意,原内容和新内容的字节数恰好相等 
		randomAccessFile.seek(replacePos); 
		
		//在指定位置,写入需要替换的内容,覆盖原来此位置上的内容 
		randomAccessFile.write(replaceContent.getBytes()); 
		
		//randomAccessFile定位到文件的开始位置,准备去读文件中的所有内容,并输出到控制台 
		randomAccessFile.seek(0); 
		while((len=randomAccessFile.read(buf))!=-1){ 
			System.out.write(buf,0,len); 
		}
		System.out.flush(); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		if(randomAccessFile!=null){ 
			try {
				randomAccessFile.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	} 
}
//运行结果: 
		hello yue123

(2)使用随机访问流进行内容插入

public static void main(String[] args) { 
	
	//randomAccessFile负责读取文件内容以及向文件中写内容
	RandomAccessFile randomAccessFile = null; 
	
	//out用于临时保存一些内容 
	ByteArrayOutputStream out = null; 
	try {
		File file = new File("src/main/java/com/tyut/demo/a.txt"); 
		
		//文件中要插入数据的位置 
		int insertPos = 6; 
		
		//文件中要插入的内容 
		String insertContent = "yue "; 
		
		//设置randomAccessFile为读写模式 
		randomAccessFile = new RandomAccessFile(file,"rw"); 
		
		//out只能写数据 
		out = new ByteArrayOutputStream(); 
		
		//读操作需要用的常规参数 
		byte[] buf = new byte[1024]; 
		int len = -1; 
		
		//randomAccessFile定位要插入数据的位置,准备去读此位置及其以后的所有数据,并写入到数组中保存 
		randomAccessFile.seek(insertPos); 
		while((len=randomAccessFile.read(buf))!=-1){ 
			out.write(buf,0,len); 
		}
		System.out.flush(); 
		
		//randomAccessFile再次定位到要插入数据的位置,准备去写要插入的内容,以及临时保存的内容 
		//因为默认写入时会将对应位置上的数据给覆盖掉 
		randomAccessFile.seek(insertPos); 
		
		//在指定位置,写入需要插入的内容 
		randomAccessFile.write(insertContent.getBytes()); 
		
		//在插入内容后面,写入刚刚临时保存起来的内容 
		randomAccessFile.write(out.toByteArray()); 
		
		//randomAccessFile定位到文件的开始位置,准备去读文件中的所有内容,并输出到控制台 
		randomAccessFile.seek(0); 
		while((len=randomAccessFile.read(buf))!=-1){ 
			System.out.write(buf,0,len); 
		}
		System.out.flush(); 
	} catch (IOException e) { 
		e.printStackTrace(); 
	}finally { 
		if(randomAccessFile!=null){ 
			try {
				randomAccessFile.close(); 
			} catch (IOException e) { 
				e.printStackTrace(); 
			} 
		} 
	}
}
//运行结果: 
		hello yue world
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值