IO流小结

IO流小总结

1.IO流总述

IO流以内存为核心,进内存为Input,出内存为Output。

1.1划分

按照大小划分:字符流,字节流。
按照出入内存划分:InputStream,OutputStream

1.2明确IO流的功能

1.明确要操作的数据是数据源还是数据目的(要读还是要写)
数据源:InputStream,Reader
数据目的:OutputStream,Writer
2.明确要用什么类
字节流:InputStream,OutputStream
字符流(中文,视频等):Reader,Writer
3.明确是否需要额外功能
需要转换:转换流InputStreamReader,OutputStreamWriter
需要高效:缓冲流Bufferedxxx
对象序列化—— ObjectInputStream、ObjectOutputStream

2.File类概述

IO流,也就是输入输出流,从文件出发到文件结束,至始至终都离不开文件,所以IO流还得从文件File类讲起。

2.1File类的一些基本操作

public class FileDemo {
	public static void main(String[] args) throws Exception {
		File file = new File("D:\\ab.txt");//在d盘创键一个文件夹
		file.createNewFile();//创建文件
		
		System.out.println("是否是文件夹:"+file.isDirectory());
		System.out.println("是否是文件:"+file.isFile());
		
		System.out.println(file.isHidden());//判断是否隐藏
		System.out.println(new Date(file.lastModified()));//创建日期
		
		System.out.println(file.getTotalSpace()/1024/1024/1024+"G");//该盘符的总内存
		System.out.println(file.getFreeSpace()/1024/1024/1024+"G");//该盘符的剩余内存
		
		File f =new File("E:\\");//定位到e盘
		
		File[]fs= f.listFiles();//取出对象
		
		for (File file2 : fs) {
			System.out.println(file2.getName());
		}
	}

}

IO流小结

2.2 文件的递归调用显示内容

public class FileDemo02 {
	
	static int count = 0;
	public static void main(String[] args) {
		File file = new File("C:\\");//定位到c盘
		find(file);
		System.out.println("图片的数量是:"+count+"张");
		
	}
	
	public static void find(File file) {
		if(file!=null&&file.isFile()) {//是文件
			String fileName = file.getName();
			if(fileName.endsWith(".jpg")||fileName.endsWith(".png")) {
				count++;
				System.out.println(fileName);
			}
			
//			System.out.println(file.getName());
		}
		
		//文件夹
		File fs[] = file.listFiles();
		
		if(fs!=null) {
			for (File file2 : fs) {
				find(file2);//反复调用
			}
		}
	}
}

IO流小结

3.输入流和输出流

3.1输入流

读取文件夹中的文件

public class InputStreamDemo {
	public static void main(String[] args) {
		
		/*
		 * 寻找路径
		 */
		String path =Thread.currentThread().getContextClassLoader().getResource("").getPath();//获得根的路径
		System.out.println(path);//我们要找的是src
		path=path.substring(0,path.length()-4);//去掉bin
		System.out.println(path);
		String fileName = path+"src/io/file/FileDemo02.java";//找到了我们要找的文件路径
		System.out.println(fileName);
		
		/*
		 * 包装
		 */
		try(InputStream in =new FileInputStream(fileName);//字节输入流
			InputStreamReader r =new InputStreamReader(in);	//转化流,因为有中文,我们要转换为字符流
				) {
			
			//输出内容
			int n =0;
			while((n=r.read())!=-1) {
				System.out.print((char) n);
			}
			
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

}

我们在使用输入流时,一般使用InputStream in =new FileInputStream();

IO流小结

3.2输出流(一般要配合缓存)

输出流和输入流略微有所不同。当数据进入内存时,读取速度往往很快。然后当数据要出内存时,内存要到达硬盘等设备时,硬盘读取速度较慢,我们往往需要缓存。

首先我们来感受一下输出流到底有什么用。

public class WriterDemo {
	public static void main(String[] args) throws Exception {
		Writer out =new FileWriter("E:\\outw.txt",false);
		out.write("abcdefghijklmn");
		out.flush();
		out.close();
	}

}
public class OutputStreamDemo {
	public static void main(String[] args) throws Exception {
		OutputStream out = new FileOutputStream("E:\\out.txt",true);//false为删除,true为尾部追加
		out.write('b');
		out.flush();
		out.close();
	}

}

这两个程序的作用时在我们创建的一个文本中添加(write)内容,特别要注意true和false,如果不写默认为false。

下面这个示例是关于缓存的使用

byte[] buffer = new byte[1024];//缓存

完整代码

public class CopyDemo {
	public static void main(String[] args) throws Exception {
		InputStream in = new FileInputStream("D:\\java2\\20220129\\video\\2022-01-29.mp4");
		OutputStream out = new FileOutputStream("E:\\copyMovie.mp4");
		
		long s1 =System.currentTimeMillis();
		
		byte[] buffer = new byte[1024];//缓存
		
		while(in.read(buffer)!=-1) {
			out.write(buffer);//边读边写
		}
		long s2 =System.currentTimeMillis();
		System.out.println("拷贝耗时:"+(s2-s1)+"ms");
		out.close();
		in.close();
	}

}

4.序列化和反序列化(ObjectOutputStream和ObjectInputStream)

序列化:一个java对象做一下“交换”,变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,另外变换成字节序列也更便于网络运输和传输。
反序列化:把字节序列恢复为原先的java对象。

序列化和反序列化的条件:该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException 。该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。(如hashmap中的某些属性就不可序列化)。

下面我们直接看代码

这是我们创建的Car类,实现Serializable接口

public class Car implements Serializable{
	
	private static final long serialVersionUID = 1L;//uid即为版本号,每次序列化和反序列化的版本号必须一致
	private int id;
	private String brand;
	private String color;
	private double price;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	public Car(int id, String brand, String color, double price) {
		super();
		this.id = id;
		this.brand = brand;
		this.color = color;
		this.price = price;
	}
	
	public Car() {

	}
	@Override
	public String toString() {
		return "Car [id=" + id + ", brand=" + brand + ", color=" + color + ", price=" + price + "]";
	}
	

}

序列化,将内容暂时存放在car.dat中

public class ObjectOutputStreamDemo {
	public static void main(String[] args) throws Exception {
		ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("E:\\car.dat"));//创建一个输出流
		
		Car c =new Car(1, "bmw", "红色", 120000.00);
		out.writeObject(c);//序列化
		out.close();
	}

}

反序列化

ublic class ObjectInputStreamDemo {
	public static void main(String[] args) throws Exception {
		ObjectInputStream in = new ObjectInputStream(new FileInputStream("E:\\car.dat"));
		
		Car car = (Car) in.readObject();//反序列化
		System.out.println(car);
	}

}

5.nio

bio:我们通常使用的是这种bio(同步阻塞):线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。
nio(同步不阻塞):线程发起IO请求,立即返回;内核在做好IO操作的准备之后,通过调用注册的回调函数通知线程做IO操作,线程开始阻塞,直到操作完成。(将单项变成多项)。

我们完成nio需要三个组件

  • buffer:缓存区实际上是一块可以写入数据,然后从中读取数据的内存。这块内存被包装为NIO Buffer对象,并提供一组方法,用来方便访问该内存。
  • Channe:通道表示打开到IO设备(例如:文件等)的连接。若需要使用NIO,需要获取连接IO设备的通道以及用于容纳数据的缓存区。然后操作缓存区,对数据进行处理。相较于Stream更加高效,可以异步双向处理,但是必须和buffer一起使用。
    -Select:Select允许单线程处理多个通道(Channe)。
public class nioDemo {
	public static void main(String[] args) throws Exception {
		//获得地址
		String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
		System.out.println(path);
		path =path.substring(0, path.length()-4);
		System.out.println(path);
		String fileName = path+"src/io/file/FileDemo02.java";
		
		RandomAccessFile raf = new RandomAccessFile(fileName, "rw");//rw表示可读可写
		
		FileChannel inChannel =raf.getChannel();//获得写通道
		
		ByteBuffer buffer = ByteBuffer.allocate(64);//缓存
		
		int n =0;
		
		while((n=inChannel.read(buffer))!=-1) {
			buffer.flip();//移动到缓存的第一个位置
			while(buffer.hasRemaining()) {//判断是否有数据
				System.out.print((char)buffer.get());
			}
			buffer.clear();
		}
		raf.close();
	}

}

上一篇:Java基础系列(15)- IO流


下一篇:VUE学习(九)、内置指令、自定义指令