在java中数据的读写是通过流的形式进行的,流可以分为输入输出流,也可以分为字节字符流
对于流,有四个抽象基类:
1,字节流:InputStream,OutputStream(数据就是二进制数据)
2,字符流:Reader,Writer(基于字节流的概念,建立二进制与字符的对应表,从而实现操作字符的功能,也就是数据就是字符)
以上四个类派生出来的子类名称都是以父类名称为后缀,前缀则表示其功能
首先,一文件的字符流输入输出为例,进入IO流的学习。
对于文件的字符流输入输出,是通过FileReader和FileWriter对象实现:
1,FileWriter:该类对象建立的目的是明确数据所要写入的目的地
构造函数:
FileWriter("目标文件路径")//在该路径下建立一个空文件,若在该目录下出现同名文件,将建立一个空文件并覆盖原有文件
FileWriter("目标文件路径",boolean flag)//若改路径下没有该文件,则新建一个空文件,否则,若flag为true,则在末尾处续写该文件,若flag为false,则从头开始覆盖该文件
基本方法(继承Writer中的方法):
write(String s)//把字符串写入流中,注意是流中,并没写入文件
write(char[] ch,int start,int end)
write(String s,int start,int end)
flush()//把流中内容送到目的文件(又称更新),并清空流
close()//更新流,并关闭流资源,即不能再使用write方法,当写入数据结束后,要调用该方法以释放资源
实例代码:
import java.io.*; //注意要导入包
class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw=new FileWriter("demo.txt");
fw.write("abcde");
fw.flush();
fw.write("abc");
fw.close();
}
}
结果出人意料:
这是因为无论是对象建立还是对输入的操作,只要与读写有关,都有可能抛出IOException,所以要处理
修改如下:
import java.io.*; //注意要导入包
class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw=null;//必须在try外面定义,否则finally中的close函数找不到对象
try
{
fw=new FileWriter("demo.txt");
fw.write("abcde");
fw.flush();
fw.write("abc");
}
catch (IOException e)
{
System.out.println("catch 1:"+e.toString());
}
finally
{
try
{
fw.close();//关闭资源是必须执行的,所以close函数放到finally中单独try
}
catch (IOException e)
{
System.out.println("catch 2"+e.toString());
}
}
}
}
成功生成文件
但还是存在漏洞,如果,填写的路径非法,换言之,无法生成一个文本,那么fw一直为空,当执行close函数时,会报空指针异常
加一个判断:
finally
{
try
{
if(fw!=null)
fw.close();//关闭资源是必须执行的,所以close函数放到finally中单独try
}
catch (IOException e)
{
System.out.println("catch 2"+e.toString());
}
}
若要续写文件,则采用另一种构造函数:
原文件:
执行代码:
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw=null;
try
{
fw=new FileWriter("demo.txt",true);
fw.write("abcde");
}
catch (IOException e)
{
System.out.println("catch 1:"+e.toString());
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.println("catch 2"+e.toString());
}
}
}
}
文件变为:
成功续写
注意:文件的读写过程中换行是\r\n
2,FileReader:建立该对象是为了将目标文件内容导入到流中进行操作
构造函数:
FileReader("目标文件路径")//若目标文件不存在,初始化时会抛出异常
基本函数(继承Reader中的方法):
int read()//返回当前位置字符的ASCII,如果都到末尾,返回-1;
int read(char[] ch)//返回读取字符个数,读取字符的个数受限于数组长度,读取的内容回从前往后覆盖数组原有内容,若一个也读不到,也就是到了末尾,返回-1
close()//关闭流
示例代码:
原文件:
使用read()
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)
{
FileReader fr=null;
int num;
try
{
fr=new FileReader("demo.txt");
while((num=fr.read())!=-1)
{
System.out.print((char)num);
}
}
catch (IOException e)
{
System.out.println("catch 1:"+e.toString());
}
finally
{
try
{
if(fr!=null)
fr.close();
}
catch (IOException e)
{
System.out.println("catch 2"+e.toString());
}
}
}
}
/*
结果:
haha
hehe hoho xixi
lalalalalaallalalala
*/
使用read(char[] ch)
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)
{
FileReader fr=null;
int num;
char[] ch=new char[1024];//一般定义为1024的整数倍
try
{
fr=new FileReader("demo.txt");
while((num=fr.read(ch))!=-1)
{
System.out.print(new String(ch,0,num));//使用字符串的构造函数字符数组转换成字符串,其中只有从0到num的字符是有效的,其余内容 //是原数组的内容,并未被覆盖
}
}
catch (IOException e)
{
System.out.println("catch 1:"+e.toString());
}
finally
{
try
{
if(fr!=null)
fr.close();
}
catch (IOException e)
{
System.out.println("catch 2"+e.toString());
}
}
}
}
/*
结果一样:
haha
hehe hoho xixi
lalalalalaallalalala
*/
==============================================================================================================================
下面介绍字符输入输出流中的缓冲区对象BufferedReader和BufferedWriter
之所以引入缓冲区对象,是为了提高流的操作效率
缓冲去对象实际上还是在操作流对象,因此,在引入缓冲区对象之前,要先建立流对象,并用流对象来初始化缓冲区
先说BufferedReader
构造函数:
BufferedReader(Reader r)//通过已有的字符输入流对象来建立缓冲区对象
带来的特有功能:
一行一行的读取:readLine()//读取一个文本行,但是不包含换行符,并且返回为字符串,若读到尽头,返回null
演示:
demo.txt
演示代码:
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args)
{
FileReader fr=null;
BufferedReader bufr=null;
try
{
fr=new FileReader("demo.txt");
bufr=new BufferedReader(fr);//通过流对象建立缓冲区
String str;
while((str=bufr.readLine())!=null)
{
System.out.println(str);//注意输出时要换行,因为str中不包含换行符
}
}
catch (IOException e)
{
System.out.println("catch 1:"+e.toString());
}
finally
{
try
{
if(fr!=null)
bufr.close();//缓冲区实际还是在操作流,所以这里的close等同于fr.close
}
catch (IOException e)
{
System.out.println("catch 2:"+e.toString());
}
}
}
}
/*
结果:
haha
hehe hoho xixi
lalalalalaallalalala
*/
下面介绍BufferedWriter:
构造函数:
BufferedWriter(Writer w)//也是通过流对象建立缓冲区
带来的特有方法:
插入行分隔符:newLine()//好处在于该方法有利于程序的跨平台使用,因为不同系统的行分隔符不同
演示代码(实现文件的拷贝,当然不用缓冲区也能实现):
demo.txt
代码:
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args)
{
FileReader fr=null;
BufferedReader bufr=null;
FileWriter fw=null;
BufferedWriter bufw=null;
try
{
fr=new FileReader("demo.txt");
bufr=new BufferedReader(fr);
fw=new FileWriter("Buffered_Copy.txt");
bufw=new BufferedWriter(fw);//通过流对象建立缓冲区
String str;
while((str=bufr.readLine())!=null)
{
bufw.write(str);
bufw.newLine();//注意要换行
}
}
catch (IOException e)
{
System.out.println("catch 1:"+e.toString());
}
finally
{
try
{
if(fr!=null)
bufr.close();
if(fw!=null)
bufw.close();
}
catch (IOException e)
{
System.out.println("catch 2:"+e.toString());
}
}
}
}
生成的Buffered_Copy.txt
=============================================================================================================================
通过LineNumberReader对象实现对文件行号的读取和设置
LineNumberReader和缓冲区对象一样,需要通过已有的流对象建立对象
特有方法:
setLineNumber(int a)//设置当前行号,且从该点开始往后行号按加1递增
getLineNumber()//读取当前行号
readLine()//和缓冲区对象中readLine方法效果一样
代码示例:
import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args)
{
FileReader fr=null;
LineNumberReader lnr=null;
String str;
try
{
fr=new FileReader("BufferedWriterDemo.java");
lnr=new LineNumberReader(fr);
lnr.setLineNumber(0);//设置初始行号为0(实际显示是从1开始),如果不加该语句,默认就是0
while((str=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+" : "+str);
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fr!=null)
lnr.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
效果:
=========================================================================================================================
下面进入字节流的学习
同样,先从文件的字节流学起
文件的字节流输入输出对象为FileInputStream和FileOutputStream
先说FileOutputStream,这是文件的输出流对象,就是写到文件中的对象
基本方法:
write(btye[] b)//写入字节数组
write(btye[] b,int s,int len)//把字节数组中从s开始的长度为len的内容写入文件中
write(int a)//写入单个字节
close()//关闭资源
代码演示:
import java.io.*;
class FileOutputStreamDemo
{
public static void main(String[] args)
{
FileOutputStream fos=null;
try
{
fos=new FileOutputStream("StreamDemo.txt");//字节流可以处理非文本文件,这里只是举例为txt
fos.write("abcdefg".getBytes());//字节流不需要更新,直接到文件
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
结果:
FileInputStream是文件的字节流输入流
基本方法:
read()//读取一个字节
read(byte[] b)//把内容读取到字节数组中
available()//返回文件中的字节总数
close()//关闭资源
代码演示:
//文件的读取具有三种办法
import java.io.*;
class FileInputStreamDemo
{
public static void main(String[] args) throws IOException
{
read_3("FileOutputStreamDemo.java");
}
public static void read_1(String s) throws IOException
{
FileInputStream fis=new FileInputStream(s);
int a;
while((a=fis.read())!=-1)
{
System.out.print((char)a);
}
}
public static void read_2(String s) throws IOException
{
FileInputStream fis=new FileInputStream(s);
byte[] b=new byte[2014];
int num;
while((num=fis.read(b))!=-1)
{
System.out.print(new String(b,0,num));
}
}
public static void read_3(String s) throws IOException
{
FileInputStream fis=new FileInputStream(s);
int a=fis.available();
byte[] b=new byte[a];
fis.read(b);
System.out.print(new String(b));
}
}
/*
方法1:
import java.io.*;
class FileOutputStreamDemo
{
public static void main(String[] args)
{
FileOutputStream fos=null;
try
{
fos=new FileOutputStream("StreamDemo.txt");//×????÷???
????í·???±?????????????????????txt
fos.write("abcdefg".getBytes());//×????÷???è???ü?????
±????????
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
方法2,3:
import java.io.*;
class FileOutputStreamDemo
{
public static void main(String[] args)
{
FileOutputStream fos=null;
try
{
fos=new FileOutputStream("StreamDemo.txt");//字节流可以
处理非文本文件,这里只是举例为txt
fos.write("abcdefg".getBytes());//字节流不需要更新,直接
到文件
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}
*/
从上面可以看出,如果一个一个字节读取,当遇到中文的需要多个字节控制的字符时,就会出现乱码,而如果用available创建一个刚刚好大小的byte数组,又有可能内存溢出,所以,在实际操作中,优选方法二。
===========================================================================================================================
在上面的学习中已知,为了提高效率,对于字符流的输入输出提供了缓冲区对象,那么同理,对于字节流的输入输出,java同样提供了缓冲区对象,叫做BufferedInputStream和BufferedOutputStream
使用方法和字符流的缓冲区对象类似,不过字节流的缓冲区对象除了继承基类的方法外,不没有提供特有的方法,比如按行读取这样的方法是没有的
以文件的复制来演示一下字节流缓冲区的使用:
众所周知,字节流比字符流强大在于,字节流不仅可以处理文本文件,字节流几乎什么文件都能处理
我先在当前目录下放入QQ的快捷键
运行代码:
import java.io.*;
class BufferedStreamDemo
{
public static void main(String[] args) throws IOException
{
FileInputStream fis=new FileInputStream("腾讯QQ.lnk");
FileOutputStream fos=new FileOutputStream("My_QQ.lnk");
BufferedInputStream bufis=new BufferedInputStream(fis);
BufferedOutputStream bufos=new BufferedOutputStream(fos);
int a;
while((a=bufis.read())!=-1)
{
bufos.write(a);
}
bufis.close();
bufos.close();
}
}
结果成功生成一个快捷键,并且能运行:
===========================================================================================================================
标准的输入输出对象System.in和System.out
所谓的标准输入输出就是指键盘输入和屏幕输出,标准的输入输出属于字节流
下面介绍一下用法:
import java.io.*;
class SystemDemo
{
public static void main(String[] args) throws IOException
{
InputStream in=System.in;
int a;
while((a=in.read())!=-1)
{
System.out.println((char)a);
}
}
}
标准的输入输出可以不关闭流
如果如上代码那样单纯直接地使用标准的输入输出,只能实现一个一个字符地输入,那么想要实现一行一行地读数据以及根据特定的输入来结束输入就比较困难,又因为标准的
输入输出都是标准的字符,如果标准输入输出对象是字符流对象就方便多了,因此,java提供了字节流转换成字符流的装饰类InputStreamReader以及OutputStreamWriter,再通
过字符流对象的其他装饰类来实现更加方便的效果,比如对转换后的输入流对象再使用BufferedReader来实现按行获取数据
代码演示:
import java.io.*;
class TransDemo
{
public static void main(String[] args) throws IOException
{
InputStream in=System.in;
InputStreamReader isr=new InputStreamReader(in);//字节-->字符
BufferedReader bufr=new BufferedReader(isr);//使用缓冲区
OutputStream out=System.out;
OutputStreamWriter osw=new OutputStreamWriter(out);//字节-->字符
BufferedWriter bufw=new BufferedWriter(osw);
String str;
while(true)
{
str=bufr.readLine();//按行读取(BufferedReader带来的好处)
if(str.equals("over"))//输入结束的标志over
break;
else
{
bufw.write(str);
bufw.newLine();//插入换行符(BufferedWriter带来的好处,可跨平台使用)
bufw.flush();//转换成字符流后,要记得更新流
str=null;//清空接收用的字符串
}
}
bufw.close();
bufr.close();
}
}
/*效果:
输入:abc
输出:abc
输入:hhh
输出:hhh
输入:over
--结束--
*/
此代码可以简化:
import java.io.*;
class TransDemo
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
String str;
while(true)
{
str=bufr.readLine();
if(str.equals("over"))
break;
else
{
System.out.println(str);
str=null;
}
}
bufr.close();
}
}
/*效果(和原来一样):
输入:abc
输出:abc
输入:hhh
输出:hhh
输入:over
--结束--
*/
在实际操作中,要熟记上述例子中输入的用法,对于标准的输出System.out,由于它提供各种的输出方法,比如print、println等,所以实际操作中一般直接使用System.out.的形式进行标准的输出
补充:System.out的具体类型是PrintStream,是OutputStream的子类,print、println等方法就是PrintStream所特有的,所以,有时把输出流定义为PrintStream更方便
对于标准的输入输出可以用System类中setIn和setOut方法来修改
比如System.setOut(new PrintStream("out.txt"))可以把输出到屏幕的内容改为输出到out.txt文件中
import java.io.*;
class SystemSetDemo
{
public static void main(String[] args) throws IOException
{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
System.setOut(new PrintStream("out.txt"));
String str;
while((str=in.readLine())!=null)
{
if(str.equals("over"))
break;
System.out.println(str);
str=null;
}
}
}
/*
输入:
hh
llll
over
*/
结果: