【Java知识点详解 9】IO流

一、前言

Java的I/O主要的用途就是文件数据的读写、数据的网络发送与接收等场合。

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。对于文件内容的操作主要分为两大类分别是:字符流和字节流。

二、I/O流的分类

根据处理数据类型的不同分为:字符流和字节流。
根据数据流向不同分为:输入流和输出流。

1、字符流和字节流

字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:

  1. 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
  2. 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
  3. 字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。

结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。

2、输入流和输出流

对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。

三、代码实例

1、字节流读操作

package com.guor.javaSE;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class IOTest {

    public static void main(String[] args) {
        test05();
    }

    private static final String const_filePath = "D:\\guor\\data\\test.txt";
    private static final String const_filePathChinese = "D:\\guor\\data\\中文.txt";
    private File const_file = null;
    private File const_fileChinese = null;

    public IOTest() {
        this.const_file = new File(const_filePath);
        this.const_fileChinese = new File(const_filePathChinese);
    }

    /**
     * 字节流读取文件:单个字符读取
     * @param b_chinese_file
     */
    private static void test01(boolean b_chinese_file) {
        IOTest ioTest = new IOTest();
        FileInputStream fis = null;
        try {
            if(true == b_chinese_file) {
                //测试字节流读取中文乱码问题
                fis = new FileInputStream(ioTest.const_fileChinese);
            }else {
                fis = new FileInputStream(ioTest.const_file);
            }
            int read = 0;
            while ((read = fis.read())!=-1) {
                log((char)read, false);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 字节流读取文件:数组循环读取
     */
    private static byte[] test02() {
        IOTest ioTest = new IOTest();
        FileInputStream fis = null;
        int len = 512;
        byte[] buffer = new byte[len];
        try {
            fis = new FileInputStream(ioTest.const_file);
            int read;
            while ((read = fis.read(buffer,0,len)) != -1) {
                log(buffer + "", true, false);
            }
            for(byte b : buffer) {
                if(true == Character.isLetterOrDigit((char)b) || (char)b == '\n') {
                    log((char)b, false, false);
                }
            }
        } catch (FileNotFoundException e) {
            return new byte[1];
        } catch (IOException e) {
            return new byte[1];
        } finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return buffer;
    }
}

2、字符流读操作

/**
 * 字符流读取中文文档,解决字节流读取中文乱码问题
 */
private static void test03() {
    IOTest ioTest = new IOTest();
    FileReader fr = null;
    try {
        fr = new FileReader(ioTest.const_fileChinese);
        int read = 0;
        while ((read = fr.read()) != -1) {
            log((char)read, false);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(fr != null) {
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3、字节流写操作

/**
 * 字节流写操作
 * @throws IOException
 * @throws FileNotFoundException
 */
private static void test04() {
    String outPath = "D:\\guor\\data\\testNew.txt";
    FileOutputStream fos = null;
    try {
        File file = new File(outPath);
        byte[] buffer = test02();
        fos = new FileOutputStream(file);
        fos.write(buffer);
    } catch (FileNotFoundException e) {
        log("FileNotFoundException: " + e);
    } catch (IOException e) {
        log("IOException: " + e);
    } finally {
        if(fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4、字符流写操作

/**
 * 字符流写操作
 * @throws IOException
 * @throws FileNotFoundException
 */
private static void test05() {
    String outPath = "D:\\guor\\data\\中文New.txt";
    IOTest ioTest = new IOTest();
    FileReader fr = null;
    FileWriter fw = null;
    try {
        fr = new FileReader(ioTest.const_fileChinese);
        StringBuilder sb = new StringBuilder();

        int read = 0;
        while ((read = fr.read()) != -1) {
            log((char)read, false);
            sb.append((char)read);
        }

        File file = new File(outPath);
        fw = new FileWriter(file);
        fw.write(sb.toString());
    } catch (FileNotFoundException e) {
        log("FileNotFoundException: " + e);
    } catch (IOException e) {
        log("IOException: " + e);
    } finally {
        if(fw != null) {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(fr != null) {
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5、log日志打印

/**
 * @param msg 带输出信息
 * @param b_wrap 是否换行
 */
private static void log(Object msg, boolean b_wrap) {
    if(true == b_wrap) {
        System.out.println(msg + "");
    }else {
        System.out.print(msg + "");
    }
}

/**
 * @param msg
 */
private static void log(Object msg) {
    log(msg, true, true);
}

/**
 * @param msg 带输出信息
 * @param b_wrap 是否换行
 * @param out 是否输出
 */
private static void log(Object msg, boolean b_wrap, boolean out) {
    if(true == out) {
        if(true == b_wrap) {
            System.out.println(msg + "");
        }else {
            System.out.print(msg + "");
        }
    }
}

四、多线程方式进行socket文件传送

socket通信时用socket.getOutputStream();获取输入输出流,不是socket通信时用new FileOutPutStream()获取输入输出流。

1、服务端

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(9999);
        while (true){
            Socket socket = server.accept();
            MyDownload download = new MyDownload(socket);
            Thread thread = new Thread(download);
            thread.start();
        }
    }
}

2、服务端Thread

public class MyDownload implements Runnable{
    private Socket socket;

    public MyDownload(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try{
            System.out.println("与客户端连接成功");
            //服务端向客户端发送消息
            OutputStream out = socket.getOutputStream();
            File file = new File("H:\\js\\jquery-2.1.4.min.js");
            //将此文件从硬盘读到内存中
            InputStream in = new FileInputStream(file);
            //定义每次发送的文件大小
            byte[] bytes = new byte[1000];
            int len = -1;
            while ((len = in.read(bytes)) != -1){
                out.write(bytes,0,len);
            }
            in.close();
            out.close();
            socket.close();
        }catch (Exception e){
            System.out.println(e);
        }
    }
}

3、客户端

import java.io.*;
import java.net.Socket;

public class MyClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9999);
        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1000];
        int len = -1;
        OutputStream out = new FileOutputStream("H:\\js\\a.js");
        while ((len = in.read(bytes)) != -1){
            out.write(bytes,0,len);
        }
        System.out.println("下载成功!");
    }
}

4、控制台输出

五、图片与byte数组互转

public class Image2ByteTest {
    public static void main(String[] args) {
        String path = "D:\\guor\\CSDN\\1.jpg";
        byte[] image2byte = image2byte(path);
        System.out.println(image2byte);
        String path2 = "D:\\guor\\CSDN\\2.jpg";
        byte2image(image2byte,path2);
    }

    // 图片到byte数组
    public static byte[] image2byte(String path) {
        byte[] data = null;
        FileImageInputStream input = null;
        try {
            input = new FileImageInputStream(new File(path));
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int numBytesRead = 0;
            while ((numBytesRead = input.read(buf)) != -1) {
                output.write(buf, 0, numBytesRead);
            }
            data = output.toByteArray();
            output.close();
            input.close();
        } catch (FileNotFoundException ex1) {
            ex1.printStackTrace();
        } catch (IOException ex1) {
            ex1.printStackTrace();
        }
        return data;
    }

    // byte数组到图片
    public static void byte2image(byte[] data, String path) {
        if (data.length < 3 || path.equals(""))
            return;
        try {
            FileImageOutputStream imageOutput = new FileImageOutputStream(new File(path));
            imageOutput.write(data, 0, data.length);
            imageOutput.close();
            System.out.println("Make Picture success,Please find image in " + path);
        } catch (Exception ex) {
            System.out.println("Exception: " + ex);
            ex.printStackTrace();
        }
    }

    // byte数组到16进制字符串
    public String byte2string(byte[] data) {
        if (data == null || data.length <= 1)
            return "0x";
        if (data.length > 200000)
            return "0x";
        StringBuffer sb = new StringBuffer();
        int buf[] = new int[data.length];
        // byte数组转化成十进制
        for (int k = 0; k < data.length; k++) {
            buf[k] = data[k] < 0 ? (data[k] + 256) : (data[k]);
        }
        // 十进制转化成十六进制
        for (int k = 0; k < buf.length; k++) {
            if (buf[k] < 16)
                sb.append("0" + Integer.toHexString(buf[k]));
            else
                sb.append(Integer.toHexString(buf[k]));
        }
        return "0x" + sb.toString().toUpperCase();
    }
}

六、read与readFully

某些时候!你可能需要读出特定的长度的字段。比如一些数据的头信息之类的!
这时候如果你用read去读,那么可能还没有读完就个你返回来了。
但是如果去用readFully读,那么就会完整的给你读出来。
但是在更多的时候,如果不是确定要用特定长度的数据,还是用read去读,用readFully有点危险。

往期精彩内容:

Java知识体系总结(2021版)

Java多线程基础知识总结(绝对经典)

【全栈最全Java框架总结】SSH、SSM、Springboot

超详细的springBoot学习笔记

常见数据结构与算法整理总结

Java设计模式:23种设计模式全面解析(超级详细)

Java面试题总结(附答案)

猜你喜欢

转载自blog.csdn.net/guorui_java/article/details/113985421