Java学习——Day13:IO流

6.1 File类

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

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

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

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

  • File类方法

    • public File(String pathname)

      • 以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果 pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
        • 绝对路径:是一个固定的路径,从盘符开始
        • 相对路径:是相对于某个位置开始
    • public File(String parent,String child)

      以parent为父路径,child为子路径创建File对象。

    • public File(File parent,String child)

      根据一个父File对象和子文件路径创建File对象


```java
package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.File;
import java.io.IOException;

/**
 *
 * File类的使用
 *
 * 1.File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
 * 2.File类声明在java.io包下
 * 3.File类中涉及到关于文件或文件目录的创建删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容,
 * 如果需要读取或写入文件内容,必须使用IO流来完成。
 * 4.后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的”重点“
 *`在这里插入代码片`
 * @author zhengxu
 * @create 2021-07-01 18:08
 */
public class FileTest {

    /*
    1.如何创建File类的实例
        File(String filePath)
        File(String parentPath,String childPath)
        File(File parentFile,String childPath)

    2.路径
      相对路径:相较于某个路径下,指明的路径
      绝对路径:包含盘符在内的文件或文件的目录

    3.路径分隔符
    windows:\\
    unix:/
     */
    @Test
    public void test(){

        //构造器1
        File file=new File("hello.txt");//相对于当前的module
        File file1=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\study\\javaSenior\\IOStream");

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

        //构造器2
        File file2=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\study\\javaSenior\\IOStream","javaSenior");
        System.out.println(file2);

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

    /*
     File类的获取功能
         public String getAbsolutePath():获取绝对路径
         public String getPath() :获取路径
         public String getName() :获取名称
         public String getParent():获取上层文件目录路径。若无,返回null
         public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
         public long lastModified() :获取最后一次的修改时间,毫秒值
        如下两个方法适用于文件目录
         public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
         public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
     File类的重命名功能
         public boolean renameTo(File dest):把文件重命名为指定的文件路径
     File类的判断功能
         public boolean isDirectory():判断是否是文件目录
         public boolean isFile() :判断是否是文件
         public boolean exists() :判断是否存在
         public boolean canRead() :判断是否可读
         public boolean canWrite() :判断是否可写
         public boolean isHidden() :判断是否隐藏
     File类的创建功能
         public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
         public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。
            如果此文件目录的上层目录不存在,也不创建。
         public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建
            注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。
     File类的删除功能
         public boolean delete():删除文件或者文件夹删除注意事项:Java中的删除不走回收站。要删除一个文件目录,
            请注意该文件目录内不能包含文件或者文件目录
     */

    //File类的获取功能
    @Test
    public void test01(){
        File file=new File("hello.txt");//相对于当前的module
        File file1=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\study\\javaSenior\\IOStream");

        System.out.println(file.getAbsoluteFile());//hello.txt
        System.out.println(file.getPath());//hello.txt
        System.out.println(file.getName());//hello.txt
        System.out.println(file.getParent());//null
        System.out.println(file.length());//0
        System.out.println(file.lastModified());//0

        System.out.println();

        System.out.println(file1.getAbsoluteFile());//D:\电脑重要文件\Java学习\JAVA进阶\src\study\javaSenior\IOStream
        System.out.println(file1.getPath());//D:\电脑重要文件\Java学习\JAVA进阶\src\study\javaSenior\IOStream
        System.out.println(file1.getName());//IOStream
        System.out.println(file1.getParent());//D:\电脑重要文件\Java学习\JAVA进阶\src\study\javaSenior
        System.out.println(file1.length());//0
        System.out.println(file1.lastModified());//1625187547834
    }

    @Test
    public void test02(){
        File file=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\study\\javaSenior");
        String[] list = file.list();
        for(String s:list){
            System.out.println(s);//打印了该文件里面的所有文件名
        }

        File[] files=file.listFiles();
        for(File f:files){
            System.out.println(f);//打印了该文件里面所有文件的文件路径
        }
    }

    /*
    File类的重命名功能:public boolean renameTo(File dest):把文件重命名为指定的文件路径
      比如:file1.renameTo(file2)为例
        要想保证返回true,需要file1在硬盘中是存在的,且file2在硬盘中不能存在
     */
    @Test
    public void test03(){
        File file1=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\hello.txt");
        File file2=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\study\\javaSenior\\IOStream\\he.txt");
        System.out.println(file2);

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

    //File类的判断功能
    @Test
    public void test04(){
        File file1=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\study\\javaSenior\\IOStream\\he.txt");

        System.out.println(file1.isDirectory());//false
        System.out.println(file1.isFile());//true
        System.out.println(file1.exists());//true
        System.out.println(file1.canRead());//true
        System.out.println(file1.canWrite());//true
        System.out.println(file1.isHidden());//false

    }

    //File类的创建、删除功能
    @Test
    public void test05() throws IOException {
        //文件创建
        File file1=new File("hi.txt");
        if(!file1.exists()){
            file1.createNewFile();
            System.out.println("创建成功");
        }else {//文件存在
            file1.delete();
            System.out.println("删除成功");
        }

        //文件目录创建
        File file2=new File("D:\\电脑重要文件\\Java学习\\JAVA进阶\\src\\study\\javaSenior\\IOStream\\test");
        boolean mkdir = file2.mkdir();
        if(mkdir){
            System.out.println("创建成功");
        }

    }

}



### 6.2 IO流原理及流的分类

- I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于 **处理设备之间的数据传输**。如读/写文件,网络通讯等。 
- Java程序中,对于数据的输入/输出操作以“**流(stream)**” 的 方式进行。 
- java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过**标准的方法**输入或输出数据。



- **输入input**:读取外部数据(磁 盘、光盘等存储设备的数据)到 程序(内存)中。
- **输出output**:将程序(内存) 数据输出到磁盘、光盘等存储设 备中。



- **流的分类**
  - 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit) 
  - 按数据流的流向不同分为:输入流,输出流 
  - 按流的角色的不同分为:节点流,处理流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hFIB6Qrp-1627972670078)(C:\Users\zhengxu\AppData\Roaming\Typora\typora-user-images\image-20210702154213229.png)]

- **Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个 抽象基类派生的。** 
- 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wPEFImD3-1627972670080)(C:\Users\zhengxu\AppData\Roaming\Typora\typora-user-images\image-20210702153945975.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RnqTNoVv-1627972670081)(C:\Users\zhengxu\AppData\Roaming\Typora\typora-user-images\image-20210702154133456.png)]





### 6.3 节点流(或文件流)

- FileInputStream、FileOutputStream、FileReader、FileWriter
- 前两个一般用来处理图片,后面两个一般用来处理文本

```java
package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 一、流的分类
 * 1.操作数据单位:字节流、字符流
 * 2.数据的流向:输入流、输出流
 * 3.流的角色:节点流、处理流
 *
 * 二、流的体系结构
 * 抽象基类          节点流(或文件流)                               缓存流(处理流的一种)
 * InputStream      FileInputStream(read(byte[] buffer))          BufferedInputStream
 * OutputStream     FileOutputStream(write(byte[] buffer,0,len))  BufferedOutputStream
 * Reader           FileReader(read(char[] buffer))               BufferedReader
 * Writer           FileWriter(write(char[] buffer,0,len))        BufferedWriter
 *
 * 前两者是对图片视频等字节流的读写
 * 后两者是对文本等字符流的读写
 * @author zhengxu
 * @create 2021-07-02 15:51
 */
public class FileReaderWriterTest {
    /*
    将hello.txt文件内容读到程序中,并输出到控制台
    说明点:
    1.read()的理解:返回读入的一个字符。如果达到文件末尾,则返回-1(可以用来做循环)
    2.异常的处理:为了保证流资源一定可以执行关闭,防止资源占用。需要使用try/catch/finaly处理,不用抛IO异常的方式。
    3.读入的文件一定要存在,否则会报FileNatFoundException
     */
    @Test
    public void testFileReader(){
        FileReader fr= null;
        try {
            //1.实例化File类的对象,指明要操作的文件
            File file=new File("src\\hello.txt");//相较于当前modul(单元测试)
            //2.提供具体的流
            fr = new FileReader(file);
            //3.数据的读入
            //read():返回读入的一个字符。如果达到文件末尾,则返回-1(可以用来做循环)
            //方式一:
//        int read = fr.read();
//        while (read!=-1){
//            System.out.print((char)read);
//            read=fr.read();
//        }

            //方式二:语法上针对方式一的修改
            int data;
            while ((data=fr.read())!=-1){
                System.out.print((char)data);//hello world!
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流的关闭
            try {
                if(fr!=null){
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //对rea()操作升级:使用read的重载方法
    @Test
    public void testFileReader01(){
        FileReader fr= null;
        try {
            //1.File类的实例化
            File file=new File("src\\hello.txt");

            //2.FileReader流的实例化
            fr = new FileReader(file);

            //3.读入的操作!!!!!!!!!!!
            //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
            char[] cbuf=new char[5];
            int len;
            while ((len=fr.read(cbuf))!=-1){
                //错误的写法,会多读取最后的几个字符
//                for(int i=0;i

6.4 缓冲流

package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之一:缓冲流的使用
 *
 * 1.缓冲流:
 * BufferedInputStream
 * BufferedOutputStream
 * BufferedReader
 * BufferedWriter
 *
 * 2.作用:提供流的读取、写入的速度
 *   提高读写速度的原因:内部提供了一个缓冲区
 *
 * 3.处理流,就是套接在已有的流的基础。
 * @author zhengxu
 * @create 2021-08-02 9:29
 */
public class BufferedTest {

    /*
    实现非文本的复制
     */
    @Test
    public void BufferedStreamTest(){
        //1.造文件
        BufferedInputStream bis= null;
        BufferedOutputStream bos= null;
        try {
            File srcFile= new File("1.jpg");
            File destFile=new File("4.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[] buffer=new byte[10];
            int len;
            while ((len=bis.read(buffer))!=-1){
                bos.write(buffer,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 testBufferedReaderBufferedWriter(){
        BufferedReader br= null;
        BufferedWriter bw= null;
        try {
            //1.创建文件、造流
            br = new BufferedReader(new FileReader(new File("src\\hello.txt")));
            bw = new BufferedWriter(new FileWriter(new File("hello3.txt")));

            //2.读写操作
            //方式一:使用char[]数组
//            char[] buf=new char[1024];
//            int len;
//            while((len=br.read(buf))!=-1){
//                bw.write(buf,0,len);
//            }
            //方式二:使用String
            String data;
            while((data=br.readLine())!=null){
                bw.write(data);//data中不包含换行符
               // bw.write("\n");
                bw.newLine();

            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //3.关闭资源
            if(br!=null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bw!=null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }

}

【练习题】:图片的加密与解密

package study.javaSenior.IOStream.exercise;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author zhengxu
 * @create 2021-08-02 11:24
 */
public class PicTest {
    //图片的加密
    @Test
    public void test01(){

        FileInputStream fis= null;
        FileOutputStream fos= null;
        try {
            fis = new FileInputStream(new File("1.jpg"));
            fos = new FileOutputStream(new File("1secret.jpg"));

            byte[] buffer =new byte[20];
            int len;
            while((len=fis.read(buffer))!=-1){
                //字节数据修改,实现加密
                for(int i=0;i<len;i++){
                    buffer[i]=(byte) (buffer[i]^5);
                }

                fos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //图片的解密
    @Test
    public void test02(){
        FileInputStream fis= null;
        FileOutputStream fos= null;
        try {
            fis = new FileInputStream(new File("1secret.jpg"));
            fos = new FileOutputStream(new File("1nosecret.jpg"));

            byte[] buffer =new byte[20];
            int len;
            while((len=fis.read(buffer))!=-1){
                //字节数据修改,实现加密
                for(int i=0;i<len;i++){
                    buffer[i]=(byte) (buffer[i]^5);//两次异或,相当于没变!!!
                }

                fos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6.5 转换流

  • 转换流提供了在字节流和字符流之间的转换
  • Java API提供了两个转换流:
    • InputStreamReader:将InputStream转换为Reader
    • OutputStreamWriter:将Writer转换为OutputStream
  • 字节流中的数据都是字符时,转成字符流操作更高效。
  • 很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TFMybAec-1627972670085)(C:\Users\zhengxu\AppData\Roaming\Typora\typora-user-images\image-20210802113925585.png)]

package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.*;

/**
 * 处理流之二:转换流的使用
 * 1.转换流:属于字符流
 *   InputStreamReader:将一个字节的输入流转换成字符的输入流
 *   OutputStreamWriter:将一个字符的输出流转换成字节的输出流
 *
 * 2.作用:提供字节流与字符流之间的转换
 *
 * 3.解码:字节、字节数组————>字符数组、字符串
 *   编码:字符数组、字符串————>字节、字节数组
 *
 * 4.字符集
 *    编码表的由来
 *     计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识
 *     别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
 *    常见的编码表
 *      ASCII:美国标准信息交换码。
 *        用一个字节的7位可以表示。
 *      ISO8859-1:拉丁码表。欧洲码表
 *        用一个字节的8位表示。
 *      GB2312:中国的中文编码表。最多两个字节编码所有字符
 *      GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
 *      Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
 *      UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。
 *
 * @author zhengxu
 * @create 2021-08-02 11:40
 */
public class InputStreamReaderTest {

    //InputStreamReader:将一个字节的输入流转换成字符的输入流
    @Test
    public void test01() {
        InputStreamReader isr = null;//使用系统默认的字符集UTF-8
        try {
            FileInputStream fis = new FileInputStream("src\\hello.txt");
            isr = new InputStreamReader(fis);
            //参数2指明的字符集,具体使用哪个字符集,取决于文件hello.txt保存时使用的字符集
            //InputStreamReader isr=new InputStreamReader(fis,"UTF-8");

            char[] buf = new char[20];
            int len;
            while ((len = isr.read(buf)) != -1) {
                String str = new String(buf, 0, len);
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isr != null) {
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //综合使用InputStreamReader和OutputStreamWriter:
    // 将一个utf-8格式的文件改成gbk格式的文件
    @Test
    public void test02(){
        InputStreamReader isr= null;
        OutputStreamWriter osr= null;

        try {
            //造文件、造流
            File file1=new File("src\\hello-utf-8.txt");
            File file2=new File("src\\hello-gbk.txt");

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

            isr = new InputStreamReader(fis,"utf-8");
            osr = new OutputStreamWriter(fos,"GBK");

            //读写过程
            char[] buf=new char[20];
            int len;
            while((len=isr.read(buf))!=-1){
                osr.write(buf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(isr!=null){
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(osr!=null) {
                try {
                    osr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TZhhr3GH-1627972670086)(C:\Users\zhengxu\AppData\Roaming\Typora\typora-user-images\image-20210802151535874.png)]

6.6 标准输入、输出流

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

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

/**
 * 其它流的使用
 * 1.标准的输入、输出流
 * 2.打印流
 * 3.数据流
 * @author zhengxu
 * @create 2021-08-02 15:20
 */
public class OtherStreamTest {
    /*
    1.标准的输入、输出流
    1.1
    System.in:标准的输入流,默认从键盘输入
    System.out:标准的的输出流,默认从控制台输出
    1.2
    System类的setIn()/setOut()方式重新指定输入和输出的设备

    1.3练习
    从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。

    方法一:使用scanner实现。调用next()返回一个字符串
    方法二:使用System.in实现。System.in————>转换流————>BufferedReader的readLine()
     */
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            InputStreamReader isr = new InputStreamReader(System.in);
            br = new BufferedReader(isr);

            while (true) {
                System.out.println("请输入字符串:");
                String data = br.readLine();
                if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
                    System.out.println("程序结束");
                    break;
                }

                String upperCase = data.toUpperCase();
                System.out.println(upperCase);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}



6.7 打印流

  • 实现将基本数据类型的数据格式转化为字符串输出
  • 打印流:PrintStream和PrintWriter
    • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
    • PrintStream和PrintWriter的输出不会抛出IOException异常
    • PrintStream和PrintWriter有自动flush功能
    • PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。 在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
    • System.out返回的是PrintStream的实例
package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

/**
 * @author zhengxu
 * @create 2021-08-02 15:44
 */
public class PrintStreamTest {
    /*
    2.打印流:PrintSteam和PrintWriter
    2.1 提供了一系列重载的print和println
    2.2 练习:将255个字符不输出到控制台,而是利用System.setOut()输出重定向到文件中
     */
    @Test
    public void test1(){
        PrintStream ps= null;
        try {
            FileOutputStream fos=new FileOutputStream(new File("src\\print.txt"));
            //创建打印输出流,设置为自动刷新模式(写入换行字符或字节‘n’时都会刷新输出缓冲区
            ps = new PrintStream(fos,true);
            if(ps!=null){//把标准输出流(控制台输出)改成文件
                System.setOut(ps);
            }
            for(int i=0;i<=255;i++){//输出ASCII字符
                System.out.print((char)i);
                if(i%50==0){//每50个数据一行
                    System.out.println();//换行
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ps!=null){
                try {
                    ps.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

6.8 数据流

  • 为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。
  • 数据流有两个类:(用于读取和写出基本数据类型、String类的数据)
    • DataInputStream 和 DataOutputStream
    • 分别“套接”在 InputStream 和 OutputStream 子类的流上
  • DataInputStream中的方法
    • boolean readBoolean() byte readByte()
    • char readChar() float readFloat()
    • double readDouble() short readShort()
    • long readLong() int readInt()
    • String readUTF() void readFully(byte[] b)
  • DataOutputStream中的方法
    • 将上述的方法的read改为相应的write即可。
package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.*;

/**
 * @author zhengxu
 * @create 2021-08-02 15:56
 */
public class DataStreamTest {
    /*
    3.数据流
    3.1 DataInputStream和DataOutputStream
    3.2 作用:用于读取或写出基本数据类型的变量或字符串

    练习:将内存中的字符串、基本数据类型的变量写到文件中。

    注意:处理异常的话,仍然应该使用try-catch-finally
     */
    @Test
    public void test1()  {
        DataOutputStream dos= null;
        try {
            dos = new DataOutputStream(new FileOutputStream("data.txt"));
            dos.writeUTF("青龙月");
            dos.writeInt(12);
            dos.writeBoolean(true);
            dos.flush();//刷新操作,一旦执行就会将内存已有的数据写入文件
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(dos!=null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /*
    将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中
    注意点:读取不同类型的数据的顺序要与当初写入文件时,保存数据的顺序一致。
     */
    @Test
    public void test2(){

        DataInputStream dis= null;
        try {
            dis = new DataInputStream(new FileInputStream("data.txt"));
            String name = dis.readUTF();
            int age=dis.readInt();
            boolean isMale=dis.readBoolean();

            System.out.println("name="+name);
            System.out.println("age="+age);
            System.out.println("isMale="+isMale);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(dis!=null){
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6.9 对象流

  • ObjectInputStream和OjbectOutputSteam

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

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

  • ObjectOutputStream和ObjectInputStream不能序列化statictransient修饰的成员变量

  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传 输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原 来的Java对象

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

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

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

    • Serializable
    • Externalizable
  • 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量

    • private static final long serialVersionUID;
    • serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象 进行版本控制,有关各版本反序列化时是否兼容。
    • 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议, 显式声明。
  • 简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验 证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同 就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异 常。(InvalidCastException)

package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.*;

/**
 * 对象流的使用
 * 1.ObjectInputStream和ObjectOutputStream
 * 2.作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把java中的对象还原回来
 *
 * 3.要想一个对象是可序列化的,需要满足如下要求。见Person.java
 *
 * 4.序列化机制:
 *   对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,
 *   或通过网络将这种二进制流传 输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原 来的Java对象
 * @author zhengxu
 * @create 2021-08-02 18:04
 */
public class ObjectStreamTest {

    /*
    序列化过程:将内存中的java对象保存到磁盘中或者通过网络传输出去
    使用ObectOutputStream实现
     */
    @Test
    public void testObjectOutputStream(){
        ObjectOutputStream oos= null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("src\\object.dat"));

            oos.writeObject(new String("我爱中国!"));
            oos.flush();//刷新操作

            oos.writeObject(new Person("龙",18));//报错:NotSerializableException: study.javaSenior.IOStream.Person
            oos.flush();

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

    /*
    反序列化:将磁盘文件中的对象还原为内存中的一个java对象
    使用ObjectInputStream来实现
     */
    @Test
    public void testObjectInputStream(){

        ObjectInputStream ois= null;
        try {
            ois = new ObjectInputStream(new FileInputStream("src\\object.dat"));

            String str=(String) ois.readObject();
            Person p=(Person) ois.readObject();

            System.out.println(str);//我爱中国!
            System.out.println(p);//Person{name='龙', age=18}
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois!=null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【自定义可序列化】:Person类

package study.javaSenior.IOStream;

import java.io.Serializable;

/**
 * Person需要满足如下要求方可序列化:
 * 1.需要实现接口:Serializable
 * 2.需要当前类提供一个全局常量:serialVersionUID
 * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。
 *   (默认情况下,基本数据类型是可序列化的)
 *
 * 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
 * @author zhengxu
 * @create 2021-08-02 18:24
 */
public class Person implements Serializable {

    public static final long serialVersionUID=42788376788L;

    private String name;
    private int age;

    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 String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

6.10 随机存取文件流

  • RandomAccessFile 声明在java.io包下,但直接继承于java.lang.Object类。并 且它实现了DataInput、DataOutput这两个接口,也就意味着这个类既可以读也 可以写。
  • RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意 地方来读、写文件
    • 支持只访问文件的部分内容
    • 可以向已存在的文件后追加内容
  • RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。
    • RandomAccessFile 类对象可以自由移动记录指针:
      • long getFilePointer():获取文件记录指针的当前位置
      • void seek(long pos):将文件记录指针定位到 pos 位置
package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;

/**
 * RandomAccessFile的使用
 * 1.RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
 * 2.RandomAccessFile既可以作为输入流,也可以作为输出流
 * 3.如果RandomAccessFile作为输出流时,写出到的文件不存在,则在执行过程中自动创建
 *   如果写出到的文件存在,则会对原有文件从头覆盖。(覆盖一部分)也可以自己设定覆盖位置。
 *
 * @author zhengxu
 * @create 2021-08-03 8:09
 */
public class RandomAccessStreamTest {

    @Test
    public void test1(){
        RandomAccessFile raf1= null;
        RandomAccessFile raf2= null;
        try {
            raf1 = new RandomAccessFile(new File("1.jpg"),"r");
            raf2 = new RandomAccessFile(new File("rand.jpg"),"rw");

            byte[] buf=new byte[1024];
            int len;
            while((len= raf1.read(buf))!=-1){
                raf2.write(buf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(raf1!=null){
                try {
                    raf1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(raf2!=null){
                try {
                    raf2.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void test2(){
        RandomAccessFile raf1= null;
        try {
            raf1 = new RandomAccessFile("src\\hello2.txt","rw");//hello world!
            raf1.seek(3);//将指针调到角标位3的位置
            raf1.write("xyz".getBytes(StandardCharsets.UTF_8));//helxyzworld!
            raf1.seek(raf1.length());//将指针调到文件末尾
            raf1.write("zzz".getBytes(StandardCharsets.UTF_8));//helxyzworld!zzz
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(raf1!=null){
                try {
                    raf1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6.11 NIO.2中Path、Paths、Files类的使用

  • Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新 的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目 的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于 通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。

  • Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网 络编程NIO。

    • |-----java.nio.channels.Channel
      • |-----FileChannel:处理本地文件
      • |-----SocketChannel:TCP网络编程的客户端的Channel
      • |-----ServerSocketChannel:TCP网络编程的服务器端的Channel
      • |-----DatagramChannel:UDP网络编程中发送端和接收端的Channel
  • 早期的Java只提供了一个File类来访问文件系统,但File类的功能比较有限,所 提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异 常信息。

  • NIO. 2为了弥补这种不足,引入了Path接口,代表一个平台无关的平台路径,描 述了目录结构中文件的位置。Path可以看成是File类的升级版本,实际引用的资 源也可以不存在。

  • 在以前IO操作都是这样写的:

    import java.io.File;

    File file = new File(“index.html”);

  • 但在Java7 中,我们可以这样写:

    import java.nio.file.Path;

    import java.nio.file.Paths;

    Path path = Paths.get(“index.html”);

  • 同时,NIO.2在java.nio.file包下还提供了Files、Paths工具类,Files包含 了大量静态的工具方法来操作文件;Paths则包含了两个返回Path的静态 工厂方法。

  • Paths 类提供的静态 get() 方法用来获取 Path 对象:

    • static Path get(String first, String … more) : 用于将多个字符串串连成路径
    • static Path get(URI uri): 返回指定uri对应的Path路径
Path 常用方法:
 String toString() : 返回调用 Path 对象的字符串表示形式
 boolean startsWith(String path) : 判断是否以 path 路径开始
 boolean endsWith(String path) : 判断是否以 path 路径结束
 boolean isAbsolute() : 判断是否是绝对路径
 Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径
 Path getRoot() :返回调用 Path 对象的根路径
 Path getFileName() : 返回与调用 Path 对象关联的文件名
 int getNameCount() : 返回Path 根目录后面元素的数量
 Path getName(int idx) : 返回指定索引位置 idx 的路径名称
 Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
 Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
 File toFile():Path转化为File类的对象
package study.javaSenior.IOStream;

import org.junit.Test;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * 1. jdk 7.0 时,引入了 Path、Paths、Files三个类。
 * 2.此三个类声明在:java.nio.file包下。
 * 3.Path可以看做是java.io.File类的升级版本。也可以表示文件或文件目录,与平台无关
 * 4.如何实例化Path:使用Paths.
 * static Path get(String first, String … more) : 用于将多个字符串串连成路径
 * static Path get(URI uri): 返回指定uri对应的Path路径
 * @author zhengxu
 * @create 2021-08-03 14:32
 */
public class PathTest {
    //如何使用Paths实例化Path
    @Test
    public void test1() {
        Path path1 = Paths.get("d:\\nio\\hello.txt");//new File(String filepath)

        Path path2 = Paths.get("d:\\", "nio\\hello.txt");//new File(String parent,String filename);

        System.out.println(path1);
        System.out.println(path2);

        Path path3 = Paths.get("d:\\", "nio");
        System.out.println(path3);
    }

    //Path中的常用方法
    @Test
    public void test2() {
        Path path1 = Paths.get("d:\\", "nio\\nio1\\nio2\\hello.txt");
        Path path2 = Paths.get("hello.txt");

//		String toString() : 返回调用 Path 对象的字符串表示形式
        System.out.println(path1);

//		boolean startsWith(String path) : 判断是否以 path 路径开始
        System.out.println(path1.startsWith("d:\\nio"));
//		boolean endsWith(String path) : 判断是否以 path 路径结束
        System.out.println(path1.endsWith("hello.txt"));
//		boolean isAbsolute() : 判断是否是绝对路径
        System.out.println(path1.isAbsolute() + "~");
        System.out.println(path2.isAbsolute() + "~");
//		Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径
        System.out.println(path1.getParent());
        System.out.println(path2.getParent());
//		Path getRoot() :返回调用 Path 对象的根路径
        System.out.println(path1.getRoot());
        System.out.println(path2.getRoot());
//		Path getFileName() : 返回与调用 Path 对象关联的文件名
        System.out.println(path1.getFileName() + "~");
        System.out.println(path2.getFileName() + "~");
//		int getNameCount() : 返回Path 根目录后面元素的数量
//		Path getName(int idx) : 返回指定索引位置 idx 的路径名称
        for (int i = 0; i < path1.getNameCount(); i++) {
            System.out.println(path1.getName(i) + "*****");
        }

//		Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
        System.out.println(path1.toAbsolutePath());
        System.out.println(path2.toAbsolutePath());
//		Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
        Path path3 = Paths.get("d:\\", "nio");
        Path path4 = Paths.get("nioo\\hi.txt");
        path3 = path3.resolve(path4);
        System.out.println(path3);

//		File toFile(): 将Path转化为File类的对象
        File file = path1.toFile();//Path--->File的转换

        Path newPath = file.toPath();//File--->Path的转换

    }
}

你可能感兴趣的:(Java学习笔记,java)