JavaIO流

1 - File类的介绍

/*

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

2 File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。 如果需要访问文件内容本身,则需要使用输入/输出流。

3 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对 象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。

4 File对象可以作为参数传递给流的构造器

5 File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)

6 File类声明在java.io包下

7 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的"终点"

*/

2 - File类的常用构造器

/*

1 public File(String pathname)  以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果 pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。

  File file = new File("hellp.txt"); // 相当于当前module

  ① 绝对路径:是一个固定的路径,从盘符开始

  ② 相对路径:是相对于某个位置开始

2 public File(String parent,String child)parent为父路径,child为子路径创建File对象。

3 public File(File parent,String child) 根据一个父File对象和子文件路径创建File对向

*/

package com.lzh.java1;
import org.junit.Test;

import java.io.File;

/*
File类的使用
1. File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
2. File类声明在java.io包下


 */
public class FileTest {
    @Test
    // 如何创建File类的实例
    /*
    相对路径:相较于某个路径下,指明的路径
    绝对路径:包含盘符在内的文件或文件夹的路径
     */
    public void test1(){
        // 构造器1
        File file1 = new File("hello.txt"); // 相对路径:相对于当前module
        // File file = new File("D:\\Java\\JavaSenior\\Day8\\src\\com\\lzh\\java1\\hello.txt"); // 绝对路径
        System.out.println(file1);

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

        // 构造器3
        File file3 = new File(file2,"test.txt");
        System.out.println(file3); // D:\Java\JavaSenior\Day8\test.txt
        
        // 注意:以上3个变量只是内存层面上对象
    }
}
代码示例

3 - File类的路径分隔符

/*

1 路径中的每级目录之间用一个路径分隔符隔开

2 路径分隔符和系统有关:

  ① windowsDOS系统默认使用“\”来表示

  ② UNIXURL使用“/”来表示

3 Java程序支持跨平台运行,因此路径分隔符要慎用。

4 为了解决这个隐患,File类提供了一个常量: public  static final String separator。根据操作系统,动态的提供分隔

1 File file1 = new File("d:\\atguigu\\info.txt");
2 File file2 = new File("d:" + File.separator + "atguigu" + File.separator + "info.txt"); 
3 File file3 = new File("d:/atguigu");
举例

*/

4 - File类的常用方法

/*

获取功能:

  1 public String getAbsolutePath():获取绝对路径

  2 public String getPath() :获取路径

  3 public String getName() :获取名称

  4 public String getParent():获取上层文件目录路径。若无,返回null

  5 public long length() :获取文件长度(即:字节数)。不能获取目录的长度。

  6 public long lastModified() :获取最后一次的修改时间,毫秒值

  如下两个方法适用于文件目录:

   7 public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组

   8 public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组

重命名功能:

  1 public boolean renameTo(File dest) : 把文件重命名为指定的文件路径

  比如:boolean isRename = file1.renameTo(file2); // 要想保证返回true,需要file1在硬盘中存在,且file2在硬盘中不能存在

判断功能:

  1 public boolean isDirectory()判断是否是文件目录

  2 public boolean isFile() 判断是否是文件

  3 public boolean exists() 判断是否存在

  4 public boolean canRead() :判断是否可读

  5 public boolean canWrite() :判断是否可写

  6 public boolean isHidden() :判断是否隐藏

创建功能 :

  1 public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false

 1     @Test
 2     public void test2() throws IOException {
 3         // 文件的创建
 4         File file = new File("hello.txt");
 5         System.out.println(file.getAbsolutePath());
 6         if(!file.exists()){
 7             file.createNewFile();
 8             System.out.println("创建成功");
 9         }else{
10             System.out.println("文件已经存在");
11         }
12     }
文件的创建

  2 public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建。

  3 public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建

  注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目 路径下。

删除功能: 

  public boolean delete():删除文件或者文件夹

  删除注意事项: Java中的删除不走回收站。 要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录

*/

5 - IO流的概念与原理

概念:

"流",最早从C语言中引入的,其可以看成是一个流动的数据缓冲区,数据从数据源方向经过缓冲区流向数据的目的地,在传送的过程中,其传送的方式是串行的,在Java中的java.io包中定义了

Java中常见的接口与类,其中包括两个最基本的抽象类,他们分别是OutputStreamInputStream。其余的流都是分别从这两个基本类中继承而来。

/*

1 I/OInput/Output的缩写, I/O技术是非常实用的技术,用于 处理设备之间的数据传输。如读/写文件,网络通讯等。

2 Java程序中,对于数据的输入/输出操作以“流(stream)” 的 方式进行。

3 java.io包下提供了各种“流”类和接口,用以获取不同种类的 数据,并通过标准的方法输入或输出数据

*/

9 - IO流的分类

 /*

1 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)

2 按数据流的流向不同分为:输入流,输出流

3 按流的角色的不同分为:节点流,处理流

*/

 /*

1 Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个 抽象基类派生的

2 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

*/

10 - IO流体系

说明:篮筐为重点

11 - 节点流与处理流

1 节点流:直接从数据源或目的地读写数据

2 处理流:不直接连接到数据源或目的地,而是“连接”在已存 在的流(节点流或处理流)之上,通过对数据的处理为程序提 供更为强大的读写功能

package com.lzh.java1;
import org.junit.Test;
import java.io.*;

/*
1 流的分类
    操作数据单位:字节流、字符流
    数据的流向:输入流、输入流
    流的角色:节点流、处理流
2 流的体系结构
    抽象基类:               节点流(或文件流)   缓冲流(处理流)
        InputStream     FileInputStream     BufferedInputStream
        OutputStream    FileOutputStream    BufferedOutputStream
        Reader          FileReader          BufferedReader
        Writer          FileWriter          BufferedWriter

3 结论:
    1. 对于文本文件(.txt .java .c .cpp),使用字符流处理
    2. 对于非文本文件(.jpg .mp3 .mp4 .avi .doc .ppt),使用字节流处理
 */
public class FileReaderWriterTest {
    public static void main(String[] args) {
        File file = new File("hello.txt"); // 相对路径,相当于当前工程
        System.out.println(file.getAbsolutePath()); // D:\Java\JavaSenior\hello.txt

        File file1 = new File("Day9\\a.txt");
        System.out.println(file1.getAbsolutePath()); // D:\Java\JavaSenior\Day9\a.txt
    }
    @Test
    /*
    将Day9下的hello.txt文件内容读入程序中,并输出到控制台(1 2 3 4)
    说明:
    ① read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
    ② 异常处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
    ③ 读入的文件一定要存在,否则就会报FileNotFoundException
     */
    public void fileReaderTest() throws FileNotFoundException {
        // 1. 实例化File类的对象,指明要操作的文件
        File file = new File("hello"); // 相对路径,相当于当前module
        // System.out.println(file.getAbsolutePath());

        if(file.exists()){
            // 2. 提供具体的流
            FileReader fileReader = new FileReader(file);
            // 3. 数据的读入 read():返回读入的一个字符。如果达到文件末尾,返回-1
            // 方式1
            try {
                int data = fileReader.read();
                while(data != -1){
                    System.out.print((char)data);
                    data = fileReader.read();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 4. 流的关闭操作(一定不要忘记)
                try {
                    if(fileReader != null)
                        fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 方式2
            /*
            int data;
            while(data = fileReader.read() != -1){
                System.out.print(char(data));
            }
             */

        }else{
            System.out.println("文件不存在");
        }
    }

    @Test
    // 对read()操作升级:使用read的重载方法
    public void test1() {
        // 1. File类的实例化
        // 2. FileReader流的实例化
        // 3. 读入操作
        // 4. 资源回收(关闭流)
        FileReader fileReader = null;
        try {
            File file = new File("hello");
            fileReader = new FileReader(file);

            char[] buffer = new char[5];
            // read(char[] cbuf):返回每次读入cbuf数组中的字符的个数,如果达到文件末尾返回-1
            int len;
            while((len = fileReader.read(buffer)) != -1){
                for(int i = 0;i < len;i++){
                    System.out.print(buffer[i]);
                }
                // 另一种写法
                /*
                String str = new String(buffer,0,len);
                System.out.print(str);
                 */
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fileReader != null) // 避免空指针异常
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    @Test
    /*
    从内存中写出数据到硬盘文件里
    说明:
        1. 输出操作,对应的File可以不存在的。并不会报异常
        2. File对应的硬盘中的文件如果果不存在,在输出的过程中,会自动创建此文件
           File对应的硬盘中的文件如果存在:
                流使用的构造器是:FileWriter(file,false) / FileWriter(file) --> 对原有文件覆盖
                流使用的构造器是:FileWriter(file,true)  --> 追加文件内容

     */
    public void fileWriterTest() {
        // 1. 提供File类的对象,指明写出到的文件
        // 2. 提供FileWriter类的对象,用于数据的写出
        // 3. 写出的操作
        // 4. 资源的回收(流的关闭)
        FileWriter fileWriter = null;
        try {
            File file = new File("hello");
            fileWriter = new FileWriter(file,false);

            fileWriter.write("write once run anywhere!".toCharArray());
            fileWriter.write("\n"+"you need have a dream!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fileWriter != null)
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

    @Test
    public void copyFileTest() {
        // 1. 提供File类的对象,指明读入与写出的文件
        // 2. 提供FileReader、FileWriter类的对象,用于数据的写入与读出
        // 3. 数据的读入与写出操作
        // 4. 资源回收(留的关闭)

        FileReader  fileReader = null;
        FileWriter fileWriter = null;
        try {
            File readFile = new File("old.txt");
            if(!readFile.exists()){
                readFile.createNewFile();
            }
            File writeFile = new File("new.txt");

            fileReader = new FileReader(readFile);
            fileWriter = new FileWriter(writeFile,false);

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

    @Test
    /*
    实现对图片(test.png)的复制
     */
    public void fileInputOutputStream() {
        // 1 提供File类的对象,指明读入与写入的文件
        // 2 提供FileInputStream和FileOutputStream类的对象,用于数据的读入与写出
        // 3 数据的读入与写出操作
        // 4 回收资源(流的关闭)
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            File srcFile = new File("test.png");
            File destFile = new File("copy.png");

            inputStream = new FileInputStream(srcFile);
            outputStream = new FileOutputStream(destFile);

            byte[] buffer = new byte[5];
            int len; // 记录每次读入到buffer数组中字节的个数
            while((len = inputStream.read(buffer)) != -1){
                outputStream.write(buffer,0,len);
            }
            System.out.println("复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
节点流与处理流的基本使用

12 - 处理流之1:缓冲流

/*

1 为了提高数据读写的速度Java API提供了带缓冲功能的流类,在使用这些流类 时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区

缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:

  ① BufferedInputStream 和 BufferedOutputStream

  ② BufferedReader 和 BufferedWriter

3 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区

4 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从 文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中 读取下一个8192个字节数组

5 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush()可以强制将缓冲区的内容全部写入输出流

6 关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也 会相应关闭内层节点流

7 flush()方法的使用:手动将buffer中内容写入文件

8 如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷 新缓冲区,关闭后不能再写

*/

package com.lzh.java1;
import org.junit.Test;

import java.io.*;

/*
处理流之一:缓冲流的使用
1 缓冲流:
    字节
        BufferedInputStream
        BufferedOutputStream
    字符
        BufferedReader
        BufferedWriter

2 缓冲流的作用:提高流的读入、写入的速度。提高速度的原因:内部提供了一个缓冲区
3 处理流,就是"套接"在已有的流的基础上。

 */
public class BufferStreamTest {
    @Test
    /*
    实现非文本文件的复制
     */
    public void bufferStreamTest(){
        // 1 造文件(提供 File类的对象,指明读入与写入的文件)
        // 2 造流(提供BufferInputStream和BufferOutputStream类的对象,用于数据的读入与写出)
        // 3 数据读入与写入的具体操作
        // 4 资源回收(流的关闭) 注意:此时造了4个流,关闭顺序有要求
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            File srcFile = new File("test.png");
            File destFile = new File("new_test.png");
            // 节点流
            FileInputStream inputStream = new FileInputStream(srcFile);
            FileOutputStream outputStream = new FileOutputStream(destFile);
            // 缓冲流
            bis = new BufferedInputStream(inputStream);
            bos = new BufferedOutputStream(outputStream);

            byte[] buffer = new byte[10];
            int len;
            while((len = bis.read(buffer)) != -1){
                bos.write(buffer,0,len);
            }
            System.out.println("复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        // 资源回收顺序要求:先关闭外侧流,在关闭内层流
        // 说明:关闭外侧流的同时,内层流也会自动进行关闭,关于内层流的关闭,可以省略
    }

    @Test
    /*
    使用BufferedReader和BufferedWriter实现文本文件的复制
     */
    public void test1(){
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        try {
            // 造文件,造流(节点流与处理流)
            bufferedReader = new BufferedReader(new FileReader(new File("hello")));
            bufferedWriter = new BufferedWriter(new FileWriter(new File("hello2")));

            // 读写操作
            char[] buffer = new char[1024];
            int len;
            while((len = bufferedReader.read(buffer)) != -1){
                bufferedWriter.write(buffer,0,len);
            }
            System.out.println("复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 资源的回收(流的关闭)
            if(bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bufferedWriter != null){
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
缓冲流的使用

练习

1 实现图片的加密与解密操作

package com.lzhexer;

import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/*
实现图片的加密与解密
 */
public class PictureTest {
    @Test
    public void test(){
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            // 图片的加密
            inputStream = new FileInputStream(new File("test.png"));
            outputStream = new FileOutputStream(new File("test_secret.png"));

            byte[] buffer = new byte[20];
            int len;
            while((len = inputStream.read(buffer)) != -1){
                // 对字节数据进行修改
                for(int i = 0;i < len;i++){
                    buffer[i] = (byte) (buffer[i] ^ 5);
                }
                outputStream.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 资源的回收
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    // 对图片进行解密
    public void test2(){
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(new File("test_secret.png"));
            outputStream = new FileOutputStream(new File("new_Picture.png"));

            byte[] buffer = new byte[20];
            int len;
            while((len = inputStream.read(buffer)) != -1){
                for(int i = 0;i < len;i++){
                    buffer[i] = (byte) (buffer[i] ^ 5);
                }
                outputStream.write(buffer,0,len);
            }
            System.out.println("解密成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
图片的加密与解密

获取文本上每个字符出现的次数   提示:遍历文本的每一个字符;字符及出现的次数保存在Map中;将Map中数据 写入文件

package com.atguigu.java;
import org.junit.Test;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
 * 练习3:获取文本上字符出现的次数,把数据写入文件
 *
 * 思路:
 * 1.遍历文本每一个字符
 * 2.字符出现的次数存在Map中
 *
 * Map<Character,Integer> map = new HashMap<Character,Integer>();
 * map.put('a',18);
 * map.put('你',2);
 *
 * 3.把map中的数据写入文件
 *
 * @author shkstart
 * @create 2019 下午 3:47
 */
public class WordCount {
    /*
    说明:如果使用单元测试,文件相对路径为当前module
          如果使用main()测试,文件相对路径为当前工程
     */
    @Test
    public void testWordCount() {
        FileReader fr = null;
        BufferedWriter bw = null;
        try {
            //1.创建Map集合
            Map<Character, Integer> map = new HashMap<Character, Integer>();

            //2.遍历每一个字符,每一个字符出现的次数放到map中
            fr = new FileReader("dbcp.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("wordcount.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 {
            //4.关流
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

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

            }
        }
    }
}
View Code

13 - 处理流之2:转换流

1 转换流提供了在字节流和字符流之间的转换

2 Java API提供了两个转换流:

  ① InputStreamReader:将InputStream转换为Reader

  ② OutputStreamWriter:将Writer转换为OutputStream

3 字节流中的数据都是字符时,转成字符流操作更高效。

4 很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能

 

InputStreamReader

1 实现将字节的输入流按指定字符集转换为字符的输入流。

2 需要和InputStream“套接”。

3 构造器

  ① public InputStreamReader(InputStream in)

  ② public InputSreamReader(InputStream in,String charsetName)

如: Reader isr = new InputStreamReader(System.in,”gbk”);

 

OutputStreamWriter

1 实现将字符的输出流按指定字符集转换为字节的输出流。

2 需要和OutputStream“套接”。

3 构造器

  ① public OutputStreamWriter(OutputStream out)

  ② public OutputSreamWriter(OutputStream out,String charsetName)

package com.lzh.java2;
import org.junit.Test;

import java.io.*;

/*
处理流2:转换流的使用
1 转换流:属于字符流
    InputStreamReader:将一个字节的输入流转换为字符的输入流
    OutputStreamWriter:将一个字符的输出流转换为字节的输出流

2 作用:提供字节流与字符流之间的转换

3 解码:字节、字节数组 ---> 字符数组、字符串
  编码:字符数组、字符串 ---> 字节、字节数组

4 字符集
    ① ASCII:美国标准信息交换码。
        用一个字节的7位可以表示。
    ② ISO8859-1:拉丁码表。欧洲码表
        用一个字节的8位表示。
    ③ GB2312:中国的中文编码表。最多两个字节编码所有字符
    ④ GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
    ⑤ Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的 字符码。所有的文字都用两个字节来表示。
    ⑥ UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

 */
public class ManageTest {
    @Test
    // 此时处理异常应该使用try-catch-finally
    // 将字节的输入流转换为字符的输入流
    public void test1() throws IOException {
        FileInputStream inputStream = new FileInputStream(new File("hello")); // 当前module下的路径

        InputStreamReader reader = new InputStreamReader(inputStream); // 使用系统默认的字符集
        // 参数2,指明了字符集,具体使用哪个字符集,取决于文件保存时使用的字符集
        // InputStreamReader reader = new InputStreamReader(inputStream,"UTF-8");

        char[] buffer = new char[1024];
        int len;
        while((len = reader.read(buffer)) != -1){
            String str = new String(buffer,0,len);
            System.out.println(str);
        }

        reader.close();
    }

    @Test
    // 综合使用 InputStreamReader 和 OutputStreamWriter
    public void test2(){
        InputStreamReader reader = null;
        OutputStreamWriter writer = null;
        try {
            reader = new InputStreamReader(new FileInputStream(new File("hello")),"utf-8");
            writer = new OutputStreamWriter(new FileOutputStream(new File("hello_gbk.txt")),"gbk");

            // 具体读写过程
            char[] buffer = new char[1024];
            int len;
            while((len = reader.read(buffer)) != -1){
                writer.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
转换流的使用示例

转换流的编码应用

  1 可以将字符按指定编码格式存储

  2 可以对文本数据按指定编码格式来解读

  3 指定编码表的动作由构造器完成

14 - 标准输入、输出流(了解)

1 System.inSystem.out分别代表了系统标准的输入和输出设备

2 默认输入设备是:键盘,输出设备是:显示器

3 System.in的类型是InputStream

4 System.out的类型是PrintStream,其是OutputStream的子类 FilterOutputStream 的子类

5 重定向:通过System类的setIn,setOut方法对默认设备进行改变。

  ① public static void setIn(InputStream in)

  ② public static void setOut(PrintStream out)

package com.lzh.java2;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/*
其他流的使用:
1 标准的输入、输出流
2 打印流
3 数据流

 */
public class OtherStreamTest {
    // 1 标准的输入、输出流
    public static void main(String[] args) {
        // System.out 标准的输入流,默认从键盘输入
        // System.in 标准的输出流,默认从控制台输出

        // System类的setIn(InputStream is) / setOut(PrintStream ps) 方式重新指定输入和输出的流
        /*
        练习:从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续 进行输入操作,
        直至当输入“e”或者“exit”时,退出程序。
        */

        // 方法1:使用Scanner实现
        // 方法2:System.in    System.in --> 转换流 --> BufferedReader的readLine()
        BufferedReader reader = null;
        try {
            InputStreamReader isr = new InputStreamReader(System.in); // 转换流
            reader = new BufferedReader(isr);

            String data;
            while (true) {
                System.out.print("请输入字符串:");
                data = reader.readLine();
                if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
                    System.out.println("程序结束");
                    break;
                }
                String newData = data.toUpperCase();
                System.out.println(newData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
标准的输入、输出流的使用

15 - 处理流6:对象流(序列化传输)

对象流

1 ObjectInputStream和OjbectOutputSteam

  用于存储和读取基本数据类型数据对象的处理流。它的强大之处就是可 以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来

2 序列化:用ObjectOutputStream保存基本类型数据或对象的机制 

3 反序列化:ObjectInputStream读取基本类型数据或对象的机制

4 ObjectOutputStreamObjectInputStream不能序列化statictransient修 饰的成员变量

package com.lzh.java1;

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

/*
对象流的使用:
1 对象流
    ObjectInputStream
    ObjectOutputStream

2 作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可 以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

3 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去。使用ObjectOutputStream实现
4 反序列化过程:将磁盘文件中的对象还原为内存中的一个java对象,使用ObjectInputStream实现

 */
public class ObjectInputOutputStreamTest {
    @Test
    // 序列化过程示例
    public void test1(){
        ObjectOutputStream writer = null;
        try {
            writer = new ObjectOutputStream(new FileOutputStream("object.dat"));

            writer.writeObject(new String("hello world"));
            writer.flush(); // 刷新操作
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    // 反序列化过程
    public void test2(){
        ObjectInputStream reader = null;
        try {
            reader = new ObjectInputStream(new FileInputStream("object.dat"));
            Object obj = reader.readObject();

            String str = (String) obj;
            System.out.println(str);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
序列化与反序列化的使用示例

16 - 对象的序列化机制

1 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从 而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传 输到另一个网络节点。

//当其它程序获取了这种二进制流,就可以恢复成原 来的Java对象 

2 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据, 使其在保存和传输时可被还原

3 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返 回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础

4 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可 序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。 否则,会抛出NotSerializableException异常

  ①Serializable

  ②Externalizable

 

使用对象流序列化对象

若某个类实现了 Serializable 接口,该类的对象就是可序列化的:

  1 创建一个 ObjectOutputStream

  2 调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象

  3 注意写出一次,操作flush()一次

反序列化

  1 创建一个 ObjectInputStream

  2 调用 readObject() 方法读取流中的对象

强调:如果某个类的属性不是基本数据类型或 String 类型,而是另一个 引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化

package com.lzh.java1;
import java.io.Serializable;
import java.util.Objects;
/*
1 要想java对象可序列化,需要满足如下条件:
    需要实现接口:Serializable
    当前类提供一个全局常量:static final long serialVersionUID = 42L;
    除了当前Person类需要实现Serializable接口值外,还必须保证其内部所有的属性也必须是可序列化的(默认情况下,基本数据类型是可序列化)

    补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修 饰的成员变量

 */
public class Person implements Serializable { // 标识接口
    public static final long serialVersionUID = 424546548L;
    private String name;
    private int age;

    public Person(){}
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    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 boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
自定义可序列化Person类
package com.lzh.java1;

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

/*
对象流的使用:
1 对象流
    ObjectInputStream
    ObjectOutputStream

2 作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可 以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

3 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去。使用ObjectOutputStream实现
4 反序列化过程:将磁盘文件中的对象还原为内存中的一个java对象,使用ObjectInputStream实现

 */
public class ObjectInputOutputStreamTest {
    @Test
    // 序列化过程示例
    public void test1(){
        ObjectOutputStream writer = null;
        try {
            writer = new ObjectOutputStream(new FileOutputStream("persons.dat"));

            writer.writeObject(new Person("李白",22));
            writer.flush(); // 刷新操作

            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    // 反序列化过程
    public void test2(){
        ObjectInputStream reader = null;
        try {
            reader = new ObjectInputStream(new FileInputStream("persons.dat"));
            Object obj = reader.readObject();

            Person person = (Person) obj;
            System.out.println(person);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
自定义类的对象传输示例(磁盘层面)
/*
1 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量: 
    ①private static final long serialVersionUID; 

    ②serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象 进行版本控制,有关各版本反序列化时是否兼容。 

    ③如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自 动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议, 显式声明。

2 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验 证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同 就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异 常。(InvalidCastException)

*/
对实现Serializable接口的类静态常量serialVersionUID的说明

17 - 面试题:对java.io.Serializable接口的理解

/*
1 实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后 完全恢复回原来的样子。

(重点)这一过程亦可通过网络进行。这意味着序列化机 制能自动补偿操作系统间的差异。

换句话说,可以先在Windows机器上创 建一个对象,对其序列化,然后通过网络发给一台Unix机器,然后在那里 准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必 关心字节的顺序或者其他任何细节。 

2 由于大部分作为参数的类如String、Integer等都实现了 java.io.Serializable的接口,也可以利用多态的性质,作为参数使接口更 灵活
*/
View Code

18 - 随机存取文件流

1 RandomAccessFile 声明在java.io包下,但直接继承于java.lang.Object类。并 且它实现了DataInput、DataOutput这两个接口,也就意味着这个类既可以读(输出流)也 可以写(输入流)。

2 RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意 地方来读、写文件

  ① 支持只访问文件的部分内容

  ② 可以向已存在的文件后追加内容

3 RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。 RandomAccessFile 类对象可以自由移动记录指针:

  ①long getFilePointer():获取文件记录指针的当前位置

  ②void seek(long pos):将文件记录指针定位到 pos 位置

 

4 构造器

  ① public RandomAccessFile(File file, String mode)

  ② public RandomAccessFile(String name, String mode)

 
5 创建 RandomAccessFile 类实例需要指定一个 mode 参数该参数指 定 RandomAccessFile 的访问模式

  r: 以只读方式打开

  rw:打开以便读取和写入

  rwd:打开以便读取和写入;同步文件内容的更新

  rws:打开以便读取和写入;同步文件内容和元数据的更新

6 如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件, 如果读取的文件不存在则会出现异常。 如果模式为rw读写。如果文件不 存在则会去创建文件,如果存在则不会创建。

package com.lzh.java1;

import org.junit.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;

/*
RandomAccessFile类的使用:
1 接继承于java.lang.Object类。并 且它实现了DataInput、DataOutput这两个接口,也就意味着这个类既可以读(输入流)也可以写(输出流)。
2 如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建
  如果写出到的文件存在,则会对原有的文件内容进行覆盖(默认情况下,是从头覆盖)

3 可以通过相关操作,实现RandomAccessFile"插入"数据的效果
 */
public class RandomAccessFileTest {
    @Test
    public void test1() {
        RandomAccessFile reader = null;
        RandomAccessFile writer = null;
        try {
            reader = new RandomAccessFile(new File("test.png"),"r");
            writer = new RandomAccessFile(new File("new_test.png"),"rw");

            byte[] buffer = new byte[1024];
            int len;
            while((len = reader.read(buffer)) != -1){
                writer.write(buffer,0,len);
            }
            System.out.println("复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void test2() throws IOException {
        RandomAccessFile writer = new RandomAccessFile(new File("test.txt"),"rw");
        writer.seek(12); // 将指针调到角标为12的位置
        writer.write("hello world".getBytes()); // 覆盖数据的操作

        writer.close();
    }

    @Test
    /*
    使用RandomAccessFile实现数据的插入效果
     */
    public void test3() throws IOException {
        // 造流、造文件
        RandomAccessFile writer = new RandomAccessFile(new File("test.txt"),"rw");
        writer.seek(12);

        // 保存指针12后面的所有数据到StringBuilder中
        StringBuilder builder = new StringBuilder((int) new File("test.txt").length());
        byte[] buffer = new byte[1024];
        int len;
        while((len = writer.read(buffer)) != -1){
            builder.append(new String(buffer,0,len));
        }

        // 调回指针,写入数据
        writer.seek(12);
        writer.write("hello".getBytes()); // 此时指针在角标为12的位置

        // 再将StringBuilder中的数据写入到文件里
        writer.write(builder.toString().getBytes());

        // 回收资源
        writer.close();
    }
}
RandomAccessFile使用示例

 

posted @ 2020-06-20 12:31  赖正华  阅读(198)  评论(0编辑  收藏  举报