JavaIO流

JavaIO流

rt.jar—>java.io里面的类

Stream流动

四大IO抽象类

InputStream/OutputStream和Reader/Writer这四个抽象类是所有IO流泪的抽象父类

  • 字节流: 二进制文件,如图片,影音 ,word文档,pdf
    • InputStream 向程序中输入数据,数据的单位为字节(8 bit)
      • int read():读取一个字节数据,将字节的值作为int类型返回(0~255)
      • void close()
    • OutputStream
      • void write(int n):写入一个字节
      • void close()
  • 字符流
    • Reader 数据单位是字符
      • int read() 将字符的值作为int类型返回(0~65535,即Unicode值)
      • void close()
    • Writer
      • void write(int n):写入一个字符
      • void close()

节点流和处理流

内容包含

  • 节点流:底层流
    • 可以直接从数据源或目的地读写数据,如FileInputStream、FileReader、FileWriter...
  • 处理流(包装流):
    • 不直接连接数据源或目的地,是处理流的流,通过对其他流的处理来提升程序性能,如BufferedInputStream、BufferedReader
    • 性能提高,操作便捷
    • 底层有一个对应的抽象基类的类型的成员变量,根据你传入的对象类型可包装处理不同的节点流
节点流/处理流 分类 字节输入流 字节输出流 字符输入流 字符输出流
节点流 抽象基类 InputStream OutputStream Reader Writer
节点流 访问文件 FileInputStream FileOutputStream FileReader FileWriter
节点流 访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
节点流 访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
节点流 访问字符串 StringReader StringWriter
处理流 缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
处理流 转换流 InputStreamReader OutputStreamWriter
处理流 对象流 ObjectInputStream ObjectOutputStream
处理流 抽象基类 FilterInputStream FilterOutputStream FilterReader FilterWriter
处理流 打印流 PrintStream PrintWriter
处理流 推回输入流 PushbackInputStream PushbackReader
处理流 特殊流 DataInputStream DataOutputStream

包装流底层逻辑

底层有一个对应的抽象基类的类型的成员变量,根据你传入的对象类型可包装处理不同的节点流

模拟实现:

创建类:

节点流/处理流 分类 名称 说明
节点流 抽象基类 ReaderTest 抽象类,方法未实现
节点流 读取文件实现类 FileReaderTest 实现读取文件方法
节点流 读取字符串实现类 StringReaderTest 实现读取字符串方法
包装流 缓冲流 BufferedReaderTest 包装读取文件和字符串方法

继承关系图:

模拟包装流

实现:

package com.packagingflow_test;

public class Test {
    public static void main(String[] args) {
        //创建一个包装流BufferedReaderTest对象,传入一个节点流FileReaderTest对象
        BufferedReaderTest brt1 = new BufferedReaderTest(new FileReaderTest());
        //创建一个包装流BufferedReaderTest对象,传入一个节点流StringReaderTest对象
        BufferedReaderTest brt2 = new BufferedReaderTest(new StringReaderTest());
        //则brt可以使用继承自父类ReaderTest的方法,也可使用自身对FileReaderTest对象中方法包装后的方法
        brt1.read(); //实现的父类的抽象方法
        System.out.println("=============================");
        brt1.readFile(); //默认调用FileReaderTest的readFile方法
        System.out.println("=============================");
        brt1.readFiles(5);   //对FileReaderTest中readFile方法包装后的方法
        System.out.println("==============================");
        brt2.readStrings(5); //对StringReaderTest中readString方法包装后的方法
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
package com.packagingflow_test;

//抽象基类
public abstract class ReaderTest {
    public abstract void read();    //抽象方法,子类都要实现
    public void readFile(){ //交由特定子类优化
        System.out.println("读取文件未实现");
    };
    public void readString(){//交由特定子类优化
        System.out.println("读取字符串未实现");
    };
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
package com.packagingflow_test;

//节点流,继承自ReaderTest
public class FileReaderTest extends ReaderTest{

    @Override
    public void read() {
        super.readFile();
    }

    @Override
    //优化父类readFile方法
    public void readFile() {
        System.out.println("读取文件成功...");
    }

}
///////////////////////////////////////////////////////////////////////////////////////////////////////
package com.packagingflow_test;

//节点流,继承自ReaderTest
public class StringReaderTest extends ReaderTest{
    @Override
    public void read() {
        super.readString();
    }
    //优化父类readString方法
    public void readString(){
        System.out.println("读取字符串成功...");
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
package com.packagingflow_test;

//包装流,继承自ReaderTest抽象类,可接收ReaderTest的任何子类,并对某些方法实现包装
public class BufferedReaderTest extends ReaderTest{
    private ReaderTest rt;

    //构造器,接收传入的ReaderTest的子类
    public BufferedReaderTest(ReaderTest rt) {
        this.rt = rt;
    }

    @Override
    //默认调用传入对象的读操作
    public void read() {
        this.rt.read();
    }

    @Override
    //默认调用传入对象的读文件操作
    public void readFile() {
        this.rt.readFile();
    }

    //多次读取文件,包装读取文件操作
    public void readFiles(int num){
        for (int i = 0; i < num; i++) {
            this.rt.readFile();
        }
    }

    @Override
    //默认调用传入对象的读字符串操作
    public void readString() {
        this.rt.readString();
    }
    //多次读取字符串,包装读字符串操作
    public void readStrings(int num){
        for (int i = 0; i < num; i++) {
            this.rt.readString();
        }
    }
}

结果:

读取文件未实现
=============================
读取文件成功...
=============================
读取文件成功...
读取文件成功...
读取文件成功...
读取文件成功...
读取文件成功...
==============================
读取字符串成功...
读取字符串成功...
读取字符串成功...
读取字符串成功...
读取字符串成功...

File类

File类是针对磁盘中的文件或目录来转换对象的包装类,可以代表一个文件或目录,可获取文件或目录的的属性,也可操作文件或目录

两种创建方式

  1. File file = new File("路径或路径加文件名");
    
  2. File file = new File("路径","文件名");
    

文件常用操作

package com.createfile;

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

public class DemoFile01 {
    public static void main(String[] args) throws IOException {
        //执行后f1即代表了“b.txt”这个文件,所有对f1的操作即是对“b.txt”这个文件的操作
        File f1 = new File("./b.txt");    //有参构造,填入文件的路径,可以使相对路径或绝对路径
        //File类的方法会抛出异常
        System.out.println(f1.createNewFile());  // createNewFile()这个方法真正的在磁盘上创建文件,成功返回true
        System.out.println(f1.exists());    //文件是否存在,存在返回true
        System.out.println(f1.delete());    //删除文件,也可删除目录,成功true
        System.out.println(f1.exists());
        System.out.println(f1.createNewFile());
        System.out.println(f1.length());    //获取文件大小,单位字节
        System.out.println(f1.getName());   //获取文件名,相当于toString,是b.txt
        System.out.println(f1.isFile());    //是否是文件,是返回true
        System.out.println(f1.isHidden());  //是否是隐藏文件,是返回true
        System.out.println(f1.getPath());   //获取相对路径
        System.out.println(f1.getAbsolutePath());   //获取绝对路径
    }
}

其他自行帮助文档

目录常用操作

package com.createfile;

import java.io.File;

public class Demo02 {
    public static void main(String[] args) {
        File f1 = new File("d:/a");
        File f2 = new File("d:/b/c/d");
        System.out.println(f1.mkdir());     //创建单级目录,成功true
        System.out.println(f2.mkdirs());    //创建多级目录,成功true
        System.out.println(f1.exists());    //查询是否存在,存在true
        System.out.println(f1.delete());    //删除目录,成功true
        System.out.println(f1.mkdir());
        System.out.println(f1.isDirectory());   //判断当前路径是否是目录,是返回true
        System.out.println(f1.getParentFile()); //获取父级目录
        System.out.println("=================================");

        File f3 = new File("f:/");
        String[] arr1 = f3.list();  //获取当前目录中的所有文件的文件名,返回字符串数组
        for (String s : arr1) {
            System.out.println(s);
        }
        System.out.println("==================================");
        File[] arr2 = f3.listFiles();   //获取当前目录中所有文件的带绝对路径的文件名,返回!!!File数组!!!
        for (File f : arr2) {
            System.out.println(f);
        }

        System.out.println(arr2[5].getName());  //打印下f3目录中第6项文件名
    }
}

实例—创建带日期的文件到我们指定目录

package com.createfile;

import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

//用java.time下的类创建带日期的文件到我们指定目录
public class Demo04 {
    public static void main(String[] args) {
        StringBuilder sb1 = new StringBuilder("d:/ErrorLog/");  //创建一个StringBuilder用来存储最终完整路径
        LocalDate ld1 = LocalDate.now();    //获取当前时间
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");  //设置时间格式
        sb1.append(ld1.format(dtf)).append("errorlog.txt");  //拼接完善路径和文件名
        File f1 = new File(sb1.toString()); //按照我们拼接好的路径和文件名创建File对象
        if (f1.getParentFile().exists()){   //判断文件目录是否存在
            try {
                f1.createNewFile(); //存在目录则直接创建文件
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            if (f1.getParentFile().mkdirs()){   //不存在则先创建目录,顺便判断是否创建成功
                try {
                    f1.createNewFile();     //创建好目录再创建文件
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else {
                System.out.println("创建错误日志出现问题");   //如果创建失败打印提示,创建失败
            }
        }

    }
}

InputStream常用子类

结构

  1. FileInputStream:文件输入流
  2. BufferedInputStream:缓冲字节输入流
  3. ObjectInputStream:对象字节输入流

InputStream常用子类关系图

FileInputStream

继承自InputStream,字节节点流

逐个字节读取文件

package com.inputstream;

import java.io.FileInputStream;
import java.io.IOException;

//字节流读取文件,FileInputStream
public class Demo01 {
    public static void main(String[] args) {
        StringBuilder sb1 = new StringBuilder();
        FileInputStream fis1 = null;
        try {
            fis1 = new FileInputStream("d:\\a1.txt");
            int num;
            while ((num = fis1.read()) != -1){  //read是一个字节一个字节的读,读取不到内容了返回-1
                sb1.append((char)num);
            }
            System.out.println(sb1);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                fis1.close();	//关闭io流,释放空间
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

按字节数组大小读取文件

package com.inputstream;

import java.io.FileInputStream;
import java.io.IOException;

public class Demo04 {
    public static void main(String[] args) {
        FileInputStream fis1 = null;
        try {
            fis1 = new FileInputStream("d:/a.txt");
            byte[] arr1 = new byte[8];  //创建一个8字节的字节数组
            int datelen = 0;
            //每次按照字节数组大小的长度读取字节,每次读取8个字节
            while ((datelen = fis1.read(arr1)) != -1){     //读取成功返回真正读取到的字节数量,到末尾返回-1
                //String的构造方法,可以将字节数组的指定长度转换为String
                System.out.print(new String(arr1 , 0 , datelen));	//写入arr1中索引为0~datelen-1的数据
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fis1.close();   //关闭io流
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

BufferedInputStream

字节包装流,继承自FilterInputStream,底层有一个InputStream类型变量(继承自FilterInputStream),所以创建时可以传任何的InputStream的子类

常用方法同FileInputStream

read();	//读取一个字节,返回其编码,末尾返回-1
read(byte[]);	//按照字节数组长度读取,返回实际读取字节个数,末尾返回-1

ObjectInputStream

字节包装流,继承自InputStream,同时实现ObjectInput和ObjectStreamConstants接口,在其底层私有静态内部类PeekInputStream中有一个InputStream类型变量(非继承),所以创建时可以传任何的InputStream的子类

实现反序列化(序列化是ObjectOutputStream):

  • 在恢复(读取)数据时,恢复数据的值和数据类型

反序列化文件中的类必须是public修饰的,否则必须要在同一包下

与ObjectOutputStream代码搭配使用理解,先写入后读取

package com.inputstream;

import com.outputstream.Person;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ObjectInput01 {
    public static void main(String[] args) {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("d:\\ObjectOutput.dat"));
            //读取文件(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
            System.out.println(ois.readInt());
            System.out.println(ois.readBoolean());
            System.out.println(ois.readChar());
            System.out.println(ois.readDouble());
            System.out.println(ois.readUTF());  //读取String
            //读取对象
            //p1的引用类型(编译类型)是Object,但实际类型(运行类型)为Person(记录在文件内)
            Object p1 = ois.readObject();
            System.out.println(p1.getClass());  //打印实际类型
            //!!!如果我们希望调用读取到的对象的方法,需要向下转型(父类转为子类类型),引用类型转为读取到的对象类型!!!
            //!!!需要我们将Person类放到我们能够引用的位置!!!
            Person person = (Person) p1;
            System.out.println(person.getName() + "," + person.getAge());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭外部流即可
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

OutputStream常用子类

结构

OutputStream常用子类继承关系图

FileOutputStream

写入文件

继承自OutputStream,字节节点流

不用close()就能写入到文件中

!!!是否替换内容是判断其他io流写入的内容,同一io流的写入操作都是末尾追加,或者说,是否替换只是在该对象第一次写入操作时判断,在该对象io流被关闭前多次的写入操作都是末尾追加!!!

package com.OutputStream;

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputDemo01 {
    public static void main(String[] args) {
        FileOutputStream fos1 = null;
        FileOutputStream fos2 = null;
        FileOutputStream fos3 = null;
        try {
            //1.覆盖式写入
            //1.1写入单个字符
            fos1 = new FileOutputStream("d:/a1.txt");   //文件不存在自动创建,目录不会自动创建,不存在会抛出异常
            fos1.write('a');
            //1.2写入多个字符
            fos2 = new FileOutputStream("d:/a2.txt");
            String str1 = "Hello,world!\n";
            fos2.write(str1.getBytes());    //String的getBytes()字方法返回当前字符串的字节数组
            //也可以指定字节数组的长度添加,从索引0开始,添加等同于其长度个元素
            fos2.write(str1.getBytes(),0,str1.getBytes().length);	
            //2.末尾添加写入
            fos3 = new FileOutputStream("d:/a3.txt",true);  //第二个参数为true表示向文件末尾添加
            fos3.write(str1.getBytes());
            fos3.write(str1.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fos1.close();	//关闭io流
                fos2.close();
                fos3.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

实例—复制文件

方式1:

package com.OutputStream;

import java.io.*;

public class CopyDemo {
    public static void main(String[] args) {
        File f1 = null;
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //创建一个File对象来获取我们想要复制的文件信息
            f1 = new File("E:\\图片\\太阳好哥哥\\20190909183350_1.jpg");
            //创建字节输入流对象,将目标文件数据读取到程序中
            fis = new FileInputStream("E:\\图片\\太阳好哥哥\\20190909183350_1.jpg");
            //创建字节输出流对象,将程序中的数据存储到我们指定的位置
            fos = new FileOutputStream("d:/太阳好哥哥.jpg");
            //创建一个字节数组存储要复制的文件数据,实现复制
            byte[] arr = new byte[(int) f1.length()];   //创建一个与目标文件大小一致的字节数组
            int datelen;    //记录输入流每次读取到的字节个数
            while ((datelen = fis.read(arr)) != -1){    //读取文件内容
                fos.write(arr,0,datelen);   //存储到指定位置,实现粘贴
            }
            System.out.println("复制成功");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                //关闭io流
                fis.close();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

方式2;

package com.OutputStream;

import java.io.*;

public class CopyDemo02 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //创建字节输入流对象,将目标文件数据读取到程序中
            fis = new FileInputStream("E:\\图片\\太阳好哥哥\\20190909183350_1.jpg");
            //创建字节输出流对象,将程序中的数据存储到我们指定的位置,并且是以添加的形式
            fos = new FileOutputStream("d:/赞美太阳.jpg",true);
            //创建一个字节数组存储要复制的文件数据,实现复制
            byte[] arr = new byte[1024];
            int datelen = 0;    //记录输入流每次读取到的字节个数
            while ((datelen = fis.read(arr)) != -1){
                //将每次读取到的数据存储到我们指定的位置,实现粘贴
                fos.write(arr,0,datelen);
            }
            System.out.println("复制成功");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("复制失败");
        } finally {
            try {
                //关闭io流
                fis.close();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }
}

BufferedOutputStream

字节包装流,继承自FilterOutputStream,底层有一个OutputStream类型变量(继承自FilterOutputStream),所以创建时可以传任何的OutputStream的子类

常用方法同FileOutputStream

write('a');	//写入一个字节
write(byte[],int off,int len)	//写入指定字节数组内容,从索引off开始,写入len个(len不是索引)

实例—复制文件

package com.outputstream;

import java.io.*;

//字节流拷贝可以拷贝所有文件,但更适合二进制文件
public class BufferedCopy01 {
    public static void main(String[] args) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream("d:\\太阳好哥哥.jpg"));
            bos = new BufferedOutputStream(new FileOutputStream("d:\\三人成虎.jpg"));
            byte[] date = new byte[1024];
            int datelen = 0;
            while ((datelen = bis.read(date)) != -1){
                bos.write(date,0,datelen);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                bis.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

ObjectOutputStream

字节包装流,继承自OutputStream,同时实现ObjectOutput和ObjectStreamConstants接口,在其底层私有内部静态类BlockDataOutputStream中有一个OutputStream类型变量(非继承),所以创建时可以传任何的OutputStream的子类

实现序列化(反序列化是ObjectInputStream)

  • 在保存数据时,保存数据的值和数据类型

要让某个类可序列化,必须实现两个接口之一

  • Serializable 标记接口,没有方法,推荐使用
  • Externalizable 继承了Serializable的接口,需要实现方法

常用方法:

write()方法不保存类型,只保存数值

与ObjectInputStream代码搭配使用理解,先写入后读取

package com.outputstream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class ObjectOutput01 {
    public static void main(String[] args) {
        ObjectOutputStream oos = null;
        try {
            //序列化存储的文件格式不是纯文本,而是以它的格式来存储的,所以文件后缀无意义
            oos = new ObjectOutputStream(new FileOutputStream("d:\\ObjectOutput.dat")); //追加模式加true
            //存储基本数据类型,write()方法不保存类型
            oos.writeInt(100); //存储int类型,int——自动装箱——>Integer(实现了Serializable接口)
            oos.writeBoolean(true); //存储boolean类型,boolean——自动装箱——>Boolean(实现了Serializable接口)
            oos.writeChar('a'); //存储char类型,char——自动装箱——>Character(实现了Serializable接口)
            oos.writeDouble(12.5);  //存储double,double——自动装箱——>Double(实现了Serializable接口)
            oos.writeUTF("赞美太阳"); //存储String(实现了Serializable接口)
            //存储对象
            oos.writeObject(new Person("Eleike",22));
            System.out.println("序列化存储成功");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//要想序列化对象,必须实现Serializable(推荐)或Externalizable接口
//若此类修饰词为default,则只有同一包下的类才能反序列化(读取文件)成功
package com.outputstream;

import java.io.Serializable;

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

PrintStream

继承自FilterOutputStream,字节包装流(字节打印流),可传入File对象、OutputStream对象、或者String类型的文件地址来指定输出的位置

输出到显示器的两种方法:

  • PrintStream ps1 = new PrintStream(new FileOutputStream(FileDescriptor.out));  //输出到显示器
    PrintStream ps1 = new PrintStream(System.out);  //输出到显示器
    

更改System.out打印为之后让System.out.println()重新输出到显示器的方法:

  • System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
    

实例:

package com.outputstream;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.PrintStream;

//PrintStream使用
public class PrintStream01 {
    public static void main(String[] args) {

        PrintStream ps1 = null;
        PrintStream ps2 = null;
        try {
            //1.new创建PrintStream对象
            //PrintStream中可以传入File对象、OutputStream对象、或者String类型的文件地址来指定输出的位置
            //还可添加指定字符编码的参数
            //具体API自查
            ps1 = new PrintStream(new FileOutputStream("d:\\newPrint.txt"));
            //ps1 = new PrintStream(new FileOutputStream(FileDescriptor.out));  输出到显示器
            //ps1 = new PrintStream(System.out);  输出到显示器
            ps1.println("你好世界");
            //println方法底层实际上调用的是write()方法,所以我们可以直接调用write方法
            ps1.write("前有幻影墙壁\n".getBytes());

            //2.指向System类中的out对象
            ps2 = System.out;   //out底层是用外部方法初始化的,默认输出到显示器
            ps2.println("你好");  //相当于System.out.println("你好")

            //可以设置out打印输出的位置
            System.setOut(new PrintStream("d:\\outPrint.txt")); //System.setOut方法设置out输出的位置,传入一个PrintStream对象
            //需要让ps2指向新的out
            ps2 = System.out;
            ps2.println("赞美太阳!!!"); //输出到outPrint.txt中
            System.out.println("黑魂YYDS");   //输出到outPrint.txt中

            //重新定向回标准输出流位置,即显示器
            System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
            System.out.println("Hello,World!"); //打印到显示器上
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭流
            ps1.close();
            ps2.close();
        }
    }
}

Reader常用子类

结构

Reader常用子类继承关系图

FileReader

继承自InputStreamReader,字符节点流

  1. 构造方法new FileReader(File/String)
  2. read():逐个字符读取
  3. read(char[]):批量字符读取
package com.reader;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReader01 {
    public static void main(String[] args) {
        FileReader fr1 = null;
        FileReader fr2 = null;
        try {
            //实例化FileReader对象
            fr1 = new FileReader("d:\\d1.txt");
            fr2 = new FileReader("d:\\d1.txt");
            //1.单个字符读取  read()
            int date = 0;   //定义int类型接受读取到的字符编码
            while ((date = fr1.read()) != -1){  //逐个字符读取文件内容,读取到末尾返回-1,否则返回字符的编码(默认Unicode)
                System.out.print((char) date);  //将字符编码转化成对应字符输出
            }
            System.out.println("\n===================================");
            //2.多个字符读取  read(char[])
            char[] arr = new char[8];   //定义的字符数组存储读取的内容
            int datelen;    //存储每次读取的字符个数
            while ((datelen = fr2.read(arr)) != -1 ){   //按照传入的字符数组长度读取字符个数,到末尾返回-1,否则返回实际读取到的字符个数
                System.out.print(new String(arr,0,datelen));    //string有参构造,将char数组(可指定长度)转化为String
            }

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

其他方法自行API

BufferedReader

继承自Reader,字符包装流,底层有一个Reader类型变量,所以创建时可以传任何的Reader的子类

关闭时(close)只需要关闭外层流即可(关闭包装流),因为底层就是调用对相应节点流的close方法

package com.reader;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReader01 {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            //传入一个节点流FileReader对象
            br = new BufferedReader(new FileReader("d:\\f1.txt"));
            String date;
            //readLine读取并返回一行数据(换行符/回车符),到末尾返回null,不打印回车符
            while ((date = br.readLine()) != null){
                System.out.println(date);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

其他方法自行API

InputStreamReader

继承自Reader,字符包装流,需要传入一个InputStream的对象

实现读取时按指定编码格式将字节流转化为字符流

InputStreamReader(InputStream,charset)	//传入一个InputStream对象,然后指定编码格式

实例:

package com.reader;

import java.io.*;

public class InputStreamReader01 {
    public static void main(String[] args) {
        InputStreamReader isr = null;
        BufferedReader br = null;
        try {
            //1.将FileInputStream(字节流)按照gbk编码转化为InputStreamReader(字符流)
            isr = new InputStreamReader(new FileInputStream("d:\\ansi.txt"), "gbk");	//gbk编码格式
            //2.用BufferedReader按照InputStreamReader的格式进行读取,为了使用BufferedReader里更方便的读取操作
            br = new BufferedReader(isr);
            //将1和2合并
            //br = new BufferedReader(new InputStreamReader(new FileInputStream("d:\\ansi.txt"), "gbk"));
            //3.读取内容
            String s = br.readLine();   //接受读取内容
            System.out.println(s);  //不再乱码:赞美太阳赞美太阳
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //4.关闭最外层流
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Writer常用子类

write()不写入回车

结构

Writer常用子类继承关系图

FileWriter

继承自OutputStreamWriter,字符节点流

FileWriter使用后必须要关闭(close)或者刷新(flush),才能真正的写入到文件里面去,不然还是只在内存中

close()和flush()底层都是用的FileOutputStream的write方法,字符流底层还是用的字节流

  1. 构造方法
    • new FileWriter(File/String):覆盖模式,指针在文件首
    • new FileWriter(File/String,true):追加模式,指针在文件尾
  2. writer(int):写入单个字符
  3. writer(char[]):写入字符数组
  4. writer(char[],off,len):写入字符数组指定部分
  5. writer(String):写入字符串
  6. writer(String,off,len):写入字符串指定部分
package com.writer;

import java.io.FileWriter;
import java.io.IOException;

public class FileWriter01 {
    public static void main(String[] args) {
        FileWriter fw1 = null;
        FileWriter fw2 = null;
        FileWriter fw3 = null;
        FileWriter fw4 = null;
        try {
            //1.覆盖模式
            //1.1单个字符写入
            fw1 = new FileWriter("d:\\e1.txt");
            fw1.write(97);  //a
            //1.2字符数组写入
            fw2 = new FileWriter("d:\\e2.txt");
            String str = "大家一起赞美太阳";
            fw2.write(str.toCharArray()); //大家一起赞美太阳
            //1.3字符串指定部分写入
            fw3 = new FileWriter("d:\\e3.txt");
            fw3.write("如果你是龙,也好",0,6);  //从索引0开始,写入前6个,如果你是龙,
            //2.追加模式
            //2.1追加模式下字符串写入
            fw4 = new FileWriter("d:\\e3.txt",true);
            fw4.write("前有隐藏墙壁");    //如果你是龙,前有隐藏墙壁
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //!!!没有关闭的话只会创建文件,不会写入数据(文件无内容)!!!
                fw1.close();
                fw2.close();
                fw3.close();
                fw4.close();
                //close()和flush()底层都是用的FileOutputStream的write方法,字符流底层还是用的字节流
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

其他方法自行API

BufferedWriter

继承自Writer,字符包装流,底层有一个Writer类型变量,所以创建时可以传任何的Writer的子类

package com.writer;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriter01 {
    public static void main(String[] args) {
        BufferedWriter bw = null;
        try {
            //传入一个节点流FileWriter的对象
            bw = new BufferedWriter(new FileWriter("d:\\f2.txt"));
            bw.write("前有幻影墙壁!");
            bw.newLine();   //插入一个跟你系统相关的换行符
            bw.write("前有幻影墙壁!");
            bw.newLine();
            bw.write("前有幻影墙壁!");
            bw.newLine();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

其他方法自行API

实例—复制文本文件

字符流拷贝只适合拷贝文本文件

package com.writer;

import java.io.*;

//字符流拷贝只适合拷贝文本文件
public class BufferedCopy01 {
    public static void main(String[] args) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = new BufferedReader(new FileReader("d:\\f1.txt"));
            bw = new BufferedWriter(new FileWriter("d:\\f1副本.txt"));
            String str;
            while ((str = br.readLine()) != null){  //不读取回车符
                bw.write(str);  //不写回车符
                bw.newLine();   //写入回车符,间隔行
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                //字符流要关闭才能真正写入
                br.close();
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

OutputStreamWriter

继承自Writer,字符包装流,需要传入一个OutputStream的对象

实现保存数据时按指定编码格式将字节流转化为字符流

package com.writer;

import java.io.*;

//OutputStreamWriter使用,实现按指定编码保存数据
public class OutputStreamWriter01 {
    public static void main(String[] args) {
        OutputStreamWriter osw = null;
        try {
            //按照gbk编码格式保存数据
            osw = new OutputStreamWriter(new FileOutputStream("d:\\oswgbk.txt"),"gbk");
            osw.write("前有幻影墙壁,前有幻影墙壁");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

PrintWriter

继承自Writer,字符包装流(字符打印流),可传入File对象,OutputStream对象,Writer对象或String类型的文件地址来指定输出打印位置

package com.writer;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

//PrintWriter使用
public class PrintWriter01 {
    public static void main(String[] args) {
        PrintWriter pw1 = null;
        PrintWriter pw2 =null;
        try {
            //写入指定文件中
            pw1 = new PrintWriter(new FileWriter("d:\\printWriter.txt"));
            pw1.println("你好世界~~");
            //打印到显示器,目前只有这一种方法
            pw2 = new PrintWriter(System.out);	
            pw2.println("你好显示器");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //Writer类及其子类必定要关闭才能真正写入数据
            pw1.close();
            pw2.close();
        }
    }
}

标准输入输出流

名称 含义 引用类型/编译类型 实际类型/运行类型 默认设备
System.in 标准输入 InputStream BufferedInputStream 键盘
System.out 标准输出 PrintStream(打印流) PrintStream 显示器
  • in变量:

    • in是System类中的一个静态常量,底层代码是public final static InputStream in = null;
  • out变量

    • out是System类中的一个静态常量,底层代码是public final static PrintStream out = null;
  • in与out在System中的初始化

    • in与out的初始化其实是在System的静态代码块中调用的registerNatives()方法实现的

      • static {
            registerNatives();	//静态代码块中调用这个方法,完成真正的in与out的初始化
        }
        //私有方法
        private static native void registerNatives();//此方法初始化in与out,使in默认读取键盘出入,out默认打印到显示器
        
    • 但是registerNatives()方法是native修饰的,native修饰的方法是外部方法/本地方法,一般是C/C++编写的

    • native含义具体请在Java基础中的native关键字查看

package com.standard;

import java.io.BufferedInputStream;
import java.util.Scanner;

//标准输入输出流   standard标准的
public class SystemInAndOut {
    public static void main(String[] args) {
        //标准输入流 System.in
        //底层是in是public final static InputStream in = null;
        //引用类型/编译类型     InputStream
        //实际类型/运行类型     BufferedInputStream
        //接受键盘输入
        System.out.println(System.in.getClass());   //java.io.BufferedInputStream

        //标准输出流 System.out
        //底层的out是public final static PrintStream out = null;
        //引用类型/编译类型     PrintStream
        //实际类型/运行类型     PrintStream
        //打印到屏幕
        System.out.println(System.out.getClass());  //java.io.PrintStream

        Scanner sc = new Scanner(System.in);    //底层还是用System.in来读取键盘输入
        System.out.println("输入内容");
        String str = sc.next(); //从System.in中读取键盘输入数据
        System.out.println("sc.next = "+str);



    }
}

序列化和反序列化注意事项

  1. 读写顺序要求一致
  2. 要求序列化和反序列化对象,需要实现Serializable
  3. 序列化的类中建议添加SerialVersionUID,提高版本的兼容性
    • SerialVersionUID是序列化的版本号,提高兼容性
    • 有了版本号,在序列化或反序列化时,系统不会因为你对类的部分修改而认为是一个新的类,而认为是原本类的升级版
  4. 序列化对象时,默认将里面所有属性都进行序列化,但除了static和transient(瞬时的)修饰的成员
    • transient修饰的意思就是不会被序列化
  5. 序列化时,要求里面属性的类型也需要实现序列化接口,即实例对象的抽象类也要实现Serializable接口
  6. 实例化具有可继承性,即如果某类已经实现了序列化,那么它的所有子类也已经默认实现了序列化
package com.outputstream;

import java.io.Serializable;

//2.要想序列化对象,必须实现Serializable(推荐)或Externalizable接口
public class Person implements Serializable {
    //3.序列化的版本号,提高兼容性,对此类的部分修改不会在序列化和反序列化时让系统认为此类是一个新的类,而认为是此类的升级版
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    //4.static和transient(瞬时的)修饰的成员不会被序列化
    private static String gender;
    private transient String home;
    
    //5.没有被static或transient修饰的实例对象,要求对象的抽象类也实现序列化接口
    All a = new All();  //public class All implements Serializable {}   对应抽象类实现序列化

    public Person() {
    }

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

    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 +
                ", home='" + home + '\'' +
                '}';
    }
}

Properties类

基本介绍

继承自Hashtable<K,V>

Properties类用来读取固定格式的配置文件(如数据库连接用户密码的配置文件),增加项目的可维护性

配置文件的格式要求:

键=值

键=值

...

  • 键值对间不要有空格,值不需要任何引号,默认是String类型

常用方法

load( InputStream/Reader )	//加载配置文件的键值对到Properties对象
list( PrintStream/PrintWriter )	//将数据显示到指定设备
getProperty(String key)	//获取指定key的值
getProperty(key,value)	//设置/修改键值对到Properties对象,没有则相当于添加键值对
store( OutputStream/Writer,String)	//将Properties中的键值对存储到配置文件中,在IDEA中,保存数据时如果含有中文,中文会存储为unicode码

读取文件

package com.properties;

import java.io.*;
import java.util.Properties;

//使用Properties来读取配置文件
public class Properties01 {
    public static void main(String[] args) {
        /*
        配置文件格式必须是:
        键=值
        键=值
        ...
        不要有空格,值不需要引号,默认String
         */
        //1.创建Properties对象
        Properties ppt = new Properties();
        try {
            //2.加载指定的配置文件
            ppt.load(new FileReader("d:\\mysql.properties"));
            //3.把K-V(键值对)打印在显示器上
            ppt.list(System.out);
            //ppt.list(new PrintStream(new FileOutputStream(FileDescriptor.out)));    //也是输出在显示器上
            //4.获取指定Key值,ppt.getProperty(String Key名)
            System.out.println("用户名是:" + ppt.getProperty("user"));
            System.out.println("密码是:" + ppt.getProperty("password"));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            ppt.clone();
        }
    }
}

写入文件

package com.properties;

import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
//Properties修改添加键值对并存储到配置文件
public class Properties02 {
    public static void main(String[] args) {
        Properties ppt = new Properties();
        //设置Properties对象键值对信息,如果key不存在则是添加,存在则是修改
        //底层是一个Entry集合,有重复则修改值,不存在则添加
        ppt.setProperty("charset","uft-8"); //第一个参数key,第二个参数是值
        ppt.setProperty("user","灰心哥");  //中文会存储为对应unicode
        ppt.setProperty("pw","asd321");
        //将Properties对象键值对存储到配置文件中
        try {
            //第二个参数为文件注释,String类型,填入会在配置文件第一行写入 #你填写的注释信息
            ppt.store(new FileWriter("d:\\new_mysql.properties"),null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

杂记

  • IO流读取文件默认编码为UTF-8
posted @ 2021-12-22 17:42  Eleike  阅读(42)  评论(0)    收藏  举报