[Java]IO流基础

IO流基础

1.基础概念

流:数据在文件和程序之间传输所经历的路径

输入流、输出流:输入输出是以程序为基准的,文件传输数据进程序为输入流,程序保存数据到文件为输出流。

2.文件

2.1 文件的创建

2.1.1 第一种

public File(String pathname) 根据路径构建一个File对象

 public void createFileOne(){
        File pathname = new File("d:/桌面/News01.txt");
        try {
            pathname.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.1.2 第二种

public File(File parent, String child) 父目录文件+子路径

    @Test
    public void createFileTwo(){
        File parent = new File("d:/桌面/");
        String child = "News02.txt";
        File file = new File(parent,child);
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.1.3 第三种

public File(String parent, String child) 父路径+子路径

@Test
public void createFileThree(){
    File file = new File("d:/桌面/","News03.txt");
    try {
        file.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.2 获取文件相关信息

public String getName()//获取文件名称
public String getAbsolutePath()//获取文件绝对路径
public String getParent()//父级目录
public long length() //文件大小 以字节计
public boolean exists() //是否存在该文件
public boolean isFile()
public boolean isDirectory()//是否是目录

2.3 目录操作及删除文件

public boolean mkdir()//创建一级目录
public boolean mkdirs()//创建多级目录
    delete()//删除空目录或文件

3.流

① IO流(Input/Output),用于处理数据传输,比如文件读取/传输,网络通讯。

② Java中对数据的输入/输出是以流(stream)的方式进行

③ Java.io 包下提供了各种流类和接口,用于存储传输不同的数据

3.1 流的分类

按数据单位类型分为:字节流(8 bit)、字符流

按数据流的方向分为:输入流、输出流

按流的角色不同分为:节点流,包装流

字节流 字符流
输入流 InputStream Reader
输出流 OutputSream Writer

Java IO流类基本由以上述四个抽象类派生

3.2 FileInputStream 文件字节输入流

3.2.1 第一种读取文件的方式 public int read()

​ read()返回int类型,当无字节可扫描时返回-1

​ fileData用于存储read()方法返回的int值,通常为字符的ASCII码

        String path = new String("d:\\桌面\\helloWorld.txt");//定义一个字符串记录文件路径
        FileInputStream fin = new FileInputStream(path);//创建输入流对象
        int fileData;
        while((fileData = fin.read()) != -1){//调用read方法
            System.out.print((char) fileData);
        }
        fin.close();//关闭流

3.2.2 第二种public int read(byte b[])

* 字符输入流一次读取一个字节(8 bit),
* 但每个汉字三个字节,如果每次只读取一个字节就打印输出的话就会造成乱码
* 它一次会扫描b.length个字节
* 区别于上面的fileData,当以字节数组扫描文件时,返回的是此次扫描到的字节个数。
public void method2() {
        String filePath = new String("d:\\桌面\\helloWorld.txt");
        byte[] b = new byte[3];  //创建byte[]数组
        int fileData;
        FileInputStream fin = null;
        try {
            fin = new FileInputStream(filePath);
            while((fileData = fin.read(b)) != -1){  //fileData存储
                System.out.print(new String(b,0,fileData));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                fin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3.3 FileOutputStream 文件字节输出流

①public void write(int b)

​ 输入单个字符。

②public void write(byte b[])

​ 输入字符数组,可以使用String类的getBytes()方法使字符串转换为字节数组

③public void write(byte b[], int off, int len)

​ off为字节数组开始下标,len为指定字节数组长度

    @Test
    public void method1(){
        String filePath = "d:/桌面/helloWorld.txt";
        FileOutputStream fout = null;
        String str = "i love China";
        try {
            fout = new FileOutputStream(filePath);
            //public void write(int b)
//            fout.write('W');

            //public void write(byte b[])
//            fout.write(str.getBytes());

            //public void write(byte b[], int off, int len)  off为字节数组开始下标,len为指定字节数组长度
            fout.write(str.getBytes(),2,5);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fout.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3.4 文件复制

文件复制实现过程为

  1. 使用文件字符输入流读取文件数据
  2. 使用文件字符输出流将读取的文件数据输出到新建文件中
 @Test
    public void methodCopy(){
        String filePath = "d:/桌面/boom_1.png";		//被复制的文件路径及文件名
        String toFilePath = "d:/桌面/爆炸.png";		//复制后的文件存储路径及文件名
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        byte[] buf = new byte[1024];
        //因为读取文件时使用文件字节输入流中的第二种read方法,因此定义一个变量用于存储此次读到的数据个数
        //用第二种方法是为了提高效率
        int fileDataNum;
        try {
            fileInputStream = new FileInputStream(filePath);
            //fileOutputStream = new FileOutputStream(toFilePath,true) 
            // 当输出流增加第二个参数boolean append,为true时,输出流对文件的操作为->在文件原本数据后继续追加新数据,而不是新数据覆盖旧数据
            fileOutputStream = new FileOutputStream(toFilePath);

            while((fileDataNum = fileInputStream.read(buf)) != -1){
                //因为使用了第二种read()方法
                //再输出数据时应该使用第三种wirte()方法,读多少输出多少,避免了读取不存在的数据
                fileOutputStream.write(buf,0,fileDataNum);
            }
            System.out.println("复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileInputStream != null) fileInputStream.close();
                if (fileOutputStream != null) fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3.5 FileReader 文件字符输入流

//构造器
public FileReader(String fileName)
public FileReader(File file)

​ 具体操作与FileInPutStream相似。

//创建FileReader对象调用read()方法
public int read()
public int read(char cbuf[])

第二种read()方法需要创建一个字符数组。

第一种read方法返回的int值为字符的ASCII值,第二种read方法返回的int值为此次读取的字符个数。

3.6 FileWriter 文件字符输出流

           
String str = "太空计划";
char[] c = {'A','B','C'};
//.....
			//public void write(int c)  单个字符
            fileWriter.write('A');

            //public void write(String str)  字符串
            fileWriter.write(str);
 
            //public void write(String str, int off, int len)  字符串子串
            fileWriter.write(str,0,3);

            //public void write(char cbuf[])  字符数组
            fileWriter.write(c);

            //public void write(char cbuf[], int off, int len)  字符数组子数组
            fileWriter.write(c,0,2);

​ 区别于字节输出流,FileWirter在使用完后必须调用flush()、close()方法,否则数据不能写入指定文件。

3.7 包装流

节点流:针对某个特定的数据源(文件、数组等)进行操作的流,比如上面提到的FileInPutStream、FileWirter等

包装流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

包装流使用了一种设计模式->模拟修饰器模式,对基础的节点流功能进行了扩展。

包装类加入了缓冲区,并对节点流功能进行了扩展,提高了读写的效率。

3.7.1 BufferedReader

//它有两种构造器,都需要提供一个Reader子类的实例
public BufferedReader(Reader in)
public BufferedReader(Reader in, int sz)    

​ BufferedReader中正是通过这个Reader in 属性,来封装一个节点流,从而使用节点流,并对节点流的功能进行扩展。

            String filePath = "d:/桌面/helloWorld.txt";
			String line;
			BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
            while ((line = bufferedReader.readLine()) != null){
                System.out.println(line);
            }

​ 例如上述中的readLine()方法正是对bufferedreader底层调用的FileReader的功能扩展,在3.5中,FileReader的实例在读取文件数据时有两种方法,一次扫描一个字符或是一次扫描一个字符数组的长度,但BufferedReader类的readLine()方法扩展了原本的read()方法功能,readLine()方法能够一次扫描一行的字符。

在关闭包装流时,只需包装流实例调用close()。

3.7.2 BufferedWirter

与BufferedReader用法别无二样。newLine()正是对FileWirter功能的扩展,该方法的为换行。
bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
bufferedWriter.write("China No.1");
bufferedWriter.newLine();
bufferedWriter.write("China No.1");
bufferedWriter.close();

3.7.3 字节流 BufferedInputStream/BufferedOutputStream

字节流处理二进制文件。

构造器传入InPutStream/OutPutStream子类实例。

若使用字符流处理二进制文件(如视频)会导致文件损坏。

4. 对象流 ObjectInputStream/ ObjectOutputStream

4.1 序列化与反序列化

从上述IO操作来看,我们要将一个数字100保存到文件中是很简单的,但是这里存在一个问题-->我们不能确定被保存至文件中的“数字”100的类型,它可以是int型,它也可以是String类型,那如何保存数据的类型和值呢?

序列化就是指在保存数据时,同时保存数据的类型和值。

反序列化就是指在读取数据时,获得数据的类型和值。

假如我们要保存的数据是某个类的实例(比如Dog类,属性age,name),我们是否要对该类做些什么才能实现序列化?

要实现类的序列化需要类继承一下两个接口其中之一:

-> Serializable (推荐版)
-> Externalizable

4.2 使用

创建ObjectOutputStream时依然需要传入一个OutputStream子类实例。

ObjectOutputStream在写入数据时,传入的实例类必须实现4.1中的两个接口之一,否则会抛出异常NotSerializableException

同时在存入数据后,在文件中保存也并非以纯文本形式保存,而是以一种特有格式完成的序列化保存

反序列化读取数据时,应与序列化存入数据的数据保持一致。例如

//序列化顺序,oos为 ObjectOutPutStream实例化对象
oos.writeInt(100);
oos.writeObject(dog);

//反序列化顺序,ois为 ObjectInPutStream实例化对象
System.out.println(ois.readInt());
System.out.println(ois.readObject());

若顺序不一致,会抛出OptionalDataException

在序列化时,会将存入的实例数据的类(包含包名)的数据也存入文件中。

在反序列化时(ObjectInPutStream),就需要能够访问到存储时的类(同个包下),否则会类型转换异常。

5. (转换流)InputStreamReader / OutputStreamWirter

InPutStreamReader可以解决中文乱码问题。

InPutStreamReader详解

6. 打印流PrintStream / PrintWirter

6.1 PrintStream 字节

public class TestPrintStream {
    public static void main(String[] args) throws IOException {
        //实例化打印流对象
        PrintStream ps = System.out;
        ps.print("hello world~");//这里print()方法其实调用了write()方法
        ps.write("你好".getBytes());

        //我们还可以修改打印流打印的位置(默认显示屏)
        //需要调用setOut()方法
        //这里我们修改打印的位置为桌面的文本文件,
        System.setOut(new PrintStream("d:/桌面/helloWorld.txt"));
        System.out.print("你好");
    }
}

6.2 PrintWirter

用法与PrintStream类似,使用后必须使用close()方法关闭流,否则不能将数据写入文件。

7. 配置文件 Properties类

Properties为HashTable子类,是一个专门用于读写配置文件的集合类

配置文件的格式为:

键 = 值

7.1 常用方法

load() //加载配置文件的键值到Properties对象
list() //将数据显示到指定设备
getProperty(key)// 根据键获取值
setProperty(key,value) // 设置键值到Properties对象
store()//将Properties中的键值对存储至配置文件中,在IDEA中,如果含有中文,会存储为unicode码

7.2 使用示例

//1.创建Properties对象
Properties p = new Properties();
//2.创建键值对保存至内存中
p.setProperty("name", "wsl");
p.setProperty("sNum", "03187023");
p.setProperty("age", "21");

//3.保存至文件中
//public void store (Writer writer, String comments)第二个参数为对配置文件的注释
p.store(new FileWriter("src/mysql.properties"),null);

//4.读取配置文件中的信息
System.out.println("学生姓名为:" + p.getProperty("name"));//->wsl

//5.修改配置文件中信息
p.setProperty("name", "汤姆");
System.out.println("学生姓名为:" + p.getProperty("name"));//->汤姆
posted @ 2021-10-26 18:03  南晨晨晨  阅读(71)  评论(0)    收藏  举报