zoukankan      html  css  js  c++  java
  • Java IO 讲解

    一、java io 概述

    1.1 相关概念

    Java IO:Java IO即Java 输入输出系统。不管我们编写何种应用,都难免和各种输入输出相关的媒介打交道,其实和媒介进行IO的过程是十分复杂的,这要考虑的因素特别多,比如我们要考虑和哪种媒介进行IO(文件、控制台、网络),我们还要考虑具体和它们的通信方式(顺序、随机、二进制、按字符、按字、按行等等)。Java类库的设计者通过设计大量的类来攻克这些难题,这些类就位于java.io包中。

    在JDK1.4之后,为了提高Java IO的效率,Java又提供了一套新的IO,Java New IO简称 Java NIO。它在标准java代码中提供了高速的面向块的IO操作。

    在Java IO中,流是一个核心的概念。流从概念上来说是一个连续的数据流。你既可以从流中读取数据,也可以往流中写数据。流与数据源或者数据流向的媒介相关联。在Java IO中流既可以是字节流(以字节为单位进行读写),也可以是字符流(以字符为单位进行读写)。

    IO相关的媒介

    Java的IO包主要关注的是从原始数据源的读取以及输出原始数据到目标媒介。以下是最典型的数据源和目标媒介:

    1. 文件
    2. 管道
    3. 网络连接
    4. 内存缓存
    5. System.in, System.out, System.error(注:Java标准输入、输出、错误输出)

    下面这幅图就清晰的描述了JavaIO的分类:

    - 字节流 字符流
    输入流 InputStream Reader
    输出流 OutputStream Writer

    我们的程序需要通过InputStream或Reader从数据源读取数据,然后用OutputStream或者Writer将数据写入到目标媒介中。其中,InputStream和Reader与数据源相关联,OutputStream和writer与目标媒介相关联。 以下的图说明了这一点:

    è¿éåå¾çæè¿°

    2.2 IO 类库
    上面我们介绍了Java IO中的四各类:InputStream、OutputStream、Reader、Writer,其实在我们的实际应用中,我们用到的一般是它们的子类,之所以设计这么多子类,目的就是让每一个类都负责不同的功能,以方便我们开发各种应用。各类用途汇总如下:

    1. 文件访问
    2. 网络访问
    3. 内存缓存访问
    4. 线程内部通信(管道)
    5. 缓冲
    6. 过滤
    7. 解析
    8. 读写文本 (Readers / Writers)
    9. 读写基本类型数据 (long, int etc.)
    10. 读写对象

    下面我们就通过几张图来大体了解一下这些类的继承关系及其作用

     java输入/输出流体系中常用的流的分类表 

    Java中IO操作基本流程分4

    1. 创建源 File file = new File("filePath")
    2. 选择处理流  FileInPutStream/FileOutPutStream/FileReader/FileWriter
    3. 开始操作流   输入还是输出流根据第二步操作
    4. 关闭操作到的所有文件IO流。

    IO流的分类:

    1.  按流向分类:输入流、输出流
    2.  按操作对象分类:字节流、字符流
    3.  按功能分类:节点流、处理流(相当于用到了装饰模式)

    IO流的设计模式为装饰设计模式;

    1. 节点流为最“原生态”的流,实现了流的基本功能,需待操作的数据创建对象,如File(文件)、Byte[](字节数组)、Char[](字符数组)等
    2. 处理流包装节点流,增强其功能(如处理编码问题防止乱码、使用缓冲流增强传输效率等),其处理的是节点流,相当于对节点流做了装饰,所以需用节点流对象创建处理流对象

      å¾10-6 èç¹æµå¤çæµç¤ºæå¾.png

    常用IO流:

    一、InputStream:字节输入抽象类(做各个字节输入类的祖先基类)

       节点流: 

    •  FileInputStream:文件字节输入流(操作对象为File文件对象)
    • ByteArrayInputStream:字节数组输入流(操作对象为字节数组)

       处理流: 

    • FilterInputStream:过滤字节输入流(简单的实现了InputSteam类,一般用它的子类BufferedInputSteam、DataInputStream等)
    • BufferedInputStream:字节输入缓冲流
    • DataInputStream:基本数据类型输入处理流
    • ObjectInputStream:引用数据类型输入处理流(对象反序列化)transient 关键字

     二、OutputStream:字节输出抽象类(做各个字节输出类的祖先基类)

      字节流: 

    • FileOutputStream:文件字节输出流(操作对象为File文件对象)
    • ByteArrayOutputStream:字节数组输出流(操作对象为字节数组)

    处理流: 

    • FilterOutputStream:过滤字节输出流(简单的实现了OutputStream类,一般用它的子类BufferedOutputSteam、DataOutputStream等)
    • BufferedOutputStream:字节输出缓冲流
    • DataOutputStream:基本数据类型输出处理流
    • ObjectOutputStream:引用数据类型输出处理流(对象序列化)

    三、Reader:字符输入抽象类(做各个字符输入类的祖先基类)

    节点流: 

    • FileReader:文件字符输入流(操作对象为File文件对象)
    • CharArrayReader:字符数组输入流(操作对象为字符数组)

    处理流: 

    • BufferedReader:字符输入缓冲流
    • InputStreamReader:字符输入格式处理流(可以设置字符编码等)

    四、Writer:字符输出抽象类(做各个字符输出类的最终基类)

    节点流: 

    • FileWriter:文件字符输出流(操作对象为File文件对象)
    • CharArrayWriter:字符数组输出流(操作对象为字符数组)

    处理流: 

    • BufferedWriter:字符输出缓冲流
    • OutputStreamWriter:字符输出格式处理流(可以设置字符编码等)

    这里写图片描述这里写图片描述这里写图片描述这里写图片描述

    三、Java IO的基本用法

    3.1 Java IO :字节流

    通过上面的介绍我们已经知道,字节流对应的类应该是InputStreamOutputStream,而在我们实际开发中,我们应该根据不同的媒介类型选用相应的子类来处理。下面我们就用字节流来操作文件媒介:

    例1,用字节流写文件

    	public static void writeByteToFile() throws IOException
    	{
    		String hello = new String("hello world!");
    		byte[] byteArray = hello.getBytes(); // 上面两行是第三部
    		File file = new File("d:/test.txt"); // 第一步
    		OutputStream os = new FileOutputStream(file); // 第二步
    		os.write(byteArray); // 第三部
    		os.flush(); // 接下来是第四部
    		os.close();
    	}

    例2,用字节流读文件

    	public static void readByteFromFile() throws IOException
    	{
    		File file = new File("d:/test.txt");
    		byte[] byteArray = new byte[(int) file.length()];
    		//因为是用字节流来读媒介,所以对应的是InputStream
    		//又因为媒介对象是文件,所以用到子类是FileInputStream
    		InputStream is = new FileInputStream(file);
    		int size = is.read(byteArray);
    		System.out.println("大小:" + size + ";内容:" + new String(byteArray));
    		is.close();
    	}

    3.2 Java IO :字符流

    同样,字符流对应的类应该是ReaderWriter。下面我们就用字符流来操作文件媒介:

    例3,用字符流读文件

      public static void readCharFromFile() throws IOException{
            File file= new File( "d:/test.txt");
             //因为是用字符流来读媒介,所以对应的是Reader
             //又因为媒介对象是文件,所以用到子类是FileReader
            Reader reader= new FileReader( file);
             char [] byteArray= new char[( int) file.length()];
             int size= reader.read( byteArray);
            System. out.println( "大小:"+size +";内容:" +new String(byteArray));
             reader.close();
      }
    

    例4,用字符流写文件

      public static void readCharFromFile() throws IOException{
            File file= new File( "d:/test.txt");
             //因为是用字符流来读媒介,所以对应的是Reader
             //又因为媒介对象是文件,所以用到子类是FileReader
            Reader reader= new FileReader( file);
             char [] byteArray= new char[( int) file.length()];
             int size= reader.read( byteArray);
            System. out.println( "大小:"+size +";内容:" +new String(byteArray));
             reader.close();
      }
    

    3.3 Java IO :字节流转换为字符流

    字节流可以转换成字符流,java.io包中提供的InputStreamReader类就可以实现,当然从其命名上就可以看出它的作用。其实这涉及到另一个概念,IO流的组合,后面我们详细介绍。下面看一个简单的例子:

    	public static void convertByteToChar() throws IOException
    	{
    		File file = new File("d:/test.txt");
    		//获得一个字节流
    		InputStream is = new FileInputStream(file);
    		//把字节流转换为字符流,其实就是把字符流和字节流组合的结果。
    		Reader reader = new InputStreamReader(is);
    		char[] byteArray = new char[(int) file.length()];
    		int size = reader.read(byteArray);
    		System.out.println("大小:" + size + ";内容:" + new String(byteArray));
    		is.close();
    		reader.close();
    	}

    3.4 Java IO :IO类的组合

           从上面字节流转换成字符流的例子中我们知道了IO流之间可以组合(或称嵌套),其实组合的目的很简单,就是把多种类的特性融合在一起以实现更多的功能。组合使用的方式很简单,通过把一个流放入另一个流的构造器中即可实现,两个流之间可以组合,三个或者更多流之间也可组合到一起。当然,并不是任意流之间都可以组合。关于组合就不过多介绍了,后面的例子中有很多都用到了组合,大家好好体会即可。

    3.5 Java IO:文件媒介操作

    File是Java IO中最常用的读写媒介,那么我们在这里就对文件再做进一步介绍。

    例6 ,File操作

    public class FileDemo
    {
    	public static void main(String[] args)
    	{
    		//检查文件是否存在
    		File file = new File("d:/test.txt");
    		boolean fileExists = file.exists();
    		System.out.println(fileExists);
    		//创建文件目录,若父目录不存在则返回false
    		File file2 = new File("d:/fatherDir/subDir");
    		boolean dirCreated = file2.mkdir();
    		System.out.println(dirCreated);
    		//创建文件目录,若父目录不存则连同父目录一起创建
    		File file3 = new File("d:/fatherDir/subDir2");
    		boolean dirCreated2 = file3.mkdirs();
    		System.out.println(dirCreated2);
    		File file4 = new File("d:/test.txt");
    		//判断长度
    		long length = file4.length();
    		//重命名文件
    		boolean isRenamed = file4.renameTo(new File("d:/test2.txt"));
    		//删除文件
    		boolean isDeleted = file4.delete();
    		File file5 = new File("d:/fatherDir/subDir");
    		//是否是目录
    		boolean isDirectory = file5.isDirectory();
    		//列出文件名
    		String[] fileNames = file5.list();
    		//列出目录
    		File[] files = file4.listFiles();
    	}
    }

    3.5.3 随机读取File文件


           通过上面的例子我们已经知道,我们可以用FileInputStream(文件字符流)或FileReader(文件字节流)来读文件,这两个类可以让我们分别以字符和字节的方式来读取文件内容,但是它们都有一个不足之处,就是只能从文件头开始读,然后读到文件结束。 但是有时候我们只希望读取文件的一部分,或者是说随机的读取文件,那么我们就可以利用RandomAccessFile。RandomAccessFile提供了seek()方法,用来定位将要读写文件的指针位置,我们也可以通过调用getFilePointer()方法来获取当前指针的位置,具体看下面的例子:

    例7,随机读取文件

    	public static void randomAccessFileRead() throws IOException
    	{
    		// 创建一个RandomAccessFile对象
    		RandomAccessFile file = new RandomAccessFile("d:/test.txt", "rw");
    		// 通过seek方法来移动读写位置的指针
    		file.seek(10);
    		// 获取当前指针
    		long pointerBegin = file.getFilePointer();
    		// 从当前指针开始读
    		byte[] contents = new byte[1024];
    		file.read(contents);
    		long pointerEnd = file.getFilePointer();
    		System.out.println("pointerBegin:" + pointerBegin + "
    " + "pointerEnd:" + pointerEnd + "
    " + new String(contents));
    		file.close();
    	}

    例8,随机写入文件

    	public static void randomAccessFileWrite() throws IOException
    	{
    		// 创建一个RandomAccessFile对象
    		RandomAccessFile file = new RandomAccessFile("d:/test.txt", "rw");// 可以指定module
    		// 通过seek方法来移动读写位置的指针
    		file.seek(10);
    		// 获取当前指针
    		long pointerBegin = file.getFilePointer();
    		// 从当前指针位置开始写
    		file.write("HELLO WORD".getBytes());
    		long pointerEnd = file.getFilePointer();
    		System.out.println("pointerBegin:" + pointerBegin + "
    " + "pointerEnd:" + pointerEnd + "
    ");
    		file.close();
    	}

    3.6 Java IO:管道媒介


    管道主要用来实现同一个虚拟机中的两个线程进行交流。因此,一个管道既可以作为数据源媒介也可作为目标媒介。需要注意的是java中的管道和Unix/Linux中的管道含义并不一样,在Unix/Linux中管道可以作为两个位于不同空间进程通信的媒介,而在java中,管道只能为同一个JVM进程中的不同线程进行通信。和管道相关的IO类为:PipedInputStream和PipedOutputStream,下面我们来看一个例子:

    例9,读写管道

    public class PipeExample
    {
    	public static void main(String[] args) throws IOException
    	{
    		final PipedOutputStream output = new PipedOutputStream();
    		final PipedInputStream input = new PipedInputStream(output);
    		Thread thread1 = new Thread(new Runnable()
    		{
    			@Override
    			public void run()
    			{
    				try
    				{
    					output.write("Hello world, pipe!".getBytes());
    				} catch (IOException e)
    				{
    				}
    			}
    		});
    		Thread thread2 = new Thread(new Runnable()
    		{
    			@Override
    			public void run()
    			{
    				try
    				{
    					int data = input.read();
    					while (data != -1)
    					{
    						System.out.print((char) data);
    						 = input.read();
    					}
    				} catch (IOException e)
    				{
    				} finally
    				{
    					try
    					{
    						input.close();
    					} catch (IOException e)
    					{
    						e.printStackTrace();
    					}
    				}
    			}
    		});
    		thread1.start();
    		thread2.start();
    	}
    }
    
    

    3.7 Java IO:网络媒介

    关于Java IO面向网络媒介的操作即Java 网络编程,其核心是Socket,同磁盘操作一样,java网络编程对应着两套API,即Java IO和Java NIO,关于这部分我会准备专门的文章进行介绍。

    3.8 Java IO:BufferedInputStream和BufferedOutputStream

    BufferedInputStream顾名思义,就是在对流进行写入时提供一个buffer来提高IO效率。在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候。使用BufferedInputStream十分简单,只要把普通的输入流和BufferedInputStream组合到一起即可。我们把上面的例2改造成用BufferedInputStream进行读文件,请看下面例子:

    例10 ,用缓冲流读文件

    	public static void readByBufferedInputStream() throws IOException {
    		File file = new File( "d:/test.txt");
    		byte[] byteArray = new byte[( int) file.length()];
    		//可以在构造参数中传入buffer大小
    		InputStream is = new BufferedInputStream( new FileInputStream(file),2*1024);
    		int size = is.read( byteArray);
    		System. out.println( "大小:" + size + ";内容:" + new String(byteArray));
    		is.close();
    	}

     关于如何设置buffer的大小,我们应根据我们的硬件状况来确定。对于磁盘IO来说,如果硬盘每次读取4KB大小的文件块,那么我们最好设置成这个大小的整数倍。因为磁盘对于顺序读的效率是特别高的,所以如果buffer再设置的大写可能会带来更好的效率,比如设置成4*4KB或8*4KB。

    还需要注意一点的就是磁盘本身就会有缓存,在这种情况下,BufferedInputStream会一次读取磁盘缓存大小的数据,而不是分多次的去读。所以要想得到一个最优的buffer值,我们必须得知道磁盘每次读的块大小和其缓存大小,然后根据多次试验的结果来得到最佳的buffer大小。BufferedOutputStream的情况和BufferedInputStream一致,在这里就不多做描述了。


    3.9 Java IO:BufferedReader和BufferedWriter

    BufferedReader、BufferedWriter 的作用基本和BufferedInputStream、BufferedOutputStream一致,具体用法和原理都差不多 ,只不过一个是面向字符流一个是面向字节流。同样,我们将改造字符流中的例4,给其加上buffer功能,看例子:
     

     public static void readByBufferedReader() throws IOException {
            File file = new File( "d:/test.txt");
             // 在字符流基础上用buffer流包装,也可以指定buffer的大小
            Reader reader = new BufferedReader( new FileReader(file),2*1024);
             char[] byteArray = new char[( int) file.length()];
             int size = reader.read( byteArray);
            System. out.println( "大小:" + size + ";内容:" + new String(byteArray));
             reader.close();
      }
    

    4.0 Java IO  :ObjectInputStream  网络传输序列化对象

    package Myudp;
    
    import java.io.BufferedInputStream;
    import java.io.ByteArrayInputStream;
    import java.io.ObjectInputStream;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.util.Date;
    
    
    
    
    package Myudp;
    //javabean 封装数据
    class Employee implements java.io.Serializable{
    	private transient String name; //该数据不需要序列化
    	private double salary;
    	public Employee() {
    	}
    	public Employee(String name, double salary) {
    		this.name = name;
    		this.salary = salary;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public double getSalary() {
    		return salary;
    	}
    	public void setSalary(double salary) {
    		this.salary = salary;
    	}
    }
    
    
    /**
     * 引用类型: 接收端
     * Address already in use: Cannot bind  同一个协议下端口不允许冲突
     * 1、使用DatagramSocket  指定端口 创建接收端
     * 2、准备容器 封装成DatagramPacket 包裹
     * 3、阻塞式接收包裹receive​(DatagramPacket p)
     * 4、分析数据    将字节数组还原为对应的类型
     *    byte[]  getData​()
     *                getLength​()
     * 5、释放资源
     * @author ljj
     *
     */
    public class UdpObjServer {
    
    	public static void main(String[] args) throws Exception {
    		System.out.println("接收方启动中.....");
    		// 1、使用DatagramSocket  指定端口 创建接收端
    		DatagramSocket server =new DatagramSocket(6666);
    		// 2、准备容器 封装成DatagramPacket 包裹
    		byte[] container =new byte[1024*60];
    		DatagramPacket packet = new DatagramPacket(container,0,container.length);
    		// 3、阻塞式接收包裹receive​(DatagramPacket p)
    		server.receive(packet); //阻塞式
    		// 4、分析数据    将字节数组还原为对应的类型
    		//    byte[]  getData​()
    		//                getLength​()
    		 byte[]  datas =packet.getData();
    		 int len = packet.getLength();		 
    		//读取 -->反序列化
    		ObjectInputStream ois =new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
    		//顺序与写出一致
    		String msg = ois.readUTF(); 
    		int age = ois.readInt();
    		boolean flag = ois.readBoolean();
    		char ch = ois.readChar();
    		System.out.println(flag);
    		//对象的数据还原  
    		Object str = ois.readObject();
    		Object date = ois.readObject();
    		Object employee = ois.readObject();
    		
    		if(str instanceof String) {
    			String strObj = (String) str;
    			System.out.println(strObj);
    		}
    		if(date instanceof Date) {
    			Date dateObj = (Date) date;
    			System.out.println(dateObj);
    		}
    		if(employee instanceof Employee) {
    			Employee empObj = (Employee) employee;
    			System.out.println(empObj.getName()+"-->"+empObj.getSalary());
    		}
    		// 5、释放资源
    		 server.close();
    	}
    }
    =================
    package Myudp;
    
    import java.io.BufferedOutputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectOutputStream;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetSocketAddress;
    import java.util.Date;
    
    /**
     *  引用类型: 发送端
     * 1、使用DatagramSocket  指定端口 创建发送端
     * 2、将基本类型  转成字节数组
     * 3、 封装成DatagramPacket 包裹,需要指定目的地
     * 4、发送包裹send​(DatagramPacket p) * 
     * 5、释放资源
     * @author ljj
     *
     */
    public class UdpObjClient {
    
    	public static void main(String[] args) throws Exception {
    		System.out.println("发送方启动中.....");
    		 // 1、使用DatagramSocket  指定端口 创建发送端
    		DatagramSocket client =new DatagramSocket(8888);
    		 //2、准备数据 一定转成字节数组
    		//写出
    		ByteArrayOutputStream baos =new ByteArrayOutputStream();
    		ObjectOutputStream oos =new ObjectOutputStream(new BufferedOutputStream(baos));
    		//操作数据类型 +数据
    		oos.writeUTF("编码辛酸泪");
    		oos.writeInt(18);
    		oos.writeBoolean(false);
    		oos.writeChar('a');
    		//对象
    		oos.writeObject("谁解其中味");
    		oos.writeObject(new Date());
    		Employee emp =new Employee("马云",400);
    		oos.writeObject(emp);
    		oos.flush();
    		byte[] datas =baos.toByteArray();	
    		
    		 //3、 封装成DatagramPacket 包裹,需要指定目的地
    		DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
    				new InetSocketAddress("localhost",6666));
    		//4、发送包裹send​(DatagramPacket p) * 
    		client.send(packet);
    		// 5、释放资源
    		client.close();
    	}
    
    }
    
    

    接下来看一个综合的例子

    package com.demo.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;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.RandomAccessFile;
    import java.io.SequenceInputStream;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Vector;
    
    /**
     * 面向对象思想封装 分割
     *
     * @author liujinjie
     */
    public class SplitFile
    {
    	//源头
    	private File src;
    	//目的地(文件夹)
    	private String destDir;
    	//所有分割后的文件存储路径
    	private List<String> destPaths;
    	//每块大小
    	private int blockSize;
    	//块数: 多少块
    	private int size;
    
    	public SplitFile(String srcPath, String destDir)
    	{
    		this(srcPath, destDir, 1024);
    	}
    
    	public SplitFile(String srcPath, String destDir, int blockSize)
    	{
    		this.src = new File(srcPath);
    		this.destDir = destDir;
    		this.blockSize = blockSize;
    		this.destPaths = new ArrayList<String>();
    
    		//初始化
    		init();
    	}
    
    	//初始化
    	private void init()
    	{
    		//总长度
    		long len = this.src.length();
    		//块数: 多少块
    		this.size = (int) Math.ceil(len * 1.0 / blockSize);
    		//路径
    		for (int i = 0; i < size; i++)
    		{
    			this.destPaths.add(this.destDir + "/" + i + "-" + this.src.getName());
    		}
    	}
    
    	/**
    	 * 分割
    	 * 1、计算每一块的起始位置及大小
    	 * 2、分割
    	 *
    	 * @throws IOException
    	 */
    	public void split() throws IOException
    	{
    		//总长度
    		long len = src.length();
    		//起始位置和实际大小
    		int beginPos = 0;
    		int actualSize = (int) (blockSize > len ? len : blockSize);
    		for (int i = 0; i < size; i++)
    		{
    			beginPos = i * blockSize;
    			if (i == size - 1)
    			{ //最后一块
    				actualSize = (int) len;
    			} else
    			{
    				actualSize = blockSize;
    				len -= actualSize; //剩余量
    			}
    			splitDetail(i, beginPos, actualSize);
    		}
    	}
    
    	/**
    	 * 指定第i块的起始位置 和实际长度
    	 *
    	 * @param i
    	 * @param beginPos
    	 * @param actualSize
    	 * @throws IOException
    	 */
    	private void splitDetail(int i, int beginPos, int actualSize) throws IOException
    	{
    		RandomAccessFile raf = new RandomAccessFile(this.src, "r");
    		RandomAccessFile raf2 = new RandomAccessFile(this.destPaths.get(i), "rw");
    		//随机读取 
    		raf.seek(beginPos);
    		//读取
    		//3、操作 (分段读取)
    		byte[] flush = new byte[1024]; //缓冲容器
    		int len = -1; //接收长度
    		while ((len = raf.read(flush)) != -1)
    		{
    			if (actualSize > len)
    			{ //获取本次读取的所有内容
    				raf2.write(flush, 0, len);
    				actualSize -= len;
    			} else
    			{
    				raf2.write(flush, 0, actualSize);
    				break;
    			}
    		}
    		raf2.close();
    		raf.close();
    	}
    
    	/**
    	 * 文件的合并
    	 *
    	 * @throws IOException
    	 */
    	public void merge(String destPath) throws IOException
    	{
    		//输出流
    		OutputStream os = new BufferedOutputStream(new FileOutputStream(destPath, true));
    		Vector<InputStream> vi = new Vector<InputStream>();
    		SequenceInputStream sis = null;
    		//输入流
    		for (int i = 0; i < destPaths.size(); i++)
    		{
    			vi.add(new BufferedInputStream(new FileInputStream(destPaths.get(i))));
    		}
    		sis = new SequenceInputStream(vi.elements());
    		//拷贝
    		//3、操作 (分段读取)
    		byte[] flush = new byte[1024]; //缓冲容器
    		int len = -1; //接收长度
    		while ((len = sis.read(flush)) != -1)
    		{
    			os.write(flush, 0, len); //分段写出
    		}
    		os.flush();
    		sis.close();
    		os.close();
    	}
    
    	public static void main(String[] args) throws IOException
    	{
    		SplitFile sf = new SplitFile("src/com/liujinjie/io/SplitFile.java", "dest");
    		sf.split();
    		sf.merge("aaa.java");
    	}
    }
    

    最终:CommonsIO    

    JDK中提供的文件操作相关的类,但是功能都非常基础,进行复杂操作时需要做大量编程工作。实际开发中,往往需要你自己动手编写相关的代码,尤其在遍历目录文件时,经常用到递归,非常繁琐。 Apache-commons工具包中提供了IOUtils/FileUtils,可以让我们非常方便的对文件和目录进行操作。  Apache IOUtils和FileUtils类库为我们提供了更加简单、功能更加强大的文件操作和IO流操作功能。非常值得大家学习和使用

    package com.hit.demo;
    
    import org.apache.commons.io.FileUtils;
    import org.apache.commons.io.IOUtils;
    import org.apache.commons.io.LineIterator;
    import org.apache.commons.io.filefilter.*;
    
    import java.io.*;
    import java.net.URL;
    import java.util.*;
    
    public class HelloWorld
    {
        public static void main(String[] args) throws IOException
        {
            // 文件大小
            long len = FileUtils.sizeOf(new File("具体文件"));
            // 文件夹大小
            len = FileUtils.sizeOf(new File("文件目录"));
            Collection<File> cf = FileUtils.listFiles(new File("文件夹路径"), EmptyFileFilter.NOT_EMPTY, null);
            // 遍历文件夹 设置文件过滤  设置文件夹过滤
            for (File file : cf)
            {
                System.out.println(file.getAbsolutePath());
            }
            System.out.println("========");
            Collection<File> cf1 = FileUtils.listFiles(new File("文件夹路径"), EmptyFileFilter.NOT_EMPTY, DirectoryFileFilter.INSTANCE);
            // 遍历文件夹 设置文件过滤 文件内容不为空  查询子孙集
            for (File file : cf1)
            {
                System.out.println(file.getAbsolutePath());
            }
            System.out.println("========");
            Collection<File> cf2 = FileUtils.listFiles(new File("文件夹路径"), new SuffixFileFilter("java"), DirectoryFileFilter.INSTANCE);
            // 遍历文件夹 设置文件过滤 文件结尾是Java 查询子孙集
            for (File file : cf2)
            {
                System.out.println(file.getAbsolutePath());
            }
            System.out.println("========");
            Collection<File> cf3 = FileUtils.listFiles(new File("文件夹路径"), FileFilterUtils.or(new SuffixFileFilter("java"), new SuffixFileFilter("class"))
                    , DirectoryFileFilter.INSTANCE);
            // 遍历文件夹 设置文件过滤 文件结尾是Java/class  查询子孙集
            for (File file : cf3)
            {
                System.out.println(file.getAbsolutePath());
            }
    
            System.out.println("========");
            Collection<File> cf4 = FileUtils.listFiles(new File("文件夹路径"), FileFilterUtils.and(new SuffixFileFilter("java"), EmptyFileFilter.NOT_EMPTY)
                    , DirectoryFileFilter.INSTANCE);
            // 遍历文件夹 设置文件过滤 文件结尾是Java且非空  查询子孙集
            for (File file : cf4)
            {
                System.out.println(file.getAbsolutePath());
            }
            // 读取文件内容到字符串
            String msg = FileUtils.readFileToString(new File("文件路径"), "UTF-8");
            byte[] msg1 = FileUtils.readFileToByteArray(new File("文件路径"));
    
            //  逐行读取
            List<String> msgs = FileUtils.readLines(new File("文件路径"), "UTF-8");
            for (String item : msgs)
            {
                System.out.println(item);
            }
    
            LineIterator it = FileUtils.lineIterator(new File("文件路径"), "UTF-8");
            while (it.hasNext())
            {
                System.out.println(it.next());
            }
    
            // 写出数据
            FileUtils.write(new File("文件路径"), "随便写点字符串", "UTF-8", true);
            FileUtils.writeStringToFile(new File("文件路径"), "随便写点字符串", "UTF-8", true);
            FileUtils.writeByteArrayToFile(new File("文件路径"), "随便写点字符串".getBytes("UTF-8"), true);
    
            // 写出列表
            List<String> datas = new ArrayList<>();
            datas.add("123");
            datas.add("1234342");
            datas.add("12441241");
            FileUtils.writeLines(new File("文件名"), datas, "
    ", true);
    
            // 复制文件
            FileUtils.copyFile(new File("src"), new File("dest"));
            // 复制文件到目录
            FileUtils.copyFileToDirectory(new File("src"), new File("文件夹"));
            // 目录到目录 lib2 下放文件夹lib
            FileUtils.copyDirectoryToDirectory(new File("lib"), new File("lib2"));
            // lib 下面到文件拷贝到 lib2 里面 有点类似 move
            FileUtils.copyDirectory(new File("lib"), new File("lib2"));
            // 拷贝URL
            FileUtils.copyURLToFile(new URL("www.baidu.com"), new File("baidu.html"));
            String datas1 = IOUtils.toString(new URL("http://www.baidu.com"), "UTF_8");
            System.out.println(datas1);
        }
    }

    参考

    IO流分类及常用讲解

    Java IO

    Java IO体系

    CommonsIO安装

    讲解视频

     
    关注公众号 海量干货等你
  • 相关阅读:
    在JavaScript的数组中进行数组元素查找和替换(JS的indexOf等)
    GNU/Linux Distribution Timeline v12.10
    makefile编写差异
    java快速排序1000万无序数组JVM-Xmx=256M 耗时2s
    Quartz cron表达式
    hdu
    action中实现对批量文件上传的封装
    MyGui笔记(1)建立第一个工程
    Jenkins参数化构建
    最完美的xslt数值函数与字符串函数(转)
  • 原文地址:https://www.cnblogs.com/sowhat1412/p/12734143.html
Copyright © 2011-2022 走看看