IO流

IO流

一、文件对象

File :代表 文件文件夹

  • File类:文件和文件目录路径的抽象表示形式,与平台无关
  • File能新建、删除、重命名文件和目录,但File不能访问文件内容本身(需要流)
  • 一个java程序中File对象可能没有真实存在文件或目录,但真实文件或目录必须用File表示
  • File对象可以作为参数传递给流的构造器

1.1 创建文件

  • 使用绝对路径创建文件
  • 使用相对路径创建文件
public static void createFile(){
        File file1 = new File("D:\\英雄时刻");
        System.out.println("file1文件的绝对路径为:"+file1.getAbsoluteFile());
        
    	//判断文件是否存在
   		System.out.println(file1.exists());
    	
    	//把file1作为父目录创建文件对象
        File file2 = new File(file1,"mysql.sql");
        System.out.println("file1文件的绝对路径为:"+file2.getAbsoluteFile());
    }

常用方法:

  • 获取

    • String getAbsolutePath():获取绝对路径
    • String getPath() :获取路径
    • String getName() :获取名称
    • String getParent():获取上层文件目录路径。若无,返回null
    • long length() :获取文件长度(即:字节数)不能获取目录的长度
    • long lastModified() :获取最后一次的修改时间,毫秒值
    • String[] list() :获取指定目录下的所有文件或者文件目录的字符串数组
    • File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
  • 重命名

    • boolean renameTo(File dest):把文件重命名为指定的文件路径
  • 判断

    • boolean isDirectory():判断是否是文件目录
    • boolean isFile() :判断是否是文件
    • boolean exists() :判断是否存在
    • boolean canRead() :判断是否可读
    • boolean canWrite() :判断是否可写
    • boolean isHidden() :判断是否隐藏
  • 创建

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

    • boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。

      如果此文件目录的上层目录不存在,也不创建

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

  • 删除

    • boolean delete():(永久删除)删除文件或者文件夹(文件夹必须为空)
public TextFile(){
    public static void fileMethod(){
        //创建文件
        File file = new File("D:\\sql.sql");
        String parentPath = file.getAbsolutePath();
        //把file作为父目录创建文件对象
        File file1 = new File(file,"mysql.sql");
        //把filePath作为父目录创建文件对象
        File file1 = new File(parentPath,"mysql.sql");
        //判断文件是否存在
        file.exists();
        //判断file是否为文件夹
        file.isDirectory();
        //判断是否为文件
        file.isFile();
        //查看文件的大小(字节)
        file.length();
        //重命名文件(物理文件的重命名,类属性不变)
        file.renameTo(new File("D:\\mysql.sql"));
        //创建文件的上级所有的文件(如果文件夹不存在)
        file.mkdirs();
        //删除文件
        file.delete();
        // JVM结束的时候,刪除文件,常用于临时文件的删除
        file.deleteOnExit();
        // 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        file.list();
        // 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        File[] files = file.listFiles();
    }
}

二、IO流

I/O是Input/Output的缩写

  • 用于处理设备之间的数据传输(读/写文件,网络通讯)
  • java程序中,数据的输入、输出操作以“**流(Stream)“**的方式进行

Java IO 原理

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

IO流_第1张图片

Java IO流众多,但全部都是从4个抽象基类派生:

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

IO流_第2张图片

2.1字节输入流(InputStream)

  • int read():读取数据的下一个字节,返回0到255范围内的int,到达末尾没有字节返回-1
  • int read(byte[] b) 读取b.length个字节到byte数组中,返回实际读取的字节数,到达末尾没有字节返回-1
  • int read(byte[] b, int off, int len) 读取byte数组中下标从off开始的len个字节到byte数组中,但读取的字节也可能小于len。返回实际读取的字节数。到达末尾没有字节返回-1
  • void close();关闭流

2.2字符输入流(Reader)

  • int read():读取数据的下一个字符,返回0到255范围内的int,到达末尾没有字符返回-1
  • int read(byte[] b) 读取b.length个字符到byte数组中,返回实际读取的字符数,到达末尾没有字符返回-1
  • int read(byte[] b, int off, int len) 读取byte数组中下标从off开始的len个字符到byte数组中,但读取的字符也可能小于len。返回实际读取的字符数。到达末尾没有字符返回-1
  • void close();关闭流

2.3 字节输出流(OutputStream)

  • void writer(int b):将指定的字节写出。write 的常规协定是:写出一个字节。要写 出的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写出0~255范围的。
  • void writer(byte[] b):将 b.length 字节从指定的 byte 数组写出,write(b) 的常规协定是:应该 与调用 write(b, 0, b.length) 的效果完全相同。。
  • void writer(byte[] b, int off, int len):写出byte数组中下标从off开始的len个字节,但写出的字节也可能小于len
  • void flush();将管道中的数据挤出
  • void close();关闭流

2.4 字符输出流(Writer)

  • void writer(int b):将指定的字符写出。write 的常规协定是:写出一个字符。要写 出的字符是参数 b 的16个低位。b 的 16 个高位将被忽略。 即写出0~65535范围的Unicode码。
  • void writer(byte[] b):将 b.length 字符从指定的 byte 数组写出,write(b) 的常规协定是:应该 与调用 write(b, 0, b.length) 的效果完全相同。。
  • void writer(byte[] b, int off, int len):写出byte数组中下标从off开始的len个字节,但写出的字符也可能小于len
  • void flush();将管道中的数据挤出
  • void close();关闭流

2.5 文件字符输入流(FileReader)

  • 建立一个流对象,将已存在的一个文件加载进流。 FileReader fr = new FileReader(new File(“Test.txt”))
  • 创建一个临时存放数据的数组。 char[] ch = new char[1024];
  • 调用流对象的读取方法将流中的数据读入到数组中。 fr.read(ch);
  • 关闭资源。 fr.close();
//方式一:
public static void read() {
        FileReader fr = null;
        try {
            File file = new File("hello_world\\test.txt");
            fr = new FileReader(file);
            char[] chars = new char[2];
            int read  ;//0~65535范围的Unicode
            while ((read=fr.read(chars))!=-1){
               for (int i = 0; i <read ; i++) {
                   System.out.print((char) chars[i]);
               }

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

  }

//方式二:
public static void read() {
        FileReader fr = null;
        try {
            File file = new File("hello_world\\test.txt");
            fr = new FileReader(file);
            char[] chars = new char[5];
            int read  ;
            while ((read=fr.read(chars))!=-1){
                String str = new String(chars,0,read);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if (fr!=null)fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

//方式三:
public static void read() {
        FileReader fr = null;
        try {
            File file = new File("hello_world\\test.txt");
            fr = new FileReader(file);
            char[] chars = new char[3];
            int read  ;
            //创建一个StringBuffer拼接字符
            StringBuffer strB = new StringBuffer();
            while ((read=fr.read(chars))!=-1){
                strB.append(chars,0,read);
            }
            System.out.println(strB.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                if (fr!=null)fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

注意:File file = new File(“hello_world\test.txt”);为相对路径,当前项目hello_world文件夹下test.txt文件

2.6 文件字符输出流(FileWriter)

1.创建流对象,建立数据存放文件

FileWriter fw = new FileWriter(new File(“Test.txt”));

2.调用流对象的写入方法,将数据写入流

fw.write(“我要征服世界”);

3.关闭流资源

fw.close();

  • 定义文件路径时,注意:可以用“/”或者“\”
  • 在写出一个文件时,如果使用构造器FileWriter(file),则目录下有同名文件将被覆盖。
  • 使用FileWriter(file,true),则目录下同名文件不会被覆盖,在文件末尾追加内容
  • 在读取文件时,必须保证该文件已存在,否则报异常
  • 字符输出流,能操作普通文本文件。最常见的文件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意**.doc,excel,ppt**这些不是文 本文件

写出字符串

public static void  write(){
        FileWriter fw = null;
        try {
            File file = new File("hello_world\\test.txt");
            //不会覆盖原有文件,追加内容
            fw = new FileWriter(file,true);
            fw.write("我要征服世界");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw!=null){
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

读取一个文件,写出到指定位置

public static void  write(){
        FileWriter fw = null;
        FileReader fr = null ;
        try {

            File file = new File("hello_world\\test.txt");
            fr = new FileReader("hello_world\\pom.xml");
            //不会覆盖原有文件,追加内容
            fw = new FileWriter(file,true);
            char[] cBuf = new char[64];
            int read ;
            while((read=fr.read(cBuf))!=-1){
                fw.write(cBuf,0,read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw!=null){
                    fw.close();
                }
                if (fr!=null)fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

2.7 文件字节输入流

  • 与文件字符输入流大致相同
  • 字节输入流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
  • 字节流不能读取文本文件(字符),出现乱码(可以复制文本文件)

2.8 文件字节输出流

  • 与文件字符输出流大致相同

  • 字节输出流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt

  • 字节流不能读取文本文件(字符),出现乱码

  • 字符流操作字符,只能操作普通文本文件。最常见的文本文

    件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文 本文件

读取git.md,写出到硬盘下

public static void  write(){
        FileOutputStream fo = null;
        FileInputStream fi = null ;
        try {

            File file = new File("hello_world\\test.md");
            //读入git.md到内存中
            fi = new FileInputStream("hello_world\\git.md");
            //覆盖当前目录下同名文件
            fo = new FileOutputStream(file);
            byte[] bBuf = new byte[64];
            int read ;
            while((read=fi.read(bBuf))!=-1){
                fo.write(bBuf,0,read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fo!=null)
                    fo.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fi!=null)
                    fi.close();
            } catch (IOException e){
                e.printStackTrace();
            }

        }
    }

2.9 缓冲流

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

    public class BufferedInputStream extends FilterInputStream {
    
        private static int DEFAULT_BUFFER_SIZE = 8192
            
            
     public class BufferedReader extends Reader {
         
        private static int defaultCharBufferSize = 8192;
    
  • 缓冲流要“套接”在相应的节点流上分为(见名知意)

    • BufferedInputStream
    • BufferedReader
    • BufferedOutputStream
    • BufferedWriter

缓冲流能有效的提升数据的读写

特点:

  • 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
  • 当读取文件时,会一次性从 文件中读取8192个(8Kb),存在缓冲区中,然后从缓冲区中读取byte数组大小到内存中, 缓冲区的文件全部读取完后,读取下一个8192个字节数组到缓冲区
  • 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush()可以强制将缓冲区的内容全部写入输出流
  • 关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也 会相应关闭内层节点流
  • flush()方法的使用:手动将buffer中内容写入文件
  • 如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷 新缓冲区,关闭后不能再写

字符缓冲流与字节缓冲流大致相同,缓冲区为8192个字符

IO流_第3张图片

public static void copyFile(String srcPath,String destPath){
        BufferedInputStream br = null;
        BufferedOutputStream bw = null;
        try {
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);

            FileInputStream fr = new FileInputStream(srcFile);
            FileOutputStream fw = new FileOutputStream(destFile);
            br = new BufferedInputStream(fr);
            bw = new BufferedOutputStream(fw);
            byte[] buff = new byte[1024];
            int len ;
            while((len=br.read(buff))!=-1){
                bw.write(buff,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //缓冲流关闭,自带关闭文件流
            if (br!=null) {
                try {
                    br.close() ;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw!=null) {
                try {
                    bw.close() ;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {

        long start = System.currentTimeMillis();
        String srcPath ="D:\\Users\\333\\Desktop\\毕业设计\\2020.3.4.zip" ;
        String destPath ="D:\\Users\\333\\Desktop\\2020.3.4.zip";
        copyFile(srcPath, destPath);
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }

2.10 转换流

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

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

构造器:

  • public InputStreamReader(InputStream in)
  • public InputSreamReader(InputStream in,String charsetName)

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

public static void change()  {
        InputStreamReader isr = null;
        try {
            File file = new File("hello_world\\java web.txt");
            InputStream is = new FileInputStream(file) ;
            //将字节流转换为字符流
            isr = new InputStreamReader(is);
            char[] cBuf = new char[1024] ;
            int len ;
            while((len=isr.read(cBuf))!=-1){
                for (int i=0;i<len;i++){
                    System.out.print((char)cBuf[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (isr!=null) {
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

2.11 键盘输入

键盘输入System.in为字节流extends InputStream,所以需要使用到转换流转换为字符流,再使用缓冲流读出

//读取键盘输入一行字符串
    public static void systemIn()  {
        InputStreamReader isr = null;
        BufferedReader br = null ;
        try {
            isr = new InputStreamReader(System.in,"GBK");
            br = new BufferedReader(isr);
            String s = br.readLine();
            System.out.println(s);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br!=null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

三、字符编码

  • 编码表的由来

计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识

别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。

这就是编码表。

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

四、序列化与反序列化

4.1 对象流

作用:用于存储和读取基本数据类型和对象的处理流,可以把Java中的对象写入到磁盘中,也能把对象从磁盘中读取回来

  • ObjectInputStream:对象输入流
  • ObjectOutputStream:对象输出流

对象流运用场景:

  • 序列化与反序列化
  • 网络传输

4.2 对象序列化

序列化:把Java中的对象转换为二进制写入到磁盘中

如果需要序列化,该类必须实现如下两个接口之一

  • Serializable(常用)
  • Extemalizable

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

注意:被staticstransient关键字修饰的成员变量不能被序列化

4.3 反序列化

反序列化:把磁盘中二进制文件转换为Java中的对象

4.4 SerialVersionUID

凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:private static final long serialVersionUID

  • 表名类的不同版本间的兼容性
  • 在序列化时,Java运行时环境根据类的内部细节自动生成一个serialVersionUID
  • 在反序列化,根据serialVersionUID与本地相应实体类的serialVersionUID的值匹配,若相同,还原该类
  • 若类的实例变量做了修改,serialVersionUID可能发生变化,在反序列化时还原不了该类
  • 显式声明SerialVersionUID,即使类的实例变量做了修改,serialVersionUID也不会变,保证序列化与反序列化的执行

未声明SerialVersionUID

序列化

public class Person implements Serializable {

    private Integer id ;
    private String name ;
    private Pet pet ;
}

改变属性

public class Person implements Serializable {

    private Integer id ;
    private String name ;
    private Pet pet ;
    private int age ;//添加了age属性
}

反序列化

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

声明SerialVersionUID后

序列化

public class Person implements Serializable {

    private static final long serialVersionUID = 10000 ;
    private Integer id ;
    private String name ;
    private Pet pet ;
}

改变属性

public class Person implements Serializable {

    private static final long serialVersionUID = 10000 ;
    private Integer id ;
    private String name ;
    private Pet pet ;
    private int age ;//添加了age属性
}

反序列化

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

4.5 序列化的过程

  1. 实现Serializable接口
  2. 创建ObjectOutputStream对象
  3. 调用**writeObject(Object object)**方法输出对象

注意:

  • 写出一次,操作flush()方法一次
  • 类的属性为引用类型,该引用类型必须也是可序列化的(实现Serializable接口)

序列化对象

public class Person implements Serializable {
    
    private Integer id ;
    private String name ;
    private Pet pet ;
}

public class Pet implements Serializable {
    private String name ;
    private Integer age ;
}

序列化

	public static void demo01() throws IOException {
        Person json = new Person(001, "json",new Pet("旺财", 3));
        File file = new File("person.txt");
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(json);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (oos!=null)
                oos.close();
        }
    }

4.6 反序列化的过程

  1. 创建 ObjectInputStream 对象
  2. 调用 **readObject()**方法读取流中的对象

反序列化

	public static void demo02() {
        File file = new File("person.txt");
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(file));
            Object o = ois.readObject();
            System.out.println(o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ois!=null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

4.7 网络传输

服务端

	public static void server()  {
        //创建ServerSocket对象,指定端口号
        ObjectInputStream ois = null;
        try {
            ServerSocket ss = new ServerSocket(9999);
            //监听客户端的连接
            Socket s = ss.accept();
            System.out.println("客户端已连接。。。");
            //获取网络传输字节输入流
            InputStream is = s.getInputStream();
            //创建对象输入流
            ois = new ObjectInputStream(is);
            //获取对象
            Object o = ois.readObject();
            System.out.println(o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭对象流
            if (ois!=null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

客户端

	public static void client()  {
        Person person = new Person(001, "json", new Pet("旺财", 3));
        //连接服务端
        ObjectOutputStream oos = null;
        try {
            Socket s = new Socket("127.0.0.1",9999);
            //获取网络传输输出流
            OutputStream os = s.getOutputStream();
            //创建对象输出流
            oos = new ObjectOutputStream(os);
            //传送person对象
            oos.writeObject(person);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭输出流
            try {
                if (oos!=null)
                    oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

IO流_第4张图片

注意:

  • 网络传输的输入输出流必须从Socket对象中获取,才能用于网络传输
  • 自定义的输入输出流只能操作磁盘

IO流_第5张图片

你可能感兴趣的:(IO流)