Java内容梳理(18)API学习(6)I/O流

目录:

1、I/O流的分类

2、常用的I/O流

3、对象的序列化和反序列化

4、对象克隆

5、图片操作


1、I/O流的分类

(1)介绍

流说明了Java中读写数据的方式:

顺序读写数据:从左到右,从上到下依此读取数据

数据的流动方向:单向

参照物:程序内存

(2)分类

1.按照方向分:

输入流:InputStream父类/Reader父类

输出流:OutputStream父类/Writer父类

2.按照单位分:

字节流:InputStream父类/OutputStream父类-------以字节为单位进行操作

字符流:Reader父类/Writer父类-------以字符为单位进行操作

3.按照功能分:

节点流(功能流):提供核心基础读写数据能力的流   

识别节点流:看类名前缀,若是一种明确的数据存放地方,那个流就是节点流。

过滤流(装备流):在节点流提供的基础能力之上进行功能加强

4、流的父类型

InputStream --- 字节输入流

OutputStream --- 字节输出流

Reader --- 字符输入流

Writer --- 字符输出流

2、常用的I/O流

(1)节点流

FileInputStream类:文件字节输入流,能实现按字节读取文件

构造方法:为欲指定的文件建立输入管道   

FileInputStream( String filepath )

读入数据:read( byte[] buf ) 返回值是int,表示读入的字节个数。

每次从硬盘上最大读满buf数组个字节,返回值为-1时,表示没有读到数据,可以通过循环读完整个文件。

FileOutputStream类:文件字节输出流,能实现将字节写入文件(追加方式和覆盖方式两种)

构造方法:与指定文件建立输出管道(默认是替换模式)

FileOutputStream( File file )

FileOutputStream( String filePath )

按指定的模式与指定的文件建立输出管道(true为追加)

FileOutputStream( File file ,boolean isAppendModel)

FileOutputStream( String filePath ,boolean isAppendModel)

写入数据:

write( byte[] buf ):每次向指定的文件写出整个buf数组个字节

write( byte[] buf,int offset,int len ):从buf数组的offset位置开始,写出len长度的字节

注意:

1.当输出的目标文件不存在时,会自动将其创建

2.输出流默认采用替换方式,不会追加

3.当文件是文本文件时,可以使用字符输入流(FileReader),字符输出流(FileWriter),效率更高。

(2)缓冲过滤流

核心:与磁盘交互的过程是由JVM自动管理,我们都从缓冲区读写数据,一般在字符输入,输出流的基础上使用

BufferedInputStream类  和  BufferedOutputStream类 不常用

BufferedReader类:  带缓冲的字符输入流(将文件数据读取到内存)

构造方法:要依赖于另外一个流来创建 

BufferedReader( Reader in ) 在指定的字符输入流上创建一个缓冲流(默认8k)

BufferedReader( Reader in ,int size)  在指定的字符输入流上创建一个size大小的缓冲流

读方法:

String readLine():  读取一行数据(换行符或回车符表示一行读完)

过程:

BufferedWriter类:带缓冲的字符输出流(将内存数据写到文件中)

不常用,通常情况下我们会使用PrintWriter流替代这个类;

调用写方法时,并不直接向文件输出,而是写到流的缓冲区中;

当缓冲区满了时JVM才会将缓冲区中的数据一次性提交到文件中;

当我们调用close()方法时,JVM在关流之前会将缓冲区中的数据提交到文件;

不关流的情况下可以使用flush()方法,强制将缓冲区中的数据提交一次。

(3)转换流(也是一种过滤流,提升数据处理单位)

InputStreamReader类 作用:InputStream ---> Reader

OuputStreamReader类  不常用,被PrintWriter代替

如下:is是InputStream字节输入流,由InputStreamReader转换成了BufferedReader缓冲字符输入流

(4)特殊流

PrintWriter类,既可以当作节点流,也可以当作过滤流,还可以当作转化流来使用

举例:用PrintWriter去实现文本文件的复制

package stream;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class PrintWriterStream {
	public static void main(String[] args) {
		File file = new File("src/a.txt");
		try {
			/**
			 * 先用字符缓冲输入流BufferedReader将文件和内存建立通道,需要用该流读取文件数据
			 * BufferedReader对象的创建,要依赖另一个字符流创建
			 * 构造方法有2个:BufferedReader( Reader in ); BufferedReader( Reader in ,int size)
			 */
			BufferedReader reader = new BufferedReader(
					new InputStreamReader( new FileInputStream(file) )
			);
			
			//测试文件是否找到
			System.out.println(file.exists());
			
			//建立内存和b.txt文件的输出通道,需要用PrintWriter流写数据到b.txt中,若没有b.txt会自动新建一个
			PrintWriter writer = new PrintWriter("src/b.txt");
			String str = reader.readLine();
			while(str != null) {
                //注意writer.println(line);在输出到bbb之后,会多一个换行	
				writer.println(str);	//用PrintWriter流写数据到b.txt中
				str = reader.readLine();
			}
			
			writer.flush();//强制提交一次,确保提交
			
			reader.close();//关流
			writer.close();//关流
			
		} catch ( IOException e) {
			e.printStackTrace();
		}
	}
}

3、对象的序列化和反序列化

前提:

该对象的类型必须实现Serializable接口;否则报NotSerializableException异常

对象的序列化:(内存对象输出(写)到文件)

ObjectOutputStream类 writeObject方法 将内存中的对象输出

对象的反序列化:(读取文件数据,解析读取出来的对象)

ObjectInputStream类 readObject方法

transient关键字:

当前进行对象序列化,将不会对transient修饰的属性进行序列化

private static final long serialVersionUID = 1L;

反序列化是否成功主要依据serialVersionUID的值

举例:将自定义对象写到文件中,再从文件中读取出来,输出到控制台上

第一步:将对象从内存写到文件

public class Student implements Serializable{
	private static final long serialVersionUID = 1L;
	
	private String name;
	private int age;
	
	
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
//...省略get和set方法
}
package stream;

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

public class Run {
	public static void main(String[] args) {
		Student stu1 = new Student("Jack",18);
		Student stu2 = new Student("Rose",13);
	
		try {
			/*建立内存和文件的输出通道*/
			ObjectOutputStream oos = new ObjectOutputStream(
					//true表示追加,后一个写进文件的数据不会覆盖前一个
					new FileOutputStream("src/c.txt",true)
			);
			
			/*将两个Student对象写(输出)到文件c.txt中去*/
			oos.writeObject(stu1);
			oos.writeObject(stu2);
			
			/*关流*/
			oos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

第二步:从文件中读取数据到内存,并输出到控制台上;为了代码完整,在上面的代码上添加

package stream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Run {
	public static void main(String[] args) {
		Student stu1 = new Student("Jack",18);
		Student stu2 = new Student("Rose",13);
	
		try {
			/*建立内存和文件的输出通道*/
			ObjectOutputStream oos = new ObjectOutputStream(
					//true表示追加,后一个写进文件的数据不会覆盖前一个
					new FileOutputStream("src/c.txt",true)
			);
			
			/*将两个Student对象写(输出)到文件c.txt中去*/
			oos.writeObject(stu1);
			oos.writeObject(stu2);
			
			/*关流*/
			oos.close();
			
			ObjectInputStream ois = new ObjectInputStream(
					new FileInputStream("src/c.txt")
			);
			
			Student s1 = (Student)ois.readObject();
			System.out.println(s1);
			
			Student s2 = (Student)ois.readObject();
			System.out.println(s2);
			
			ois.close();
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

4、对象克隆

含义:

复制一个副本,并不是同一个对象

浅克隆:

能被浅克隆的前提是:

类型必须实现Clonable接口,没有任何抽象方法,称为标识接口

仅复制一层 Object中的clone方法是浅克隆

深克隆:

所有层次均复制一个副本

利用对象的序列化和反序列化来完成深克隆:

ByteArrayInputStream类

ByteArrayOutputStream类

来完成内存到内存,再到内存的一个深克隆的过程,把对象转化成Byte数组,再转化成对象

浅克隆举例:

package clone;


public class Resume implements Cloneable{
	
	private String name;
	private int age;
	private WorkExperience work;
	

	public Resume(String name, int age, WorkExperience work) {
		super();
		this.name = name;
		this.age = age;
		this.work = work;
	}
	

	public WorkExperience getWork() {
		return work;
	}

	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;
	}

	public void setWork(WorkExperience work) {
		this.work = work;
	}




	//Object中clone()方法是被声明为protected
	//protected被修饰的方法只能在本类,同一个包中的类,或它的子类中使用;
	public Resume Clone() {
		try {
			return (Resume) this.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public String toString() {
		return "Resume [name=" + name + ", age=" + age + ", work=" + work + "]";
		
	}
	
	
}
package clone;

public class WorkExperience {
	private String timeArea;
	private String company;
	
	public WorkExperience(String timeArea, String company) {
		super();
		this.timeArea = timeArea;
		this.company = company;
	}

	@Override
	public String toString() {
		return "WorkExperience [timeArea=" + timeArea + ", company=" + company + "]";
	}

	public String getTimeArea() {
		return timeArea;
	}

	public void setTimeArea(String timeArea) {
		this.timeArea = timeArea;
	}

	public String getCompany() {
		return company;
	}

	public void setCompany(String company) {
		this.company = company;
	}
	
	
}
package clone;

public class Run {
	public static void main(String[] args) {
		WorkExperience work1 = new WorkExperience("1999-2008", "中国航空公司");
		Resume r1 = new Resume("小明", 12, work1) ;
		System.out.println(r1);
		Resume r2 =(Resume) r1.Clone();
		System.out.println(r2);
		
		System.out.println(r1 == r2);//显示false,表示克隆出来的对象和原来的对象不是一个
		System.out.println(r1.getWork() == r2.getWork());//显示true,表示它们内部的WorkExperience对象其实是一个
		
		//上面的测试表明,浅克隆其实只复制了一层对象,对象中的对象并没有克隆,String对象是特殊对象,也被复制了一份
		//再测试:如修改work1的内容两个Resume的WorkExperience都会改变;但r2改变name不会影响r1
		work1.setCompany("美国白宫");
		r2.setName("小红");
		System.out.println(r1);
		System.out.println(r2);
		
	}
}

举例:深克隆:利用对象序列化实现深克隆;A对象被序列化,而B是A下的属性也会被序列化,C在B类下,也会被序列化;序列化完成再反序列化成对象;赋值给新的A对象,就实现了深克隆。

5、图片操作

1、图片类: java.awt.Image类

用来表示图片数据 是一个抽象类,是所有图片类的父类

BufferedImage类 是Image类的实现类,封装了图片数据

2、图片读写: 主要用的是BufferedImage类

当程序需要使用一张图片时(或者需要使用到BufferedImage类对象时),我们可以利用ImageIO来读写图片。

ImageIO的用法(了解):

BufferedImage read( File file ): 加载指定的图片文件到内存(通常用来加载本地文件)

BufferedImage read( URL url ) 加载指定url位置上的图片文件到内存(通常用来加载网络图片)

BufferedImage read( InputStream input ) 将图片数据在网络上传输

write( File file )

write( OutputStream output )

猜你喜欢

转载自blog.csdn.net/Carl_changxin/article/details/82886095