Java基础IO流学习笔记

请添加图片描述

一.什么是IO流

I : 指的是input(输入):读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
O:指的是output(输出):将程序(内存)数据输出到磁盘、光盘等存储设备中
所以IO流就是输入输出流,流以程序为中心,往程序里面的就是输入流,往程序外的就是输出流(比如人喝水)
IO流最最重要的地方就是知道在什么时候选用什么流!!!

二.File类

在了解IO流之前先了解一些常用的File类API

1.创建文件

	1.绝对路径直接创建:
		String filePath = "e:\\new1.txt";	
		File file = new File(filePath);		//创建文件对象,相当于只在内存里面创建,还跟磁盘没有发生关系
		file.createNewFile();                                 //然后调用创建文件的方法(写入到磁盘)
	2.父目录文件加子路径创建:
		File parentFile = new File("e:\\");
		String fileName = "new2.txt";
		File file = new File(parentFile,fileName);
		file.createNewFile(); 
	3.父目录加子路径构建:
		String parentFile = new File("e:\\");
		String fileName = "new2.txt";
		File file = new File(parentFile,fileName);
		file.createNewFile(); 

2.File类常用API

		File file = new File("e:\\new1.txt");          创建文件对象
		调用相应方法得到对应信息
		file.getName();			//得到文件名字
		file.getAbsolutePath();	//文件绝对路径
		file.getParent();		//文件父级目录
		file.length();			//文件大小(字节)
		file.exists();			//文件是否存在
		file.isFile();			//是不是一个文件
		file.isDirectory();		//是不是一个文件夹
		创建删除文件夹:
		if(file.exists){file.mkdir();}	//只能创建一级目录("e:\\ddd")
		if(file.exists){file.mkdirs();}	//可以在多级目录下创建,所以一般都用这个
		if(file.exists){file.delete();}	//如果文件存在,删除文件(在java中目录也是特殊文件)

三.流的分类

	1.按操作数据单位不同分为:字节流(8 bit)二进制文件,字符流(按字符)文本文件
	2.按数据流的流向不同分为:输入流,输出流
	3.按流的角色的不同分为:节点流,处理流/包装流
	
	IO流由以下四部分顶级基类组成
	输入流:字符输入流(Reader),字节输入流(InputStream)
	输出流:字符输出流(Writer),字节输出流(OutputStream)
	
	选用什么流的话具体看传输的是什么东西,比如二进制文件(音频,视频
	,docx,pdf等等用字节流,因为用字符流的话文件很容易损坏,像文本文档之类的用字符流效率更高)

四.常用的一些流

节点流和处理流的区别:

流按功能又分为节点流和处理流:

  • 节点流:可以从一个特定的数据源读写数据,如FileReader、FileWriter,是底层流/低级流,直接跟数据源相接。
  • 处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader、BufferedWriter。节点流既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出,对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连(相当于给节点流包了一层)

处理流的功能主要体现在以下两个方面:

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

下面是所有节点流和处理流的直观图
请添加图片描述

InputStream常用子类(字节输入流)

1.FilelnputStream:

示例:

1.第一种读取文件的方式:read()无参,一个字节一个字节地读,效率低,一般不用
		String filePath = "e:\\hello.txt";
        int readDate = 0 ;						 //临时存放数据
        FileInputStream fileInputStream = null;            			 //这样定义是为了提升作用域,不然finally里面拿不到fileInputStream对象
       	 try {
    
    
        	fileInputStream = new FileInputStream(filePath);		//创建FileInputStream对象,用于读取文件
          	while ((readDate=fileInputStream.read())!=-1){
    
    		//每读取一个字节输出一个字节  当没有下一个值时返回-1
             		System.out.print((char)readDate);			//如果文本里面有中文会出现乱码,因为一个中文占3个字节,而一次只读取一个字节
        			}
     		  } catch (IOException e) {
    
    
         			e.printStackTrace();
       		        }finally {
    
    
         				fileInputStream.close();				//读取完一定要关闭流,不然很占用资源
      			 }
2.第二种读取方式:read(byte [ ] b)    一次最多读取b.length长度的字节,读不到返回-1(通常用这种!!!!)   
		String filePath = "e:\\hello.txt";
        int readLen = 0 ;					 //存放字节长度
        byte[] buf = new byte[8];   //一次读取八个字节
		FileInputStream fileInputStream = null;            			
       		try {
    
    
        			fileInputStream = new FileInputStream(filePath);		
         			while ((readLen=fileInputStream.read(buf))!=-1){
    
    		//每读取8个字节,返回整数8,最后一次返回最后一次读取了多少字节的整数,没读取到返回负1
             			System.out.print(new String(buf,0,readLen));		//String(字节数组,从哪个索引开始读取,读取字节的长度)
        			}
     			} catch (IOException e) {
    
    
         				 	  e.printStackTrace();
       			}finally {
    
    
         				fileInputStream.close();				
      			 }

2.BufferedInputStream:

缓冲流,构造器里面可以放任意节点流,外边包了一层,效率更高。
使用案例:

    @Test
    public void bufferedInputStream() throws IOException {
    
    
        String path = "f:\\s.txt";
        int readLen = 0;
        byte[] buf = new byte[1024];
        BufferedInputStream bi = new BufferedInputStream(new FileInputStream(path));
        while ((readLen=bi.read(buf))!= -1){
    
    
            System.out.println(new String(buf,0,readLen));
        }
        bi.close();                             //这里底层会去实现FileReader.close()方法
    }

3.ObjectInputStream:

看一个需求

1.将int num = 100这个int数据保存到文件中,注意不是10数字,而是int 100,并且,能够 从文件中直接恢复int 100
2.将Dog dog = new Dog(“小黄”,3)这个dog对象保存到文件中,并且能够从文件恢复.
3.上面的要求,就是能够将基本数据类型或者对象进行序列化和反序列化操作

序列化和反序列化

1.序列化就是在保存数据时,保存数据的值和数据类型
2.反序列化就是在恢复数据时,恢复数据的值和数据类型
3.需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该
类必须实现如下两个接口之一:
Serializable 这是一个标记接口(通常都是实现这个)
Externalizable

对象处理流注意事项和细节说明

1)读写顺序要一致(怎么写进去的就按那个顺序去序列化读取)
2)要求序列化或反序列化对象,需要实现Serializable(基本类型会自动装箱(基本类型封装类已经实现了Serializable接口),对象的话需要实现Serializable接口)
3)序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性(private static final long serialVersionUID = 1L;)
4)序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
5)序列化对象时,要求里面属性的类型也需要实现序列化接口
6)序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

使用案例:

    @Test
    public void objectInputStream() throws IOException, ClassNotFoundException {
    
    
        String filePath ="e:\\data.txt";
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filePath));
        //读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致否则会出现异常
        System.out.println(objectInputStream.readInt());
        System.out.println(objectInputStream.readBoolean());
        System.out.println(objectInputStream.readChar());
        System.out.println(objectInputStream.readDouble());
        System.out.println(objectInputStream.readUTF());
        //dog的编译类型是 0bject , dog的运行类型是 Dog
        Object dog = objectInputStream.readObject();
        System.out.println("运行类型="+dog.getClass());
        System.out.println("dog信息=" + dog);
        //这里是特别重要的细节:
        //1.如果我们希望调用Dog的方法,需要向下转型
        //2.需要我们将Dog类的定义,拷贝到可以引用的位置(!!!因为序列化的时候会把具体包名什么都写进去,可以把Dog类做成一个public类)
        Dog dog2 = (Dog) dog;
        System.out.println(dog2.getName());
        objectInputStream.close();
    }
public class Dog implements Serializable {
    
    
    private String name;
    private int age;
    public Dog(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 "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

OutputStream常用子类(字节输出流)

1.FileOutputStream:

文件输入流,按字节方式
示例代码:

    @Test
    public void fileOutputStream() throws IOException {
    
    
        String path = "e:\\a.txt";
        FileOutputStream fileOutputStream = null;
        try {
    
    
            fileOutputStream = new FileOutputStream(path,true);     //这个构造器后面加个true,表示续写而不是覆盖文件原有内容
            fileOutputStream.write('s');                        	//第一种方式,写入一个字节
            String str = "asdf,sdffff";
            fileOutputStream.write(str.getBytes());                 //第二种方式,把一个字符串装换成字节数组输入      str.getBytes()-->可以把字符串变成字节数组
           // fileOutputStream.write(byte[] b,int 0,int len);       //第三种方式,可以从自定位置输入自定字节数    off表示从下标几,len表示写入前几个字节
            } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            fileOutputStream.close();
        }
    }

文件的拷贝(普通拷贝)

    @Test
    public void copyfile(){
    
    
        String InputFath =  "f:\\cjy.png";
        String OutputFath = "f:\\cjy2.png";
        FileInputStream   fileInputStream =null;
        FileOutputStream fileOutputStream =null;
        int readLen = 0;                   //存一次读取了几个字节(因为最后一次可能没有读完,或者说没有读到数据会返回-1)
        byte[] data = new byte[2];        //设置一个读取几个字节
        try {
    
    
            fileInputStream = new FileInputStream(InputFath);
            fileOutputStream = new FileOutputStream(OutputFath);
            while((readLen=fileInputStream.read(data))!=-1){
    
    
                fileOutputStream.write(data,0,readLen);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            try {
    
    
                fileOutputStream.close();
                fileInputStream.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }

2.BufferedOutputStream:

使用案例:

    @Test
    public void bufferedOutputStream() throws IOException {
    
    
        String path = "f:\\s.txt";
        BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(path));
        bo.write("这是一个字符串===".getBytes());
        bo.close();                             //这里底层会去实现FileReader.close()方法
    }

3.ObjectOutputStream:

使用案例:

    @Test
    public void objectOutputStream() throws IOException {
    
    
        String filePath ="e:\\data.txt";     //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeInt(100);               //int(自动装箱)--> Integer(实现了 Serializable)
        oos.writeBoolean(true);         // boolean -> Boolean(实现了Serializable)
        oos.writeChar( 'a');            //Char --> Character(实现了Serializable)
        oos.writeDouble(9.5);           // double -> Double(实现了Serializable)
        oos.writeUTF("韩顺平教育");     //String -->实现了Serializable
        //保存一个dog对象
        oos.writeObject(new Dog("asd",10));      //对象的类需要实现序列化接口
        oos.close();
        System.out.println("数据保存成功");
    }
public class Dog implements Serializable {
    
    
    private String name;
    private int age;
    public Dog(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 "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Reader常用子类(字符输入流)

1.FileReader:

一次只读取一个字符或字符数组,效率很低
使用案例:

    @Test
    public void fileReader() throws IOException {
    
    
        String path = "f:\\s.txt";
        int readChar = 0;
        char[] buf = new char[8];
        FileReader fileReader = new FileReader(path);
        while ((readChar=fileReader.read(buf))!=-1){
    
    
            System.out.print(new String(buf,0,readChar));
        }
        fileReader.close();
    }

2.BufferedReader:

文本文档之类的用这个,效率高!
使用案例:

    @Test
    public void bufferedReader() throws IOException {
    
    
        String path = "f:\\s.txt";
        String readLine = null;
        BufferedReader bufferedReader = new BufferedReader(new FileReader(path));
        while ((readLine=bufferedReader.readLine())!=null){
    
         //readLine()是按行读取,读不到的话返回null
            System.out.println(readLine);
        }
        bufferedReader.close();             					//这里底层会去实现FileReader.close()方法
    }

3.InputStreamReader(转换流)

Reader子类

转换流的必要性:

	文件的读取默认是UTF-8编码的,如果读取的文件不是UTF-8编码的,那读取出来的就会是乱码,
	而装换流可以把字节流装换成字符流并设置指定的读取编码(InputStreamReader 构造器自带的)
	其实也相当于给字节流又包装了一层

使用案例:

    @Test
    public void InputStreamReader() throws IOException {
    
    
        String filePath = "e:\\a.txt";
        //指定gbk(看文件是什么编码的就用什么编码方式)的编码方式把FileInputStream转换成InputStreamReader
        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
        BufferedReader     br = new BufferedReader(isr);        //读取还是用缓冲流读取效率高
        String reader = null;
        while ((reader = br.readLine())!=null){
    
    
            System.out.println(reader);
        }
        br.close();
    }

Writer常用子类(字符输出流)

1.FileWriter:

使用案例:

    @Test
    public void fileWriter() throws IOException {
    
    
        String path = "f:\\s.txt";
        char[] chars = {
    
    'a','b','c'};
        FileWriter fw = new FileWriter(path);
        fw.write('d');        //写入一个字符
        fw.write(chars);         //写入一个字符数组
        fw.write("字符串转数组".toCharArray(),0,3);   //写入前三个数据
        fw.write("这是一个案例");     //写入整个字符串
        System.out.println("写入文件成功!");
        fw.close();
    }

2.BufferedWriter:

使用案例:

    @Test
    public void bufferedWriter() throws IOException {
    
    
        String path = "f:\\s2.txt";
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path,true));   //追加的话是在节点流有这个属性,BufferedWriter构造器没有这个属性
        String str = "sfsdfSDFDASFsdfasdf收待发放";
        bufferedWriter.write(str);
        bufferedWriter.newLine();           //插入一个换行符号
        bufferedWriter.write(str);
        bufferedWriter.close();             //这里底层会去实现FileReader.close()方法
    }

文件的拷贝(缓冲流拷贝)

这种方式效率高,通常用这种
使用案例:

    @Test
    public void bufferedCopy() throws IOException {
    
    
        String Readerpath = "f:\\s.txt";
        String Writerpath = "f:\\s2.txt";
        String readLine = null;
        BufferedReader bufferedReader = new BufferedReader(new FileReader(Readerpath));
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(Writerpath));
        while ((readLine=bufferedReader.readLine())!=null){
    
    
            bufferedWriter.write(readLine);
            bufferedWriter.newLine();
        }
        if (bufferedReader!=null){
    
    
            bufferedReader.close(); 
        }
        if (bufferedWriter!=null){
    
    
            bufferedWriter.close();
        }
        bufferedWriter.close();
    }

3.OutputStreamWriter:(转换流)

Writer子类
使用案例:

    @Test
    public void outputStreamWriter() throws IOException{
    
    
        String filePath = "e:\\a2.txt";
        String charSet = "utf-8";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
        osw.write("这是一个案例");
        osw.close();
        System.out.println("按照"+charSet+"写入文件成功");
    }

五.标准输入输出流

  • System.in 标准输入流 类型:InputStream 可以从键盘进行输入
  • System.out 标准输出流 类型: OutpuStream 平常控制台输出的(默认输出位置)
    使用案例:
		Scanner scanner = new Scanner(System.in);     //Scanner会一直等待控制台输入并回车才会执行接下去的程序(获取控制台数据)
		System.out.println("输入内容");
		String next = scanner.next();
		System.out.print1n("next=" +next);

六.打印流

打印流只有输入流没有输出流

  • PrintStream(字节流): OutputStream子类
    使用案例:
    @Test
    public void printStream() throws IOException {
    
    
        PrintStream out = System.out;       //System.out是OutpuStream类型的输出流
        //普通方式打印
        out.print("demo===");
        out.write("demo2".getBytes());     //因为print底层使用的是write ,所以我们可以直接调用write进行打印/输出,默认输出位置是控制台
        out.close();
        //我们可以去修改打印流输出的位置/设备//1.输出修改成到“e:\\f1.txt"
        System.setOut(new PrintStream("e:\\fi.txt"));
        System.out.println("hello,韩顺平教育");              //这样这个打印的位置就不再是控制台而是e:\\f1.txt里了
    }
  • PrintWriter (字符流): Writer子类
    使用案例:
    @Test
    public void printWriter() throws IOException {
    
    
        PrintWriter printWriter = new PrintWriter(System.out);        //控制台输出
        PrintWriter printWriter2 = new PrintWriter(new FileWriter("e:\\f2.txt"));  //重定向输出位置到某个文件
        printWriter.print("哈哈");            //控制台输出
        printWriter2.print("哈哈2");          //指定文件输出
        printWriter.close();
        printWriter2.close();
    }

七.个人小结

流的用法都大同小异,主要是必须清楚什么时候使用什么流合适!还有就是数据读取完一定要关闭流,处理流的话关闭外层流就行,不关闭流的话资源消耗比较大。

学习的地址

猜你喜欢

转载自blog.csdn.net/cc1373709409/article/details/123049927