Java学习之IO流


大纲

一、File文件类

import org.junit.Test;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/24 21:05
 * @Description: File
 */
public class FileTest {
    /**
     * 1. 如何创建File类的实例
     * File(String filePath)
     * File(String parentPath,String childPath)
     * File(File parentPath,String childPath)
     * <p>
     * 2. 相对路径与绝对路径
     * <p>
     * 3. 路径分隔符
     * windows: \\
     * unix: /
     * 通用: File.separator ,根据系统,动态提供分隔符
     * 4. 用.表示当前目录,..表示上级目录
     */
    @Test
    public void test() throws IOException {
        //构造器1
        File file = new File("hello.txt");  //在当前目录下
        File file1 = new File("D:\\workspace\\java\\hello1.txt");

        System.out.println(file);
        System.out.println(file1);

        //构造器2
        File file2 = new File("D:\\workspace", "Java");
        System.out.println(file2);

        //构造器3
        File file3 = new File(file2, "hi.txt");
        System.out.println(file3);

        File f = new File("..");
        System.out.println(f.getPath());
        System.out.println(f.getAbsolutePath());
        System.out.println(f.getCanonicalPath()); // 它和绝对路径类似,但是返回的是规范路径。

        System.out.println(File.separator); // 根据当前平台打印"\"或"/"
    }

    /**
     * File 常用方法(获取):
     *      public String getAbsolutePath():获取绝对路径
     *      public String getPath(): 获取路径
     *      public String getName(): 获取名称
     *      public String getParent(): 获取上层文件目录
     *      public long length(): 获取文件长度(字节数),不能是目录
     *      public long lastModified(): 获取最后一次的改动时间,毫秒值
     * 下面两个 适用于文件目录:
     *      public String[] list(): 获取指定目录下的所有文件或文件目录的名称数组
     *      public File[] listFiles(): 获取指定目录下的所有文件或文件目录的File数组
     */
    @Test
    public void test2() {
        File file1 = new File("hello.txt");
        System.out.println(file1.getAbsoluteFile());
        System.out.println(file1.getPath());
        System.out.println(file1.getName());
        System.out.println(file1.getParent());
        System.out.println(file1.length());
        System.out.println(new Date(file1.lastModified()));
        System.out.println("文件目录操作如下:");
        File file2= new File("E:\\Workspaces\\IdeaWorkspace");
        String[] list = file2.list();  //要求目录必须存在
        for (String s:list){
            System.out.println(s);
        }
        System.out.println();

        File[] files = file2.listFiles(new FilenameFilter() { // 仅列出.txt文件
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".txt");
            }
        });
        for (File f:files){
            System.out.println(f);
        }
    }

    /**
     * public boolean renameTo(File dest): 把文件重命名为指定的文件路径
     *  比如: file1.renameTo(file2)
     *      要求 file1必须存在,file2必须不存在
     */
    @Test
    public void test3(){
        File file1 = new File("hello.txt");
        File file2 = new File("hi.txt");

        boolean b = file1.renameTo(file2);
        System.out.println(b);
    }

    /**
     * 判断功能方法:
     *      public boolean isDirectory(): 判断是否是目录
     *      public boolean isFile(): 判断是否是文件
     *      public boolean exists(): 判断是否存在
     *      public boolean canRead(): 判断是否可读
     *      public boolean canWrite(): 判断是否可写
     *      public boolean isHidden(): 判断是否隐藏
     */
    @Test
    public void test4(){
        File file = new File("hello.txt");
        System.out.println(file.isDirectory()); //false
        System.out.println(file.isFile());  //true
        System.out.println(file.exists());  //true
        System.out.println(file.canRead());  //true
        System.out.println(file.canWrite());  //true
        System.out.println(file.isHidden());  //false
    }

    /**
     * 创建功能:
     *      public boolean createNewFile():创建文件,若存在,则不创建,返回false
     *      public boolean mkdir(): 创建目录,如果此目录存在或此目录上级目录不存在,都不创建
     *      public boolean mkdirs(): 若上层目录不存在,则一起创建
     * 删除功能:
     *      public boolean delete(): 删除文件或文件夹
     *          注意: java中删除不走回收站
     */
    @Test
    public void test5() throws IOException {
        File file = new File("hi.txt");
        if(!file.exists()){
            file.createNewFile();
            System.out.println("创建成功");
        }else{
            file.delete();
            System.out.println("删除成功");
        }
    }

    @Test
    public void test6(){
        File file1 = new File("d:\\io\\io1\\io3");
        if (file1.mkdir()){
            System.out.println("创建成功1");
        }
        File file2 = new File("d:\\io\\io1\\io3");
        if (file2.mkdirs()){
            System.out.println("创建成功2");
        }
    }

    /**
     * 程序需要读写一些临时文件,
     * File对象提供了createTempFile()来创建一个临时文件,
     * 以及deleteOnExit()在JVM退出时自动删除该文件。
     */
    @Test
    public void test7() throws IOException {
        File f = File.createTempFile("tmp-", ".txt"); // 提供临时文件的前缀和后缀
        f.deleteOnExit(); // JVM退出时自动删除
        System.out.println(f.isFile());
        System.out.println(f.getAbsolutePath()); //C:\Users\mei_ming\AppData\Local\Temp\tmp-5026117585641750301.txt
    }

    /**
     * Path
     *  位于java.nio.file包。Path对象和File对象类似,操作更加简单
     */
    @Test
    public void test8(){
        Path p1 = Paths.get(".", "project", "study"); // 构造一个Path对象
        System.out.println(p1);
        Path p2 = p1.toAbsolutePath(); // 转换为绝对路径
        System.out.println(p2);
        Path p3 = p2.normalize(); // 转换为规范路径
        System.out.println(p3);
        File f = p3.toFile(); // 转换为File对象
        System.out.println(f);
        for (Path p : Paths.get("..").toAbsolutePath()) { // 可以直接遍历Path
            System.out.println("  " + p);
        }
    }

    //删除指定的目录
    public void deleteDirectory(File file){
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for(File f : files){
                deleteDirectory(f);
            }
        }
        file.delete();
    }

}


提示:以下是本篇文章正文内容,下面案例可供参考

二、IO流原理及流的分类

jaava IO流原理

  1. I/O是Input/OutPut的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读写文件,网络通讯等
  2. Java程序中,对于数据的输入/输出操作以“流(Stream)” 的方式进行
  3. java.io 包下提供了各种“流”类和接口,用以获取不同类型的数据,并通过方法输入或输出数据
  4. 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
  5. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

流的分类

  • 按操作数据单位不同分为:字节流(8bit)二进制文件,字符流(按字符)文本文件
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流/包装流
(抽象基类)字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
  1. java的IO流共涉及40多个类,实际上非常规则,都是从上面4个抽象基类派生的
  2. 由这四个类派生出来的子类名称都是以其父类名做为子类名后缀

具体实现类
分类

常用IO类

FileReader / FileWriter (节点流)

import org.junit.Test;
import java.io.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 13:55
 * @Description: FileReader / FileWriter 的使用
 */
public class FileReaderWriterTest {
    public static void main(String[] args) {
        File file = new File("hello.txt"); //相较于当前工程下
        System.out.println(file.getAbsolutePath());
    }

    /**
     * 读取hello.txt的内容,并输出到控制台
     * 说明:
     * 1. read(): 返回读入的一个字符,如果达到文件末尾,返回-1
     * 2. 异常的处理:为了保证流资源一定可以执行关闭操作,需要实用try-catch-finally
     * 3. 读入的文件一定要存在,否则就会报FileNotFoundException。
     * 4. 中文字符无乱码
     */
    @Test
    public void testFileReader() {
        FileReader fr = null;
        try {
            File file = new File("hello.txt"); //相较于当前Module下的
            fr = new FileReader(file);
            //read(): 返回读入的一个字符,如果达到文件末尾,返回-1
            int data;
            while ((data = fr.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 使用read(char[] cbuf) 优化
     */
    @Test
    public void testFileReader2() {
        FileReader fr = null;
        try {
            File file = new File("hello.txt"); //相较于当前Module下的
            fr = new FileReader(file);
            //read(char[] cbuf): 返回每次读入cbuf数组中的字符的个数,如果达到文件末尾,返回-1
            char[] cbuf = new char[5];
            int len;
            while ((len = fr.read(cbuf)) != -1) {
                //方式1:
                //System.out.print(new String(cbuf,0,len));
                //方式2:
                for (int i = 0; i < len; i++) {
                    System.out.print(cbuf[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 从内存写出数据到磁盘的文件里
     * 说明:
     * 1. 如果使用new FileWriter(file)/FileWriter(file,false):
     * 如果 文件不存在,则创建,如果存在则覆盖源文件
     * 2. 如果使用new FileWriter(file,true):
     * 如果 文件不存在,则创建,如果存在则追加
     * 
     * FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件
     */
    @Test
    public void testFileWriter() {
        FileWriter fw = null;
        try {
            File file = new File("hello1.txt"); //相较于当前Module下的
//        FileWriter fw = new FileWriter(file);
            fw = new FileWriter(file, true); //追加

            fw.write("You have a dream! 中国\n");
            fw.write("I have a dream!\n");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 文本文件复制
     */
    @Test
    public void testFileCopy() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");

            //不能拿字符流处理图片,视频等文件
//            File srcFile = new File("101.jpg");
//            File destFile = new File("102.jpg");

            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);

            char[] cbuf = new char[5];
            int len;  //记录每次读取到cbuf数组中的字符的个数
            while ((len = fr.read(cbuf)) != -1) {
                //每次写入len个字符
                fw.write(cbuf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

fileWriter.close 和fileWriter.flush的区别:
​ 实际上,当我们写好new FileWriter ,进行下一步的操作,将数据写入文本的时候,这时的数据并没有写入文本,而是存在了计算机中的流中。这也是JAVA能够在Windows 系统中调用文本流的作用。而如果在这里我们使用fileWriter.flush,是可以将存储在计算机流中的数据放入fileWriter对象的,但是如果我们之后再想加入数据的时候,也就是说我们将写入数据这句话放在fileWriter.flush之后的话,之后输入的数据就不会放入到note.txt中去。
​ 再说一说fileWriter.close, 我们可以去查询close的定义,很明确的写了 先刷新一次,然后关闭数据流。也就是说close是包含了两步,flush 和close 这两步。flush 刷新后,流可以继续使用,close刷新后,会将流关闭。

FileInputStream / FileOutputStream (节点流)

import org.junit.Test;
import java.io.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 15:21
 * @Description: 测试FileInputStream / FileOutputStream
 *
 * 结论:
 *  1. 对于文本文件(.txt,.java,.c),使用字符流处理
 *  2. 对于非文本文件(.jpg,.mp3,.doc,,,),使用字节流处理
 *
 *  文本文件若只想复制,用字节流处理也行
 *  非文本文件若只想复制,只能用字节流处理
 */
public class FileInputOutputStreamTest {

    //使用字节流FileInputStream处理文本文件,可能出现乱码
    @Test
    public void testFileInputStream(){
        FileInputStream fis = null;
        try {
            File file = new File("hello.txt");
            fis = new FileInputStream(file);

            int data;
            while ((data = fis.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
	
	//写入
    @Test
    public void testFileOutputStream(){
        FileOutputStream fos = null;
        try {
            File file = new File("hello.txt");
            fos = new FileOutputStream(file);

            String str="我爱中国";
            fos.write(str.getBytes());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 图片文件的复制
     */
    @Test
    public void testFileCopy() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            File srcFile = new File("101.jpg");
            File destFile = new File("102.jpg");

            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            byte[] buf = new byte[5];
            int len;
            while ((len = fis.read(buf)) != -1) {
                //每次写入len个字符
                fos.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    @Test
    public void test1(){
        long start = System.currentTimeMillis();
        String srcPath = "101.jpg";
        String destPath = "103.jpg";
        fileCopy(srcPath,destPath);
        long end = System.currentTimeMillis();
		System.out.println(end-start);
    }
    public void fileCopy(String srcPath,String destPath) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);

            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            byte[] buf = new byte[5];
            int len;
            while ((len = fis.read(buf)) != -1) {
                //每次写入len个字符
                fos.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

节点流与处理流

  1. 节点流: 可以从一个特定的数据源读写数据,如FileReader,FileWriter
  2. 处理流:是连结 在已存在的流之上,为程序提供更为强大的读写功能,如BufferedReader、BufferedWriter

节点流与处理流
节点流和处理流的区别和联系

  1. 节点流是底层流(低级流),直接和数据源相连接。
  2. 处理流(包装流)对 节点流 进行了包装,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
  3. 处理流 对 节点流 进行了包装,使用了修饰器的设计模式,不会直接与数据源相连接。

处理流特点

  1. 性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
  2. 操作的便捷:处理流提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。

修饰器模式

Reader_ :

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 20:33
 * @Description: 模拟包装流的修饰器模式
 */
public abstract class Reader_ { //抽象类
    public void readFile(){}
    public void readString(){}
}

FileReader_:

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 20:35
 * @Description: FileReader_
 */
public class FileReader_ extends Reader_{  //模拟节点类
    public void readFile(){
        System.out.println("节点-对文件进行读取");
    }
}

StringReader_:

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 20:36
 * @Description: StringReader_
 */
public class StringReader_ extends Reader_ {  //模拟节点流
    public void readString() {
        System.out.println("节点-读取字符串...");
    }
}

BufferedReader_:

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 20:37
 * @Description: BufferedReader_ 模拟处理流
 */
public class BufferedReader_ extends Reader_ {  // 模拟处理流
    private Reader_ reader_;//属性是Reader_类型

    public BufferedReader_(Reader_ reader_) {
        this.reader_ = reader_;
    }

    public void readFile(){//封装一层
        reader_.readFile();
    }

    //让方法更加灵活,比如多次读取文件,或者加缓冲byte[]...
    public void readFiles(int num) {
        for (int i = 0; i < num; i++) {
            reader_.readFile();
        }
    }

    //又如扩展readString方法,例如批量处理字符串数据...
    public void readStrings(int num) {
        for (int i = 0; i < num; i++) {
            reader_.readString();
        }
    }
}

测试类:

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 20:38
 * @Description: TODO
 */
public class Test_ {
    public static void main(String[] args) {
        BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());
        bufferedReader_.readFiles(3);  //经过包装流的处理
        bufferedReader_.readFile();

        BufferedReader_ bufferedReader02_ = new BufferedReader_(new StringReader_());
        bufferedReader02_.readStrings(2);  //经过包装流的处理
    }
}

BufferedInputStream / BufferedOutputStream (缓冲流)

import org.junit.Test;
import java.io.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 20:50
 * @Description: 处理流之 缓冲流
 *
 * 1. 缓冲流
 * BufferedInputStream
 * BufferedOutputStream
 * BufferedReader
 * BufferedWriter
 *
 * 2. 作用:提高流的读取写入速度
 *      原因:内部提供了一个缓冲区
 */
public class BufferedTest {

    /**
     * 实现非文本文件的复制
     */
    @Test
    public void BufferedStreamTest(){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1. 造文件
            File srcFile = new File("101.jpg");
            File destFile = new File("104.jpg");
            //2. 造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3. 复制
            byte[] bytes = new byte[10];
            int len;
            while((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4. 资源关闭
            if(bos!= null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bis!= null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            //处理流关外层的会把内层的节点流也关闭,所以不用关
//          fos.close();
//          fis.close();
        }
    }

    @Test
    public void testCopyFileWithBuffered(){
        long start = System.currentTimeMillis();
        String srcPath = "101.jpg";
        String destPath = "103.jpg";
        fileCopyWithBuffered(srcPath,destPath);
        long end = System.currentTimeMillis();
        System.out.println(end-start);  //54 - 5

    }
    public void fileCopyWithBuffered(String srcPath,String destPath) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1. 造文件
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);
            //2. 造流
            //2.1 造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //2.2 造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3. 复制
            byte[] bytes = new byte[5];
            int len;
            while((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4. 资源关闭
            if(bos!= null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bis!= null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            //处理流关外层的会把内层的节点流也关闭,所以不用关
//          fos.close();
//          fis.close();
        }
    }
}

BufferedReader / BufferedWriter (缓冲流)

import org.junit.Test;
import java.io.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 21:24
 * @Description: BufferedReader BufferedWriter
 */
public class BufferedReaderWriterTest {
    /**
     * 文本文件复制
     */
    @Test
    public void testFileCopy() {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");

            br = new BufferedReader(new FileReader(srcFile));
            bw = new BufferedWriter(new FileWriter(destFile));

            //方式1: 使用char[]
//            char[] cbuf = new char[5];
//            int len;
//            while ((len = br.read(cbuf)) != -1) {
//                //每次写入len个字符
//                bw.write(cbuf, 0, len);
//            }

            //方式2: 使用String
            String data;
            while((data=br.readLine())!=null){
                //方法1:
//                bw.write(data+"\n");  //data中不包含换行符
                //方法2:
                bw.write(data);
                bw.newLine();
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

练习:

  1. 图片的加密,解密
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 21:41
 * @Description: 图片的加密与解密
 */
public class PicTest {

    // 图片加密
    @Test
    public void test1() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            File srcFile = new File("101.jpg");
            File destFile = new File("102secret.jpg");

            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            byte[] buf = new byte[5];
            int len;
            while ((len = fis.read(buf)) != -1) {
                //字节数组进行修改
                //错误的
//                for(byte b : buf){
//                    b= (byte) (b^5);
//                }
                //正确的
                for (int i = 0; i < len; i++) {
                    buf[i] = (byte) (buf[i]^5);
                }
                fos.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // 图片解密
    @Test
    public void test2() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            File srcFile = new File("102secret.jpg");
            File destFile = new File("102.jpg");

            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            byte[] buf = new byte[5];
            int len;
            while ((len = fis.read(buf)) != -1) {
                //字节数组进行修改
                //错误的
//                for(byte b : buf){
//                    b= (byte) (b^5);
//                }
                //正确的
                for (int i = 0; i < len; i++) {
                    buf[i] = (byte) (buf[i]^5);
                }
                fos.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

  1. 词频统计:
import org.junit.Test;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/25 21:52
 * @Description: 词频统计
 */
public class WordCount {
    @Test
    public void testWordCount() {
        FileReader fr = null;
        BufferedWriter bw = null;

        try {
            //1. 创建Map集合
            HashMap<Character, Integer> map = new HashMap<>();

            //2. 遍历每一个字符,每一个字符出现的次数放在map中
            fr = new FileReader("hello.txt");
            int c = 0;
            while ((c = fr.read()) != -1) {
                // int 还原 char
                char ch = (char) c;
                //判断char是否在map中第一次出现
                if (map.get(ch) == null) {
                    map.put(ch, 1);
                } else {
                    map.put(ch, map.get(ch) + 1);
                }
            }

            //3. 把map中的数据存在 count.txt
            //3.1 创建Writer
            bw = new BufferedWriter(new FileWriter("count.txt"));

            //3.2 遍历map,再写入数据
            Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();
            for (Map.Entry<Character, Integer> entry : entrySet) {
                switch (entry.getKey()) {
                    case ' ':
                        bw.write("空格=" + entry.getValue());
                        break;
                    case '\t': // \t 表示 tab
                        bw.write("tab=" + entry.getValue());
                        break;
                    case '\r':
                        bw.write("回车=" + entry.getValue());
                        break;
                    case '\n':
                        bw.write("换行=" + entry.getValue());
                        break;
                    default:
                        bw.write(entry.getKey() + "=" + entry.getValue());
                        break;
                }
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

InputStreamReader / OutputStreamWriter (转换流)

转化关系

import org.junit.Test;
import java.io.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/27 20:45
 * @Description: 转换流的使用
 *
 * 1. 转化流:属于字符流
 *  InputStreamReader : 将一个字节的输入流转换为字符的输入流
 *  OutputStreamWriter : 将一个字符的输出流转换为字节的输出流
 * 2. 作用:提供字节流与字符流的转换
 *
 * 3. 解码: 字节-->字符  InputStreamReader
 *    编码: 字符-->字节  OutputStreamWriter
 *
 * 4. 字符集
 *  ASCII: 美国标准信息交换码(用一个字节的7位可以表示)
 *  ISO8859-1: 拉丁码表,欧洲码表(用一个字节的8位表示)
 *  GB2312: 中国的中文编码表,最多两个字节编码所有字符
 *  GBK: 中国的中文编码表的升级,融合了更多的中文文字符号。最多两个字节编码
 *  Unicode: 国际标准码,融合了目前所有的字符,为每个字符分配唯一的字符码
 *  UTF-8: 变长的编码方式,可用1-4个字节表示一个字符  汉字占3个字节
 *
 *  补充: 字符编码
 *  Unicode符号范围(16进制)  |  UTF-8编码方式(2进制)
 *  0000 0000-0000 007F    | 0xxx xxxx (兼容原来的ASCII)
 *  0000 0080-0000 07FF    | 110x xxxx  10xx xxxx
 *  0000 0800-0000 FFFF    | 1110 xxxx  10xx xxxx  10xx xxxx
 *  0001 0000-0010 FFFF    | 1111 0xxx  10xx xxxx  10xx xxxx  10xx xxxx
 *
 *  符号 : ‘尚’
 *  Unicode编码值 23578
 *  十六进制 5C1A
 *  二进制  0101 1100 0001 1010
 *
 *  填充x
 *  1110 xxxx  10xx xxxx  10xx xxxx
 *  1110 0101  1011 0000  1001 1010
 *
 *  UTF-8 : 1110 0101  1011 0000  1001 1010
 *              e5        b0         9a
 *              -27       -80       -102
 *
 *
 */
public class InputStreamReaderTest {

    //不使用转换流 - InputStreamReader
    @Test
    public void test1(){
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("count.txt");
            byte[] bytes = new byte[5];
            int len;
            while((len=fis.read(bytes)) != -1){
                System.out.println(new String(bytes,0,len));//出现乱码
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //使用转换流 - InputStreamReader
    @Test
    public void test2(){
        FileInputStream fis = null;
        InputStreamReader isr = null;
        try {
            fis = new FileInputStream("count.txt");
            //第二个参数指明字符集,若为空,使用系统默认的字符集
//            isr = new InputStreamReader(fis);
            //指明字符集,具体使用哪个字符集,取决于文件count.txt保存时使用的字符集
            isr = new InputStreamReader(fis,"UTF-8");
            char[] cbuf = new char[5];
            int len;
            while((len=isr.read(cbuf)) != -1){
                System.out.println(new String(cbuf,0,len));//出现乱码
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(isr!=null){
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //使用转换流 - InputStreamReader 与 OutputStreamWriter
    @Test
    public void test3(){
        InputStreamReader isr = null;
        OutputStreamWriter osw = null;
        try {
            File file1 = new File("count.txt");
            File file2 = new File("count_gbk.txt");

            FileInputStream fis = new FileInputStream(file1);
            FileOutputStream fos = new FileOutputStream(file2);

            isr = new InputStreamReader(fis, "utf-8");
            osw = new OutputStreamWriter(fos, "gbk");

            char[] cbuf = new char[5];
            int len;
            while ((len=isr.read(cbuf))!=-1){
                osw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isr!=null){
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (osw!=null){
                try {
                    osw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

ObjectInputStream / ObjectOutputStream (对象流)

  • 序列化和反序列化
    1. 序列化就是保存数据时,保存数据的值和数据类型
    2. 反序列化就是在恢复数据时,恢复数据的值和数据类型
    3. 需要让某个支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable (这个是一个标记接口,没有方法)、Externalizable(该接口有方法需要实现)
import org.junit.Test;
import java.io.*;

/**
* @Author: mei_ming
* @DateTime: 2022/9/27 22:48
* @Description: 对象流
* 注意事项和细节说明:
*      1. 读写顺序要求一致
*      2. 要求序列化或反序列化对象,需要实现 Serializable
*      3. 序列化的类中,建议添加SerialVersionUID, 为了提高版本的兼容性
*      4. 序列化对象时,默认将里面的属性都进行序列化,但除了static或transient修饰的成员
*      5. 序列化对象时,要求里面的属性的类型也需要实现序列化接口
*      6. 序列化具备可继承性,也就是如果某类已经实现了序列化,则子类默认实现了序列化
*/
class Master implements Serializable{   // 注意5
   private String name;

   public Master(String name) {
       this.name = name;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   @Override
   public String toString() {
       return "Master{" +
               "name='" + name + '\'' +
               '}';
   }
}
class Cat implements Serializable{
   private String name;
   private int age;
   private static String nation;  //注意4
   private transient String color;  //注意4
   private static final long serialVersionUID = 1L;  // 注意3

   private Master master = new Master("小明");

   public Cat() {
   }

   public Cat(String name, int age, String nation, String color) {
       this.name = name;
       this.age = age;
       this.nation = nation;
       this.color = color;
   }

   public static String getNation() {
       return nation;
   }

   public static void setNation(String nation) {
       Cat.nation = nation;
   }

   public String getColor() {
       return color;
   }

   public void setColor(String color) {
       this.color = color;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   @Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               ", age=" + age +
               ", color='" + color + '\'' +
               ", master=" + master +
               '}'+ "nation=" + nation;
   }
}

public class ObjectSteamTest {

   //序列化
   @Test
   public void test1(){
       ObjectOutputStream oos = null;
       try {
           oos = new ObjectOutputStream(new FileOutputStream("object.txt"));

           //序列化
           oos.writeInt(100);  //int->Integer(实现了Serializable)
           oos.writeBoolean(true);  //boolean->Boolean(实现了Serializable)
           oos.writeChar('a'); //char -> Character(实现了Serializable)
           oos.writeDouble(12.3); //double -> Double(实现了Serializable)
           oos.writeUTF("中国"); //String(实现了Serializable)

           oos.writeObject(new Cat("Tom",11,"中国","black"));
       } catch (IOException e) {
           e.printStackTrace();
       } finally {
           if (oos!=null){
               try {
                   oos.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }

   //反序列化
   @Test
   public void test2(){
       ObjectInputStream ois = null;
       try {
           //指定反序列化的文件
           ois = new ObjectInputStream(new FileInputStream("object.txt"));
           int i = ois.readInt();
           boolean b = ois.readBoolean();
           char c = ois.readChar();
           double v = ois.readDouble();
           String s = ois.readUTF();
           System.out.println(i+" "+" "+b+" "+c+" "+v+" "+s);  //100  true a 12.3 中国

           Object cat = ois.readObject();
           System.out.println(cat.getClass());  //运行类型是 class ming4.fileT.Cat
           System.out.println(cat);  // Cat{name='Tom', age=11, color='null', master=Master{name='小明'}}nation=null

           /**
            * 注意:
            *  1. 如果我们希望调用Cat方法,需要向下转型
            *  2. 需要我们将Cat类的定义,放到可以引用的位置
            */
           Cat cat2 = (Cat)cat;
           System.out.println(cat2.getName());  // Tom
       } catch (IOException e) {
           e.printStackTrace();
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } finally {
           if (ois!=null){
               try {
                   ois.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }
}

调用readObject()可以直接返回一个object对象,要把他变成特定类型,必须强制转型。
readObject()可能抛出的异常:

  • ClassNotFoundException:没有找到对应的Class
  • InvalidClassException:Class不匹配
    对于ClassNotFoundException,这种情况常见于一台电脑上的Java程序把一个Java对象,例如,Person对象序列化以后,通过网络传给另一台电脑上的另一个Java程序,但是这台电脑的Java程序并没有定义Person类,所以无法反序列化。
    对于InvalidClassException,这种情况常见于序列化的Person对象定义了一个int类型的age字段,但是反序列化时,Person类定义的age字段被改成了long类型,所以导致class不兼容
    要特别注意反序列化的几个特点:
    反序列化时,由JVM直接构造出Java对象,不调用构造方法,构造方法内部的代码,在反序列化时根本不可能执行,
    安全性: 因为Java的序列化机制可以导致一个实例能直接从byte[] 数组创建,而不经过构造方法,因此,它存在一定的安全隐患,一个精心构造的byte[] 数组被反序列化后,可以执行特定的Java代码,从而导致严重的安全漏洞。

标准输入、标准输出(了解)

Sytem.in和System.out分别代表了系统标准的输入和输出设备
默认输入设备是键盘,输出设备是显示器
System.in 的类型是InputStream
System.out 的类型是PrintStream,其是OutputStream的子类
重定向:通过System类的setIn,setOut方法对默认设备进行改变
public static void setIn(InputStream in)
public static void setOut(PrintStream out)
练习:

import java.io.*;

/**
 * @Author: mei_ming
 * @DateTime: 2022/9/28 22:24
 */
public class OtherStreamTest {
    /**
     * 练习:
     *  从键盘输入字符串,要求读取到的整行字符串转成大写输出,然后进行输入操作
     *  直到 输入 e 或者 exit 时退出
     */
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            InputStream is = System.in;
            InputStreamReader isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            while(true){
                System.out.println("请输入字符串:");
                String line = br.readLine();
                if ("e".equalsIgnoreCase(line) || "exit".equalsIgnoreCase(line)){
                    break;
                }
                String s = line.toUpperCase();
                System.out.println(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(br!=null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

PrintStream / PrintWriter 打印流(了解)

@Test
    public void test2(){
        PrintStream ps = null;
        try {
            FileOutputStream fos = new FileOutputStream(new File("a.txt"));
            ps = new PrintStream(fos, true);
            if (ps!=null){  //把控制台打印输出改成文件
                System.setOut(ps);
            }

            for (int i=0;i<=255;i++){
                System.out.print((char)i);
                if (i%50==0){
                    System.out.println();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ps!=null){
                ps.close();
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值