在整个Java.io 包中最重要的就是5个类和一个接口。5个类指的是File、InputStream、OutputStream、Reader、Writer;一个接口指的是 Serializable。掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了。
一、IO概述IO(Input/Output):即输入和输出.
下图是一个描述输入流和输出流的类层次图
1、File类和四大基流
File类(文件特征与管理):Java中 File类(文件、目录和文件过滤器)
四大基流:( InputStream字节输入流,OutputStream字节输出流,Reader字符输入流,Writer字符输出流 )
四大基流都是抽象类,其他流都是继承于这四大基流的,我们并不能创建这四大基流对象来执行输入/输出,只能创建其子类对象。它们的方法是所有输入/输出流都可使用的方法。
在InputStream/Reader中的一些方法:
abstract int
read()
从输入流中读取数据的下一个字节。 int
read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b
中。 int
read(byte[] b, int off, int len)
将输入流中最多 len
个数据字节读入 byte 数组。 int
read()
读取单个字符。 int
read(char[] cbuf)
将字符读入数组。abstract int
read(char[] cbuf, int off, int len)
将字符读入数组的某一部分。void
close()
关闭此输入流并释放与该流关联的所有系统资源。 void
mark(int readlimit)
在此输入流中标记当前的位置。 boolean
markSupported()
测试此输入流是否支持 mark
和 reset
方法。void
reset()
将此流重新定位到最后一次对此输入流调用 mark
方法时的位置。 long
skip(long n)
跳过和丢弃此输入流中数据的 n
个字节。
在OutputStream/Writer中的一些方法:
void
write(byte[] b)
将 b.length
个字节从指定的 byte 数组写入此输出流。 void
write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off
开始的 len
个字节写入此输出流。abstract void
write(int b)
将指定的字节写入此输出流。void
write(char[] cbuf)
写入字符数组。abstract void
write(char[] cbuf, int off, int len)
写入字符数组的某一部分。 void
write(int c)
写入单个字符。 void
write(String str)
写入字符串。 void
write(String str, int off, int len)
void
close()
关闭此输出流并释放与此流有关的所有系统资源。 void
flush()
刷新此输出流并强制写出所有缓冲的输出字节。
无论是什么流,都有close方法,用来关闭资源;如果操作文件,就得开通一个流对象关联我们得磁盘文件,如果不关闭资源,那么磁盘的文件一直被程序所引用着,不能删除,也不能更改,浪费资源,所以操作完流时勿忘关闭流。
2、流的概念和作用
流:代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象。 -- 《Thinking in Java》
流的本质:数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
流的作用:为数据源和目的地建立一个输送通道。
Java中将输入输出抽象称为流,就好像水管,将两个容器连接起来。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流.
3、Java IO流的分类
站在不同的角度,分类方式是不一样的
1)根据数据流向不同分为:输入流和输出流.
2)根据处理数据类型(单位)的不同分为:字节流和字符流.
字节流:以字节为单位进行输入输出
字符流:以字符为单位进行输入输出
使用字节流操作汉字和特殊符号语言时,容易乱码,建议使用字符流
一般操作二进制文件(图片、音频和视频等)必须使用字节流。
一般操作文本文件使用字符流。如果不清楚是哪类文件,使用字节流。
3)根据功能的不同分为:节点流和包装流(包装流又称处理流)。
4、操作IO流的模板
1)创建源或者目标对象(拿文件流举例)
输入操作:把文件中的数据流向到程序中,此时文件是源,程序是目标.
输出操作:把程序中的数据流向到文件中,此时文件是目标,程序是源.
2)创建IO流对象
输入操作:创建输入流对象.
输出操作:创建输出流对象.
3)具体的IO操作
输入操作:输入流对象的read方法.
输出操作:输出流对象的write方法.
4):关闭资源(勿忘).:一旦资源关闭之后,就不能使用流对象了,否则报错.
输入操作:输入流对象.close().
输出操作:输出流对象.close().
5、操作IO流的六字箴言:读进来,写出去
读进来:进来强调了是输入,读说明是read方法.
写出去:出去强调了是输出,写说明是write方法.
6、flush 刷新操作,输出流中都有flush方法
计算机访问外部设备(磁盘文件)要比直接访问内存慢很多,如果每次write都要直接写出到磁盘文件中,CPU都会花费更多时间,此时我们可以准备一个内存缓冲区,程序每次write方法都是直接写到内存缓冲区中,当内存缓冲区写满后,系统才把缓冲区的内容一次性的写出给磁盘文件。缓冲区大小一般使用容量的整数倍,可提高IO性能。
使用缓冲区的好处:
1)提高CPU使用率
2)有机会回滚写入的数据。
对于字节流的flush方法不是都有作用(部分字节流才有作用),对于字符流都起作用,如果我们调用close方法,系统在关闭资源前会先调用flush方法,所以,我们不需要关心flush方法,只关注close方法即可。
注意:
1)与类型相关的流操作的是内存,内存流的close()是无效的。
2)当我们使用包装流/处理流套接到节点流上使用的时候,只需要关闭最上层的处理l流即可,Java会自动帮我们关闭下层的节点流。
7、Java输入/输出流体系中常用的流的分类:下面的一个个实例操作
注: 表中粗体字所标出的类代表节点流,必须直接与指定的物理节点关联,表中抽象基类,无法直接创建实例。
只有清楚每个IO流的特性和方法,才能在不同的需求面前正确的选择对应的IO流进行开发,这些IO流的具体方法自行查看API。
二、文件流:程序和文件的IO它们都是节点流,会直接和指定文件关联。
注意:程序的最后一定要使用 close() 来正确关闭流,程序里面打开的文件IO资源不属于内存的资源,垃圾回收机制无法回收该资源,所以必须在程序里关闭打开的IO资源。关闭流除了可以保证流的物理资源被回收之外,还可以将输出流缓冲区中的数据flush到物理节点中里(因为在执行close()方法之前,自动执行输出流的flush()方法)。
1、文件字节流(FileInputStream、FileOutputStream)FileOutputStream: 文件字节输出流,该类用来创建一个文件并向文件中写数据。
//创建文件
File file = new File("E:/javaSE/file01/text.txt");
//如果file文件不存在,则自动创建该文件,每次从文件开头写入数据
FileOutputStream fos = new FileOutputStream(file);
//每次从文件末尾写入数据
//FileOutputStream fos = new FileOutputStream(file,true);
//写入字节流数据
fos.write(97); //写单个字节 a
fos.write("hello! abc.".getBytes()); //getByte()将字符串转换成字节数组
fos.flush();
fos.close();
FileInputStream:文件字节输入流,该流用于从文件读取数据。
//创建文件
File file = new File("E:/javaSE/file01/text.txt");
FileInputStream fis = new FileInputStream(file);
//创建一个字节数组存放读取的字节数据
byte[] buf = new byte[10]; //一次最多读取10个字节
int len = -1;
while((len=fis.read(buf)) != -1) {
String str = new String(buf,0,len);//字节数据构造新的 String
System.out.println(str);
}
fis.close();
//结果
ahello! ab
c.
使用字节流完成文件的拷贝操作(边读边写),正确关闭资源
public static void main(String[] args) {
InputStream in = null;
OutputStream out = null;
try {
// 1.创建源和目标
File srcf = new File("E:/javaSE/file01/text.txt");
File destf = new File("E:/javaSE/file01/text_copy.txt");
// 2.创建输入流和输出流对象
in = new FileInputStream(srcf);
out = new FileOutputStream(destf);
// 3.读取/写出操作
byte[] buffer = new byte[1024]; // 创建容量为1024的缓冲区(存储已经读取到的数据)
int len = -1; // 返回-1 表示读取完毕
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len); // 将数据从buffer中写出去
}
} catch (Exception e) { // 处理异常
e.printStackTrace();
} finally {
// 4.关闭资源
try {
if (in == null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out == null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用字节流完成文件的拷贝操作(边读边写),jdk7 自动关闭关闭资源,(推荐使用jdk7)。Java 7改写了所有的IO资源类,它们都实现了 AntoCloseable接口,因此都可以通过自动关闭资源的 try语句来关闭这些IO流。
public static void main(String[] args) {
// 1.创建源和目标
File srcf = new File("E:/javaSE/file01/text.txt");
File destf = new File("E:/javaSE/file01/text_copy.txt");
try (
//打开资源的代码
// 2.创建输入流和输出流对象
InputStream in = new FileInputStream(srcf);
OutputStream out = new FileOutputStream(destf);
) {
// 3.读取/写出操作
byte[] buffer = new byte[1024];//创建容量为1024的缓冲区(存储已经读取到的数据)
int len = -1; //返回-1 表示读取完毕
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len); //将数据从buffer中写出去
}
} catch (IOException e) {
e.printStackTrace();
}
}
FileReader:文件的字符输入流, FileWriter:文件的字符输出流
操作方法和上面的差不多, 具体查看官方 API 。注意:char[]
使用字符流完成文件的拷贝操作(边读边写),jdk7 自动关闭关闭资源,(推荐使用jdk7)
public static void main(String[] args) {
// 1.创建源和目标
File srcf = new File("E:/javaSE/file01/text.txt");
File destf = new File("E:/javaSE/file01/text_copy.txt");
try (
//打开资源的代码
// 2.创建输入流和输出流对象
Reader in = new FileReader(srcf);
Writer out = new FileWriter(destf);
) {
// 3.读取/写出操作
char[] buffer = new char[1024];//创建容量为1024的缓冲区(存储已经读取到的数据)
int len = -1; //返回-1 表示读取完毕
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len); //将数据从buffer中写出去
}
} catch (IOException e) {
e.printStackTrace();
}
}
数组流:可以将字节数组写到输出流中,也可以将字节数组从输入流中读出来,不涉及磁盘。可以看作是btye[]/char[]和输入/输出流之间的一个数据形式的转换。
字节数组流对象分别是:ByteArrayInputStream和ByteArrayOutputStream。
字节字符流对象分别是:CharArrayReader和CharArrayWriter。
1、字节数组流(ByteArrayInputStream和ByteArrayOutputStream)字节字符流同理,就不举例了
ByteArrayInputStream一些方法:
ByteArrayInputStream(byte[] buf)
创建一个 ByteArrayInputStream
,使用 buf
作为其缓冲区数组。ByteArrayInputStream(byte[] buf, int offset, int length)
创建 ByteArrayInputStream
,使用 buf
作为其缓冲区数组。
int
available()
返回可从此输入流读取(或跳过)的剩余字节数。
ByteArrayOutputStream一些方法:
ByteArrayOutputStream()
创建一个新的 byte 数组输出流。ByteArrayOutputStream(int size)
创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量(以字节为单位)。
int
size()
返回缓冲区的当前大小。 byte[]
toByteArray()
创建一个新分配的 byte 数组。 String
toString()
使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。
public static void main(String[] args) {
// 1.创建源和目标
File srcf = new File("E:/javaSE/file01/text.txt");
try (
//打开资源的代码
// 2.创建输入流和输出流对象
InputStream in = new FileInputStream(srcf);
// 创建字节数组输出流
ByteArrayOutputStream arrayOut = new ByteArrayOutputStream()
) {
// 3.读取/写出操作
byte[] buffer = new byte[1024];//创建容量为1024的缓冲区(存储已经读取到的数据)
int len = -1; //返回-1 表示读取完毕
while ((len = in.read(buffer)) != -1) {
arrayOut.write(buffer, 0, len); //将数据从buffer中写出去
}
byte[] bytes = arrayOut.toByteArray();
System.out.println(bytes.length); //80
// 创建字节数组输入流
ByteArrayInputStream arrayInput = new ByteArrayInputStream(bytes);
System.out.println((char)arrayInput.read()); // s
System.out.println(arrayInput.available()); //79:读一个,还剩79
arrayInput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
字节数组输出流扩容:一个可自动扩容的 byte 数组,默认初始化 32 个字节的大小,最大容量是 2^31-9 个字节(2G)。只要数据不超过2G,都可以往里写。每次写数据之前,会先计算需要的容量大小,如果需要扩容,扩大到原来的两倍,但不会超过最大值。
管道流主要用在多个线程之间通进行信息传递的Java流。参考文章:Java IO 之 管道流 原理分析
管道流也分为字节流(PipedInputStream、PipedOutputStream)与字符流(PipedReader、PipedWriter)两种类型.
PipedOutputStream、PipedWriter 是写入者/生产者/发送者。
PipedInputStream、PipedReader 是读取者/消费者/接收者。
这里用字节管道流举例,,字符管道流原理跟字节管道流一样,只不过底层一个是 byte 数组存储 一个是 char 数组存储的。
public class PipedStreamDemo {
public static void main(String[] args) {
//创建一个线程池
ExecutorService executorService = Executors.newCachedThreadPool();
try {
//创建输入和输出管道流
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
//创建发送线程和接收线程对象,并把管道流赋值
Sender sender = new Sender(pos);
Reciever reciever = new Reciever(pis);
//提交给线程池运行发送线程和接收线程
executorService.execute(sender);
executorService.execute(reciever);
} catch (IOException e) {
e.printStackTrace();
}
//通知线程池,不再接受新的任务,并执行完成当前正在运行的线程后关闭线程池。
executorService.shutdown();
try {
//shutdown 后可能正在运行的线程很长时间都运行不完成,这里设置超过1分钟,强制执行 Interruptor 结束线程。
executorService.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 发送者
static class Sender extends Thread {
private PipedOutputStream pos;
public Sender(PipedOutputStream pos) {
super();
this.pos = pos;
}
@Override
public void run() {
try {
String s = "hello world, amazing java !";
System.out.println("Sender:" + s);
byte[] buf = s.getBytes();
pos.write(buf, 0, buf.length);
pos.close();
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 接受者
static class Reciever extends Thread {
private PipedInputStream pis;
public Reciever(PipedInputStream pis) {
super();
this.pis = pis;
}
@Override
public void run() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = pis.read(buf)) != -1) {
baos.write(buf, 0, len);
}
byte[] result = baos.toByteArray();
String s = new String(result, 0, result.length);
System.out.println("Reciever:" + s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
定义:字符串流,以一个字符为数据源,来构造一个字符流。有字符串输入流 StringReader和字符串输出流 StringWriter。
作用:在Web开发中,客户端经常要从服务器端上获取数据,数据返回的格式通过一个字符串(XML、JSON),我们需要把这个字符串构造为一个字符流。然后再用第三方数据解析器来解析数据。
本质上,我们也可以像数组流那样去理解,字符串流就是字符串和流之间的转换!
java.io.StreamTokenizer 类可以获取输入流并将其分析为Token(标记)。参考文章:java StreamTokenizer使用
字符串输出流StringWriter的一些特有方法:
StringBuffer
getBuffer()
返回该字符串缓冲区本身。StringWriter
append(char c)
将指定字符添加到此 writer。StringWriter
append(CharSequence csq)
将指定的字符序列添加到此 writer。
public static void main(String[] args) {
//内部维护缓存(string对象)
StringWriter sw = new StringWriter();
sw.append("go ");
sw.write("test ");
sw.write("sdk ");
sw.write("hello");
sw.flush();
//获取缓存中的内容
System.out.println(sw.toString());
//获取缓存中的内容,内容装在StringBuffer对像中缓存
StringBuffer sb = sw.getBuffer();
//获取StringBuffer中缓存中的内容
System.out.println(sb.toString());
//
stringReader(sb.toString());// go test sdk hello输出count = 4
}
//计算一个字符串有多少个单词
private static void stringReader(String info) {
StringReader sr = new StringReader(info);
// 流标记器,分析流
StreamTokenizer st = new StreamTokenizer(sr);
int count = 0;
try {
while (st.nextToken() != StreamTokenizer.TT_EOF) {
//在调用 nextToken 方法之后,ttype字段将包含刚读取的标记的类型
//ttype字段将包含刚读取的标记的类型,TT_EOF指示已读到流末尾的常量。
// 如果是一个单词
if (st.ttype == StreamTokenizer.TT_WORD) {
//TT_NUMBER指示已读到一个数字标记的常量 //TT_WORD指示已读到一个文字标记的常量
count++;
}
}
} catch (IOException e) {
e.printStackTrace();
}
sr.close();
System.out.println("count = " + count);
}
包装流又称处理流
1)隐藏了底层节点流的差异,并对外提供了更方便的IO操作,相对于节点流更高级
2)使用包装流包装节点流,程序直接操作处理流,让节点流和底层的设备做IO操作。
3)只需要关闭包装流即可。
包装流如何区分
写代码时,如果发现创建对象时需要传递另一个流对象,即 new 包装流(节点流对象),此时用到包装流。
缓冲流也是一个包装流
操作节点流时,习惯定义一个byte/char数组(其实也是一个缓冲区),每次都从磁盘文件中读取一个字节,直接操作磁盘文件性能极低。
解决方案:定义一个数组作为内存缓冲区,程序 - 内存内存缓冲区 - 磁盘文件之间进行IO操作
缓冲流作用:为了提高读写效率,不必为写入的每个字节导致底层系统的调用,减少对磁盘的频繁的读写操作,起到保护磁盘的作用。
1、字节缓冲流(BufferedInputStream、BufferedOutputStream)操作方法和上面的差不多, 具体查看官方 API .
BufferedOutputStream: 字节缓冲输出流
//创建文件
File file = new File("E:/javaSE/file01/text.txt");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
//写入字节流数据
bos.write(97); //写单个字节 a
bos.write("hello! abc.".getBytes()); //getByte()将字符串转换成字节数组
bos.flush();
bos.close();
BufferedInputStream: 字节缓冲输入流
//创建文件
File file = new File("E:/javaSE/file01/text.txt");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
//创建一个字节数组存放读取的字节数据
byte[] buf = new byte[10]; //一次最多读取10个字节
int len = -1;
while((len=bis.read(buf)) != -1) {
String str = new String(buf,0,len);//字节数据构造新的 String
System.out.println(str);
}
bis.close();
使用字节缓冲流完成文件的拷贝操作(边读边写),jdk7 自动关闭关闭资源,(推荐使用jdk7)
public static void main(String[] args) {
// 1.创建源和目标
File srcf = new File("E:/javaSE/file01/text.txt");
File destf = new File("E:/javaSE/file01/text_copy.txt");
try (
//打开资源的代码
// 2.创建输入流和输出流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcf));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destf));
) {
// 3.读取/写出操作
byte[] buffer = new byte[1024];;//创建容量为1024的缓冲区(存储已经读取到的数据)
int len = -1; //返回-1 表示读取完毕
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len); //将数据从buffer中写出去
}
} catch (IOException e) {
e.printStackTrace();
}
}
缓冲流源码: 内存缓冲区默认8192,构造器可自定义大小,
2. 字符缓冲流(BufferedReader、BufferedWriter)
BufferedWriter:字符缓冲输出流
-
-
void
newLine()
写一行行分隔符。
-
//创建文件
File file = new File("E:/javaSE/file01/text.txt");
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
//写入字符流数据
bw.write(97); //写单个字符 a
bw.newLine(); //写入换行符
bw.write("hello! abc."); //写字符串
bw.flush();
bw.close();
BufferedReader:字符缓冲输入流,注意readLine()末尾返回 null
-
-
Stream
lines()
返回一个
Stream
,其元素是从这个BufferedReader
读取的行。int
read()
读一个字符
int
read(char[] cbuf, int off, int len)
将字符读入数组的一部分。
String
readLine()
读一行文字。如果已达到流的末尾,则为null
-
//创建文件
File file = new File("E:/javaSE/file01/text.txt");
BufferedReader br = new BufferedReader(new FileReader(file));
//读数据
String str = null;
while((str=br.readLine()) != null) { //readLine()读取一行
System.out.println(str);
}
br.close();
//结果
a
hello! abc.
使用字符缓冲流完成文件的拷贝操作(边读边写),jdk7 自动关闭关闭资源,(推荐使用jdk7)
public static void main(String[] args) {
// 1.创建源和目标
File srcf = new File("E:/javaSE/file01/text.txt");
File destf = new File("E:/javaSE/file01/text_copy.txt");
try (
//打开资源的代码
// 2.创建输入流和输出流对象
BufferedReader br = new BufferedReader(new FileReader(srcf));
BufferedWriter bw = new BufferedWriter(new FileWriter(destf));
) {
// 3.读取/写出操作
//byte[] buffer = new byte[1024];;//创建容量为1024的缓冲区(存储已经读取到的数据)
String line = null; //返回-1 表示读取完毕
while ((line = br.readLine()) != null) {
bw.write(line); //读一行写一行,也可以和上边实例一样使用read(char[] cbuf, int off, int len)
bw.newLine(); // 换行
}
} catch (IOException e) {
e.printStackTrace();
}
}
转换流:把字节流转换为字符流,转换流是字符流。
作用:字符流和字节流之间沟通的桥梁,可对读取到的字节数据经过指定编码转换成字符,转换的时候还可以指定我们需要字符的编码方式。可对读取到的字符数据经过指定编码转换成字节。
为什么有字节流转换为字符流,没有字符流转字节流:
字节流可以操作一切文件(纯文本/二进制),字符流是用来操作纯文本中文使用,本身是对字节流的增强。
OutputStreamWriter : 将字节输出流转换字符输出流
//创建文件
File file = new File("E:/javaSE/file01/text.txt");
//将字节输出流转换字符输出流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file));
BufferedWriter bw = new BufferedWriter(osw);
//写入字符流数据
bw.write(97); //写单个字符 a
bw.newLine(); //写入换行符
bw.write("hello! abc."); //写字符串
bw.flush();
bw.close();
InputStreamReader : 将字节输入流转换字符输入流
//创建文件
File file = new File("E:/javaSE/file01/text_ggbk.txt");
//将字节输入流转换字符输入流
InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "GBK");
BufferedReader br = new BufferedReader(isr);
//读数据
String str = null;
while((str=br.readLine()) != null) { //readLine()读取一行
System.out.println(str);
}
br.close();
//结果
a
hello! abc.
使用对象流来完成序列化和反序列化操作:
ObjectOutputStream: 通过 writeObject方法做序列化操作的.
ObjectInputStream: 通过 readObject方法做反序列化操作的.
注意:做反序列化操作必须存在对象的字节码对象,对User对象做序列化
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
transient private String password; // 理论上说静态和 瞬态 的字段是不能做序列化的
private int age;
...
}
public static void main(String[] args) throws Exception {
File file = new File("E:/javaSE/file01/text.txt");
//writeObject(file);
ReaderObject(file);
}
// 序列化操作
private static void writeObject(File file) throws IOException {
//对象只能调用自己编译类型里面的方法
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
out.writeObject(new User("admin","admin123",18));//将对象内容写入文件
out.close();
}
// 序列化操作
private static void ReaderObject(File file) throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
User user = (User)in.readObject();
System.out.println(user); // User{username='admin', password='null', age=18}
in.close();
}
打印流包含字节打印流 PrintStream 和字符打印流 PrintWriter。
打印流是输出信息最方便的类,可以打印任何类型的数据信息,例如:小数,整数,字符串。
Java对 PrintStream 功能进行了扩充,增加了格式化输出功能。直接使用Print即可。但是输出的时候需要指定输出的数据类型。
1)PrintStream: 字节打印流
-
-
void
write(int b)
将指定的字节写入此流。
void
println(Object x)
打印一个对象,然后终止该行。
-
public static void main(String[] args) throws Exception {
PrintStream ps = new PrintStream(new FileOutputStream(new File("E:/javaSE/text.txt"),true));//true是带自动刷新
ps.write("abbbaaa".getBytes());
ps.print(true); //可以往文件打印任意类型的数据
ps.close();
}
2)PrintWriter: 字符打印流
-
-
void
write(String s)
写一个字符串
void
println(Object x)
打印一个对象,然后终止该行。
-
public static void main(String[] args) throws Exception {
PrintWriter pw = new PrintWriter(new FileOutputStream(new File("E:/javaSE/text.txt"),true));//true是带自动刷新
pw.write("abbbaaa");
pw.print(true); //可以往文件打印任意类型的数据
pw.close();
}
通常我们使用输入流的时候,我们读取输入流是顺序读取,当读取的不是我们想要的怎么办,又不能放回去,虽然我们可以使用程序做其他的处理来解决,但是Java提供了推回输入流来解决这个问题,
推回输入流可以做这样子的事情:将已经读取出来的字节或字符数组内容推回到推回缓冲区里面,从而允许重复读取刚刚读取的我们不想要的东西之前内容
推回输入流主要为两个类: PushbackInputStream 和 PushbackReader;
十一、数据流数据流:提供了可以读/写任意数据类型的方法:
DataOutputStream数据输出流允许应用程序将基本Java数据类型写到基础输出流中,提供了 writeXxx(xxx value)方法。
DataInputStream数据输入流允许应用程序以机器无关的方式从底层输入流中读取基本的Java类型,提供了 readXxx()方法。
.注意: writeXxx和readXxx必须要对应起来, writeByte写出的数据,此时只能使用readByte读取回来.
public static void main(String[] args) throws Exception {
File file = new File("E:/javaSE/text.txt");
// write(file);
read(file);
}
private static void read(File file) throws Exception {
DataInputStream in = new DataInputStream(new FileInputStream(file));
System.out.println(in.readByte());
System.out.println(in.readChar());//只能用相对应的方式来读取
System.out.println(in.readUTF());
//System.out.println(in.readInt());
in.close();
}
private static void write(File file) throws Exception {
DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
out.writeByte(66);// byte
out.writeChar('穷');// char
out.writeUTF("天王盖地虎!");// string
out.close();
}
1、标准的IO
标准的输入:通过键盘录入数据给程序.
标准的输出:在屏幕上显示程序数据.
2、在System类中有两个常量:
InputStream in = System.in; // “标准”输入流。
PrintStream out = System.out; //“标准”输出流。
3、标准流的重定向操作:
标准的输入:通过键盘录入数据给程序,重新指定输入的源不再是键盘,而是一个文件.
标准的输出:在屏幕上显示程序数据,重新指定输出的目标不再是屏幕,而是一个文件.
-
-
static InputStream
in
“标准”输入流。
static PrintStream
out
“标准”输出流。
static void
setIn(InputStream in)
重新分配“标准”输入流。
static void
setOut(PrintStream out)
重新分配“标准”输出流。
-
public static void main(String[] args) throws Exception {
// System.out.println("begin..");
// int data = System.in.read();// 接收键盘录入的一个字节 /标准输入
// System.out.println(data); // 标准输出
// System.out.println("end..");
// 重新定向标准输入流
System.setIn(new FileInputStream("E:/javaSE/text.txt"));
// 重新定向标准输出流
System.setOut(new PrintStream("E:/javaSE/text.txt"));
System.out.println("begin..");
int data = System.in.read();
System.out.println(data);
System.out.println("end..");
}
java.util.Scanner类:扫描器类,表示输入操作.
存在的方法: xxx表示数据类型,如byte,int ,boolean等.
boolean hasNextXxx():判断是否有下一种类型的数据
Xxx nextXxx():获取下一个该类型的数据.
public static void main(String[] args) throws Exception {
// 扫描文件中的数据
Scanner sc= new Scanner(new File("E:/javaSE/text.txt"),"UTF-8");
// 扫描键盘输入的数据
//Scanner sc = new Scanner(System.in);
// 扫面字符串的数据
// Scanner sc = new Scanner("天王盖地虎");
while (sc.hasNextLine()) {
String line = sc.nextLine();
System.out.println("--》 " + line);
}
sc.close();
}
配置文件:资源文件/属性文件(以 .properties作为拓展名的文件)。
防止一些配置输在在代码中写死,即“硬编码”。比如:数据库的连接信息放在db.properties文件中,而Java代码需要获取该文件中的信息即可。
在Java中,使用 java.util.Properties 类(Hashtable的子类,Map接口的实现类)来加载
-
-
void
load(InputStream inStream)
从输入字节流读取属性列表(键和元素对)。
void
load(Reader reader)
以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。
String
getProperty(String key)
使用此属性列表中指定的键搜索属性。
-
public static void main(String[] args) throws Exception {
// 创建Properties对象
Properties p = new Properties();
InputStream inStream = new FileInputStream("E:/javaSE/db.properties");
p.load(inStream);// 加载之后.数据都在p对象中;
System.out.println(p); // {password=admin123, username=admin3}
System.out.println("账号 : " +p.getProperty("username"));
System.out.println("密码 : " +p.getProperty("password"));
}
合并流(SequenceInputStream):把多个输入流合并成一个流对象
public static void main(String[] args) throws IOException {
File f1 = new File("E:/javaSE/file01/text.txt");
File f2 = new File("E:/javaSE/file01/text_copy.txt");
SequenceInputStream in = new SequenceInputStream(new FileInputStream(f1),new FileInputStream(f2));
byte[] ch = new byte[1024];
int len = -1 ;
while((len = in.read(ch))!= -1 ){
System.out.println(new String(ch,0,len));
}
in.close();
}
结论:
1)字节流与字符流,操作基本类同,不同点在于创建流对象上 转换流也一样,认真体会读和写的操作过程, 更多查看官方 API .
2)在开发中,根据需要灵活的使用不同的 IO流进行开发,正确使用 IO流原则:
- 一般操作二进制文件(图片、音频和视 频等)必须使用字节流。一般操作文本文件使用字符流。如果不清楚是哪类文件,使用字节流。
- 尽可能的多使用包装流/处理流,这会使我们的代码更加 灵活,复用性更好。
ends~