IO流基础

IO流

IO就是Input和Output的简写头字母,也就是输入和输出的意思

就是读写数据时像流水一样从一端流到另外一端,因此得名为"流"
也可以理解为数据从硬盘与程序之间交互的这个过程

文件

文件我们都不陌生,在日常生活中使用的word文件,excel文件,pdf文件等等…都是文件
简单的说 “文件”就是保存数据的地方。

输出流和输入流

文件在程序中是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的途径

输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
抽象点形式就是,例如,一个java程序要从C盘的某个文件夹中读取某个文件的数据。这个就是输入流
如果是一个java程序要创建一个文件,且在这个文件中写数据。然后将这个文件保存在C盘中,这个就是输出流

IO流基础_第1张图片

创建文件

IO流基础_第2张图片

在java中可以使用一个类来在硬盘中创建文件,也就是输出流。
下面用File类来创建一个文件。

File类有三种构造器来创建文件的方法

第一种:
new File(String pathname)//根据路劲构建一个File对象
第二种:
new File(File parent,String child)//根据父目录文件+子路径构建
第三种
new File(String parent,String child)//根据父目录+子路径构建

public class test1 {
    public static void main(String[] args) {
        //第一种  new File(String pathname)//根据路劲构建一个File对象
        String filePath = "D:\\test/news1.txt";//路劲和文件名以文件后缀
        File file = new File(filePath);//在程序内存中创建一个file对象
        try {
            file.createNewFile();//将文件写入硬盘中
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        
        //第二种  new File(File parent,String child)//根据父目录文件+子路径构建
        File parentFile = new File("D:\\");//定义父类目录文件
        String fileName = "test/news2.txt";//定义子路劲,以及创建的文件名和后缀
        File file2 = new File(parentFile, fileName);//在内存中创建file对象
        try {
            file2.createNewFile();//将文件写入硬盘中
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        
        //第三种  new File(String parent,String child)//根据父目录+子路径构建
        String parentFile1 = "D:\\";//定义父类目录文件
        String childFile = "test/news3.txt";//定义子路劲,以及创建的文件名和后缀
        File file3 = new File(parentFile1, childFile);//在内存中创建file对象
        try {
            file3.createNewFile();//将文件写入硬盘中
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

File类的常用方法

File有很多方法,包括对文件的操作,信息读取等等…

获取文件的名字:getName()
获取文件的绝对路径:getAbsolutePath()
获取文件的父类目录:getParent()
返回文件的大小(字节:file.length()
文件是否存在:exists()
是不是一个文件:isFile()
是不是一个目录:isDirectory()

public class test2 {
    public static void main(String[] args) {
        File file = new File("D:\\test/news1.txt");
        System.out.println("获取文件的名字"+file.getName());
        System.out.println("获取文件的绝对路劲"+file.getAbsolutePath());
        System.out.println("获取文件的父类目录"+file.getParent());
        System.out.println("返回文件的大小(字节)"+file.length());
        System.out.println("文件是否存在"+file.exists());
        System.out.println("是不是一个文件"+file.isFile());
        System.out.println("是不是一个目录"+file.isDirectory());
    }
}

运行结果:
获取文件的名字news1.txt
获取文件的绝对路径D:\test\news1.txt
获取文件的父类目录D:\test
返回文件的大小(字节)5
文件是否存在true
是不是一个文件true
是不是一个目录false

File类对目录的创建和删除

目录也就是文件夹,在编程中其实目录也可以看做是一个文件。下面有几种方法可以对目录进行创建和删除

mkdir:创建一级目录
mkdirs:创建多级目录
delete:删除空目录或文件

小练习

  1. 判断D盘中的news1.txt是否存在,如果存在就删除
public static void main(String[] args) {
        File file = new File("D:\\test/news1.txt");
        if (file.exists()){
            file.delete();
        }else{
            System.out.println("文件不存在");
        }
    }
  1. 判断D盘中的demo2目录是否存在,如果存在就删除,否则提示不不存在
public static void main(String[] args) {
        File file = new File("D:\\demo2");
        if (file.exists()){
            file.delete();
        }else{
            System.out.println("文件不存在");
        }
    }
  1. 判断D盘中的demo2目录是否存在,如果存在就提示存在,否则就创建,且在demo2中创建目录a,a中创建b目录
public static void main(String[] args) {
        File file = new File("D:\\demo2\\a\\b");
        if (file.exists()){
            System.out.println("存在");
        }else{
            System.out.println("不存在,正在创建");
            file.mkdirs();
        }
    }

IO流原理和流的分类

IO流的原理

IO就是Input和Output的缩写
IO技术是非常实用的技术,用于处理数据间的传输,如读/写,网络通讯等等

java程序中对于数据的输入和输出操作是以“流(stream)”的方式进行的
java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出

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

流的分类

:程序使用相同的方式来访问不同的输入/输出源。stream(流)是从起源(source)到接收的(sink)的有序数据
通俗的说:例如 一个人在家网购,商品从商家 到 网购人的手中,这个过程有物流公司来实现。那么这个物流公司就可以看做是一个流,快递小哥也可以看做是流。

  • 按操作数据单位,流可以分为:字节流,字符流
  • 按数据流的流向不同可以分为:输出流,输入流
  • 按流的角色可以分为:节点流,处理流/包装流

字节流可以处理一切,字符流只能处理纯文本。
java中提供了很多流的类用于操作不同类似的文件

抽象基类 字节流 字符流
输入流 inputStream Reader
输出流 outputStream Writer
java的IO流共涉及40多个类,但是其实都是从4个抽象基类派生的
由上面4个抽象类派生的子类名称都是以其父类名作为子类名后缀的

IO流基础_第3张图片

inputStream抽象类

inputStream抽象类是字节输入流基类,用于从外部读取文件,并对文件进行操作

FileInputStream类

FileInputStream是inputStream的子类,是一个文件输入流。
可以理解为,物流公司的快递小哥,负责拿到你的包裹为你处理派送等任务。
类图
IO流基础_第4张图片
构造方法

  1. FileInputStream(File file),打开一个到实际文件的连接来创建一个FileInputStream对象,该文件通过File对象指定
  2. FileInputStream(FileDescriptor fdobj)使用文件描述符fdobj,创建一个FileInputStream对象,该对象表示到文件系统中某个实际文件的现有连接
  3. FileInputStream(Srting name)打开一个实际文件的连接来创建一个FileInputStream对象,该文件通过文件系统的路径(name)指定

常用方法
close():关闭文件输入流,且释放与此流有关的所有系统资源

read():从该输入流中读取一个字节,如果到达文件末尾返回-1
read(byte[] b):从该输入流中读取b.length个字节存到b数组中,如果到达文件末尾返回-1
read(byte[] b,int off,int len):从该输入流中off处,读取最多len个字节,存入b数组中,如果到达文件末尾返回-1

小练习

要求从D盘中读取test目录下的hello.txt文件(文件里内容是:hello,world),并在控制台输出。
记得关闭流,防止资源浪费。如果不关闭,该流会一直指向该文件资源
使用read(),read(byte[] b),read(byte[] b,int off,int len),三种方法分别实现。

思路分析:
首先创建一个FileInputStream,对象,并让该对象指向hello.txt
然后使用read读取文件,存入程序中的某个属性。
因为一次读取的是一个字节,所以需要遍历读取

代码
第一种:read()

public void readHello1(){
        //1.创建一个File对象,hello.txt的路径放进去
        File file = new File("D:\\test/hello.txt");
        FileInputStream fI = null;
        try {
            //2.创建一个FileInputStream输入流对象,将路劲给到流,编译异常处理一下
            fI = new FileInputStream(file);
            //3.创建循环,使用read方法读取字节,因为读取回来的是数字,所以先用int接受,输出强转成char。
            int date;
            while ((date = fI.read())!= -1){//这里表示先把read返回的值给date,然后判断date是不是-1,如果是-1就表示
                System.out.print((char)date);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fI.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

运行结果:
hello,world

第二种:read(byte[] b)

public void readHello2(){
        //1.创建一个File对象,hello.txt的路径放进去
        File file = new File("D:\\test/hello.txt");
        FileInputStream fI = null;
        try {
            //2.创建一个FileInputStream输入流对象,将路劲给到流,编译异常处理一下
            fI = new FileInputStream(file);
            //3.创建循环,使用read(byte[] b)方法读取字节,存入b数组
            byte [] b = new byte[20];
            int readlen = 0;
            while ((readlen = fI.read(b))!= -1){//这里表示先把read返回的值给date,然后判断date是不是-1,如果是-1就表示

                System.out.print(new String(b,0,readlen));//从b数组 0 下标处读取readlen个元素拼接成一个String

            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fI.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

运行结果:
hello,world

第三种
read(byte[] b,int off,int len)

//第三种:read(byte[] b,int off,int len)
    @Test
    public void readHello3(){
        //1.创建一个File对象,hello.txt的路径放进去
        File file = new File("D:\\test/hello.txt");
        FileInputStream fI = null;
        try {
            //2.创建一个FileInputStream输入流对象,将路劲给到流,编译异常处理一下
            fI = new FileInputStream(file);
            //3.创建循环,使用read(byte[] b,int off,int len)方法读取字节,存入b数组,从文件的0处开始读取,到len处。
            byte [] b = new byte[20];
            int readlen = 0;
            while ((readlen = fI.read(b,0,11))!= -1){//这里表示先把read返回的值给date,然后判断date是不是-1,如果是-1就表示

                System.out.print(new String(b,0,readlen));//从b数组 0 下标处读取readlen个元素拼接成一个String

            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fI.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

运行结果:
hello,world

outputStream抽象类

outputStream抽象类是字节输出流基类,用于将程序的数据写出到外部存储容器中。

FileOutputStream类

FileOutputStream类是outputStream的子类,是一个文件输出流

构造器
FileOutputStream(File file):创建一个向指定File对象表示的文件写入数据的文件输出流
FileOutputStream(File file,boolean append):创建一个向指定File对象表示的文件写入数据的文件输出流,(在文件内容后面追加内容)
FileOutputStream(String name):创建一个具有指定name的文件,写入数据的文件输出流
FileOutputStream(String name,boolean append):创建一个具有指定name的文件,写入数据的文件输出流,(在文件内容后面追加内容)

常用方法
close:关闭流
write (int b):将指定字节写入文件输出流

write (byte[] b):将b.length个 字节写入文件输出流

write (byte[] b,int off ,int len ):将b.length个 字节写入文件输出流,从b的off处开始,len处结束

小练习
一:
使用FileOutputStream类,在D盘的txt文件夹中创建a.txt,且添加内容为:hello。

public void m1(){
        //1.创建FileOutputStream类,并放入 路径和文件名以及类型
        String fileName = "D:\\test/a.txt";
        FileOutputStream fileOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream(fileName);
            //2.创建文件且写入内容,注意如果发现路径没有该文件,则该方法会自动创建
            //此方法只能写入一个字节,fileOutputStream.write('H');
            String s = "heelo";
            //此方法可以写入一个byte数组,s.getBytes可以将字符串拆分成数组
            fileOutputStream.write(s.getBytes());
            //此方法表示写入该数组的从0号下标位置写数组.length个
            fileOutputStream.write(s.getBytes(),0,s.getBytes().length);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();//关闭流
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

二:使用FileInputStream和FileoutputStream配合实现拷贝图片或者音乐
思路:
所有的文件都是二进制的,所以可以创建一个输入流一个输出流,输入流获取原文件,然后再给输出流,输出流负责输出到外部硬盘的指定位置。
注意点:注意文件过大的情况,write方法使用write (byte[] b,int off ,int len )的形式防止文件过大没有读取完整

public void copyFile(){
        //1.定义原文件的路径和要拷贝到哪里的路径,同时创建输入和输出流
        String src = "C:\\4.jpg";
        String dest = "D:\\test/1.jpg";
        FileInputStream input = null;
        FileOutputStream output = null;
        //2. 定义一个数组保存文件的二进制编码。然后使用循环给输出流
        byte[] b = new byte[1024];
        int len = 0;
        try {
            input = new FileInputStream(src);
            output = new FileOutputStream(dest);
            while ((len = input.read(b))!= -1){
                output.write(b,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                input.close();
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

FileReader类和FileWriter类

FileReader类和FileWriter类都是字符流
FileReader类是字符输入流
FileWriter类是字符输出流
IO流基础_第5张图片
IO流基础_第6张图片

FileReader类

文件字符输入流
构造方法
new FileReader(File /String),使用file对象或string对象创建一个输入流

常用方法
read:每次读单个字符,且返回该字符,如果到达文件末尾则返回-1
read(char[]):批量读取多个字符到数组中,返回读取到的字符数,如果到达文件末尾返回-1

相关API
new String(char[]),将一个char数组 ,转换成string
new String(char[],off,len),将char数组的off到len处转换成string

小练习
使用FileRead从story.txt读取内容,并输出打印在控制台

public class FileReader_ {
    @Test
    public void m1(){
        String fileName = "D:\\test/story.txt";
        FileReader Fr = null;
        try {
            int i = 0;
            Fr = new FileReader(fileName);
            while ((i = Fr.read()) != -1){
                System.out.print((char) i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                Fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

FileWriter类

文件字符输出流

构造方法
new FileWriter(File /String),使用file对象或string对象创建一个输出流(覆盖模式)
new FileWriter(File /String,true),使用file对象或string对象创建一个输出流,表示在文件末尾追加,而不是覆盖(追加模式)

常用方法
writer(int):写入单个字符
writer(char[]):写入指定数组
writer(char[],off,len):写入指定数组的指定部分
writer(String):写入整个字符串
writer(String,off,len):写入整个字符串的指定部分

相关API
String.toCharArray:将string转换成char[]

注意点
FileWriter使用后必须close或者flush,否则写入不到指定的文件

小练习
使用FileWriter将“风雨之后,定见彩虹”,写入到story.txt文件中(覆盖)

public class FileWriter_ {
    @Test
    public void m2(){
        String fileName = "D:\\test/story.txt";
        FileWriter Fw = null;

        try {
            Fw = new FileWriter(fileName);
            Fw.write("风雨之后,定见彩虹");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                Fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

节点流和处理流

节点流

节点流可以从一个指定的数据源读/写数据,例如FileReader,FileWriter
节点流只能对单个的数据源进行操作。
节点流不仅有对文件操作的流,还有对数据操作的流,对管道操作的流,等等

在这里插入图片描述
IO流基础_第7张图片

处理流

处理流也叫包装流,是“连接”在已存在的流(节点流或处理流)之上。为程序提供更为强大的读写功能。
处理流类在源码中都会维护一个流的基类,例如BufferedReader,里面就维护了一个Reader的属性。BufferedWriter,里面就维护了一个Writer的属性。这样就可以放入它们的子类到处理六中,使之更加的灵活,功能更加的强大
处理流可以抽象的理解为,包裹电线的一层绝缘胶。
现实生活中,让电流正常运输的其实还是里面的导体(节点流),而外面的绝缘胶可以防止漏电,导体破损等功能(处理流)
而在java中,实际干活的是节点流,处理流虽然包裹节点流,但是处理流并不是干活的,他只是为节点流提供服务的。如图所示

IO流基础_第8张图片

处理流类IO流基础_第9张图片

IO流基础_第10张图片
IO流基础_第11张图片

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

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

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

修饰器设计模式模拟

下面使用简单的代码来模拟一下修饰器设计模式

public abstract class Reader_ {//基类流
    public void FileReader(){}
    public void StringReader(){}
}
class FileReader_ extends Reader_{//节点流
    @Override
    public void FileReader() {
        System.out.println("读取了1次文件");
    }
}
class StringReader_ extends Reader_{//节点流
    @Override
    public void StringReader() {
        System.out.println("读取了1次字符");
    }
}
class BufferedReader_ extends Reader_{//处理流
    Reader_ reader_ ;//维护了一个基类属性

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

    public void FileReaders(int nums) {//在原本的读取上进行扩展,可以读多次
        for (int i = 0; i < nums; i++) {
            reader_.FileReader();
        }
    }
    public void StringReaders(int nums) {//在原本的读取上进行扩展,可以读多次
        for (int i = 0; i < nums; i++) {
            reader_.StringReader();
        }
    }
}
class test{
    public static void main(String[] args) {
        FileReader_ fileReader_ = new FileReader_();
        StringReader_ stringReader_ = new StringReader_();
        BufferedReader_ bufferedReader_ = new BufferedReader_(fileReader_);
        bufferedReader_.FileReaders(4);
    }
}

处理流的缓冲流

在诸多处理流中,有一个非常重要,那就是缓冲流。

我们知道,程序与磁盘的交互相对于内存运算是比较慢的,拖累程序的性能。普通流每次读写一个字节就会向磁盘交互一次,那么减少程序与磁盘的交互,就是提升程序效率一种有效手段。缓冲流,就应用这种思路:缓冲流会在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样总体交互的数据量是没有变化的,但是交互的次数减少了,每次交互的数量变多了

处理流(缓冲流):BufferedReader和BufferedWriter

BufferedReader和BufferedWriter是处理流的缓冲流同时也是字符流,所以它们只能负责处理纯文本。不可以处理二进制文件[音频,视频,图片…]

在上面有了解到流在使用完后需要关闭,但是在处理流中实际工作的还是节点流,按照逻辑上来说只需要关闭被处理流包裹的节点流即可。
但是在java中设计了处理流去关闭节点流的形式,所以在使用完处理流后,使用外层流也就是处理流的close方法就可以了,处理流的方法会去关闭节点流的,就不用我们手动去关闭了。

演示使用
使用BufferedReader读取一个纯文本文件,输出在控制台

public class TT1 {
 public static void main(String [] args)throws Exception{
     String fileName = "D:\\JavaYu\\cheek1/BitOperator.java";
     BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName));
     String line = "null";
     while ((line = bufferedReader.readLine())!= null){
         System.out.println(line);
     }
     bufferedReader.close();//
 }
}

演示使用
使用BufferedWriter在一个一个纯文本文件里写入“hello,world”

public class TT1 {
    public static void main(String[] args) throws Exception {
        String fileName = "D:\\A.txt";
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(fileName));
        bufferedWriter.write("hello,world");
        bufferedWriter.newLine();//换行
        bufferedWriter.write("hello,world2");
        bufferedWriter.close();
    }
}

演示使用
使用BufferedReader和BufferedWriter完成对一个文本文件的拷贝。

public class TT1 {
    public static void main(String[] args) throws Exception {
        String srcFileName = "D:\\A.txt";
        String detFileName = "D:\\A2.txt";
        BufferedReader br = new BufferedReader(new FileReader(srcFileName));
        BufferedWriter bf = new BufferedWriter(new FileWriter(detFileName));
        String line ;
        while ((line = br.readLine())!= null){
            bf.write(line);
            bf.newLine();
        }
        br.close();
        bf.close();
    }

处理流(缓冲流):BufferedInputStream和BufferedOutputStream

BufferedInputStream和BufferedOutputStream也是处理流的缓冲流,同时他们也是字节流,用于处理二进制文件

与其他处理流一样,BufferedInputStream和BufferedOutputStream也都各自维护了一个基类的属性,便于处理流操作
BufferedInputStream维护是inputStream
BufferedOutputStream维护的是OutputStream

练习:
要求使用BufferedInputStream和BufferedOutputStream完成对一个图片/视频/音频的拷贝

public static void main(String[] args) {
        //定义拷贝源路径和目的路径
        String dirFile = "D:\\gugedown/周杰伦 - 我不配.mp3";
        String srcFile = "D:\\gugedown/1.mp3";
        //定义输入流和输出流
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        //实例化
        try {
            bis = new BufferedInputStream(new FileInputStream(dirFile));
            bos = new BufferedOutputStream(new FileOutputStream(srcFile));
            //定义一个char数组。一次读取1024个字节
            byte [] b = new byte[1024];
            int len= 0;
            while ((len = bis.read(b))!= -1){
                bos.write(b,0,len);
            }
            System.out.println("拷贝成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bis.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

处理流(对象处理流):ObjectInputStream和ObjectOutputStream

ObjectInputStream和ObjectOutputStream也是处理流,用于处理对象的。
他们提供了对基本类型或对象类型的序列化和反序列化的方法

先看需求:
1.要求将 int a = 100,存储到文件中。要求不仅是存储值,也要存储数据类型。同时可以恢复到程序中
2.要求将Dog dog = new Dog(“小黄”,3)这个对象存储到文件中,且能够恢复

上面的要求就是要求能够将基本数据类型或者对象 进行 序列化反序列化,也叫串行化

什么是序列化和反序列化

  1. 序列化就是在保存数据时,保存数据的值和数据类型
  2. 反序列化就是在回复数据时,恢复数据的值和数据类型
  3. 如果需要让某个对象支持序列化机制,则必须让该类是可序列化的,如此就需要实现下面两个接口的其中一个:
    Serializable接口 — 这是一个标记接口,没有任何内容只是一个空接口
    Externalizable接口

图示:
IO流基础_第12张图片

ObjectInputStream: 提供了反序列化功能,因为是输入流

ObjectOutputStream:提供红了序列化功能,因为是输出流

了解完后我们先用ObjectInputStream来试着将数据使用序列化的方式保存到文件中

public class ObjectInputStream_ {
    public static void main(String[] args)throws IOException {
        String fileName = "D:\\at.txt";//序列化的文件后缀无所谓
        //定义一个可序列化的输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName));
        //存点已经实现Serializable接口的基本数据
        oos.write(100);//会自动转成包装类Integer
        //注意序列化的基本数据不一样调用的方法也不一样
        oos.writeUTF("11");
        oos.writeDouble(1.23);

        //存一个可序列化的对象
        oos.writeObject(new Dog("小黄",3));
    }
}
class Dog implements Serializable {
    String name;
    int age;
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

使用txt文件打开发现是乱码,这是正常的,因为已经不是只存值了,而是连同数据类型一起存了
IO流基础_第13张图片
然后我们再使用ObjectOutputStream读取刚才保存的数据

public void ObjectInputStream_() throws IOException, ClassNotFoundException {
        String fileName = "D:\\at.txt";//序列化的文件后缀无所谓
        //定义一个输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
        //读取数据,注意读取需要和保存的顺序一致
        System.out.println(ois.readInt());
        System.out.println(ois.readUTF());
        System.out.println(ois.readDouble());
        Object o = ois.readObject();
        System.out.println(o);
        //关闭流
        ois.close();
    }

运行结果:
100
11
1.23
Dog{name=‘小黄’, age=3}

对象处理流的注意事项

  1. 读写顺序要一致
  2. 要求序列化或反序列的对象,必须实现Serializable或Externalizable接口
  3. 序列化的类中建议添加SerialVersionUID这个属性,相当于序列号,可以提高兼容性
  4. 序列化对象时,默认将里面所有的属性都进行序列化,除了static和transient修饰的
  5. 序列化对象时,要求将里面所有属性的类型都实现序列化接口(同第一点)
  6. 序列化具可继承性,如果父类实现了Serializable接口,那么子类默认也可进行序列化

标准输入和标准输出

在之前的学习中有用到 System.in和System.out这两个属性

System.in 标准输入 编译类型是inputStream 运行类型 BufferedInputStream默认设备:键盘
System.out 标准输出 编译/运行类型都是 PrintStream 默认设备:显示屏

in和out都是System类中维护的静态属性。

常见用法:
Scanner sc = new Scanner(System.in),传给扫描器,接受键盘输入
System.out.println(“sss”);打印某些内容在显示器上

转换流InputStreamReader和OutputStreamWriter

InputStreamReader和OutputStreamWriter都是字符流,他们有一个功能就是可以将字节流转换成字符流
IO流基础_第14张图片
为什么需要进行转换呢?

首先需要了解一下,字节 有很多编码方式例如 UTF-8 ANSI GBK… 而字符流就没有那么多的编码方式
这就造成一个现象就是字节流可以指定编码方式,字符流不可以指定编码方式。
现在有这么一个例子,一个txt文件里面有“你好sdsdsd”这几个字符,是以UTF-8的编码存储的。
我们用BufferedReader字符流去读取它,显示没有问题。
但是我们改一下文件的编码方式为国标ANSI,再去读取就会发现变成了乱码。
UTF-8
在这里插入图片描述
ANSI
在这里插入图片描述
这里就引发了一个问题,编码不同会导致乱码。而解决方式就是先用字节流指定编码方式,然后再转换成字符流。

InputStreamReader

使用InputStreamReader解决上面的乱码问题
首先先看一下InputStreamReader的构造器
IO流基础_第15张图片

可以看到InputStreamReader支持将字节流的基类放入,且指定编码。
由此进行一个包装(转换)

练习
将字节流FileInputStream包装(装换成)字符流InputStreamReader对文件进行读取(按照utf-8/gbk格式),再使用BufferedReader读取文本

思路:先将路径放入FileInputStream,然后再将FileInputStream放入InputStreamReader同时指定编码。再将InputStreamReader放入BufferedReader进行读取

public static void main(String[] args) throws IOException {
        String fileName = "D://a.txt";
        BufferedReader bf = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),"gbk"));
       String s = bf.readLine();
        System.out.println(s);
        bf.close();
    }

可以看到就可以成功读取不同编码的文本了,没有乱码
在这里插入图片描述

OutputStreamWriter

既然上面有可以读取不同编码的操作,那么OutputStreamWriter自然也可以将不同编码的文本写出到文件中。
下面就使用OutputStreamWriter来写utf-8和gbk两种编码方式的文本到文件中。
首先还是看一下构造器
IO流基础_第16张图片
同样的OutputStreamWriter也可以包装字节输出流的基类同时指定编码

练习
将字节流FileOutputStream包装成(装换成)字符流OutputStreamWriter对文件进行写入(gbk和utf-8)

public static void main(String[] args) throws IOException {
        String fileName = "d:\\b.txt";
        FileOutputStream fo = new FileOutputStream(fileName);
        OutputStreamWriter osw = new OutputStreamWriter(fo, "utf8");
        osw.write("你好,世界");
        osw.close();
    }

打印流PrintStream和PrintWriter

PrintStream 字节打印流
PrintWriter 字符打印流

打印流只有输出流没有输入流,一般用于将内容输出到指定的地方,例如控制台,文件中…

PrintStream

PrintStream是字节打印流
IO流基础_第17张图片
IO流基础_第18张图片
可以通过构造方法看出,可以将内容输出到很多地方,而且还可以指定编码

演示使用PrintStream将内容输出到控制台和文件中

public static void main(String[] args) throws IOException {
        PrintStream out = System.out;
        //打印到控制台
        out.print("你好!!!!");
        out.close();
        //打印到文件中
        String fileName = "d:\\c.txt";
        System.setOut(new PrintStream(fileName));
        System.out.println("你好你好你好");
    }

PrintWriter

PrintWriter是字符打印流
IO流基础_第19张图片
它同样可以打印内容到不同的地方

演示

public static void main(String[] args) throws IOException {
        //打印到控制台
        PrintWriter printWriter = new PrintWriter(System.out);
        printWriter.print("didiid");
        printWriter.close();
        //打印到文件
        PrintWriter printWriter1 = new PrintWriter("d:\\c.txt");
        printWriter1.print("hahhah");
        printWriter1.close();
    }

Properties类

在实际开发中可能会用到很多的配置文件,例如需要获取配置文件中的IP,user,pwd。
使用传统方法读取数据是这样的。

public static void main(String[] args) throws IOException {
        String fileName = "src\\properties";
        BufferedReader bf = new BufferedReader(new FileReader(fileName));
        String len = " ";
        while ((len = bf.readLine())!= null){
            System.out.println(len);
        }
    }

但是这样的话比较麻烦,如果要改动的什么的就需要去改动源码,不灵活

Properties介绍

Properties是一个专门用于读取配置文件的集合类
配置文件的格式:
键 = 值
键 = 值
注意键值对不需要空格,也不用双引号包裹,默认是String类型

Properties的常用方法
load:加载配置文件的键值对到Properties对象
list:将数据显示到指定设备
getProperty(key):根据键获取值
setProperty(key,value):根据键修改值,如果没有键就添加。
store:将Properties对象中的键值对存储到配置文件中,如果含有中文则以unicode码存储

演示使用Properties类对配置文件进行读取和修改

public static void main(String[] args) throws IOException {
        String fileName = "src\\properties";
        Properties properties = new Properties();
        //先将配置文件加载到Properties对象中
        properties.load(new FileReader(fileName));
        //读取全部配置文件的数据
        properties.list(System.out);
        //根据键查找值
        System.out.println("IP是"+properties.getProperty("ip"));
        //根据键修改值
        properties.setProperty("pwd","999");
        System.out.println(properties.getProperty("pwd"));
        
    }

演示使用Properties类将配置数据保存到新的配置文件

public void test7() throws IOException{
        String fileName = "src\\properties2";
        Properties properties = new Properties();
        properties.setProperty("charset","gbk");
        properties.setProperty("user","嗡嗡嗡");
        properties.setProperty("pwd","9090");
        properties.store(new FileWriter(fileName),"这里可以写注释");
    }

IO练习

一:编程题
1)判断D盘下有没有文件夹mytemp,如果没有就创建
2)在D:\mytemp目录下,创建文件hello.txt
3)如果hello.txt已经存在。提示该文件存在,就不要重复创建了
4) 并且在hello.txt写入hello.world

public static void main(String[] args) throws IOException {
        //定义文件夹的路径
        String filePath = "d:\\mytemp";
        //创建File对象,判断该文件是否存在
        File file = new File(filePath);
        if (!file.exists()){
            //如果不存在就创建
            if (file.mkdirs()){
                //创建成功就提示创建成功
                System.out.println("创建文件夹成功");
            }else{
                System.out.println("创建失败");
            }
        }else{
            //如果存在就提示不需要创建
            System.out.println("已经存在不需要创建");
        }

        //定义hello.txt的路径
        String fileName = "D:\\mytemp/hello.txt";
        //判断该文件是否已经存在
        File file1 = new File(fileName);
        if(file1.exists()){
            //如果存在就提示已经存在
            System.out.println("hello.txt,已存在不需要创建");
        }else{
            //如果不存在就创建且写入内容
            file1.createNewFile();
            //创建输出流写出
            FileWriter fileWriter = new FileWriter(file1);
            fileWriter.write("hello.word1");
            fileWriter.close();
        }
    }

二:编程题
使用BufferedReader读取一个文本文件,为每行加上行号,连同内容一起输出在屏幕上

public static void main(String[] args) throws IOException {
        String fileName = "d:\\mytemp/hello.txt";
        BufferedReader bf = new BufferedReader(new FileReader(fileName));
        String len = null;
        int num = 0;
        while ((len = bf.readLine())!= null){
            System.out.println(++num +len);
        }
        bf.close();
    }

如果改变了文本的编码,就需要用到转换流。

public class test2 {
    public static void main(String[] args) throws IOException {
        String fileName = "d:\\mytemp/hello.txt";
        FileInputStream fr = new FileInputStream(fileName);
        InputStreamReader isr = new InputStreamReader(fr,"gbk");
        BufferedReader bf = new BufferedReader(isr);
        String len = null;
        int num = 0;
        while ((len = bf.readLine())!=null){
            System.out.println(++num + len);
        }
        bf.close();
        fr.close();
    }
}

三:编程题
1编写一个dog.properties配置文件
name=tom
age=5
color=red
2编写Dog类(name,age,color)创建一个dog对象,读取dog配置文件用相应的内容完成初始化,并输出
3创建完成后将改对象序列化保存到d:\mytemp文件中,然后反序列化读取输出

public class test3 {
    public static void main(String[] args) throws IOException, ClassCastException, ClassNotFoundException {
        String fileName = "src\\dog";
        Properties p = new Properties();
        p.setProperty("name","tom");
        p.setProperty("age","5");
        p.setProperty("color","red");
        p.store(new FileWriter(fileName),null);

        Dog dog = null;
        String name1 = p.getProperty("name");
        int age1 = Integer.parseInt(p.getProperty("age"));
        String color1= p.getProperty("color");
        dog = new Dog(name1,age1,color1);
        System.out.println(dog);
       //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\mytemp/dog.date"));
        oos.writeObject(dog);
        oos.close();
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\mytemp/dog.date"));
            Dog d2 = (Dog) ois.readObject();
            System.out.println(d2);
        ois.close();
    }
}
class Dog implements Serializable {
    String name;
    int age;
    String color;
    public Dog(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}

你可能感兴趣的:(JAVASE,All,java,jvm,开发语言)