JAVA IO流 基础

IO流的分类

从流向分类:
从文件, 键盘, 网络, 扫描仪等等 输入java程序 ==> 输入流
从java程序 写入==> 文件,网络等, 输出流

每次处理单位来分:
字节流, 字符流

数据操作方法来分:
节点流,处理流


IO 的核心类

字节流 (byte类型数组)
字节流的核心类InputStream和OutputStream,
是所有字节流的父类 , 两个都是抽象类, 也就是说不能直接构建对象



InputStream     常用的子类FileInputStream
OutputStream 常用的子类如FileOutputStream


InputStream 核心方法
读入byte类型数据, 放入到byte[] b数组里面,从off位置开始放入len长度
int read(byte[] b, int off, int len)

OutputStream 核心方法
写入byte类型数据. 从byte[] b的off位置开始写入len长度的数据
void write(byte[] b, int off, int len)

字节流的例子 , 如下面代码读取data.txt文件, 生成FileInputStream字节流对象file, 调用read方法来读取,
file.read(buffer,0,buffer.length);   以byte为单位,读取的数据存放到字节数组buffer中, 从0位置开始存放, 最多读取buffer.length字节, 
read调用完成后再来把buffer数组输出, 程序运行的结果是,   data.txt文本文件中的字符被按ASCII编码的方式输出, 其余的buffer默认填充是0.

偏移量的作用 file.read(buffer,5,buffer.length - 5); 从第六个位置开始放数据 .

比如, buffer数组的前面5个byte保留的话,那么read方法读取数据的长度应相应减少,否则保留5个byte, 却依然读取buffer.length长度的数据的话就发生了数组越界

		FileInputStream file = null;
		try {
			file = new FileInputStream("D:/java-workspace/data.txt");
			byte buffer [] = new byte[100];
			file.read(buffer,0,buffer.length);
			for(int i=0;i<buffer.length;i++) {
				System.out.println(buffer[i]);
			}
			
		} catch (Exception e) {
			System.out.println(e);
		}



字节流 read方法读取到的都是原始字节, 存放在byte数组里面, 那么怎么还原成字符呢?
String s = new String(buffer);
System.out.println(s);

结果发现buffer里空余的位置默认填充的0也被转换成了相应的字符, 空字符, 于是把空字符输出会看到乱码

调用s.trim()去掉首位空格和空字符.


字节流写数据
FileOutputStream fo = null;
fo = new FileOutputStream("D:/java-workspace/to.txt");
然后调用write方法, 第一个参数是需要写入的byte数组, 比如就是上面例子中的buffer,
第二个参数偏移量, 第三个参数字节流数组的长度, 比如上面例子中的buffer虽然数组是100byte , 但是真正的数据长度只有一部分, 那么要获取数据的真正的长度其实就是前面调用read方法时的返回值
int read_len = file.read(buffer,0,buffer.length);
fo.write(buffer,0,read_len);

字符流读入到buffer得到数据长度 , 然后把相同的buffer数组以字节流的形式调用FileOutputStream对象的write方法写入到新的文件,于是这样就写入完成了.


read 读取数据返回值为 -1 表示 读到文件末尾了没有数据了

于是读文件循环就应该判断返回值来看是否读完文件

IO流 用完还需要进行关闭操作, 调用close方法
一般IO操作都是放在try语句里面, 但close方法, 放在try里面是不合适的, 因为可能出现异常而导致close语句不能执行, 放在catch里面当然也是不合适的
所以close应该放在finally语句块里面,   而close方法本身也会产生异常, 于是close方法应该放在finally里面并且try,catch才算完整

	public static void main(String args[]) {
		//System.out.println("hello world");
		FileInputStream file = null;
		FileOutputStream fo = null;
		try {
			file = new FileInputStream("D:/java-workspace/data.txt");
			fo = new FileOutputStream("D:/java-workspace/to.txt");
			byte buffer [] = new byte[100];
			while(true) {
				int read_len = file.read(buffer,0,buffer.length);
				if(read_len == -1) {
					break;
				}


				fo.write(buffer,0,read_len);
			}
			String s = new String(buffer);
			s.trim();
			System.out.println(s);
			
		} catch (Exception e) {
			System.out.println(e);
		}finally {
			try {
				file.close();
				fo.close();
			}catch (Exception e){
				System.out.println(e);
			}


		}
	}

结果发现上面的有个地方需要特别注意 , 代码测试文件读取写入没有问题, 内容是一致的, buffer最后一次写入后, 用这个的buffer生成string输出, 
想象中, 这个string应该是文本文件的结尾, 但实际这个输出的结果并不是文件的结尾, 比如我在文件结尾加上了end here, 实际的输出如下, 可见在end here的后面还有一部分内容.
 2.0. For details and restrictions, see the Content
end herecontent is licensed under Apache

为什么会这样呢?    因为buffer每次读取后, 并没有被重置清零 . 于是最后一次比如读取了50个字节 , 那buffer后面的部分 其实还有倒数第二次读取到的数据.
但是如果每次都重新生成buffer  似乎不是很效率, 或者是每次我们手工给这个buffer byte数组用循环来重置清零.
但实际程序并不会出错就在于读取的长度和写入的长度一致 , 也就是说我们必须要很关注这个长度, 否则可能会读取到不需要的数据自己还不知道, 一定要做到读取, 写入的数据一致才不会出错
就像这个例子里read_len
int read_len = file.read(buffer,0,buffer.length);
if(read_len == -1) {
break;
}

fo.write(buffer,0,read_len);


字符流 (char 类型数组)

上面就是字节流的读取,写入,主要是read, write来操作byte数组, 那么字节可读性是不方便的, 于是java里还有字符流的类, 
操作的数据单位是字符, 也就是char类型 可读性就好很多了, 对应字节流的InputStream , OutputStream 字符流对应的是Reader, Writer 
同样的, Reader Writer 两个类都是抽象类, 不能直接实例化, 常用的类是FileReader , FileWriter



int read(char [] , int off, int len);
int write(char [], int off, int len);


	public static void main(String args[]) {
		//System.out.println("hello world");
		FileReader file = null;
		FileWriter fo = null;
		try {
			file = new FileReader("D:/java-workspace/data.txt");
			fo = new FileWriter("D:/java-workspace/to.txt");
			char buffer [] = new char[100];
			while(true) {
				int read_len = file.read(buffer,0,buffer.length);
				if(read_len == -1) {
					break;
				}


				fo.write(buffer,0,read_len);
			}
			String s = new String(buffer);
			s.trim();
			System.out.println(s);
			
		} catch (Exception e) {
			System.out.println(e);
		}finally {
			try {
				file.close();
				fo.close();
			}catch (Exception e){
				System.out.println(e);
			}


		}
		


	}

这个例子和上面的例子除了把类名改一下, buffer的数组类型改一下, 其他是一摸一样
可见字符流的操作和字节流的操作没什么区别, 只是处理的数据单位不同, 一个是byte, 一个是char
这个例子中当中如果把buffer数组用循环一个char, 一个char的输出的话,   输出的结果是字符本身类似abc,  而byte 输出的是ASC编码


节点流和处理流


前面讲到的字符流的类和字节流的类, 都是直接去操作文件,   只是读写的单位有所不同,  比如FileReader 处理字符流比FileInputStream 可以直接读取到字符, 但是比如我们希望每次读取一行的话, 那我们就需要在读数据的时候去对"换行符"进行处理,但这样的操作可能很频繁,
每次读一个文件都需要自己去写代码对换行符进行处理来读取一行数据...
于是java提供了 另外一种流,    这种流不直接处理文件, 而是装饰者的角色, 也就是处理流, 它的工作就是在已经存在的流的基础上实现一些功能
比如一次读一行, readLine方法

它的实现方法是 , 这个装饰流对象里面   包含了一个节点流对象的成员变量 , 这个节点流的类只需要实现特定的接口即可.

于是, 装饰流就可以让节点流对象去做些基础工作, 然后在的基础工作的结果上再去实现一些经常需要的功能, 避免代码重复.

而且节点流 可以是多种多样的,   只需要实现特定的接口即可, 比如只要能读取到字符, 无论是从文件中, 还是从其他输入设备,   

而在处理流里面, 会把这个多种多样的节点流对象转型为接口类, 调用接口类里指定的方法去完成基础工作, 然后处理流再进一步加工实现附加功能

总之, IO 处理流类 指的就是不直接操作文件的流, 它是套在节点流上的实现功能的修整流, 所以它的构造函数里传参一定是一个其他的完成基础工作的流对象.



	public static void main(String args[]){
		FileReader fileReader = null;
		BufferedReader bufferedReader = null;
		try{
			fileReader = new FileReader("d:/work/src/users.txt");
			//以上是把fileReader对象传入bufferedReader引用中
			bufferedReader = new BufferedReader(fileReader);
			String line = null;//定义一个空值的line引用(对象)
			while(true){
				line = bufferedReader.readLine();//这是一个while循环
				if(line == null){
					break;
				}
				System.out.println(line);
			}
		}
		catch(Exception e){
			System.out.println(e);
		}
		finally{
			try{
				bufferedReader.close();
				fileReader.close();
			}
			catch(Exception e){
			System.out.println(e);
			}
		}
	}



这里的bufferedReader是一个装饰者的角色,并不是真正去读取文件,而是装饰fileReader去读取文件,同样的也可装饰键盘等读取其他数据。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值