IO流学习笔记

IO流,什么是IO
  • I:Input

  • O:Output

通过IO可以完成硬盘文件的读和写。

IO流的分类

有多种方式分类:

  • 按照流的方向进行分类

以内存作为参照物,往内存中去叫做输入(Input)或者叫做读(Read)

从内存当中取出来叫做输出(Output),或者叫做写(Write)

  • 按照读取数据的方式进行分类

1.有的流是按照字节的方式去读取数据,一次读取一个字节,等同于一次读取8个二进制位,这种流是万能的,什么类型的文件都可以读取,包括:文本文件,图片,声音文件,视屏文件

2.还有的流是按照字符的方式去读取数据,一次读取一个字符,这种流是为了方面读取普通文本文件而存在的,这种流不能够读取:图片、声音、视频,只能够读取文本文件,连world文件也不能读取。
在这里插入图片描述

java io的四大家族
  • java.io.InputStream 字节输出流

  • java.io.OutputStream字节输入流

  • java.io.Reader字符输入流

  • java.io.Writer字符输出流

    所有的流都实现了:java.io.Closeable接口,他们都是可以关闭的,都有Close方法,因为流是一个管道,是内存和硬盘之间的通道,用完之后一定要关闭,不然会占用很多的资源。

    所有的输出流都实现了:java.io.Flushable接口,都是可以刷新的,都有这个flush方法,输出流在最终输出之后一定要记得刷新一下,这个刷新表示将管道的中剩余没有输出的资源强行输出完(清空管道),注意如果没有执行flush可能会造成资源丢失。

注意:在java中只要是以Stream结尾的都是字节流,以“Reader,Writer”结尾的都是字符流。

需要掌握的十六个流
  • 文件专属

java.io.FileInputStream

java.io.FileOutputStream

java.io.FileReader

java.io.FileWriter

  • 缓冲流专属的

java.io.BufferdReader

java.io.BufferdWriter

java.io.BufferdInputStream

java.io.BufferdOutputStream

  • 数据流专属的

java.io.DataInputStream

java.io.DataOutputStream

  • 转换流专属(将字节流转换成字符流)

java.io.InputStreamReader

java.io.OutputStreamWriter

  • 对象专属流

java.io.ObjectInputStream

java.io.ObjectOutputStream

  • 标准输出流

java.io.PrintWriter

java.io.PrintReader

FileInputStream(从文件中读)

通过FileInputStream来读取txt文件

  • 先在C:\Users\zhengbo目录下t新建一个FileInput.txt文件
  • 在java中通过获取输出流调用read方法来读取我们的文件
  • read方法每执行一步就会向后访问一个字节,当访问完毕没有了时,read返回-1
  • 输出的是对应字符的ascll码值

package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/23 15:58
 **/
public class TestDemo2 {
    public static void main(String[] args) {
        //1.先获取一个输出流
        FileInputStream file = null;
        try {
            //2.创建输入流对象,传出文件路径
            file = new FileInputStream("C:\\Users\\zhengbo\\FileInput.txt");
            //3.调用读方法
            int tag=0;
           while((tag=file.read())!=-1)
           {
               System.out.println(tag);
           }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4.在finally子句中关闭输入流
            try {
                if (file!=null)
                {
                    file.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

  • 补充一个知识点idea中的默认路径是project的根

  • 缺陷:这种通过调用read()方法来一个字节一个字节的读取的这种方法存一定的问题,因为这样每读取一个字节就会调用一次方法这样会使读取效率大大降低。

往byte数组中读

可以通过往指定大小的byte数组中读这样就可以避免一次只能够读取一个字节,提高读取的效率

package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/23 16:55
 **/
public class TestDEmo3 {
    public static void main(String[] args) {
        FileInputStream file = null;
        try {
            file=new FileInputStream("src/com/zb/File/TestFile");
            //new 一个byte数组
            byte[] bytes =new byte[4];
            //file.read(bytes)执行这个方法返回的是byte数组的长度
            //第一次调用read方法
            file.read(bytes);
            System.out.println("调用read方法第一次读的结果:"+new String(bytes));
            //第二次调用read方法
            file.read(bytes);
            System.out.println("调用read方法第二次读的结果:"+new String(bytes));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(file!=null)
            {
                try {
                    file.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

在这里插入图片描述
发现问题在第二次读取的时候我们想要的是gh结果可是,最终出来的是ghdf

在这里插入图片描述

  • 改进

    我们再读的时候可以给定输出的范围,我们每次读的时候可以只需要读从0开始一直到read()返回的数值即可。

int readCount = 0;//定义一个接受阅读字节数的变量
//第一次调用read方法
readCount = file.read(bytes);
//输出的时候输出从0到返回的字节数位置处
System.out.println("调用read方法第一次读的结果:"+new String(bytes,0,readCount));
//第二次调用read方法
readCount=file.read(bytes);
System.out.println("调用read方法第二次读的结果:"+new String(bytes,0,readCount));

在这里插入图片描述

  • 再改进(采用循环的方式输出)
while(true)
{
    readCount = file.read(bytes);
    if(readCount!=-1)
    {System.out.print(new String(bytes,0,readCount));}
    else {
        break;
    }
}
  • 这个循环的代码可以再改进为
		int tag=0;
         while((tag=file.read(bytes))!=-1)
         {
             System.out.print(new String(bytes,0,tag));
         }

这样可以使代码更加简洁!

输出的结果为:

在这里插入图片描述

FileInputStream中一些其他的方法
  • available(读取文件当中的总字节数)

这个方法的应用点:调用这个方法我们直接就可以知道文本文件的字节数,我们通过这个字节数直接一次读取全部的字节,就只需要读取一次就可以读取完,可以有效提高效率。(这个方法对于大文件不太适用)

package com.zb.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/23 22:04
 **/
public class Testdemo5 {
    public static void main(String[] args) {
        int count = 0;//定义一个变量用来接受总字节数
        try {
            FileInputStream file = new FileInputStream("src/com/zb/File/TestFile1");
            count=file.available();
            byte[] bytes = new byte[count];
            int tag=0;
            while((tag=file.read(bytes))!=-1)
            {
                System.out.print(new String(bytes,0,count));
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

只需要读取一次即可读出全部字节

在这里插入图片描述

  • skip(跳过几个字节不读)
file.skip(2);//调用方法,采取跳过两个字节不读
System.out.println(file.read());//输出他的ASCII码值

原文本

在这里插入图片描述
结果(跳过了两个直接读取d的ASCII码值100)
在这里插入图片描述

FileOutputStream的使用(往文件中写)

获取FileOutputStream对象调用writer方法,向文件中写入数据

注意点:

  • 我们在绑定文件地址时,如果原先不存在这个文件,那么在执行writer方法时,会自动先生成一个指定路径的文件
  • 在进行写入操作时,在最后一定要记得刷新一下,并关闭释放资源。
package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/23 22:45
 **/
public class TestDemo6 {
    public static void main(String[] args) {
        //1.获取一个FileOutputStream对象
        FileOutputStream file = null;
        //2.绑定文件地址
        try {
            file = new FileOutputStream("src/com/zb/File/TestFile3");
            //将要写的数据存放在byte的数组中
            byte[] bytes = {'z','h','e','n','g'};
            //调用write方法写
            file.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (file!=null)
            {
                //最后我们一定要记得去刷新一下我们的file
                try {
                    file.flush();
                    file.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }

        }
    }
}

程序完成之后在这里多了一个文件:
在这里插入图片描述

里面的内容为:

在这里插入图片描述

发现问题:

当我们再去存放一些数据时,发现写完后的文件内容变为了

			//将要写的数据存放在byte的数组中
            byte[] bytes = {'w','a','n'};
            //调用write方法写
            file.write(bytes);

在这里插入图片描述

所以:调用writer方法时,其实是先将文件当中的内容清空掉以后才写入新的数据,所以这个方法需要谨慎使用,不然可能会造成数据的丢失

解决方案:采用追加的方式向原文件中写入数据,具体实现的犯法是:再绑定文件的时候将append属性设置为true

//FileOutputStream有两个属性,一个是文件路径一个是append,设置为true就是再原文件后面最追加写入了
file = new FileOutputStream("src/com/zb/File/TestFile3",true);

测试

file = new FileOutputStream("src/com/zb/File/TestFile3",true);
byte[] bytes = {97,98,99};
file.write(bytes);

注明:这里存入的的97,98,99,其实是ASCII码值,会在文件中转换为对应的字符

在这里插入图片描述

这样就不回造成源数据丢失啦!

  • 存入字符串,其本质还是将字符串转换成了字节数组byte[]
 			String str = "我是中国人我自豪";//创建一个字符串
            byte[] bytes = str.getBytes();//调用getBytes方法,将字符串转换成字节数组
            file.write(bytes);//写入数据

执行结果:

在这里插入图片描述

文件拷贝

文件拷贝原理:一边读一边写,原理和读写一样

在这里插入图片描述

package com.zb.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/23 23:43
 **/
public class TestDemo7 {
    public static void main(String[] args) {
        FileInputStream fis=null;
        FileOutputStream fos = null;

        try {
            //fis绑定我们需要复制的文件,与就是我们要读的数据的文件
           fis=new FileInputStream("C:\\Users\\zhengbo\\Documents\\" +
                    "WeChat Files\\wxid_rburdjipc9an22\\FileStorage" +
                    "\\Video\\2021-10\\舒克长胶.mp4");
            //fos绑定我们要写入的文件名
            fos=new FileOutputStream("C:\\test\\舒克长胶.mp4",true);
            /*最核心的是一边读一边写*/
            byte[] bytes = new byte[1024*1024];//一次最多读取1024*1024个字节也就是1M
            int readcount=0;//定义一个变量用来接收读取的字节数
            while ((readcount=fis.read(bytes))!=-1)
            {
                fos.write(bytes,0,readcount);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fos!=null)
            {
                try {
                    fos.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis!=null)
            {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
FileReader(字符输入流使用方法)
  • 它只能够读文本文件,读文本文件比FileInputStream更加便捷有效

  • 他和FileInputStream使用的方式差不多,只是在这里我们使用的是char[]数组来保存我们读的数据,其余流程基本相似。

package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 0:08
 **/
public class TestDemo8 {
    public static void main(String[] args) {
        FileReader fr=null;
        try {
            fr=new FileReader("src/com/zb/File/TestFile3");
            //和FireInputStream不同的是这里需要使用char[]数组来保存我们读出来的数据,因为这个叫做字符输入流
            char[] chars = new char[4];
            int readcount=0;
            while ((readcount=fr.read(chars))!=-1)
            {
                System.out.print(new String(chars,0,readcount));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fr!=null)
            {
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

原文件:

在这里插入图片描述
输出结果:
在这里插入图片描述

FileWriter(字符输出流)
  • 字符输出流对文本文件的写操作更加便捷,而且方式更多
  • 在操作普通文本文件的使用尽量使用FileReader/FileWriter会大大提高工作的效率
package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 0:27
 **/
public class TestDemo9 {
    public static void main(String[] args) {
        FileWriter fw=null;
        try {
            //添加append为true避免每次进行写时清空原数据,并且也会自动生成原本没有的文本文件
            fw = new FileWriter("src/com/zb/File/TestFile4",true);
            //直接可以写入char[]数组
            char[] chars = {'为','中','华','之','崛','起','而','读','书'};
            fw.write(chars);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fw!=null)
            {
                try {
                    fw.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fw!=null)
            {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 执行结果:

在这里插入图片描述

  • 可以在调用writer方法时直接写入一个字符串
fw.write("\n");//写入\n表示换行
fw.write("键盘敲烂,月薪过万!");//直接写入一个字符串
  • 执行结果

在这里插入图片描述

字符流的拷贝

字符流的拷贝和字节流大致相似,在此不再赘述!

BufferedReader(自带缓冲区的输入流)
  • 使用这个流不需要自定义char[]/byte[]数组,自带缓冲
  • 当一个流的构造函数需要一个流的时候,这个被传入的流被叫做:节点流
  • 外部负责包装的这个流叫做包装流,或者叫做处理流
  • 像下面这个程序中BufferedReader就是包装流,Reader就是传入的节点流
  • 这个地方我们这需要关闭外部的包装流,因为在包装流当中有close方法关闭节点流,通过查看源码可以知道。
package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 0:57
 **/

/*
* BufferedReader:
*   带有缓冲区的输入流
*   使用这个流不需要自定义char[]/byte[]数组,自带缓冲
* */
public class TestDemo10 {
    public static void main(String[] args) {
        BufferedReader br=null;
        FileReader re = null;
        try {
                //实例化一个re对象
                re = new FileReader("src/com/zb/File/TestFile4");
                //这个流的创建他的构造函数需要我们需要我们去传入一个Reader流类的参数
                br= new BufferedReader(re);
            System.out.println(br.readLine());//直接读取一行
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(br!=null)
            {
                try {
                    br.close();//这个地方我们这需要关闭外部的包装流,因为在包装流当中有close方法关闭节点流,通过查看源码可以知道。
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    }
  • 原文件

在这里插入图片描述

  • 执行结果,我只执行了一次readLine(),所以只会读取第一行,执行几次就会读几行,下面展示执行3次

在这里插入图片描述

  • 执行三次的结果
	br= new BufferedReader(re);
	System.out.println(br.readLine());
	System.out.println(br.readLine());
	System.out.println(br.readLine());

在这里插入图片描述

  • 注意,readLine()这个方法本身不具备,换行功能,是我的println执行的换行

  • 如果使用print方法输出即可明白

br= new BufferedReader(re);
System.out.print(br.readLine());
System.out.print(br.readLine());
System.out.print(br.readLine());
  • 执行结果,可见readLine()本身是不具备换行的

在这里插入图片描述

包装流和节点流

在获取BufferedReader对象的时候需要给他一个FileReader(字符输入流)的属性,如果传入的是FileInputStream(字节输入流),就会报错,这时候我们可以使用InputStreamReader来将我们的字节流装换成字符流。

	BufferedReader br=null;
	//先注释掉原本获取到的字符流对象。
	//FileReader re = null;
	//获取一个字节流对象
	FileInputStream re1  = null;
	//使用字符转换流将字节流转换成字符流
	InputStreamReader re = new InputStreamReader(re1);
	try {
    //实例化一个re对象
    re = new FileReader("src/com/zb/File/TestFile4");
    //这个流的创建他的构造函数需要我们需要我们去传入一个Reader流类的参数
    br= new BufferedReader(re);

几点注意

  • 包装流和节点流是相对而言的,在某个场景下的包装流在另外一个场景下也有可能是节点流
  • 在上面这份到代码中,InputStreamReader包装了FileIputStream,所以在这里InputStreamReader是一个包装流,FileIputStream是传入的节点流,然后在下面,FileReader又包装了InputStream所以在这里FileReader又成为了包装流,而原本的包装流InputStream在这里却变成了传入FileReader属性中的节点流。
BufferedWriter(自带缓冲区的输出流)
  • 理论部分和自带缓冲区的输入流差不多,不需要自己构建缓冲数组(char[],byte[])

具体实现:

package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 14:01
 **/
public class TestDemo12 {
    public static void main(String[] args) {
        BufferedWriter buf = null;
        FileWriter fw = null;
        try {
            fw = new FileWriter("src/com/zb/File/TestFile5",true);
            buf = new BufferedWriter(fw);
            buf.write("此生不悔如华夏,来世还做中国人!");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (buf!=null)
            {
                try {
                    buf.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    buf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}
  • 还是一样往里写入的时候要在最后执行一下刷新
  • 只需要关闭最外层的包装流即可
  • 执行结果(成功创建一个文本文件并写入指定内容)

在这里插入图片描述

数据专属的流
  • java.io.DataOutputStream

  • DataOutputStream 这个是数据专属流,这个流不仅可以将数据写入文件中,还可以将数据的类型写进文本中

  • 值得注意的是这个文件就不是普通的文件了,用记事本无法正常打开

  • 通过查看原码得知DataOUtputStream的构造方法需要传一个OutputStream out属性,但是OutputStream是一个抽象类不能new对象,我们需要new一个他的子类对象======》FileOutputStream

    package com.zb.test;
    
    import java.io.DataOutputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    /**
     * @Author 啵儿
     * @Email 1142172229@qq.com
     * @date 2021/10/24 19:24
     **/
    public class TestDemo13 {
        public static void main(String[] args) {
            /*
            * DataOutputStream  这个是数据专属流,这个流不仅可以将数据写入文件中,还可以将数据的类型写进文本中
            * 注意:这个文件就不是普通的文本文件了,记事本无法正常打开
            * */
            DataOutputStream dos = null;
            FileOutputStream fos = null;
          /*  通过查看原码得知DataOutputStream的
          构造方法需要传一个OutputStream out属性,
          但是OutputStream是一个抽象类
          不能new对象,我们需要new一个他的子类对象======》FileOutputStream
          */
            try {
                 fos = new FileOutputStream("src/com/zb/File/TestFile6",true);
                 dos = new DataOutputStream(fos);
                 byte b = 1;
                 short s=2;
                 int i=3;
                 float f = 4.5F;
                 double d = 5.6;
                 boolean bool=true;
                 char c = 'z';
                 long l = 100L;
                 dos.writeByte(b);
                 dos.writeShort(s);
                 dos.writeInt(i);
                 dos.writeFloat(f);
                 dos.writeDouble(d);
                 dos.writeBoolean(bool);
                 dos.writeChar(c);
                 dos.writeLong(l);
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(dos!=null)
                {
                    try {
                        dos.flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    try {
                        dos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 如果强行用普通文本文件打开的话会出现乱码

在这里插入图片描述

所以我们要用DataInputStream这个数据专用的输入流来读出才能正常显示

  • java.io.DataInputStream

    package com.zb.test;
    
    import java.io.DataInputStream;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    /**
     * @Author 啵儿
     * @Email 1142172229@qq.com
     * @date 2021/10/24 19:50
     **/
    public class TestDemo14 {
        public static void main(String[] args) {
            FileInputStream fis = null;
            DataInputStream dis = null;
            try {
                fis = new FileInputStream("src/com/zb/File/TestFile6");
                dis = new DataInputStream(fis);
                System.out.println(dis.readByte());
                System.out.println(dis.readShort());
                System.out.println(dis.readInt());
                System.out.println(dis.readFloat());
                System.out.println(dis.readDouble());
                System.out.println(dis.readBoolean());
                //System.out.println(dis.readLong());
                System.out.println(dis.readChar());
                System.out.println(dis.readLong());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if (dis!=null)
                {
                    try {
                        dis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 读的结果在这里插入图片描述

  • 注意这个地方在读的时候需要和原本写入时的顺序保持一致,才能正常读出

    原本写入的顺序:

    dos.writeByte(b);
    dos.writeShort(s);
    dos.writeInt(i);
    dos.writeFloat(f);
    dos.writeDouble(d);
    dos.writeBoolean(bool);
    dos.writeChar(c);
    dos.writeLong(l);
    

    读的顺序:

    System.out.println(dis.readByte());
    System.out.println(dis.readShort());
    System.out.println(dis.readInt());
    System.out.println(dis.readFloat());
    System.out.println(dis.readDouble());
    System.out.println(dis.readBoolean());
    System.out.println(dis.readChar());
    System.out.println(dis.readLong());
    
  • 如果读得顺序不一致,我将最后两个反一下验证一下

    System.out.println(dis.readByte());
    System.out.println(dis.readShort());
    System.out.println(dis.readInt());
    System.out.println(dis.readFloat());
    System.out.println(dis.readDouble());
    System.out.println(dis.readBoolean());
    System.out.println(dis.readLong());
    System.out.println(dis.readChar());

结果:

在这里插入图片描述

结果出现了问题,所以要记得怎么输出就怎么输入,要保持顺序相同

标准输出流
  • java.io.Printstream是标准的字节输出流
  • 标准字节输出流默认输出到控制台,这个是可以更改的
  • 标准字节输出流不需要自己手动的关闭

我们日常使用的打印方法就是一个标准输出流

package com.zb.test;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 20:37
 **/
public class TestDemo15 {
    public static void main(String[] args) {
        System.out.println("中国共产党万岁!");
    }
}

在这里插入图片描述

  • 改变一下输出的位置,我不想输出到控制台了,我想让标准字节输出流输出到文件当中可以吗?

  • 指定一个文件进行输出

package com.zb.test;

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

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 20:37
 **/
public class TestDemo15 {
    public static void main(String[] args) {
        /*System.out.println("中国共产党万岁!");*/
        PrintStream ps = null;
        FileOutputStream fis = null;
        try {
            fis = new FileOutputStream("src/com/zb/File/TestFile7",true);
            ps = new PrintStream(fis);
            ps.println("键盘敲烂,月薪过万!");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • 成功输出到指定文件当中

在这里插入图片描述

  • 利用PrintStream编写一个日志工具类
package com.zb.util;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 20:56
 **/
public class logutil {

    public void log(String msg)
    {
        PrintStream ps = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("src/com/zb/File/mylog.txt",true);
            ps = new PrintStream(fos);
            //改变输出的位置
            System.setOut(ps);
            //标准化一个时间格式
            Date nowdate = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("YYYY/MM/DD HH:MM:SS sss");
            String date = sdf.format(nowdate);
            //输出时间和传入的日志信息
            System.out.println(date+":"+msg);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • 测试日志工具
package com.zb.test;

import com.zb.util.logutil;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/24 21:00
 **/
public class TestDemo16 {
    public static void main(String[] args) {
        logutil mylog = new logutil();
        mylog.log("调用了main方法");
        mylog.log("调用了自定义日志工具");
        mylog.log("键盘敲烂月薪过万!");
    }
}
  • 输出结果

在这里插入图片描述

关于File类的理解
  • File类和四大家族没有关系,所以File类不能完成文件的读写
  • File类对象代表什么
  • 文件和路径名的抽象表达式
  • C:\Driver这是一个文件的对象
  • C:\Diver\a\b\c这也是一个文件的对象
  • 一个File对象可能是目录也可能是文件
File类对象当中的常用方法
  • 判断文件是否存在exists()方法
//创建一个File对象
File file = new File("C:/TestFile");

//判断文件是否存在
System.out.println(file.exists());
  • 判断文件是否存在,不存在则创建新的文件createNewFile()方法
try {
    //如果文件不存在创建一个指定路径的文件,如果文件存在输出文件存在
    System.out.println(!file.exists()?file.createNewFile():"文件存在");
} catch (IOException e) {
    e.printStackTrace();
}
  • 以目录方式创建mkdir()方法
try {
    //如果文件不存在创建一个指定路径的文件,如果文件存在输出文件存在
    System.out.println(!file.mkdir()?file.createNewFile():"文件存在");
} catch (IOException e) {
    e.printStackTrace();
}
  • 创建一个多重路径的目录mkdirs()方法
		File file1 = new File("D:/a/b/c/d");
        if(!file1.exists())
        {
            file1.mkdirs();
        }
  • 获取文件的父路径getParent()方法(相对路径)
String parent = file.getParent();
System.out.println(parent);
  • 获取绝对路径(getAbsolutePath())
String absolutePath = file.getAbsolutePath();
System.out.println(absolutePath);
  • 获取文件最后一次的修改时间lastModified()

    long haomiao = file.lastModified();//这个方法返回的是long类型是毫秒,是1970年到现在的总毫秒数
    Date time = new Date(haomiao);
    //设置时间输出格式
    SimpleDateFormat sdf = new SimpleDateFormat("YYYY/MM/dd HH:MM:SS sss");
    String format = sdf.format(time);
    System.out.println(format);
    
  • 获取文件的大小length()

System.out.println(file.length());
  • 获取指定路径下所有的文件listFiles()
File[] files = file.listFiles();
for (File file1 : files) {
    System.out.println(file1);
}

在这里插入图片描述

  • 获取文件的文件名getName()
File[] files = file.listFiles();
for (File file1 : files) {
    System.out.println(file1.getName());
}

在这里插入图片描述

序列化和反序列化的理解

在这里插入图片描述

  • 首先我们要知道,参加序列化的对象要实现Serializable接口。不然会导致这个对象不能进行序列化

  • 这个Serializable接口是一个标致接口,当中没有方法。

  • Serializable接口原码,在这个接口当中没有任何的抽象方法,只起到一个标志作用

public interface Serializable {
}
  • 什么叫标志接口:标志接口中都是没有方法的只是起到一个标志作用,标志实现了这个接口的类,给JVM看的,得到这个标志之后就可能会受到特定的待遇
  • 创建一个学生类
package com.zb.pojo;

import java.io.Serializable;
import java.util.Objects;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/23 11:29
 **/
public class User1 implements Serializable {
    int id;
    String name;

    public User1() {
    }

    public User1(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User1 user1 = (User1) o;
        return id == user1.id &&
                Objects.equals(name, user1.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}
  • 序列化过程
package com.zb.test;

import com.zb.pojo.User1;

import java.io.*;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/25 0:38
 **/
public class TestDemo18 {
    public static void main(String[] args) {
        //new 一个user对象
        User1 user1 = new User1(1,"张三");
        //序列化
        ObjectOutputStream oos =null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("src/com/zb/File/TestFile9"));
            oos.writeObject(user1);
        } catch (IOException e) {
                e.printStackTrace();
        }finally {
            if (oos!=null)
            {
                try {
                    oos.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 序列化之后的效果
    在这里插入图片描述

  • 反序列化过程

package com.zb.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/25 1:03
 **/
public class TestDemo19 {
    public static void main(String[] args) {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("src/com/zb/File/TestFile9"));
            Object o = ois.readObject();
            //反序列化回来的是user对象,因为我自己的user对象是重写了toString方法的,所以在反序列化输出时会调用toString
            System.out.println(o);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ois!=null)
            {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 反序列化之后就会拿到当是序列化时new的对象

在这里插入图片描述

同时序列多个对象
  • 可以将对象放在序列化集合当中

  • 需要注意的是需要序列化的对象和集合都要实现Serializable接口,List集合自身已经实现了Serializable接口

  • 需要实现序列化的对象

package com.zb.pojo;

import java.io.Serializable;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/25 8:43
 **/
//实现Serializable接口
public class Student implements Serializable {
    private int sid;
    private String sname;

    public Student() {
    }

    public Student(int sid, String sname) {
        this.sid = sid;
        this.sname = sname;
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                '}';
    }
}
  • 序列化多个对象的具体操作

  • 先创建一个List集合,然后将我们需要序列化的对象一次放在这个集合当中,然后通过序列化集合来实现序列化多个对象

package com.zb.test;

import com.zb.pojo.Student;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/25 8:37
 **/
public class TestDemo20 {
    public static void main(String[] args) {
        //先创建一个集合用来存放多个对象
        List<Student> list = new ArrayList<>();
        list.add(new Student(1,"张三"));
        list.add(new Student(2,"李四"));
        list.add(new Student(3,"王五"));
        ObjectOutputStream oos = null;
        try {
            oos =new  ObjectOutputStream(new FileOutputStream("src/com/zb/File/TestFile10",true));
            oos.writeObject(list);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (oos!=null)
            {
                try {
                    oos.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 反序列化上面的序列化的多个对象
package com.zb.test;

import com.zb.pojo.Student;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;


/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/25 8:54
 **/
public class TestDemo21 {
    public static void main(String[] args) {
        ObjectInputStream ois = null;
        try {
            ois =new ObjectInputStream((new FileInputStream("src/com/zb/File/TestFile10")));
            //调用readObject方法读出来的是Object类型,将类型装换成List<Student>
            List<Student> studentList=(List<Student>)ois.readObject();
            //调用foreach遍历输出集合当中的内容
            for(Student student : studentList)
            {
                System.out.println(student);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if (ois!=null)
            {
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
transient关键字
  • 咱就是说这个关键字的作用就是比方你有一个类,你在序列化你这个类的时候呢,你不想序列化你这个类其中的某个属性,那你就可以加上这个关键字,那么在序列化的过程当中就不会序列化被transient关键字修饰的属性了
private transient String sname;//加入transient关键字修饰
  • 以上就表示我在序列化这个Student对象的时候,我是不想序列化这个sname属性的
关于序列化版本号
  • 当java虚拟机看到Serializable接口会自动生成一个序列化版本号
  • 序列化版本号的作用:用来区分两个不同的类
  • 区分两个不同的类时,首先看的是两个类的类名,类名不同代表两个类不是同一个类,如果在两个类的类名相同的情况下,这个时候就要查看序列化的版本号,序列化的版本号不同则类的版本号不同
  • 自动生成的序列化版本号有什么缺陷:自动生成的序列化版本号,当类的内容被改写,就需要重新编译,编译之后生成全新的字节码文件,这时候java自动生成的序列化版本号也会发生变化
  • 但序列化版本号发生改变的时候,再去反序列化就会发生异常
  • 最终结论

凡是一个类实现了Serializable接口,建议给该类手动书写一个固定的序列化版本号,这样即使这个类被修改了,但是版本号不变,java虚拟机也会认为还是同一个类

  • 手动设置序列化版本号的方法:在类中加一个属性
private  static final long SerialVersionUID = 1L;//手动设置序列化版本号
io和properties的联合使用
  • propreties是配置文件,它的本质是一个Map集合,k,v都是String类型

  • 一个非常好的设计理念:

以后经常改变的数据可以单独存放在一个文件当中,使用程序动态读取,将来需要修改数据的时候,只需要修改这个文件内容即可,不需要修改java代码,修改java代码需要重新编译,可以能还需要重启服务器,修改文件内容就不会这么麻烦。

类似于以上这种机制的文件,我们叫做配置文件,并且当配置文件的格式是

key=value的时候,我们把这种配置文件叫做属性配置文件。

java规范中有相关要求,属性配置文件建议用properties结尾,但不用定是非要这样

这种以properties结尾的文件在java当中被称作为属性配置文件

其中Properties是专门用来存放属性配置文件内容的一个类

  • 测试用例
package com.zb.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/25 14:54
 **/
public class TestDemo22 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        //1.new 一个流
        try {
            fis = new FileInputStream("src/com/zb/File/TestFile11.properties");
            //2.new 一个map集合,Properties也是一个map集合,可以通过Properties直接new一个map集合
            Properties pro = new Properties();
            //3.调用load方法加载数据到集合当中,文件当中的数据会顺着管道流到map集合当中
            pro.load(fis);
            //4.通过key来获取value
            String username = pro.getProperty("username");
            String password = pro.getProperty("password");
            System.out.println("username:"+username);
            System.out.println("password:"+password);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fis==null)
            {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

在这里插入图片描述

  • 这是原本的属性配置文件
    在这里插入图片描述
  • 可以看到成功取出属性配置文件当中的内容!
    java规范中有相关要求,属性配置文件建议用properties结尾,但不用定是非要这样

这种以properties结尾的文件在java当中被称作为属性配置文件

其中Properties是专门用来存放属性配置文件内容的一个类

  • 测试用例
package com.zb.test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

/**
 * @Author 啵儿
 * @Email 1142172229@qq.com
 * @date 2021/10/25 14:54
 **/
public class TestDemo22 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        //1.new 一个流
        try {
            fis = new FileInputStream("src/com/zb/File/TestFile11.properties");
            //2.new 一个map集合,Properties也是一个map集合,可以通过Properties直接new一个map集合
            Properties pro = new Properties();
            //3.调用load方法加载数据到集合当中,文件当中的数据会顺着管道流到map集合当中
            pro.load(fis);
            //4.通过key来获取value
            String username = pro.getProperty("username");
            String password = pro.getProperty("password");
            System.out.println("username:"+username);
            System.out.println("password:"+password);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fis==null)
            {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

在这里插入图片描述

  • 这是原本的属性配置文件

在这里插入图片描述

  • 可以看到成功取处属性配置文件当中的内容!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值