一、什么是流?
所谓的流,是指同一台计算机或者网络中不同计算机之间的有序运动着的数据序列,简单的来说,流是内存与存储设备之间传输数据的通道。Java把这些不同来源和目标的数据都统一抽象为数据流。
二、流的分类
1、根据流的方向
流的方向可以将流分为输入流和输出流。这种划分是根据程序或者内存来说的。
输入流:如果程序是数据的使用者,需要从外界读入数据,这种称为输入流。
输出流: 如果程序是数据的提供者,需要从外界提供数据,这种称为输出流。
2、根据流的结构
①流的结构可以将流分为字节流和字符流。字节流的处理单位是字节,而字符流的处理单位是字符。
②这里强调的是:字节是8位的,而字符流处理的字符是16位的。
③虽然使用字节流或者字符流都可以达到同样的效果,但是相对来说,字节流处理的Unicode码相对麻烦一点。
④不管字节流还是字符流,都提供输入输出的功能。Java的数据输入/输出功能通过java.io包中的类和接口来实现。InputStream、OutputStream、Reader、Writer是几个常用流的基类,它们都直接继承了Object。其中,InputStream、OutputStream是基于字节的输入类和输出类的基类,两个具有共同特征。Reader、Writer是基于字符的输入类、输出类的父类,它定义了所有基于字符的输入流、输出流具有共同特征。
3、根据流的建立方式和工作原理
将流分为节点流和过滤流。
三、IO流的四大家族首领
1、字节流
- java.io.InputStream 字节输入流
- java.io.OutputStream 字节输出流
2、字符流
- java.io.Reader 字符输入流
- java.io.Writer 字符输出流
注:
1、四大家族都是抽象类abstract class
2、所有流都实现了Closeable接口,都是可关闭的,都有close方法。流是一个管道,是内存与硬盘之间的通道。用完之后一定要关闭,不然会占用很多资源。
3、java.io.Flushable接口,都是可刷新的,都有 flush()
方法。养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道,否则可能导致数据丢失。
4、在java中只要类名以 Stream
结尾的都是字节流,“ Reader/Writer
”结尾的都是字符流。
四、节点流
1、FileInputStream类
public class TestInputStream {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("F:\\target.txt");
// //一次读一个字节
// int n = fis.read();
// System.out.println(n + "\t" + (char)n);
//一次读一组字节
byte[] fiss = new byte[4];
fis.read(fiss);
for(int i=0;i<fiss.length;i++) {
System.out.print((char)fiss[i] + "\t");
}
System.out.println();
}
}
2、FileOutputStream类
public class TestOutputStream {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("F:\\target.txt");
//一次输出一个字节
fos.write(65);
fos.write(66);
fos.write(67);
fos.write(68);
fos.write(65);
System.out.println("运行结束!");
//一次输出多个字节
byte[] bs = new byte[]{65,66,67,68,69};
fos.write(bs);
}
}
3、FileReader类、FileWriter类
public class TestFileReader {
public static void main(String[] args) throws IOException {
Writer w = new FileWriter("D:\\target.txt");
w.write("Hello\n");
w.write("World");
w.flush();
w.close();
Reader r = new FileReader("D:\\target.txt");
//BufferedReader:从字符输入流中读取文本,缓冲字符以便有效地读取字符、数组和行。
BufferedReader br = new BufferedReader(r);
for(;;) {
String i = br.readLine();
if(i == null) break;
System.out.println(i);
}
br.close();
}
}
五、过滤流
前面介绍的节点流实现了基本的文件操作,就可以实现文件的读写操作。系统为了提高文件的读写能力,引入了过滤流。
过滤流也成缓冲流。数据存储在缓冲区中,可以利用flush方法刷新缓冲,将数据写入文件中;也可以直接使用close方法,直接关闭缓冲。
过滤流的优点:提高文件的读写效率,避免对文件的多次访问操作。
1、BufferedInputStream类、BufferedOutputStream类
2、BufferedWriter类、BufferedReader类
BufferedWriter类:
将文本写入字符输出流,缓冲字符,以便高效地写入单个字符、数组和字符串。
BufferedReader类:
从字符输入流中读取文本,缓冲字符以便有效地读取字符、数组和行。
3、ObjectInputStream类、ObjectOutputStream类(对象流)
1.增强了缓冲区功能
2.增强了读写8种数据类型和字符串的功能
3.增强了读写对象的功能(readObject、writeObject)
基础例子:
public class TestObjectStream {
public static void main(String[] args) throws IOException {
OutputStream os = new FileOutputStream("F:\\target.txt");
//os.write(3.5);// error,OutputStream类型中的方法write(int)不适用于参数(double)
//思考:如何输出Double类型的数据??
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeDouble(3.6);
oos.writeDouble(4.2);
oos.writeDouble(9.8);
oos.writeDouble(5.5);
oos.flush();//刷新缓冲区
InputStream is = new FileInputStream("F:\\target.txt");
ObjectInputStream ois = new ObjectInputStream(is);
while(true) {
try {
double d = ois.readDouble();
System.out.println(d);
}catch(Exception e) {//当循环遍历到最后一个会捕获异常,捕获到了就退出循环
break;
}
}
ois.close();
}
}
升级例子:
public class TesttallObjectStream {
public static void main(String[] args) throws Exception {
OutputStream os = new FileOutputStream("F:\\target.txt");
//思考:如何输出一个对象?
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new Student("陈陈",18,"男",90.0));
oos.writeObject(new Student("大明",20,"男",60.0));
oos.writeObject(new Student("小红花",16,"女",50.0));
oos.flush();
InputStream is = new FileInputStream("F:\\target.txt");
//is.read();
ObjectInputStream ois = new ObjectInputStream(is);
//Student stu = ois.readObject();//error,类型不匹配:无法从Object转换为Student
// Object obj = ois.readObject();
// Student stu = (Student)obj; //做类型强转
// System.out.println(stu.name + "\t" + stu.age + "\t" + stu.sex + "\t" + stu.score);
while(true){//当遍历到没有数据时,会报EOFException异常,所以做一个判断
try {
Object obj = ois.readObject();
Student stu = (Student)obj; //做类型强转
System.out.println(stu.name + "\t" + stu.age + "\t" + stu.sex + "\t" + stu.score);
}catch(Exception e){
break;
}
}
}
}
class Student implements Serializable{
String name;
int age;
String sex;
double score;
public Student(String name, int age, String sex, double score) {
super();
this.name = name;
this.age = age;
this.sex = sex;
this.score = score;
}
}
4、DataOutputStream类、DataInputStream类(对象流)
DataOutputStream类:
数据输出流允许应用程序以可移植的方式将原始 Java 数据类型写入输出流。然后应用程序可以使用数据输入流来读回数据。
多个并发线程使用 DataOutputStream 是不安全的。如果一个 DataOutputStream 被多个线程使用,那么对数据输出流的访问应该通过适当的同步来控制。
DataInputStream类:
数据输入流允许应用程序以独立于机器的方式从底层输入流中读取原始 Java 数据类型。应用程序使用数据输出流来写入稍后可以由数据输入流读取的数据。
多个并发线程使用 DataInputStream 是不安全的。如果一个 DataInputStream 被多个线程使用,那么对数据输入流的访问应该通过适当的同步来控制。
【补充1】:学习对象流前言
1、java.io.NotSerializableException: Student对象不支持序列化!!!!
2、参与序列化和反序列化的对象,必须实现 Serializable
接口。
3、注意:通过源代码发现,Serializable接口只是一个 标志接口
:
public interface Serializable {
}
这个接口当中什么代码都没有!!!
【补充2】:Windows/Linux小知识点(路径的写法)
例如:
Windows:D:\Soft\QQ\Plugin
Linux: D:/Soft/QQ/Plugin
注意: Windows各个文件之间分隔符为:” \ “;Linux各个文件之间分割符为:” / “。
六、PrintStream
PrintStream
向另一个输出流添加功能,即能够方便地打印各种数据值的表示形式。还提供了另外两个功能。与其他输出流不同,PrintStream
永远不会抛出 IOException
;相反,异常情况只是设置一个内部标志,可以通过 checkError
方法进行测试。可选地,可以创建一个 PrintStream
以便自动刷新;这意味着在写入字节数组、调用 println
方法之一或写入换行符或字节 ('\n'
) 后,将自动调用底层输出流的 flush
方法。
七、File类
概念:代表物理盘符中的一个文件或者一个文件夹的对象。
常用方法:
例如:
public class TestFile {
public static void main(String[] args) throws IOException {
File file = new File("File\\target1.txt");
System.out.println(file.canExecute());//判断是否可执行
System.out.println(file.canRead());//判断是否是只读模式
System.out.println(file.canWrite());//判断是否是可修改的文件
System.out.println(file.createNewFile());//当路径的文件不存在,为此新建文件,否则返回false
System.out.println(File.createTempFile("aasadgf", ".java"));//在临时文件创建文件
//System.out.println(file.delete());//删除该路径的文件或者文件夹
System.out.println(file.equals(file));
System.out.println(file.getAbsoluteFile());//返回此路径名的绝对路径
System.out.println(file.getAbsolutePath());//返回此路径名的绝对路径字符串
System.out.println(file.getName());//返回当前文件或目录
System.out.println(file.getParent());//返回当前文件的父目录名字符串
System.out.println(file.getParentFile());//返回当前文件的父目录抽象路径名
}
}
祝君成愿!!!