java程序员从笨鸟到菜鸟之(三十五)IO流之字节流

本节我们讲解与File有关的IO流

程序的主要任务是操作数据,程序从输入流中读取数据,从输出流中写入数组


通过程序读取和写入数据

流:有序的数据序列

数据流中最小的数据单元划分(操作数据类型):字节流和字符流

字节流的划分:InputStream(字节输入流)和OutputStream(字节输出流)

字符流的划分:Reader(字符输入流)和Writer(字符输出流)

字节输出流----OutputStream---抽象基类

向输出流写入数据的三种重载形式

方式1

void write(int b):向输出流写入一个字节

方式2
public void write(byte[] b)
功能说明:把参数b指定的字节数组中的所有的字节写入输出流

方式3

public void write(byte[] b, int off,int len)
功能:把参数b指定的字节数组的若干字节写入到输出流
开发中:该方法和读数据一块使用
参数:off--字节数组的起始索引,len--从指定位置开始指定字节的数目

常用的方法

void flush()

功能:把缓冲区内的数据写入到输出流中

说明:一些带缓冲区的子类(BufferedOutputStreeam和PrintStream)覆盖了flush()方法

带缓冲区的输出流的特点:写数据时,数据先保存在缓冲区中积累到一定程度才会真正写入到输入流

缓冲区说明:通常是字节数组实现,实际上是一块内存空间

void close()

说明:关闭输出流,释放资源

注意:流一旦关闭,不能再写入数据了,并且该流对象输出完毕之后,不指向这个文件了,所以需要将它关闭掉

补充:Java语言不能创建系统资源,通过C或C++间接创建系统资源

向输入流写入数据的三种重载形式

方式1

int read()

说明:从输出流读取一个8位的字节,把它转换成0-255的整数,返回这一整数

疑问:字节转换过程?

方式2

int read(byte [] b)

说明:从输入流读取若干字节,把它们保存到参数b指定的字节数组中

返回值:读取的实际字节数

注意:参数b指定的字节数组(实际上是一个字节数组缓冲区),一般数组的大小是1024

方式3

int read(byte [] b,int off,int len)

说明:从输出流中读取若干个字节,把它们保存到参数b指定的数组中

参数:off--保存数据的起始下标;len--从起始下标读取的字节数

返回值:实际读取的字节数

注意对于read()方法如果遇到输入流的结尾,则返回-1

字节输入流子类---FileInputStream--文件输入流

FileInputStream的两种构造方式

实例1

package org.westos_01;

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
	
	public static void main(String[] args) throws IOException {
		
		//1)创建文件输入流对象
		FileInputStream fis = new FileInputStream("fis.txt") ;
		
		//2)读数据
		
		//读取,赋值,判断---三合一
		//一次读取一个字节的模板代码
		int by = 0 ;
		while((by=fis.read())!=-1){
		//a--->97
                System.out.print(by);//转换成ASCII码表中的0-255的整数
 System.out.print((char)by);//转换为原来的字符形式}//释放资源fis.close() ;}}
练习1
package org.westos_01;

import java.util.Arrays;

public class Stringdemo {
	public static void main(String[] args) {
		
		String s = "我爱你中国" ;
		
		byte[] bys = s.getBytes() ;//编码---GBK模式
		System.out.println(Arrays.toString(bys));
		//将字节数组转化成字符串
		//[-50, -46, -80, -82, -60, -29, -42, -48, -71, -6]
	}
}
//说明:中文在GBK编码下的对应字节
练习2

package org.westos_02;

import java.io.FileInputStream;
import java.io.IOException;


public class FileInputStreamDemo2 {
		
	public static void main(String[] args) throws IOException {
		
		//1)创建文件字节输入流对象
		FileInputStream fis = new FileInputStream("fis2.txt") ;

		//2)读数据
		
		//指定的字节数组的长度是1024或者是1024的倍数
		
		//如果使用模板带去去读取数据:一次读取一个字节数组
		
		//定义缓冲区大小:指定长度为1024
		byte[] bys = new byte[1024] ;
		int len = 0 ; //读取字节数的实际长度
		while((len=fis.read(bys))!=-1){
			System.out.println(new String(bys));      //解码:将字节数组转化成对应的字符串--看得懂的形式
			//从指定的索引开始(指定0),读取的是实际长度
			System.out.println(new String(bys,0,len));//推荐使用这种格式读取数据:从0开始,读取实际长度
		}
		
	}
}
字节输出流子类---FileOutputStream--文件输出流
 构造方法--1
public FileInputStream(File file)
 构造方法--2
public FileInputStream(String name)
构造方法--3
public FileInputStream(String name,boolean append)
构造方法说明:如果文件路径不存早或者文件路径存在(但是代表一个文件目录,不是文件),会抛出FileNotFoundException异常,其它情况没有的话,自己会创建文件
关于构造方法3说明: 默认写入数据时,将覆盖文件中的原内容,如果append为true,将在文件的末尾写入数据---追加数据

实例2

package org.westos_01;

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo {
	
	public static void main(String[] args) throws IOException {
		
		//创建一个文件输出流对象
		FileOutputStream fos = new FileOutputStream("fos2.txt",true) ;//方式3
		fos.write(98) ;
		byte[] bys = {97,98,99,100,101} ;
		fos.write(bys) ;
		fos.write(bys, 1, 3) ;
		//关闭资源
		fos.close() ;
	}
}
说明:常用构造方式2、3,注意二者写入数据后看文件的区别
  写入了这个数据, 发现数据和数据之间没有换行?
  需要写入换行符号,各种操作系统对应IO这块换行符号是不一样的
  对于 windows操作系统来说:换行符号--\r\n(两个字节)
  对于Linux操操作系统来说:\n
  对于Mac操作系统来说:\r

实例3

package org.westos_01;

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamDemo2 {
	
	public static void main(String[] args) throws IOException {
		
		//创建一个输出流对象
		FileOutputStream fos = new FileOutputStream("fos3.txt",true) ;
		
		//写数据
		for(int x = 0 ; x <10 ; x ++){
			fos.write(("helo"+x).getBytes());
			/* 等价
			 * String s="hello"+x;
			 * byte [] by=s.getBytes()
			 * fos.write(by);
			 */
			
			//写入一个换行符号
			fos.write("\r\n".getBytes());
		}
		//关闭资源
		fos.close() ;
	}
}
实例4  文件路径名不存在和异常情况

package org.westos_01;

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

/**
 * IO流中加入异常操作
 * @author Apple
 */
public class FileOutputStreamDemo3 {

		public static void main(String[] args) {
			
			//方式3:加入标准格式:try...catch...finally
			//IO流中加入异常操作的标准用法:
			//声明一个变量
			FileOutputStream fos = null ;
			try {
				fos = new FileOutputStream("fos4.txt") ;
				//写数据
				fos.write("hello,Javaweb".getBytes()) ;
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}finally{
				//释放资源的
				//由于是流对象,要对流对象做非空判断
				if(fos !=null){
					try {
						fos.close() ;
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
}
实例5 文件复制

需求:在当前项目下有一个a.txt文件,将a.txt文件的内容赋值到b.txt文件中
方式1 一次读取一个字节

package org.westos_03;

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

/**
 * 分析:
 * 1)数据源:a.txt------->FileInputStream:输入流------->读数据
 * 2)目的地:b.txt------->FileOutputStream:输出流------>写数据
 * @author Apple
 */
public class CopyFileDemo {
	
	public static void main(String[] args) throws IOException {
		
		//封装数据源:
		//创建一个文件字节输入流对象
		FileInputStream fis = new FileInputStream("a.txt") ;
		//封装目的地
		//创建文件输出流对象
		FileOutputStream fos = new FileOutputStream("b.txt") ;
		
		//一次读取一个字节:模板代码
		int by = 0 ;
		while((by=fis.read())!=-1){
			//一次读一个字节,使用输出流给b.txt文件写一个字节
			fos.write(by) ;
		}
		//释放资源
		fos.close() ;
		fis.close() ;
	}
}
说明:文本、音频、视频、图片与上雷同

方式2  一次读取一个字节数组
需求:E盘下有一个高圆圆.jpg文件,将它图片内容复制到当前项目下的mm.jpg

package org.westos_04;

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

public class CopyImageDemo {
	
	public static void main(String[] args) throws IOException {
		
		//封装数据源和目的地
		FileInputStream fis = new FileInputStream("E:\\高圆圆.jpg") ;
		FileOutputStream fos = new FileOutputStream("mm.jpg") ;
		
		//一次读取一个字节数组
		byte[] bys = new byte[1024] ;//字节数组缓冲区
		int len = 0 ;
		while((len=fis.read(bys))!=-1){
			//写数据的
			fos.write(bys, 0, len) ;
		}
		
		//释放资源
		fis.close() ;
		fos.close() ;
	}
}
注意:其它文件雷同,对于向输出流写入数据的第三种重载形式好处:不用输出空白,换行等
说明:一次写入一个字节数组比写入一个字节快的原因?减少了 物理写数据的次数
由于字节数组比一次读取一个字节更快,从而引出类更高效的一种流:字节缓冲流

缓冲流---BufferedInputStream(字节缓冲输入流)

package org.westos_05;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class BufferedInputStreamDemo {
	
	public static void main(String[] args) throws IOException {
		
		//创建一个字节缓冲输入流对象
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt")) ;
		/**
		 * 通过构造方法的参数可以看出
		 * 节点流:FileInputStream---InputStream的子类
		 * 处理流来装饰节点流
		 */
		
		//一次读取一个字节数组或者一次读取一个字节都可以
		byte[] bys = new byte[1024] ;
		int len = 0 ;
		while((len=bis.read(bys))!=-1){
			System.out.println(new String(bys,0,len));
		}
		
		//释放资源
		bis.close() ;
	}
}
缓冲流---BufferedOutputStream(过滤输出流的子类)
说明:此类覆盖了被装饰的输出流(节点流)的写数据行为,利用缓冲区提高写数据的效率;默认情况下,缓冲区满的时候,才把缓冲区的数据写入到输出流
常用构造方式:
      public BufferedOutputStream(OutputStream out):默认缓冲区大小(默认缓冲大小已经足够大了)

package org.westos_05;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputStreamDemo {
	
	public static void main(String[] args) throws IOException {
		
		//创建字节缓冲输出流对象
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream("bos.txt"));//Java的一种设计模式
		
		//写数据
		bos.write(97) ;
		//刷新该缓冲区的流
		bos.flush() ;//迫使将这写字节强制输出流中
		bos.write(98) ;
		
		
		//释放资源
		bos.close() ;
		bos.flush() ;
//		bos.write(99) ;
	}
}
问题1:为什么字节缓冲流不能 直接针对文件进行操作呢?
答:缓冲流只是提供一个缓冲区,针对IO进行实现数据的传输实际是通过底层基本流进行实现的,所以缓冲流不能直接对文件操作--装饰
计算机是如何识别中文的?
  1)每个中文左边对应的字节一定是负数
  2)GBK格式:右边的字节可以是正数,可以是0也可以是负数

package org.westos_05;

import java.util.Arrays;


public class StringDemo {
	
	public static void main(String[] args) {
		
		String s = "我爱你中国" ;
		//当前平台默认的编码集:GBK---一个中文对应两个字节
		//[-50, -46, -80, -82, -60, -29, -42, -48, -71, -6]
		
		//将当前字符串转换成字节数组
		byte[] bys = s.getBytes() ;
		System.out.println(Arrays.toString(bys));
	}
}
对几种字节流 复制文件速率的比较

package org.westos_06;

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

/**
 * 
 * 对于图片文件,音频文件,视频文件,优先采用字节缓冲输入流(高效流)一次读取一个字节数组!
 * 
 * 
 * 字节流复制文件:
 * 基本字节流(FileInputStream)一次读取一个字节:共耗时:253820毫秒
 * 基本字节流一次读取一个字节数组:共耗时:396毫秒
 * 高效字节流(字节缓冲输入流)一次读取一个字节:共耗时:98020毫秒

 * 高效字节流(字节缓冲输入流)一次读取一个字节数组:共耗时:317毫秒
 * 
 * 复制e:\\abc.mp4文件---->当前项目下的Copy.mp4文件
 * @author Apple
 */
public class CopyFileDemo {
	
	public static void main(String[] args) throws IOException {
		long startTime = System.currentTimeMillis() ;
		
		method1("E:\\abc.mp4","Copy.mp4") ;
		method2("E:\\abc.mp4","Copy.mp4") ;
		method3("E:\\abc.mp4","Copy.mp4") ;
		method4("E:\\abc.mp4","Copy.mp4") ;
		long endTime = System.currentTimeMillis() ;
		
		System.out.println("共耗时:"+(endTime-startTime)+"毫秒");
	}
	
	//高效字节流一次读取一个字节数组
	private static void method4(String SrcString, String destString) throws IOException{
		
		//封装数据源和目的地
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SrcString)) ;
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString)) ;
		
		//一次读取一个字节数组
		byte[] bys = new byte[1024] ;
		int len = 0 ;
		while((len=bis.read(bys))!=-1){
			bos.write(bys, 0, len) ;
			bos.flush() ;//刷新缓冲区的流
		}
		
		//释放资源
		bis.close() ;
		bos.close() ;
	}

	//字节缓冲输入流一次读取一个字节
	private static void method3(String SrcString, String destString) throws IOException {
		
		//创建字节缓冲输入对象
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SrcString)) ;
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destString)) ;
		
		//一次读取一个字节
		int by = 0 ;
		while((by=bis.read())!=-1){
			bos.write(by)  ;
			bos.flush() ;
		}
		
		//释放资源
		bis.close() ;
		bos.close() ;
	}

	//基本的字节流一次读取一个字节数组
	private static void method2(String SrcString, String destString) throws IOException {
		//封装数据源
		FileInputStream fis = new FileInputStream(SrcString) ;
		//封装目的地
		FileOutputStream fos = new FileOutputStream(destString) ;
		
		//一次读取一个字节数组
		byte[] bys = new byte[1024] ;
		int len = 0 ;
		while((len=fis.read(bys))!=-1){
			//边读边写
			fos.write(bys, 0, len) ;
		}
		
		//释放资源
		fis.close() ;
		fos.close() ;
		
	}

	//基本字节流一次读取一个字节
	private static void method1(String StrString, String destString) throws IOException {
		
		//封装数据源
		FileInputStream fis = new FileInputStream(StrString) ;
		//封装目的地
		FileOutputStream fos = new FileOutputStream(destString) ;
		
		//一次读取一个字节
		//并且一次写一个字节
		int by = 0 ;
		while((by=fis.read())!=-1){
			//写数据
			fos.write(by) ;
		}
		
		
		//释放资源
		fis.close() ;
		fos.close() ;
		
	}
}
字节流一次读取一个字节,出现了中文乱码
原因:给字节进行强制类型转换,代码的注释中有中文,并且平台默认编码GBK格式:一个中文对应的两个字节
代码

package org.westos_07;

import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamDemo {
	
	public static void main(String[] args) throws IOException {
		
		//创建字节输入流对象
		FileInputStream fis = new FileInputStream("FileInputStreamDemo2.java") ;
		
		//一次读取一个字节
		int by = 0 ;
		while((by=fis.read())!=-1){
			System.out.print((char)by);
		}
		
		//释放资源
		fis.close() ;
	}
}
解决方案: 使用字符流来进行操作;说明:字符流必须指定编码格式
回顾编码和解码
package org.westos_07;

import java.io.IOException;
import java.util.Arrays;

/**
 * 编码:就是能看懂的字符串转换成看不懂的字节数组
 * 		public byte[] getBytes(Charset charset):将字符串转换成字节数组,指定编码格式(字符集)
 * 解码:就是将看不懂的字节数组----->能看懂的:将字节数组--->字符串
 * 		public String(byte[] bytes, Charset charset)通过使用指定的 编码格式 解码指定的 byte 数组,构造一个新的 String
 * 
 * 举例:谍战片
 * 		老地方见...
 * 
 * 编码
 * "老"----->字节数组----->二进制数据---->
 * 解码:二进制数据----->十进制数据----->字节数组----->构造方式:字符串
 * 
 * 		
 * @author Apple
 */
public class StringDemo {

	public static void main(String[] args) throws IOException {
		
		String s = "爱你" ;
		
		//编码

		//如果不写编码格式:默认GBK
		byte[] bys = s.getBytes() ;//[-80, -82, -60, -29]
		System.out.println(Arrays.toString(bys));
		
		//解码
		//public String(byte[] bytes, Charset charset)
		String str = new String(bys) ;//默认GBK
		System.out.println(str);//爱你
	}
}
处理流中的转换流--InputStreamReader
说明:采用了 适配器模式,将字节输入流转化成字符输入流
常用构造方式:
  字符输入流:字节输入流+编码格式(默认GBK)

public InputStreamReader(InputStream in,charset sc) ;

package org.westos_08;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class InputStreamReaderDemo {
	
	public static void main(String[] args) throws IOException {
		
		//创建字符输入流对象
		/*InputStreamReader isr = new InputStreamReader(new FileInputStream(
				"osw.txt"), "GBK");*/
		InputStreamReader isr = new InputStreamReader(new FileInputStream(
				"osw.txt"));//以GBK格式读数据
		
		//读数据:一次读取一个字符
		/*int ch = 0 ;
		while((ch=isr.read())!=-1){
			System.out.print((char)ch);
		}*/
		
		char[] chs = new char[1024] ;
		int len = 0;
		while((len=isr.read(chs))!=-1){
			System.out.println(new String(chs, 0, len));
		}
		
		//释放资源
		isr.close() ;
	}
}
I/O流的分类

总结:

1.Java IO是采用的是装饰模式,即采用处理流来包装节点流的方式,来达到代码通用性。

2.处理流和节点流的区分方法,节点流在新建时需要一个数据源(文件、网络)作为参数,而处理流需要一个节点流作为参数。

3.处理流的作用就是提高代码通用性,编写代码的便捷性,提高性能。

4.节点流都是对应抽象基类的实现类,它们都实现了抽象基类的基础读写方法。其中read()方法如果返回-1,代表已经读到数据源末尾。


未完待续。。。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值