JavaSE——day16&day17IO流

IO流

    在设备与设备之间的一种数据传输。

分类

按流的方向分:输入流:读文件  从硬盘上读取出来文件,输入到文件中。

                        输出流:写文件  将文件的内容读出来,写到盘中新的文件中。

按处理数据类型分为:字节流:字节输入流InputStream    字节输出流:OutputStream

                                  字符流:   字符输入流Reader            字符输出流:Writer

以上4个类都是抽象类,不能直接创建对象,我们只能用他们的子类创建对象。

    一些常用的子实现类:FileInputStream,FileOutputStream,FileReader,FileWriter

开发步骤:

    ①创建字节输出流对象  ②写数据  ③关闭资源

字节输出流

例子:

import java.io.FileOutputStream;

public class OutputStream {

	public static void main(String[] args) throws Exception {
		//创建字节输出流对象
		/**
		 * 1)构造了输出流对象 (系统创建对象)
		 * 2)指向了当前项目下输出fos.txt
		 */
		FileOutputStream out = new FileOutputStream("fos.txt");
		//写字节
		out.write("hello".getBytes());
		
		//关闭资源
		/**
		 * 1)将文件和流对象不建立关系了 (fos对象不指向fos.txt)
		 * 2)及时释放掉流对象所占用的内存空间
		 */
		out.close();
		
	}
}
  关于字节输出流写数据的方法
  public void write(int b):一次写一个字节
  public void write(byte[] b) :一次写一个字节数组

  public void write(byte[] b, int off,int len):一次写一部分字节数组


我们在写字节流的时候可以一次写一个字节、可以一次写一个字节数组、可以一次写一部分字节数组。

例子:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class Test2 {

	public static void main(String[] args) throws Exception {
		
		FileOutputStream out = new FileOutputStream("fos2.txt");
		
		for(int x = 0 ;x < 10 ;x++) {
//			out.write(("hello"+x+"\n").getBytes());//这样写的兼容性太差了,不同操作系统换行不一定都是\n
			//一般高级记事本都换行都有效果写入换行符/r/n
			out.write(("hello"+x).getBytes());
			out.write(("\r\n").getBytes());
		}
		
		out.close();
	}
}

我们还可以使用这种构造方法指定末尾追加:当append为true时

FileOutputStream(File file, boolean append)
          创建一个向指定 File 对象表示的文件中写入数据的文件输出流。这种构造方式当append定义为true时自动添加到末尾。(但是当我把append改成false后怎么还是自动添加到末尾?!)

字节输入流

    开发步骤:
   1)创建字节文件输入流对象
2)读数据
3)释放资源

    读数据方式:
   public abstract int read():一次读取一个字节

  public int read(byte[] b):一次读取一个字节数组 (读取实际的字节数)

代码(一次读取一个字节):

		//使用输入流将文件中的adcsupport读出并在控制台打印
		FileInputStream fis = new FileInputStream("down.txt") ;
		int by = 0 ;//必须要给by初始化为0,否则输出会缺少
		while((by=fis.read()) != -1) {
			System.out.print((char)by);
		}
		
		fis.close();

    为什么不能直接用fis.read()作为循环的和判断的值而是要先给by初始化为0,然后把fis.read()的值赋给by,遍历输出by的值???

构造方法摘要
构造方法摘要
FileInputStream(File file)
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
FileInputStream(FileDescriptor fdObj)
          通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。
FileInputStream(String name)
          通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

方法摘要
 intread()
          从此输入流中读取一个数据字节。
 intread(byte[] b)
          从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
 intread(byte[] b, int off, int len)
          从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
 voidclose()
          关闭此文件输入流并释放与此流有关的所有系统资源。

代码(一次读取一个字节数组) :

package IO.file.inputstream;


import java.io.FileInputStream;


public class InputStreamDemo2 {


	public static void main(String[] args) throws Exception {
		
		FileInputStream fis = new FileInputStream("down.txt") ;
		
		//一次读取一个字节数组
		//指定字节数组的长度是:1024或者1024的倍数
		byte[] by =new byte[1024] ;
		int length = 0 ;
		while((length = fis.read(by)) != -1) {
			System.out.println(new String(by , 0 ,length));
		}
		
		fis.close();
		
	}
}

复制内容

    即是从一个文件中读取内容写到另一个文件中。

思路:①封装目的文件和源文件  ②进行边读边写  ③释放资源

    代码:

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test {

	public static void main(String[] args) throws Exception {
		//创建src和dest的对象
		FileOutputStream fileOutputStream = new FileOutputStream("dest.txt");
		FileInputStream fileInputStream = new FileInputStream("src.txt");
		//遍历fis文件内容并写入fos中
		int by = 0 ;
		while((by=fileInputStream.read()) != -1) {
			fileOutputStream.write(by);
		}
		if(fileOutputStream != null) {
			fileOutputStream.write(97);
		}
		//关闭资源
		fileOutputStream.close();
		fileInputStream.close();
	}
}

如果源文件和目的文件不在同一目录中,那么创建流对象时需要加绝对路径。当然,这种复制不仅仅针对的是文本文件,图片也可以,甚至我猜测视频也可以。下面是复制图面的例子:

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test1 {

	public static void main(String[] args) throws Exception {
		
		//创建复制的源文件对象保护伞.jpg和目的文件对象umbrella.jpg
		FileInputStream fileInputStream = new FileInputStream("保护伞.jpg");
		FileOutputStream fileOutputStream = new FileOutputStream("umbrella.jpg");
		
		byte[] byt = new byte[1024] ;
		int len = 0 ;
		while((len=fileInputStream.read(byt)) != -1 ) {//注意这里的read方法是有参的,作用是读取byt长度的字节
			
			fileOutputStream.write(byt , 0 , len);
		}
		
		fileOutputStream.close();
		fileInputStream.close();
	}
}

字节流的效率

    不同的字节读写方法有不同的效率,基本规律是:

        ①一次读取一个字节的效率小于一次读取一个字节数组。

        ②基本字节流的效率小于缓冲流

所以分为:基本的字节流一次读取一个字节、基本的字节流一次读取一个字节数组、高效的字节流一次读取一个字节 、高效的字节流一次读取一个字节数组。


下面以一个代码的例子说明:

package day17TestCharStream;

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

/**
 * 四种字节流效率的比较
 * @author malaganguo
 *
 */
public class TestCharStream {

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


		baseCharPerByte();
		baseCharPerByteArray();
		bufferedCharStreamPerByte();	
		bufferedCharStreamPerByteArray();
		
	}

	private static void bufferedCharStreamPerByteArray() throws FileNotFoundException, IOException {
		long start = System.currentTimeMillis();
		//创建流对象
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe")) ;
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy3.exe")) ;
		//读一个缓冲流数组,写一个缓冲流数组
		int len = 0 ;
		byte[] bufferbys = new byte[1024] ;
		while((len = bis.read(bufferbys)) != -1 ) {
			bos.write(bufferbys, 0, len);
		}
		bos.close();
		bis.close();
		long end   = System.currentTimeMillis();
		System.out.println("运行"+(end-start)+"ms");
	}

	private static void bufferedCharStreamPerByte() throws FileNotFoundException, IOException {
		long start = System.currentTimeMillis();
		 	//创建流对象
		 	BufferedInputStream bis = new BufferedInputStream(new FileInputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe"));
		 	BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy3.txt")) ;
		 	//读一个缓冲区字节写一个缓冲区字节
		 	int len = 0 ;
		 	while((len = bis.read()) != -1) {
		 		bos.write(len);
		 	}
		 	//关闭流
		 	bis.close();
		 	bos.close();
		 	
		 long end   = System.currentTimeMillis();
		System.out.println("运行"+(end-start)+"ms");
	}

	private static void baseCharPerByteArray() throws FileNotFoundException, IOException {
		long start;
		long end;
		start = System.currentTimeMillis();
		
		 //创建复制对象
		 FileOutputStream fos = new FileOutputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe");
		 FileInputStream fis = new FileInputStream("copy2.txt") ;
		 //读一个数组,写一个数组
		 int len = 0 ;
		 byte[] bys = new byte[1024] ;
		 while((len = fis.read(bys)) != -1 ) {
			 fos.write(bys, 0, len);
		 }
		 //关闭流
		 fos.close();
		 fis.close();
		 end   = System.currentTimeMillis();
		System.out.println("运行"+(end-start)+"ms");
	}

	private static void baseCharPerByte() throws FileNotFoundException, IOException {
		
		long start = System.currentTimeMillis();
		//创建流对象
		FileOutputStream fos = new FileOutputStream("copy1.exe");
		FileInputStream fis = new FileInputStream("02_由一次读取一个字节数组方式引入字节缓冲流.exe");
		//读一个字节写一个字节
		int len = 0 ;
		while((len = fis.read()) != -1) {
			fos.write(len);
		}
		//关闭资源
		fos.close();
		fis.close();
		long end   = System.currentTimeMillis();
		System.out.println("运行"+(end-start)+"ms");
	}

}

这里利用了四种方法复制一个较大的exe文件,我们得出运行时间结果:


可以验证我们前面提出的运行速率规律。

虽然说字节缓冲流对文件的读写速率得到了大大提高,可是,数据库的速率更快!

使用字节流一次读取一个字节的方式,会造成中文乱码--->Java提供了一个字符流(专门用来解决中文乱码问题)

    如何解决中文写出乱码的问题?

        使用相同的编码解码方式。 有些方法没有明确编码方式,则默认同平台(eclipse中为GBK)。有些方法提供了使用指定编码格式解码的有参构造,如:

                    编码: 将字符串变成一个字节数组
   public byte[] getBytes() :平台默认编码集(默认的是Gbk)
      public byte[] getBytes(Charset charset) ;"指定编码格式
  
  解码:将字节数组--->字符串
   public String(byte[] bytes) :使用平台默认编码集(gbk)
   public String(byte[] bytes,Charset charset):用指定的编码格式来解码

    

Charset charset这个形式参数即时我们自定义的编解码方式。书写如"utf-8"。utf-8是一种针对Unicode的可变长度字符编码,又称万国码。一个中文字符是3个字节。

       Read类及其子类

        用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
        直接已知子类1:InputStreamReader

            构造方法:

 InputStreamReader(InputStream in) :构造一个字符转换输入流,默认编码
 public InputStreamReader(InputStream in,Charset cs) 构造一个字符转换输入流,指定编码

                是字节流通向字符流的桥梁。它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

            FileReader(便捷类)是InputStreamReader类的子类,是一个读取字符文件的便捷类。FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream

            例子:

            

package streamReader_Writer;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Test {

	public static void main(String[] args) throws Exception {
		
		//创建字符流对象
		InputStreamReader isr = new InputStreamReader(
				new FileInputStream("CopyDemo.java"));
		OutputStreamWriter osw = new OutputStreamWriter(
				new FileOutputStream("copyedDemo.java")) ;
		
		//读一个字符数组,写一个字符数组
		int len = 0 ;
		char[] chs = new char[1024] ;
		while((len = isr.read(chs)) != -1) {
			osw.write(chs, 0, len);
		}
		//关闭流
		osw.close();
		isr.close();
	}
}

        便捷类的例子:

package streamReader_Writer;


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


public class Convenient {


	public static void main(String[] args) throws Exception {
		//创建字符的便捷类
		FileWriter fw = new FileWriter("copyedDemo2.java") ;
		FileReader fr = new FileReader("copyDemo.java") ;
		//读一个字符数组,写一个字符数组
		int len = 0 ;
		char[] chs = new char[1024] ;
		while((len = fr.read(chs)) != -1) {
			fw.write(chs, 0, len);
		}
		//关闭流
		fw.close();
		fr.close();
	
	}
}

    

为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:

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

    Writer类(完全可以类比Reader类)


  

        写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。 

    
 直接已知子类1:OnputStreamReader
      构造方法:
    public OutputStreamWriter(OutputStream out):使用默认的编码格式构造一个字符转换输出流对象
    public OutputStreamWriter(OutputStream out, Charset cs):使用指定编码格式构造一个字符转换输出流对象

        

为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。例如:

 Writer out
   = new BufferedWriter(new OutputStreamWriter(System.out));

 字符输出流写数据的功能:

  public void write(int c):写单个字符 
  public void write(char[] cbuf):写字符数组
  public abstract void write(char[] cbuf, int off,  int len):写字符数组的一部分
  public void write(String str):写字符串

  public void write(String str,int off, int len):写字符串的某一部分


字符缓冲IO流

    BufferedReader:字符缓冲输入流

        构造方法
  public BufferedReader(Reader in)创建一个使用默认大小输入缓冲区的缓冲字符输入流。 

  public BufferedReader(Reader in, int sz)创建一个使用指定大小输入缓冲区的缓冲字符输入流。

    BufferedWriter:文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入

       构造方法
   public BufferedWriter(Writer out) :默认缓冲区大小构造字符缓冲输出流对象

   public BufferedWriter(Writer out,int size):指定缓冲区大小


         字符缓冲输出流:
  特有功能:public void newLine():写入一个行的分隔符号
 
         字符缓冲输入流:

  特有功能:public String readLine():一次读取一行

举个栗子:

package bufferedwr;

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

public class Test {

	public static void main(String[] args) throws Exception {
		//创建字符缓冲输出输入流对象
		BufferedReader br = new BufferedReader(new FileReader("CopyDemo.java"));
		BufferedWriter bw = new BufferedWriter(new FileWriter("bufferedwr.java"));
		//读一个字符数组,写一个字符数组
		String line = null ;
		String[] bys = new String[1024] ;
		while((line=br.readLine()) != null) {
			bw.write(line);
			bw.newLine();
			bw.flush();
		}
		//关闭流
		bw.close();
		br.close();
		
		
	}
}

注意:

        在字符流缓冲输出中,结束输出后在我们关闭资源前,我们需要对缓冲区进行刷新,及调用方法:

                flush();

为什么要字符流缓冲区输出结束一定要调用flush方法呢?

    因为输出流在进行输出时,比如像某个文件中写入内容,其实是先将输出流写入到缓冲区,当缓冲区写满后才将缓冲区的内容输出到文件中。但是当主机完成输出流的输出后,有可能缓冲区这个时候还没有被填满,这样的话,就会一直等待主机发送内容,这时候,就可以使用flush将缓冲区的内容强制输出到文件中,清空缓冲区。 

所以,一般在关闭输出流之前,要先调用flush方法强制缓冲区中的内容输出,并清空缓冲区。

Demo

例子1——将集合中的元素存储的文件中:

package ArrayList_Buffered;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.ArrayList;

/**
 * 需求:把ArrayList集合中的字符串数据存储到文本文件
 * @author malaganguo
 *
 */
public class Demo {

	public static void main(String[] args) throws Exception {
		
		//创建集合对象
		ArrayList<String> al = new ArrayList<String>() ;
		//往集合中添加元素
		al.add("aloha") ;
		al.add("nihao") ;
		al.add("hello") ;
		al.add("你好") ;
		//创建字符输出缓冲区对象
		BufferedWriter bf = new BufferedWriter(new FileWriter("demo.txt")) ;
		for(String s : al) {
			bf.write(s);
			bf.newLine();
			bf.flush();
		}
		//关闭资源
		bf.close();
	}

}

例子2——将文件中的内容读到集合中

package ArrayList_Buffered;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;

/**
 * 有一个文本文本,需要将文本文件中的内容放到ArrayList集合中,遍历集合获取元素 
 */
public class Demo1 {

	public static void main(String[] args) throws Exception {
		
		//那我们就把我们创建的文本文件demo.txt中的内容放进ArrayList中
		
		//创建字符缓冲输入对象
		BufferedReader br = new BufferedReader(new FileReader("demo.txt")) ;
		//创建容器:集合对象
		ArrayList<String> al = new ArrayList<String>() ;
		String line = null ;
		byte[] bys = new byte[1024] ;
		while((line=br.readLine()) != null ) {
			al.add(line);
		}
		//关闭资源
		br.close();
		System.out.println(al);
		
	}
}


其他流

    一个不需要关闭的临时流——内存操作流。

        内存操作流:一个程序结束后,那么这个程序的变量就会从内存中消失。所以不需要关闭流对象。

输入流:byteArrayInputStream

             有参构造:byteArrayInputStream (byte[] bys)

输出流:byteArrayOutputStream

             有参构造:byteArrayOutputStream(byte[] bys)

    针对java基本类型的数据进行读写操作——数据流

输入流:dataInputStream

输出流:dataOutputStream

打印流

    字符打印流:printWriter

    字节打印流:printStream

特点:

    1、打印流只有输出流,没有输入流。只能写数据(只能针对目的地文件进行操作),不能读数据(不能针对源文件进行操作)

    2、可以针对文件直接进行操作

    3、自动刷新功能::PrintWriter(OutputStream out/Writer out,boolean autoflush);第二个参数如果是true 表示启动自动刷新功能

    4、打印的方法:print(XXX x)/println(XXX  xx)

合并流:

构造方法:

public sequenceInputStream(file1,file2) ;

public sequenceInputStream(Enumeration e) ;

例子1:

package test.dat17;

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.SequenceInputStream;

public class Test1 {

	public static void main(String[] args) throws Exception {
	
		//创建两个待合并的流对象
		FileInputStream fis = new FileInputStream("a.txt");
		FileInputStream fos = new FileInputStream("b.txt");
		
		//创建合并流对象
		SequenceInputStream sis = new SequenceInputStream(fis,fos);
		
		//创建字符缓冲输入流对象
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a&b.txt")) ;
		
		//遍历
		int len = 0 ;
		byte[] bys = new byte[1024] ;
		while((len = sis.read(bys)) != -1) {
			bos.write(bys, 0, len);
			bos.flush();
		}
		
		//关闭流
		fos.close();
		fis.close();
		bos.close();
		sis.close();
	}
}


例子2:

package test.dat17;

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;

public class Test2 {

	public static void main(String[] args) throws IOException {
		
		//定义一个InputStream类型的Vector集合
		Vector<InputStream> v = new Vector<InputStream>() ;
		//创建流对象
		FileInputStream fis1 = new FileInputStream("a.txt");
		FileInputStream fis2 = new FileInputStream("b.txt");
		FileInputStream fis3 = new FileInputStream("c.txt");
		//往集合中添加元素
		v.add(fis1) ;
		v.add(fis2) ;
		v.add(fis3) ;
		//枚举对象
		Enumeration<InputStream> e = v.elements() ;
		//创建合并流对象
		SequenceInputStream sis = new SequenceInputStream(e);
		//创建输出缓冲区
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream("a&b&c.txt"));
		//遍历输出
		int len = 0 ;
		byte[] bys = new byte[1024] ;
		while((len = sis.read(bys)) != -1 ) {
			bos.write(bys, 0, len);
			bos.flush() ;
		}
		
		bos.close();
		sis.close();
	}
}

标准输入输出流
        InputStream in =  System.in ;

  PrintStream out = System.out ;

java装修者模式:创建键盘输入对象。创建键盘输出对象

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

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)) ;

序列化流和反序列化流

序列化:将对象按照流的方式存储到文本文件中或者再网络中传输    对象---->流数据 序列化流 (ObjectOutputStream)

反序列化:将文本文件中的流对象或者网络传输中的流对象还原成对象   流数据--->对象  反序列化流(ObjectInputStream)

持久属性集


Properties:表示了一个持久的属性集(简称:属性集合类)  extends Hashtable<K,V> Map集合的。

可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值