IO流
分类
- 根据处理数据类型的不同分为:字符流和字节流
- 根据数据流向不同分为:输入流和输出流
字节流和字符流
- 字节流:数据流中最小的数据单元是字节;能读写所有类型文件
- 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。只能读纯文本数据;
字符流的由来: Java中字符是采用Unicode标准,一个字符是16位,即一个字符使用两个字节来表示。为此,JAVA中引入了处理字符的流。因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。
输入流和输出流
- 输入流:程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道
- 输出流:程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道
非流式
File类
- 一个File类对象可以代表一个文件或目录
- File对象可以对文件或目录的属性进行操作,如:文件名、最后修改日期、文件大小等
- File对象无法操作文件的具体数据,即不能直接对文件进行读/写操作
//构造方法
File(String pathname)
通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(File parent, String child)
从父抽象路径名和子路径名字符串创建新的 File实例。
File file1 = new File("S:/demo1.txt"); //前提文件目录存在
File parent = new File("S:/");
File file2 = new File(parent,"demo2.txt");
//常用
boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
boolean canExecute() 是否可执行
boolean canRead() 是否可读
boolean canWrite() 是否可写
int compareTo(File pathname) 比较两个抽象的路径名字典。
long length() 返回由此抽象路径名表示的文件的长度。
boolean delete() 删除由此抽象路径名表示的文件或目录。
File getAbsoluteFile() 返回此抽象路径名的绝对形式。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
String toString() 返回此抽象路径名的路径名字符串。
String getName() 返回由此抽象路径名表示的文件或目录的名称。
String getParent() 返回此抽象路径名的父 null的路径名字符串,如果此路径名未命名为父目录,则返回null。 boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。
//创建文件
boolean createNewFile()
当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
//创建文件夹
boolean mkdir() 创建单级文件夹(目录)
boolean mkdirs() 多级文件夹
-
创建文件
File file3 = new File("S:/demo3.docx"); if (!file3.exists()){ //判断是否不存在 try { file3.createNewFile();//io异常,防止文件路径找不到 } catch (IOException e) { e.printStackTrace(); } }
-
//遍历子文件 File file1 = new File("E:/api"); String [] fileNames = file1.list();//获得当前目录下的所有文件名和目录名(以字符串形式返回) for(String s : fileNames){ System.out.println(s); } File[] files = file1.listFiles();//获得当前目录下的所有文件名和目录名(返回的是文件对象) for(File f : files){ System.out.println(f);//重写了toString(),也输出字符串 } //条件遍历 File[] files = file1.listFiles(new FileFilter() {//把满足条件的放到file[]里 @Override public boolean accept(File pathname) { return pathname.getName().endsWith("chm");//自定义的文件过滤条件 } }); for(File f : files){ System.out.println(f); }
流式
字节流
节点流 : 封装的是某种特定的数据源,如文件、字符串、字符串数组等
//把一个文件的内容读取并写入其他文件
public static void main(String[] args) {
//建立输入输出流管道
FileInputStream in = null;//告诉java输入流,去读取计算机上的哪个文件
FileOutputStream out = null;//告诉java输出流,输出到哪个文件
try {
in = new FileInputStream("S:/demo1.txt");
out = new FileOutputStream("E:/demo2.txt");
//循环读写
//in.read(); 每次读取一个字节并返回,当文件内容读完之后,会返回一个-1
//out.write(); 一次向外输出(写)一个字节
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件未找到");
} catch (IOException e) {
e.printStackTrace();
System.out.println("读写错误");
} finally {
//关闭流通过,解除文件占用
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
缓冲数组
每次只能读或写一个字节 , 效率低 , 所以一次读多个字节存到byte[]中,放满之后写byte[]
byte[] b = new byte[1024];//数组长1024,每次读1024个字节
int length = 0;//用length来获取数组的实际字节长度,防止多输出null
while ((length = in.read(b)) != -1) {
//read(byte[] b)每次最多读byte数组长度个字节,返回数组中实际装入的字节个数,读完返回-1
out.write(b, 0, length);
//每次向外写一个byte个字节,从指定位置开始(一般情况从0开始),向外写length个长度
}
高效缓冲流
处理流 : 封装的是其它流对象
对节点流进行包装
- BufferedInputStream 有一个缓存数组(默认8k,可自定义),用来存放read()进去的byte[]或字节,存满之后write()
- 自己定义的byte[]长度需要小于BufferedOutputStream内的缓存数组长度才能提高效率
public static void main(String[] args) {
//用带缓冲区的高级管道包装一下
try{
FileInputStream in = new FileInputStream("S:/demo1.txt");
BufferedInputStream bin = new BufferedInputStream(in);
FileOutputStream out = new FileOutputStream("E:/demo2.txt");
BufferedOutputStream bout = new BufferedOutputStream(out);
byte[] b = new byte[1024];
int length;
while ((length=bin.read())!=-1){
bout.write(b,0,length);
}
bin.close();
bout.flush();//刷新缓冲区
bout.close();
} catch (IOException e){
e.printStackTrace();
}
}
字符流
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("S:/demo.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
FileWriter fileWriter = new FileWriter("E:/demo.txt",true);//ture:追加
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
String line = null; //一次读入一行数据
while((line=bufferedReader.readLine())!=null){
bufferedWriter.write(line);//一次写出一行数据
bufferedWriter.newLine();//换行
}
bufferedReader.close();
bufferedWriter.close();
}
Print流
打印:只做输出,没有输入
从程序中向外输出(访问网页–>服务器就会将网页内容以流的形式响应到浏览器)
public static void main(String[] args) throws FileNotFoundException {
PrintWriter out =new PrintWriter("S:/printWriterDemo.html");
out.println("<h1>我是服务器传来的信息<h1>");
out.println("<h1>我是服务器传来的信息<h1>");
out.close();
}
对象输入输出流
处理流
- 对象的输入输出流 : 主要的作用是用于写入对象信息与读取对象信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化了
对象的输出流: ObjectOutputStream
对象的输入流: ObjectInputStream
- 要将序列化之后的对象保存下来,需要通过对象输出流(ObjectOutputStream)将对象状态保存,之后再通过对象输入流(ObjectInputStream)将对象状态恢复。
在ObjectInputStream 中用readObject()方法可以直接读取一个对象,
ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中。
常用类
public static void main(String[] args) throws IOException, ClassNotFoundException {
//将程序运行的时间存储到文件中(直接将对象的信息存起来,对象序列化)
Date date = new Date(); //Date类型
FileOutputStream out = new FileOutputStream("S:/ObjectInputStream.txt");
ObjectOutputStream objOut = new ObjectOutputStream(out);//处理流包装
objOut.writeObject(date);//存储
objOut.close();
//将信息从文件中输入到程序中,这个过程称为反序列化
FileInputStream in = new FileInputStream("S:/ObjectInputStream.txt");
ObjectInputStream objIn = new ObjectInputStream(in);
Object obj = objIn.readObject();
if (obj instanceof Date){//判断object中包含的是否是Date类型
Date date1 =(Date)obj;
System.out.println(date1);
}
}
自定义类
-
如果将一个类的对象信息序列化到文件中,此类需要生成一个序列化id号(版本号)
-
implements Serializable接口会自动生成一个版本号,一旦类中的内容改变,版本号就会自动变化
也可以显示版本号,类修改是版本号不会改变
-
生成版本号 : setting–>serialization issues–>serializable class without 'serialVersionUID’打钩
/*
如果将一个类的对象信息序列化到文件中,此类需要生成一个序列化id号(版本号)
implements Serializable接口会自动生成一个版本号,一旦类中的内容改变,版本号就会自动变化
*/
public class Student implements Serializable {
//生成版本号//setting-->serialization issues-->serializable class without 'serialVersionUID'打钩
private static final long serialVersionUID = -6839779773264328401L;//显示版本号,类修改是版本号不会改变
private String name;
private transient int age;//在对象序列化时,忽略此属性
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//将学生信息在文件中存取
public class ObjectStreamDemo{
public static void main(String[] args) throws IOException, ClassNotFoundException{
Student student1 = new Student("张三1",18);
Student student2 = new Student("张三2",19);
FileOutputStream out = new FileOutputStream("S:/StudentMassage.txt");
ObjectOutputStream objOut = new ObjectOutputStream(out);
objOut.writeObject(student1);
objOut.writeObject(student2);
objOut.close();
FileInputStream in = new FileInputStream("S:/StudentMassage.txt");
ObjectInputStream objIn =new ObjectInputStream(in);
Student student3 =(Student) objIn.readObject();
Student student4 =(Student) objIn.readObject();
System.out.println(student3);
System.out.println(student4);
}
}
-------------------------------------
Student{name='张三1', age=0}
Student{name='张三2', age=0}
BIO、NIO、AIO区别
BIO是同步阻塞,NIO是同步非阻塞,AIO是异步非阻塞。
阻塞指的是线程发起读写请求之后,是不是阻塞住的,是不是可以干别的事。
同步就是还得主动去轮询操作系统,异步就是操作系统反过来通知