在Java程序中,对于数据的输入/输出操作以“流”(stream)方式进行。流只能单方向流动,输入流用来读取数据,输出流用来写出。
节点流可以从一个特定的节点读写数据。
处理流是“连接”在已存在的流之上,通过对数据的处理为程序提供更为强大的读写功能。
用于向程序输入数据,数据单位为字节。
InputStream相关子类
注:上图深色为节点流,浅色为处理流。
InputStream基本方法
abstract int read()
从输入流读取数据的下一个字节。
int read(byte[] b)
从输入流读取一些字节数,并将它们存储到缓冲区 b 。
int read(byte[] b, int off, int len)
从输入流读取最多 len字节的数据到一个字节数组。
void close()
关闭此输入流并释放与流相关联的任何系统资源。
long skip(long n)
跳过并丢弃来自此输入流的 n字节数据。
用于向程序输出数据,数据单位为字节。
OutStream相关子类
OutStream基本方法
void close()
关闭此输出流并释放与此流相关联的任何系统资源。
void flush()
刷新此输出流并强制任何缓冲的输出字节被写出。
void write(byte[] b)
将 b.length字节从指定的字节数组写入此输出流。
void write(byte[] b, int off, int len)
从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
abstract void write(int b)
用于向程序输入数据,数据单位为字符(16 bit)。
Reader相关子类
Reader基本方法
int read()
读一个字符
int read(char[] cbuf)
将字符读入数组。
abstract int read(char[] cbuf, int off, int len)
将字符读入数组的一部分。
int read(CharBuffer target)
尝试将字符读入指定的字符缓冲区。
long skip(long n)
跳过字符
abstract void close()
关闭流并释放与之相关联的任何系统资源
用于向程序输出数据,数据单位为字符。
Writer相关子类
Writer基本方法
abstract void close()
关闭流,先刷新。
abstract void flush()
刷新流。
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)
写一个字符串的一部分。
以上关于基本方法的介绍部分摘自JDK API文档
通俗地讲,节点流就像一根管子,程序就像一个水桶,管子直接插入水桶并通过管子进行数据的输入/输出。
下面给出FileInputStream,FileOutputStream,FileReader,FileWriter的小例子。
InputStream
import java.io.*;
public class TestFileInputStream {
public static void main(String args[]){
int b = 0;
FileInputStream in = null;
try{
in = new FileInputStream("C:\\Users\\86198\\IdeaProjects\\zhoulr\\src\\com\\company\\TestFileInputStream.java");
//此处文件路径根据你要读取的文件进行书写
}catch(FileNotFoundException e){
System.out.println("找不到指定文件");
System.exit(-1);//结束程序
}
try {
long num = 0;
while ((b = in.read()) != -1)//检测是否输到文件尾
{
System.out.print((char)b);
num++;
}
in.close();
System.out.println();
System.out.println("共读取了"+num+"个字节");
}catch(IOException e1){
System.out.println("文件读取错误");System.exit(-1);
}
}
}
结果:
我只截取了后面的一部分,其实就是把这个代码打印出来了(因为我的文件路径就是这个代码的路径)。其中出现乱码的原因是InputStream是字节流,单位是一个字节,但中文需要两个字节所以会出现乱码。
OutputStream
import java.io.*;
public class TestOutputStream {
public static void main(String args[]){
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try{
in = new FileInputStream("D:/HelloWorld.java");
//写需要复制的文件路径
out = new FileOutputStream("D:/JAVA/test.txt");
//写被复制的文件路径
while((b=in.read())!=-1)
out.write(b);
in.close();//关闭输入流
out.close();//关闭输出流
}catch(FileNotFoundException e1){
System.out.println("找不到指定文件");
System.exit(-1);
}catch(IOException e2){
System.out.println("文件复制错误");
System.exit(-1);
}
System.out.println("文件已成功复制");
}
}
结果
这个就只有一句文件成功复制…当然如果路径不正确的话应该会显示错误。然后在文件里就可以查看相关信息了。
FileReader
import java.io.*;
public class TestFileReader {
public static void main(String args[]) {
FileReader fr = null;
int a = 0;
try {
fr = new FileReader("C:\\Users\\86198\\IdeaProjects\\zhoulr\\src\\com\\company\\TestFileReader.java");
int l = 0;
while ((a = fr.read()) != -1) {
System.out.print((char) a);
}
fr.close();
} catch (FileNotFoundException e1) {
System.out.println("找不到指定文件");
} catch (IOException e2) {
System.out.println("文件读取错误");
}
}
}
结果
其实这个程序跟上一个InputStream是有很多相似之处的,但它的结果不会出现乱码,因为Reader本身是字符流,单位是字符,可以输出中文。
FileWriter
import java.io.*;
public class TestFileWriter {
public static void main(String args[]){
FileWriter fw = null;
try{
fw = new FileWriter("D:\\JAVA\\test.txt");
for(int i=0;i<=50000;i++){
fw.write(i);
}
fw.close();
}catch(IOException e){
e.printStackTrace();
System.out.println("文件写入错误");
System.exit(-1);
}
}
}
结果
这个程序和OutputStream那个其实还是有很大区别的,这个程序利用Writer也是字符输出的形式,将0~50000的字符都读入了文件中(最大应该是65535个字符),Java采用的是Unicode码,65535个可以包含差不多所有国家的文字了。接下来看一下这个文件里的内容。有一些是乱码,应该是因为在我的电脑上不支持查看…但条件允许的话应该都是可以显示的。
处理流相当于是另一根管道包在节点流或处理流的外面(人生无常大肠包小肠hhhh),通过处理流能让数据的处理更简洁高效,为程序提供更强大的读写功能。
缓冲流套接在相应节点流上,对输入输出的数据提供缓冲的功能,提高了读写的效率,同时增加了一些新方法。
缓冲流相关构造方法
1.BufferdReader(Reader in)
2.BufferedReader(Reader in,int sz)//sz为自定义缓存区大小
3.BufferedWriter(Writer out)
4.BufferedWriter(Writer out,int sz)
5.BufferedInputStream(InputStream in)
6.BufferedInputStream(InputStream in,int sz)
7.BufferedWriter(OutputStream out)
8.BufferedWriter(OutputStream out,int sz)
注意事项
缓冲流相关应用
Example1
import java.io.*;
public class TestBufferedStream1 {
public static void main(String args[]){
try{
FileInputStream fis=new FileInputStream("D://HelloWorld.java");
BufferedInputStream bis =new BufferedInputStream(fis);
int c=0;
System.out.println(bis.read());
System.out.println(bis.read());
//输入fis的前两个字符
bis.mark(100);
for(int i=0;i<=10&&(c= bis.read())!=-1;i++){
System.out.println(c+" ");
}
//依此从第三个字符开始输出十一个字符
System.out.println(" ");
bis.reset();
for(int i=0;i<=10&&(c= bis.read())!=-1;i++) {
System.out.println(c + " ");
}
//再输出十一个字符
bis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
Example2
import java.io.*;
public class TestBufferedStream2 {
public static void main(String args[]){
try{
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\JAVA\\test.txt"));
BufferedReader br = new BufferedReader(new FileReader("D:\\HelloWorld.java"));
String a = null;
for(int i=1;i<=100;i++){
a = String.valueOf(Math.random());
bw.write(a);
bw.newLine();
}
//此段代码的作用是a生成100个随机数输入到test.txt文件中
bw.flush();
while((a=br.readLine())!=null){
System.out.println(a);
}
//将HelloWorld.java内的内容输出
bw.close();
br.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
个人理解转换流是Reader和InputStream的转换,Writer和OutputStream的转换(本质上说就是字符流和字节流的转换吧)。
这块内容会更好理解一点点吧,现在就举一个小例子。
Example1
import java.io.*;
public class TestTransForm {
public static void main(String args[]){
try{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\JAVA\\test.txt"));
osw.write("Javahaonan!!");
System.out.println(osw.getEncoding());
osw.close();
osw = new OutputStreamWriter(new FileOutputStream(("D:\\JAVA\\test.txt"),true),"ISO8859_1");
//ISO8859_1包含所有的西欧语言,别名latin_1。
//这个true表示Bluemsun great是加在前面的数据后面的,如果不写true就会覆盖掉前面的字符串。
osw.write("Bluemsun great");
System.out.println(osw.getEncoding());
osw.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
DataInputStream和DataOutputStream分别继承自InputStream和OutputStream,它属于处理流,需要分别套接在InputStream和OutputStream类型的节点流上。
数据流提供了可以存取与机器无关的Java原始类型数据(如int,double等)的方法。
下面举一个简单的例子来阐述一下数据流的构造方法和使用。
import java.io.*;
public class TestDataStream {
public static void main(String args[]){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
//构造方法:DataOutputStream (OutputStream out);
try{
dos.writeDouble(Math.random());
dos.writeBoolean(true);
//内存里double类型的一个数据,Boolean类型一个数据
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());
//输出内存所占空间
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readDouble());
//输出double型数据
System.out.println(dis.readBoolean());
//输出boolean型数据
dis.close();dos.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
顾名思义,Print流是用来输出的处理流。
注意事项
关于Print流的相关构造方法在这里不再赘述,需要学习的话可以自行打开API文档查看。
下面举一个关于Print流的应用实例。
Example
import java.io.*;
import java.util.*;
import java.util.Locale;
public class TestPrintStream3 {
public static void main(String args[]){
String s = null;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try{
FileWriter fw = new FileWriter("D:\\JAVA\\test.txt",true);
PrintWriter pw = new PrintWriter(fw);
while((s=br.readLine())!=null){
if(s.equalsIgnoreCase("exit"))
break;
System.out.println(s.toUpperCase());
pw.println("-----");
pw.println(s.toUpperCase());
pw.flush();
//虽然可以自动flush但是还是养成习惯flush比较好
}
pw.println("==="+new Date()+"===");
pw.flush();
pw.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
}
这个代码可以算是一个简单的写日志的代码叭。每次编辑都会显示日期,然后只有你输入才会在文件里输出。还挺有意思的。
结果看下图。
绿色是我的输入,白色是输出。
这是记事本内的内容。
Object流可以直接将Object整个写入或读出。
关于Object流先以一个例子来说明。
Example
import java.io.*;
public class TestObjectIO {
public static void main(String args[]) throws Exception{
T t = new T();
t.k=8;
FileOutputStream fos = new FileOutputStream("D:\\JAVA\\test.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(t);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("D:\\JAVA\\test.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
T tread = (T)ois.readObject();
System.out.println(tread.i+" "+tread.j+" "+ tread.k+" "+ tread.n);
}
}
class T
implements Serializable
{
int i = 7;
int j = 9;
double k = 21.0;
transient int n = 13;
}
结果如下图:
通过以上程序 Object有几个知识点需要掌握。
transient关键字
transient是流动的,暂时的的意思。它是屏蔽了在此关键词后所定义的内容,故要输出的话,会输出它的初始值,也就是0.
serializable接口
上面的程序可以看到在class T的定义中后面写了implements serializable说明该方法时可序列化的(给编译器看的),如果没有写这句话,Object整体被输入输出的话是会报错的。
externalizable接口
由serializable接口我们可以知道是可以声明一个类是可序列化的,但序列化的这个过程是由jdk来完成。而externalizable接口则可以控制序列化的过程。(但最好还是jdk给你序列化)
终于写完了…这篇博客真的有够长的。上一节学异常的时候其实很懵都是在网上东拼西凑的看课预习,这节其实好很多,就看了学长学姐推荐的网课,我感觉条理很清晰。然后但是其中还是有很多细节的方法我不知道的,就一直在看API文档,比如printStackTrace,equalsIgnoreCase等。
这一节的内容其实很好分类,主要分成输出和输入两个大类,然后是节点流,处理流。节点流相对简单一点吧,File****这样的格式。处理流包括很多类,有不同功能,Buffered缓冲,转换流,Data数据流,然后是Print输出流和Object流。
还有很多方法没有写出来,需要用到的话,拥抱API叭~~~~