黑马程序员之IO流

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

学习概述:装饰设计模式的含义,IO流体系中的第二种字节流的用法,File类的用法,Properties的用法

学习目标:熟悉装饰设计模式的流程和原理,熟练掌握字节流体系对象的用法,对一些常用的对象必须烂熟于心,对于文件操作类File也要熟练掌握,Properties也是一个极其重要的类,了解RandomAccessFile以及管道流。

1.装饰设计模式

     (1)装饰设计模式定义:当想要对已有对象的功能进行增强时,可将已有对象传入,基于已有对象的功能,并提供增强功能,那么自定义的该类称为装饰类。装饰类通常会通过构造方法接收被装饰对象。关键词:构造函数传递!

     (2) 装饰和继承的区别:装饰由继承结构编程组合结构,装饰模式比比继承模式更加灵活,降低了类之间的耦合度,避免了继承体系的臃肿,装饰类因为增强已有对象,只是单纯的增强功能,所以装饰类和被装饰类通常在一个体系中。

     练习:模拟一个带行号的缓冲区对象

     

mport java.io.FileReader;  
import java.io.IOException;  
import java.io.Reader;  
  
//简化版  
class MyLineNumberReader extends MyBufferedReader  
{  
 private int lineNumber;  
 MyLineNumberReader(FileReader r)  
 {  
  super(r);  
 }  
   
 public String myReaderLine()throws IOException  
 {  
  lineNumber++;  
  return super.myReadLine();  
 }  
 public void setLineNumber(int lineNumber)  
 {  
  this.lineNumber = lineNumber;  
 }  
 public int getLineNumber()  
 {  
  return lineNumber;  
 }  
   
}  
  
/* 
class MyLineNumberReader 
{ 
 private Reader r; 
 private int lineNumber; 
 MyLineNumberReader(Reader r) 
 { 
  this.r = r; 
 } 
  
 public String myReadLine()throws IOException 
 { 
  lineNumber++; 
  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 setLineNumber(int LineNumber) 
 { 
  this.lineNumber= lineNumber; 
 } 
 public int getLineNumber() 
 { 
  return lineNumber; 
 } 
  
 public void myClose()throws IOException 
 { 
  r.close(); 
 } 
} 
*/  
class MyLineNumberReaderDemo {  
 public static void main(String[] args)throws IOException  
 {  
  FileReader fr = new FileReader("PersonDemo.java");  
    
  MyLineNumberReader mnr = new MyLineNumberReader(fr);  
    
  String line = null;  
  while((line = mnr.myReadLine())!=null)  
  {  
   System.out.println(mnr.getLineNumber()+":"+line);  
  }  
  mnr.myClose();  
 }  
}  

2.字节流的File操作

字节流的一系列操作方式与字符流几乎完全一样,区别是操作的数据单元不一样,要注意字节流的read和write方法参数中的数组是字节数组,不再是字节数组。字节输出流的write()方法中不能包括String字符串,这是与字符输出流很大的不同,在以后的使用过程中要注意。如果没有指定字节流缓冲区,字节流是不需要刷新的,但是close()一样必须要写,因为你必须要关闭资源。为什么会这样?因为本质上字符流是基于字节流的。

思考:对于FileOuputputStream的available方法,该方法返回一个整型值,得到文件的字节长度,是不是我们可以定义byte数组的时候指定byte数组长度,无需利用缓冲循环读取呢?理论上是可行的的,但是要考虑硬件条件,如果你要操作一个很大的文件,比如说电影文件,该文件大小超过了内存大小,那么采用这种方法将会出现内存溢出的情况,所以这种方法要慎用,还是以循环为主!

   练习 拷贝文件:

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

public class CopyPic {
	public static void mian(String[] args){
		FileOutputStream fos =null;
		FileInputStream  fis=null;
		try{
			fos = new FileOutputStream("E:\\copy.jpg");
			fis = new FileInputStream("D:\\test.jpg");
			byte[] temp = new byte[1024];
			int len=0;
			while((len=fis.read(temp))>0){
				fos.write(temp, 0, len);
			}
		}
		catch(IOException e){
			e.printStackTrace();
		}
		finally{
			try{
				if(fis!=null){
					fis.close();
				}
				if(fos!=null){
					fos.close();
				}
			}
			catch(IOException e){
				e.printStackTrace();
			}
		}
	}

}

思考1:用字符流是否可以实现?

     回答:字符流可以完成该操作,但是操作完之后就会发现,复制过去的图片往往打不开。因为字符流的本质也是依赖于字节流。 

思考2:为什么read()方法的返回值一定是int?

     Java 下 IO 中 FileReder 和 FileInputStream 分别是以字符和字节的形式来完成数据的读取的,然而返回值确是 int 类型的数据,这样做的核心目的只是要取到到一个 int 类型下的 -1 来表示数据流的末尾。为什么要这样做?又是怎么实现的呢?

 

首先看 FileReder :

FileReader fr = new FileReader("src.txt");
        int ch = fr.read();

如上面的代码,FileReader 的 read 方法返回值是一个 int 类型的变量来接收的,然而 read 方法在实际中却是以字符形式来进行数据的读取的。通过上面的基本数据类型的取值范围我们能发现 char 类型数据的取值范围为 0 ~ 65535 ,也就是说 char 类型数据是取不到负值的;int 类型数据的取值范围为 -2147483648 ~ 2147483647 ,可以取到负值;同时 int 的取值范围又包含 char 的取值范围,这就为使用 int 作为返回值类型提供了可能,因为流需要一个特殊的值来表示流末尾,这个值不应该在 char 的取值范围内,如果使用 char 取值范围内的值作为流末尾标志,那么这个值同样有可能出现在数据流中间作为数据来传输,流在读到这个值的时候会认为已经到达流末尾,后面未读取的数据将被截断。所以 Java 中选择了使用 -1 来作为流末尾,这个值不在 char 的取值范围内,所以不存在数据截断,然而 -1 又在 int 的取值范围内,同时 int 的取值范围包含 char 的取值范围,所以 FileReader 下 read 方法返回的 char 类型数据直接转为了 int 类型。

再看 FileInputStream :

FileInputStream fis = new FileInputStream("src.txt");
        int b = fis.read();

同理 FileInputStream 也需要一个自己取不到的值来作为流末尾的标志,Java 同样使用 -1 来作为字节流的流末尾,从上面基本数据类型的取值范围我们可以看到 byte 的取值范围为 -128 ~ 127 ,这就意味走着 byte 可以取到 -1 ,如果把 -1 直接当作 int 作为流末尾,那么就无法区分这个读到的结果是流末尾还是流中的数据了,那么 Java 是如何实现取值 -1 的呢?在 Java 内部,Java 通过高位补 0 来实现数据从 byte 到 int 的转换,举个例子:

-1 在 byte 类型和 int 类型中都可以取到,-1 在 byte 类型下的二进制存储形式为 11111111 ,然而使用 read 方法的时候,Java 内部将 byte 的高位补 0 将 byte 转为 int 类型,所以 byte 类型的 -1 在 int 类型下的二进制存储形式为 00000000 00000000 00000000 11111111,对应的 int 值为 255,通过高位补 0 ,所有 byte 类型的负数都转为了正数。然而在使用这些读到的 byte 数据时,只要将这些数据从 int 强转回 byte 即可得到原有的数据。所以就可以使用 -1 来作为流末尾的标志,因为 Java 内部将 byte 的负数通过高位补 0 将其转换为了负数。

3. 字节流的缓冲区

   原理和使用方式和字符流缓冲区几乎完全相同

4.File

    File类是Java IO体系中一个重要的对象主要有以下几个作用

     <1>用来将文件或者是文件夹封装成一个对象

     <2>很方便的对文件夹进行操作,File类的出现弥补了流的不足,流不能操作文件,流只能操作数据

     <3>File对象可以作为参数传递给流对象

     <4> 了解File类的常用方法

     练习1:使用递归方式得到目录下的所有内容

      

package com.lee;

import java.io.File;
/**
 * 
 * @李亮亮
 *递归方式得到文件夹所有内容
 */

public class FileDemo {
	private static void showDir(File file){
		//首先打印出文件夹内容
		System.out.println(file);
		File[] files = file.listFiles();
		for(int i=0;i<files.length;i++){
			if(files[i].isDirectory()){
			    //递归
				showDir(files[i]);
			}
			System.out.println(files[i]);
		}
		
	}

	
	public static void main(String[] args) {
		File file = new File("test");
		showDir(file);

	}

}

练习2:删除一个带内容的文件夹(从里往外删除,如果文件夹为空,那么直接将文件夹删除,注意文件夹也要删除)

private static void deleteDir(File file){
		File[] files = file.listFiles();
		for(int i=0;i<files.length;i++){
			if(files[i].isDirectory()){
				//递归调用
				deleteDir(files[i]);
			}
			System.out.println(files[i]);
			files[i].delete();
		}
		//删除文件夹
	   System.out.println(file);
	   file.delete();
	}

  注意:File类的删除方法是不走回收站的,删除时一定要注意,以免防止误删。    

5.Properties

   Properties是Hashtable的子类,所以存放方式具有map结构,它里面存储的键值都是String类型,是集合与IO流结合的的集合容器,在配置文件中被广泛使用,所以要熟练掌握这个类的用法。

下面这个练习基本上已经把Properties的各种方法都涉及到了,

 练习:编写一个程序,在硬盘上生成配置文件记录程序的运行次数

package com.lee;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;


public class PropertiesDemo {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		Properties props = new Properties();
		File file = new File("E:\\run2.txt");
		if(!file.exists()){
			file.createNewFile();
		}
		FileInputStream ips = new FileInputStream(file);
		props.load(ips);
		int count =0;
		String value= props.getProperty("time");
		if(value!=null){
			count = Integer.parseInt(value);
		}
		count++;
		props.setProperty("time", count+"");
		FileOutputStream fops = new FileOutputStream(file);
		props.store(fops, "");
		fops.close();
		ips.close();
		
		

	}

}


学习总结:

      1.熟练掌握了有关字节流的各种流对象,包括FileInputStream,FileOutputStream,以及字节流对象的缓冲流对象BufferredInputStream和BufferredOuputStream,

      2.虽然字符流和字节流的使用步骤差别不大,但是也要注意这两种流的区别,它们在什么环境中合适使用,特别要注意字符输出流FileWriter和字节输出流FileOutputStream的一点区别,就是FileWriter需要刷新,但是FileOutputStream不需要。

     3.除了IO体系的两大分支:字节流和字符流之外,对于Properties和RandAccessFile也有所了解


            

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值