目录
IO:
流的分类:
基本IO操作:
IS 与 OS:
常用方法:
文件流:
缓冲流:
对象流:
文本数据IO操作:
Reader和Writer:
常用方法:
转换流:
缓冲字符流:
输入流 | 输入流 | |||
低级流 | 低级流 | 低级流 | 高级流 | |
字节流 | 文件流 FilelnputStream |
缓冲字节入流:BufferedInputStream
|
文件流FileOutputStream | 缓冲字节输出流:BufferedOutputStreant |
对象输入流:ObjectInputStream | 对象输出流:ObjectOutputStream | |||
字符流 | 转换输入流:InputStreamReader | 转换输出流:OutputStreamWriter | ||
缓冲字符输入流:BufferedReader | 缓冲字符输出流:PrintWriter |
节点流与处理流:
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
节点流:可以从或向一个特定的地方(节点)读写数据。 数据源明确,是真实连接程序与另一端的"管道",负责实际读写数据的流
读写一定是建立在节点流的基础之上进行的.它在行业里也被称为低级流。
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。处理流的构造方法总是要带个其他的流对 象做参数。不能独立存在,必须连接在其它流之上,目的是当数据流经当前流时,可以对数据进行加工处理.简化我们的操作,行业里也称为高级流.。
一个流对象经过其他流的多次包装, 串联一组高级流并最终连接到低级流上,使得读写数据以流水线式的方式进行,
这种操作称为流的链接。这也是学习IO的精髓所在。
流的读写形式是顺序读写模式..--即:读写数据只能顺序向后进行,是不能回退的.
字节流:读写单位为byte 1字节低八位
输入与输出:
我们编写的程序除了自身会定义一-些数据信息外, 经常还会引|用外界的数据,或是将自身的数据发送到外界。
比如,我们编写的程序想读取一个文本文件,又或者我们想将程序中的某些数据写入到一个文件中。这时我们
就要使用输入与输出。
什么是输入: java.io.InputStream
输入是一个从外界进入到程序的方向,通常我们需要"读取"外界的数据时使用输入。所以输入是用来读取数据的。
InputStream是所有字节输入流的父类,其定义了基础的读取方法.
什么是输出: java.io.OutputStream
输出是一个从程序发送到外界的方向,通常我们需要写出”数据到外界时,使用输出。所以输出是用来写出数据的。
OutputStream是所有字节输出流的父类,其定义了基础的写出方法,常用的方法如下:
方法 | 功能 |
int read( ) | 读取一个字节,以int形式返回,该int值的"低八位"有效,若返回值为1则表示EOF(End Of File) |
int read(byte[ ] d) | 尝试最多读取给定数组的length个字节并存入该数组,返回值为实际读取到的字节量。 |
void write(int d) | 写出一个字节写的是给定的int的"低八位" |
void write(byte[ ] d) | 将给定的字节数组中的所有字节全部写出 |
java.io.FileInputStream:
java.io.FileOutputStream:文件的字节输出流,以字节为单位将数据写入文件。
文件流是一对低级流,用于读写文件的流。功能上与RandomAccessFile一致,但是文件流是基于JAVA标准的IO,读写是分开的,并且流的读写是顺序读写,只能顺序向后进行读或写操作,是不能回退的。但是RAF是基于指针的随机读写形式,可以调整指针位置对文件任意位置读写。
文件流的两种创建方式:
模式 | 构造方法 | |
覆盖写模式 | FileOutputStream(String path) | 创建文件流时,如果指定的文件已经存在,则会将该文件原数据全部清除,然后再开始使用当前流写入新数据。 |
FileOutputStream(File file) | ||
追加写模式 | FileOutputStream(String path,boolean append) | 当第二个参数为true,创建文件流时,如果指定的文件已经存在,则通过当前流写入的内容都会被顺序追加到文件末尾。 |
FileOutputStream(File file,boolean append) |
文件输出流写出数据:
public class FOSDemo {
public static void main(String[] args) throws IOException {
// File file = new File("./fos.txt");
// FileOutputStream fos = new FileOutputStream(file);
/*
* 文件流的两种创建方式:
* 1:覆盖写模式,对应的构造方法:
* FileOutputStream(String path)
* FileOutputStream(File file)
* 覆盖模式:创建文件流时,如果指定的文件已经存在,则会将该文件原数据
* 全部清除,然后再开始使用当前流写入新数据。
*
* 2:追加写模式,对应的构造方法:
* FileOutputStream(String path,boolean append)
* FileOutputStream(File file,boolean append)
* 追加模式:当第二个参数为true,创建文件流时,如果指定的文件已经存在,
* 则通过当前流写入的内容都会被顺序追加到文件末尾。
*/
// FileOutputStream fos = new FileOutputStream("./fos.txt");
FileOutputStream fos = new FileOutputStream("./fos.txt",true);
// String line = "摩擦摩擦~在光滑的马路上摩擦~";
// byte[] data = line.getBytes("UTF-8");
// fos.write(data);
//
// line = "我的滑板鞋,时尚时尚最时尚~";
// data = line.getBytes("UTF-8");
// fos.write(data);
String line = "嘿嘿";
byte[] data = line.getBytes("UTF-8");
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}
文件输入流,用于从文件中读取字节:
public class FISDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("./fos.txt");
byte[] data = new byte[200];
int len = fis.read(data);
System.out.println("实际读取到了"+len+"个字节");
String str = new String(data,0,len,"UTF-8");
System.out.println(str);
fis.close();
}
}
文件流完成文件的复制操作:
public class CopyDemo {
public static void main(String[] args) throws IOException {
/*
* 1:创建一个文件输入流读取原文件
* 2:创建一个文件输出流写复制文件
* 3:循环通过文件输入流都组一组字节(10k)并通过文件输出流写入
* 复制文件,最终完成复制工作
* 4:将两个流关闭
*/
FileInputStream fis = new FileInputStream("raf.dat");
FileOutputStream fos = new FileOutputStream("raf_cp.dat");
byte[] data = new byte[1024*10];
int len = 0;
while((len = fis.read(data))!=-1) {
fos.write(data,0,len);
}
System.out.println("复制完毕!");
fis.close();
fos.close();
}
}
java.io.BufferedInputStream:
java.io.BufferedOutputStream:
缓冲流是一对高级流,在流的连接中作用为加快读写效率。无论我们使用块读写,还是单字节读写,缓冲流最终都会统一转换为块读写以保证读写效率。
BufferedOutputStream缓冲输出流内部维护着一个缓冲区,每当我们向该流写数据时, 都会先将数据存入缓冲区,当缓冲区已满时,缓冲流会将数据一次性全部写出。使用缓冲输出流可以提高写出效率,但是这也存在着一
个问题,就是写出数据缺乏即时性。有时我们需要在执行完某些写出操作后,就希望将这些数据确实写出,而非在缓冲区中保存直到缓冲区满后才写出。这时我们可以使用缓冲流的一一个方法flush。
void flush( )
清空缓冲区,将缓冲区中的数据强制写出。
BufferedInputStream是缓冲字节输入流。其内部维护着一个缓冲区(字节数组) , 使用该流在读取一个字节时该流会尽可能多的一次性读取若干字节并存入缓冲区,然后逐的将字节返回,直到缓冲区中的数据被全部读取完毕,会再次读取若干字节从而反复。这样就减少了读取的次数,从而提高了读取效率。是一个处理流,该流为我们提供了缓冲功能。
缓冲流复制文件:
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("唱歌的孩子.mp3");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("唱歌的孩子_cp.mp3");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d = 0;
long start = System.currentTimeMillis();
while((d = bis.read())!=-1) {
bos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:"+(end-start)+"ms");
bis.close();
bos.close();
}
}
缓冲字节输出流写数据的缓冲区问题:
public class BOS_flushDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String str = "你在南方的艳阳里,大雪纷飞。";
byte[] data = str.getBytes("UTF-8");
bos.write(data);
/*
* void flush()
* 强制将缓冲流的缓冲区中已经缓存的数据写出
*/
// bos.flush();
System.out.println("写出完毕!");
bos.close();
}
}
java.io.ObjectInputStream:
java.io.ObjectOutputStream:
对象流是一对高级流,作用是方便读写任何java对象。
对象序列化:在流连接中,对象流的工作是:对象序列化,就是将给定的对象按照其结构转换为一组字节的过程。
数据持久化:这组字节再经过文件流写入了文件,这个过程称为数据持久化。因为写入文件就是写入了磁盘,断电也可以保存。
对象的反序列化:一个字节序列需要将其转换为对应的对象,这个过程就称为对象的反序列化。
ObjectOutputStream是用来对对象进行序列化的输出流。
其实现对象序列化的方法为:
- void writeObject(Object o)
该方法可以将给定的对象转换为一一个字节序列后写出。
public class OOSDemo {
public static void main(String[] args) throws IOException {
/*
* 将一个Person实例写入文件person.obj
*/
String name = "苍老师";
int age = 18;
String gender = "女";
String[] otherInfo = {"是个演员","来自日本","爱好是写毛笔字","促进中日文化交流","广大男性同胞的启蒙老师"};
Person p = new Person(name, age, gender, otherInfo);
System.out.println(p);
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
/*
* writeObject方法要求写出的对象所属的类必须实现接口:Serializable
* 否则会抛出异常
*
* 在流连接中,对象流的工作是:对象序列化,就是将给定的对象按照其结构
* 转换为一组字节的过程。
*
* 然后这组字节再经过文件流写入了文件,这个过程称为数据持久化。因为写入
* 文件就是写入了磁盘,断电也可以保存。
*
*/
oos.writeObject(p);
System.out.println("写出完毕!");
oos.close();
}
}
ObjectInputStream是用来对对象进行反序列化的输入流。
其实现对象反序列化的方法为:
Object readObject()
该方法可以从流中读取字节并转换为对应的对象。
public class OISDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("person.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
/*
* ObjectInputStream提供的方法:
* Object readObject()
* 该方法用于进行对象的反序列化,如果读取的字节不是描述一个对象
* 的时候(字节不是通过对象输出流序列化的),会抛出异常
*/
Person p = (Person) ois.readObject();
System.out.println(p);
ois.close();
}
}
Serializable 接口:
ObjectOutputStream在对对象进行序列化时有一个要求就是需要序列化的对象所属的类必须实现Serializable接口。
实现该接口不需要重写任何方法。其只是作为可序列化的标志。
通常实现该接口的类需要提供一一个常量serialVersionUID表明该类的版本。若不显示的声明,在对象序列化时也会根据当前类的各个方面计算该类的默认serialVersionUID ,但不同平台编译器实现有所不同,所以若想跨平台,都应显示的声明版本号。
transient关键字:
当一个属性被该关键字修饰后,那么当对象序列化时,这个属性的值会被忽略忽略不必要的属性可以达到对象瘦身的目的。
public class Person implements Serializable{
private static final long serialVersionUID = 1227440665375321720L;
private String name;
private int age;
private String gender;
/*
* transient关键字
* 当一个属性被该关键字修饰后,那么当对象序列化时,这个属性的值会被忽略
* 忽略不必要的属性可以达到对象瘦身的目的。
*/
private transient String[] otherInfo;
public Person(String name, int age, String gender, String[] otherInfo) {
super();
this.name = name;
this.age = age;
this.gender = gender;
this.otherInfo = otherInfo;
}
public String toString() {
return name+","+age+","+gender+","+Arrays.toString(otherInfo);
}
}
字符流: 读写单位 char 底层仍然是基本的字节流,字符流仅适合读写文本数据
java.io.Reader是字符输入流的父类。
java.io.Writer是字符输出流的父类。
字符流是以字符(char)为单位读写数据的。次处理一个unicode。
字符流的底层仍然是基本的字节流。
字符流封装了字符的编码解码算法。
方法 | 功能 |
int read( ) | 读取一个字符,返回的int值"低16" 位有效。 |
int read(char[ ] chs) | 从该流中读取一个字符数组的length个字符并存入该数组,返回值为实际读取到的字符量。 |
void write(int c) | 写出一个字符写出给定int值”低16"位表示的字符。 |
void write(char[ ] chs) | 将给定字符数组中所有字符写出。 |
void write(String str) | 将给定的字符串写出 |
void write(char[ ] chs,int offset,int len): | 将给定的字符数组中从offset处开始连续的len个字符写出 |
转换流是字符流的一对常用实现类,是一对高级流。在实际开发中我们不会直接操作这个流,但是在流连接中它是非常重要的一环,负责衔接字节流与其他的高级字符流。
InputStreamReader :字符输入流。使用该流可以设置字符集,并按照指定的字符集从流中按照该编码将字节数据转换为字符并读取。
OutputStreamWriter:字符输出流.使用该流可以设置字符集,并按照指定的字符集将字符转换为对应字节后通过该流写出。
转换流读取写出文本数据:
public class OSWDemo {
public static void main(String[] args) throws IOException {
//向文件中写入文字
FileOutputStream fos = new FileOutputStream("osw.txt");
/*
* 第二个参数用来指定在字符集,这样当字符串通过转换流时会按照该
* 字符集转换为对应的一组字节。如果不指定则是按照系统默认的字符集
* 转换,不推荐这样的作法。
*/
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
osw.write("来~左边跟我一起画个龙~");
osw.write("右边画一道彩虹~");
System.out.println("写出完毕!");
osw.close();
}
}
public class ISRDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
int d = 0;
while((d = isr.read())!=-1) {
char c = (char)d;
System.out.print(c);
}
isr.close();
}
}
java.io.BufferedWriter:缓冲字符输出流
java.io.BufferedReader:缓冲字符输入流
缓冲字符流内部有缓冲区,可以块读写文本数据提高读写效率。并且缓冲字符流还有一个特点就是可以按行读写字符串。
java.io.PrintWriter:具有自动行刷新的缓冲字符输出流,内部总是连接 BufferedWriter作为缓冲功能。
BufferedReader:特点:块读,并且可以按行读取字符串
案例演示:
public class PWDemo1 {
public static void main(String[] args) throws IOException {
//向文件中写入字符串
PrintWriter pw = new PrintWriter("pw.txt","UTF-8");
pw.println("让我再看你一眼,从南到北.");
pw.println("像是被五环路,蒙住的双眼.");
System.out.println("写出完毕!");
pw.close();
}
}
在流连接中使用PrintWriter
public class PWDemo2 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("pw.txt",true);
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
pw.println("夜空中最亮的星,能否听清。");
pw.println("那仰望的人心底的孤独和叹息。");
System.out.println("写出完毕!");
pw.close();
}
}
简易记事本工具
程序启动后,要求用户输入一个文件名,然后对该文件操作之后用户输入的每行字符串都按行写入到文件中。当单独输入exit时程序退出.要求:使用流连接形式创建PW。
public class PWDemo3 {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入文件名:");
String fileName = scanner.nextLine();
FileOutputStream fos = new FileOutputStream(fileName+".txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
/*
* PrintWriter构造方法如果第一个参数是一个流(字节流字符流都可以)
* 那么就支持第二个boolean型参数,如果该值为true,则具有自动行刷新
* 功能,即:每当使用println方法写入一行字符串后会自动flush
*/
PrintWriter pw = new PrintWriter(bw,true);
System.out.println("请开始你的表演...");
while(true) {
String line = scanner.nextLine();
if("exit".equals(line.toLowerCase())) {
break;
}
pw.println(line);
}
System.out.println("再见!");
pw.close();
}
}
缓冲字符输入流
特点:块读,并且可以按行读取字符串
public class BRDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("./src/io/BRDemo.java");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String line = "";
while((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}
}
问文本文件转换字符集:
程序启动后,输入要转换字符集的文本文件的名字,然后再输入该文件的字符集名字然后再输入要转换的字符集名字。之后程序会将该文件内容以指定的字符集写入 另一个文件。 例如:有一个文件名为demo.txt,该文件的字符集用的是UTF-8。现在要将该文件内容转换为GBK编码的。
public class Test03 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入文件名:");
String fileName = scanner.nextLine();//demo.txt
System.out.println("请输入该文件的字符集:");
String charset = scanner.nextLine();//utf-8
System.out.println("请输入要转换的字符集:");
String newCharset = scanner.nextLine();//gbk
//截取原文件名
String name = fileName.substring(0,fileName.lastIndexOf("."));
//截取原文件的后缀名
String ext = fileName.substring(fileName.lastIndexOf(".")+1);
try (
FileInputStream fis = new FileInputStream(fileName);
InputStreamReader isr = new InputStreamReader(fis,charset);
BufferedReader br = new BufferedReader(isr);
FileOutputStream fos = new FileOutputStream(name+"_"+newCharset+"."+ext);//demo_gbk.txt
OutputStreamWriter osw = new OutputStreamWriter(fos,newCharset);
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
){
String line = "";
while((line = br.readLine())!=null) {
pw.println(line);
}
System.out.println("转码完毕!");
} catch (Exception e) {
e.printStackTrace();
}
}
}