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());
}
}
}
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);//反复调用
}
}
}
}
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();
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();
}
}