天天看点

Java IO 流体系

文件类File

1、java.io.File类:文件和目录路径名的抽象表示方式与平台无关。

2、File能新建、删除、重命名文件和目录,但是不能访问文件本身的内容;File类关心的是磁盘上文件的存储,而流类关心的是文件的内容。

3、File 类可以作为参数传递流的构造函数。

相对路径:在当前文件(工程)目录下文件的路径。

绝对路径:包括盘符在内的完整文件路径。

4、File类的使用的方法

package com.io;
import java.io.File;
/*文件类的一些常用 方法*/
public class FileMethod {
	public static void main(String[] args) {
		File file=new File("E:\\wjq.txt");//使用绝对路径构造一个File
		String fileName=file.getName();//获取文件名,包括后缀
		String filePath=file.getParent();//获取文件路径,其中file.getAbsolutePath()方法可以获取绝对路径 
		long length=file.length();//得到文件的大小 
		long lastModify=file.lastModified();//文件最后更改时间
		boolean IsExist=file.exists();//文件是否存在
		boolean IsHidden=file.isHidden();//文件是否隐藏
		boolean IsFile=file.isFile();//判断是一个具体的文件不是一个文件夹
		boolean Canread=file.canRead();//文件是否可读
		boolean IsDirectory=file.isDirectory();//判断文件是否是一个文件夹
		File [] files=file.listFiles();//表示列出某个文件夹下面的所有文件
		boolean Success=file.renameTo(new File("E:\\wjq_copy.txt"));//为文件夹更改名字
	}
}
           

当我们在统计一个文件夹大小时需要遍历整个文件夹中的文件做一个统计,才能获取这个文件夹的大小。下面使用递归来统计文件夹大小:

package com.io;
import java.io.File;
import java.text.DecimalFormat;
public class ReadFileSize {
	//使用递归统计一个文件夹下面所有的文件大小
	public static long getFileSize(File file){
		long size=0;
		File [] files=file.listFiles();
		for(int i=0;i<files.length;i++){
			if(files[i].isDirectory()){
				size=size+getFileSize(files[i]);
			}else{
				size=size+files[i].length();
			}
		}
		return size;
	 }
	  //格式化长度表示
	  public static String formatFileSize(File file){
		  String fileSize="";
		  DecimalFormat df=new DecimalFormat("#.00");
		  long size=getFileSize(file);
		  if(size<1024){
			  fileSize=df.format((double)size)+"B";
		  }else if(size<1048576){
			  fileSize=df.format((double)size/1024)+"KB";
		  }else if(size<1073741824){
			  fileSize=df.format((double)size/1048576)+"MB";
		  }else{
			  fileSize=df.format((double)size/1073741824)+"G";
		  }
		  return fileSize;
	  }
	    //测试
		public static void main(String[] args) {
			File file=new File("E:\\tomcat7");
			String size=ReadFileSize.formatFileSize(file);
			System.out.println(size);
			
		}
}
           

Java IO 流原理

1、IO流用来处理设备之间的数据传输。 2、Java程序中,对于数据输入/输出操作以"流(Stream)"的方式进行。 3、输入:input读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存);输出:output将程序(内存中)数据输出到磁盘,等外部存储设备中。 4、原理图

Java IO 流体系

IO 流的分类

1、IO流的抽象基类 InputStream、OutputStream、Reader、Writer。这四个类是IO流的源头,这四个类都不能被实例化,所有的子类都是继承这四个抽象基类。 2、IO流可以根据流向、数据单位、角色进行分类

流向

  • 输入流       InputStream(字节流,只能读取英文)                            Reader(字符流,可以读取所有的字符)
  • 输出流       OutStream(字节流,只能读取英文)                              Writer(字符流,可以读取所有的字符)

数据单位

  • 字节流   读取视屏、音频、图片、二进制文件
  • 字符流   读取文本文件

 角色

  • 节点流      直接作用在文本文件上(FileInputStream、FileOutputStream、FileReader、FileWriter)
  • 处理流      又称节缓存流,可以加快节点流的速度,一般作用在节点流上。

2、IO流体系结构

Java IO 流体系

3、不同的IO流使用方式  a、字节输入/输出流  FileInputStream/FileOutputStream FileInputStream  使用

package com.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/*字节流*/
public class FileIn_OutStreamTest {
   //测试
   public static void main(String[] args) {
	   FileIn_OutStreamTest.TestFileInputStream();
   }
   //从硬盘存在的一个文件中,读取内容 到内存中
 	public static void TestFileInputStream(){
 		FileInputStream fis=null;
 		try{
 			//1、创建一个文件
 			File file=new File("E:\\test.txt");
 			//2、创建一个FileInputStream类的对象 
 			fis=new FileInputStream(file);
 			byte [] b=new byte[5];//一次读取1024个字节,写到这个字节数组里面
 			int len;//每次读入到byte中字节的长度
                        //3、将文件内容读取到内存并写出到控制台
 			while((len=fis.read(b))!=-1){
 				String str=new String(b,0,len);
 				System.out.print(str);
 			}
 		}catch(Exception e){
 			e.printStackTrace();
 		}finally{
 				try {
 					fis.close(); //4、关闭相应的流
 				} catch (IOException e) {
 					e.printStackTrace();
 				}
 			}
 		}
 	}
           

FileOutputStream  使用

package com.io;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/*字节流*/
public class FileIn_OutStreamTest {
   //测试
   public static void main(String[] args) {
	   FileIn_OutStreamTest.TestFileOutputStream();
   }
    //从内存中将流写出到一个文件
 	public static void TestFileOutputStream(){
 		//1、创建一个file对象
 		File file=new File("E:\\test.txt");
 		//2、创建一个FileOutputStream对象 
 		FileOutputStream fos=null;
 		try{
 			//3、写入到目标文件中
 			fos=new FileOutputStream(file);
 			fos.write(new String("I love china").getBytes());
 		}catch(Exception e){
 			e.printStackTrace();
 		}finally{
 			try {
 				fos.close();//4、关闭资源
 			} catch (IOException e){
 				e.printStackTrace();
 			}
 		}
 	}
}
           

由上述代码,可以得出使用字节输入/输出流的一般步骤为: 新建一个目标文件File类----->创建一个FileInputStream或FileOutputStream对象将File类作为形参传入--->调用相应的方法,读取/写入 文件---->最后关闭相应的资源。 b、字符输入/输出流  FileReader/FileWriter 对于非文本文件(视频、图片、音频)只能用字节流。 FileReader

package com.io;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/*字节流*/
public class FileReader_WriterTest {
   //测试
   public static void main(String[] args) {
	   FileReader_WriterTest.TestFile_Reader();
   }
	//字符流:从硬盘存在的一个文件中,读取内容 到内存中
	public static void TestFile_Reader(){
		File file=new File("E:\\test.txt");//1、创建一个file对象
		FileReader fr=null;//2、创建一个FileReader对象
		try{
			fr=new FileReader(file);
			char [] c=new char[20];
			int len;
			while((len=fr.read(c))!=-1){//3、读入到内存的过程 
				String str=new String(c, 0, len);
				System.out.print(str);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				fr.close();//4、关闭相应的流
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
           

FileWriter

package com.io;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/*字节流*/
public class FileReader_WriterTest {
   //测试
   public static void main(String[] args) {
	   FileReader_WriterTest.TestFile_Writer();
   }
	//字符流:从硬盘存在的一个文件中,读取内容 到内存中
	public static void TestFile_Writer(){
		File file=new File("E:\\test.txt");//1、创建一个file对象
		FileWriter wr=null;//2、创建一个FileWriter对象
		try{//3、写入到文件。
			wr=new FileWriter(file);
			wr.write("this is a FileWriter Test!".toCharArray());
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				wr.close();//4、关闭相应的流
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
           

由上述代码,可以得出使用字符输入/输出流的一般步骤为: 新建一个目标文件File类----->创建一个FileReader或FileWriter对象将File类作为形参传入--->调用相应的方法,读取/写入 文件---->最后关闭相应的资源。 c、缓存输入/输出流(BufferInputStream/BufferOutputStream),这是一个处理流,一般都是作用在节点流上,其作用是提高存储效率。下面用缓存流来实现一个文件的拷贝过程:

package com.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*缓存流*/
public class FileReader_WriterTest {
   //测试
   public static void main(String[] args) {
	   FileReader_WriterTest.TestBuffered_input_output_Stream();
   }
 //缓冲流:BufferedInputStream BufferedOutputStream  实现文件的复制 
 	public static void TestBuffered_input_output_Stream() {
 		//1、提供读入、写出文件
 		File file_in=new File("E:\\test.txt");
 		File file_out=new File("E:\\test_copy.txt");
 		//定义两个缓存流
 		BufferedInputStream bis=null;
 		BufferedOutputStream bos=null;
 		try{
 			//2、先创建相应的节点流FileInputStream,FileOutputStream
 			FileInputStream fis=new FileInputStream(file_in);
 			FileOutputStream fos=new FileOutputStream(file_out);
 			//3、将创建的节点流的对象作为形参传递给缓冲流的构造器中
 			bis=new BufferedInputStream(fis);
 			bos=new BufferedOutputStream(fos);
 			byte [] b=new byte[1024];
 			int len;
 			while((len=bis.read(b))!=-1){
 				bos.write(b, 0, len);
 				bos.flush();//刷新清空一下
 			}
 		}catch(Exception e){
 			e.printStackTrace();
 		}finally{
 			try {
 				bos.close();//关闭资源的顺序要注意
 				bis.close();
 			} catch (IOException e) {
 				e.printStackTrace();
 			}
 		}
 	}
}
           

d、转换流InputStreamReader  OutputStreamWriter (处理流)     输入型的字节流 ----------------------->输入型的字符流     输出型的字节流------------------------>输出型的字符流 其具体实现过程,此处就不是叙述。 e、标准的输出/输入流(System.out  ,System.in)   f、打印流(PrintStream,PrintWriter)都是输出流,也属于节点流。 g、数据流(DataInputStream,DataOutputStream),用来处理基本数据类型,String、字节数组的数据 ,数据流属于处理流,需要套接在InputStream,OutputStream。 h、对象流(ObjectInputStream,ObjectOutputStream),它可以把java中的对象写人到数据源中,也能把对象从数据源中还原。

  • 对象序列化机制:允许把内存中的java对象转换成与平台无关的二进制流,从而允许把这种二进制流持久的保存在磁盘上(序列化),通过网络将这种二进制流传输到另一个网络节点,当其它程序获取了这种二进制流,就可以将其恢复成原来的Java对象(反序列化)。
  • 对象类,想要实现序列化的要求:要求此类是可以序列化的,即此类实现了Serializable接口;类的属性同样也实现了Serializable接口;不能序列化static和transient声明的变量。

下面是用对象流来实现对对象的深度拷贝:

package com.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class CopyObject {
	public static void main(String[] args) throws IOException,ClassNotFoundException,CloneNotSupportedException{
		Student stu=new Student(1,"admin","admin");
		//deep copy
		/*将对象写入到内存流中*/
		ByteArrayOutputStream bos=new ByteArrayOutputStream();
		ObjectOutputStream oos=new ObjectOutputStream(bos);
		oos.writeObject(stu);
		/*从内存中将对象通过流的方式读出来*/
		ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
		ObjectInputStream ois=new ObjectInputStream(bis);
		Object deepCopy=ois.readObject();
		System.out.println("deepCopy:"+deepCopy);
	}
}
class Student implements Serializable{
	private static final long serialVersionUID = 1L;
	private Integer id;
	private String userName;
	private String password;
    public Student(Integer id, String userName, String password) {
		super();
		this.id = id;
		this.userName = userName;
		this.password = password;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
      @Override
    public String toString() {
    	// TODO Auto-generated method stub
    	return "Student[id:"+id+", userName:"+userName+", password:"+password+"]";
    }
}
           

i、RanomAccessFile类,支持随机访问的方式 ,程序可以直接跳到文件的任意位置来读写文件

  • 支持只访问文件的部分内容
  • 支持在已经存在的文件后追加内容
  • 构造器:public RandomAccessFile(File file,String mode) 其中mode表示访问模式,必须指定,这里mode有四种方式:r:表示只读方式打开文件;rw:表示写以读写文件;rwd:读写、同步文件内容更新;rws:同步文件内容和源数据更新

下面是使用RandomAccessFile类对文件进行读写:

package com.io;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/*RandomAccessFile:支持随机访问
 * 1、既可以充当一个输入流,也可以充当一个输出流
 * 2、支持从文件的开头读取、写入
 * 3、支持从任意位置的读取、写入 
 * */
public class RandomAccessFile_Test {
	public static void main(String[] args) {
		RandomAccessFile_Test.RandomAccessFile_read_write();
	}
	public static void RandomAccessFile_read_write(){
		RandomAccessFile raf1=null;
		RandomAccessFile raf2=null;
		try{
			raf1=new RandomAccessFile(new File("person.txt"), "r");//读取一个文件
			raf2=new RandomAccessFile(new File("person.txt"), "rw");//写出一个文件
			byte[] b=new byte[20];
			int len;
			while((len=raf1.read(b))!=-1){
				raf2.write(b,0,len);
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			try {
				raf2.close();
				raf1.close();
			} catch (IOException e){
				e.printStackTrace();
			}
		}
	}
	public static void write_position(){//指定位置的写入
		RandomAccessFile raf1=null;
		try{
			raf1=new RandomAccessFile(new File("person.txt"), "rw");
			//指定位置,实现插入的效果(默认是覆盖)
			raf1.seek(3);
			byte [] b=new byte[10];
			int len;
			StringBuffer sb=new StringBuffer();
			while((len=raf1.read(b))!=-1){
				sb.append(new String(b,0,len));
			}
			raf1.seek(3);
			raf1.write("xy".getBytes());
			raf1.write(sb.toString().getBytes());
		}catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				raf1.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
           

上面只是介绍了几种比较常用的流,其它没提及的流大家可以参考JDK API。