Java 中的 IO流 处理
一、输入流&输出流
输入流和输出流相对于内存设备而言。
将外设中的数据读取到内存中:输入
将内存中的数据写入到外设中:输出
二、字节流&字符流
字符流的由来: 其实就是:字节流读取文字字节数据后,不直接操作而是先查找指定的编码表,获取对应的文字。 再对这个文字进行操作。简单说:字节流 + 编码表
三、IO常用的基类
字节流的抽象基类:InputStream/OutputStream 字符流的抽象基类:Reader/Write
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。 如:InputStream 的子类 FileInputStream,如:Reader 的子类 FileReader
四、FileWrite
public static void main(String[] args) throws IOException{
//创建一个可以往文件中写入字符数据的字符输出流对象
//既然是往一个文件中写入文字数据,那么再创建对象时,就必须明确该文件(用于存储数据的目的地)
//如果文件不存在,则会自动创建。如果文件存在,则会被覆盖
FileWrite fw = new FileWrite("demo.txt");
//调用write对象中的write(string)方法,写入数据。
//其实数据写到临时存储缓冲区中
fw.write("abcde");
//进行刷新,将数据直接写到目的地中
fw.flusg();
//关闭流,关闭资源,在关闭前会先调用flush刷新缓冲区中的数据到目的地。
fw.close();
}
换行和续写
//换行
fw.write("abcd\r\nhahah");
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
//续写
//如果构造函数中加入true,可以实现对文件进行续写。
FileWrite fw = new FileWrite("demo.txt",true);
异常处理
public static void main(String[] args){
FileWrite fw = null;
try{
fw = new FileWrite("demo.txt",true);
fw.write("abcde"+LINE_SEPARATOR+"hahaha");
}catch(IOException e){
System.out.println(e.toString());
}finally{
try{
fw.close();
}catch(IOException e){
throw new RuntimeException("关闭失败");
}
}
}
五、FileReader
public static void main(String[] args) throws IOException{
//创建读取字符数据的流对象
//在创建读取流对象是,必须要明确被读取的文件。一定要确定该文件是存在的。
FileReader fr = new FileReader("demo.txt");
//用reader中的read方法读取字符
int ch = fr.read();
System.out.println(ch);
while(ch=fr.read()!=-1){
System.out.println(ch);
}
fr.close();
}
//使用read(char[])读取文本文件数据
//先创建字符数组
char[] buf = new char[3];
//将读取到的字符存储到数组中
int num = fr.read(buf);
System.out.println(new String(buf));
int len = 0;
while(len=fr.read(buf)!=-1){
System.out.println(new String(buf,0,len));
}
六、示例
1、将C盘的一个文本文件复制到d盘
第一种方式
public static void main(String[] args)throws IOException{
//1、读取一个已有的文本文件,使用字符读取流和文件相关联
FileReader fr = new FileReader("c:/io.txt");
//2、创建一个目的,用于存储读到的数据
FileWrite fw = new FileWrite("d:/io.txt");
//3、频繁的读写操作
int ch = 0;
while((ch = fr.read())!=-1){
fw.write(ch);
}
//4、关闭流资源
fw.close();
fr.close();
}
第二种方式
public static void main(String[] args){
FileReader fr = null;
FileWrite fw = null;
try{
fr = new FileReader("c:/io.txt");
fw = new FileWrite("d:/io.txt");
//创建一个临时容器,用于缓存读到的字符
char[] buf = new char[1024]
//定义一个变量记录读取到的字符数(其实就是往数组里装的字符个数)
int len = 0;
while(len=fr.read(buf) != -1){
fw.write(buf,0,len);
}
}catch(Exception e){
}finally{
if(fw!=null){
try{fw.close()}catch(){}
}
if(fr!=null){
try{fr.close()}catch(){}
}
}
}
七、字符流-缓冲区
缓冲区的出现提高了对数据的读写效率。对应类:
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强
public static void main(String[] args) throws IOException{
FileWrite fw = new FileWrite("buf.txt");
//为了提高写入的效率。使用字符流的缓冲区
//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
BufferedWriter bufw = new BufferedWriter(fw);
//使用缓冲区的写入方法将数据先写入到缓冲区中
bufw.write("abcdef");
//使用缓冲区的刷新方法将数据刷到目的地
bufw.flush();
//关闭缓冲区(其实关闭的就是被缓冲的流对象)
bufw.close();
//fw.write("abcdef");
//fw.close();
}
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
String line1 = bufr.readLine();
System.out.println(line1);
String line2 = bufr.readLine();
System.out.println(line2);
//或
String line = null;
while(line=bufr.readLine() !=null){
System.out.println(line);
}
bufr.close();
//char[] buf = new char[1024];
//int len = 0;
//while(len=fr.read(buf) != -1){
//}
}
八、字符流-缓冲区-装饰设计模式
对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。
public class PersonDemo{
public static void main(String[] args){
Person p = new Person();
p.chifan();
NewPerson p1 = new NewPerson();
p1.chifan();
NewPerson2 p2 = new NewPerson2();
p2.chifan();
}
}
class Person{
void chifan(){
System.out.println("吃饭");
}
}
//这个类的出现是为了增强Person而出现的。
class NewPerson{
private Person p;
NewPerson(NewPerson p){
this.p = p;
}
public void chifan(){
System.out.println("开胃菜");
p.chifan();
System.out.println("甜点");
}
}
//继承和覆盖
class NewPerson2 extends Person{
public void chifan(){
System.out.println("开胃菜");
super.chifan();
System.out.println("甜点");
}
}
装饰和继承都能实现一样的特点:进行功能的扩展增强,有什么区别呢?
首先有一个继承体系:
Writer
|--TextWriter:用于操作文本
|--BUfferTextWriter:加入了缓冲技术的操作文本的对象
|--MediaWriter:用于操作媒体
|--BufferMediaWriter:加入了缓冲技术的操作媒体的对象
但是这样做好像并不理想,如果这个体系进行功能扩展,有多个流对象。
那么这个流要提高效率,是不是也要产生子类呢?
是,这时就会发现只为提高功能,进行的继承,导致继承体系越来越臃肿,不够灵活。
重新思考这个问题?
既然加入的都是同一种技术-缓冲。
前一种是让缓冲和具体的对象相结合。
可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
class BufferWriter TextWriter{
BufferWriter(Writer w){}
}
Writer
|--TextWriter:用于操作文本
|--MediaWriter:用于操作媒体
|--BufferWriter:加入了缓冲技术的操作写的对象
装饰要比继承灵活,特点:装饰类和别装饰类都必须所属同一个接口或者父类。
九、字符流-缓冲区-LineNumberReader
跟踪行号的缓冲字符输入流
LineNumberReader extends BufferedReader
class LineNumberReaderDemo{
public static void main(String[] args){
FileRead fr = new FileRead("test.txt");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100)
while(line=lnr.readLine()!=null){
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
fr.close();
}
}
十、字节流
基本操作于字符流类相同,但它不仅可以操作字符,还可以操作其他媒体文件
public static void demo_write() throws IOException{
//1、创建字节流输出流对象。用于操作文件
FileOutputStream fos = new FileOutputStream("bytedemo.txt");
//2、写数据。直接写入目的地中。不用flush
fos.write("abcde".getBytes());
//3、关闭资源
fos.close();
}
public static void demo_read() throws IOException{
//1、创建一个读取流对象。和指定文件关联
FileInputStream fis = new FileInputStream("bytedemo.txt");
/*byte[] buf = new byte[1024];
int len = 0;
while(len=fis.read(buf) != -1){
System.out.println(new String(buf,0,len));
}*/
System.out.println(fis.available());//5
byte[] buf = new byte[fis.available()]
fis.read(buf);
System.out.println(new String(buf));
}
示例:复制 mp3
class CopyMp3Test{
public static void main(String[] args) throws IOException{
copy_1();//第一种方式
copy_2();//第二种方式
copy_3();//第三种方式
copy_4();//第四种方式
}
public static void copy_1() throws IOException{
FileInputStream fis = new FileInputStream("test.mp3");
FileOutputStream fos = new FileOutputStream("test1.mp3");
byte[] buf = new byte[1024];
int len = 0;
while(len=fis.read(buf) != -1){
fos.write(buf,0,len);
}
fis.close();
fos.close();
}
public static void copy_2() throws IOException{
FileInputStream fis = new FileInputStream("test.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("test1.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while(ch=bufis.read() != -1){
bufos.write(ch);
}
bufis.close();
bufos.close();
}
//不建议
public static void copy_3() throws IOException{
FileInputStream fis = new FileInputStream("test.mp3");
FileOutputStream fos = new FileOutputStream("test1.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fis.close();
fos.close();
}
//千万不要用,效率没有
public static void copy_3() throws IOException{
FileInputStream fis = new FileInputStream("test.mp3");
FileOutputStream fos = new FileOutputStream("test1.mp3");
int ch = 0;
while(ch = fis.read() != -1){
fos.write(ch);
}
fis.close();
fos.close();
}
}
十一、标准输入、输出
System.in;//标准输入(键盘) System.out;//标准输出
public static void main(String[] args) throws IOException{
InputStream in = System.in;//标准输入(键盘)
int ch = in.read();//阻塞方法
System.out.println(ch);
}
十二、转换流
//InputStreamReader是字节流通向字符流的桥梁,解码 //OutputStreamWrite是字符流通向的字节流桥梁,编码
public static void main(String[] args) throws IOException{
//字节流
InputStream in = System.in;
//字符流
//BufferedReader bufr = new BufferedReader(in);//字符流不能操作字节流对象,因此需要转换流
//InputStreamReader是字节流通向字符流的桥梁
//OutputStreamWrite是字符流通向的字节流桥梁
//将字节转换为字符的桥梁,转换流
InputStreamReader isr = new InputStreamReader(in);
//字符流
BufferedReader bufr = new BufferedReader(isr);
String line = null;
while(line=bufr.readLine()!=null){
if("over".equals(line)){
break;
}
System.out.println(line.toUpperCase());
}
}
流的操作规律
之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。
想要知道开发时候用到哪个对象。只要通过四个明确即可。
1、明确源和目的
源:InputStream Read
目的:OutputStream Write
2、明确数据是否是纯文本数据
源:是纯文本:Reader
:否:InputStream
目的:是纯文本:Write
:否:OutputStream
到这里,就可以明确需求中具体要使用哪个体系。
3、明确集体的设备
源设备:
硬盘:File
键盘:System.in
内存:数组
网络:Socket流
目的设备:
硬盘:File
键盘:System.out
内存:数组
网络:Socket流
4、是否需要其他额外功能
是否需要高效(缓冲区)
是:就加上buffer
十三、转换流的编码解码
OutputStreamWrite 是字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定 否则将接受平台默认的字符集。
public static void writeText() throws IOException{
//这两句代码的功能是等同的。
//FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象。可以方便操作文本文件。
//简单说:操作文件的字节流+本机默认的编码表。这是按照默认码表来操作文件的便捷类
//如果操作文本文件需要明确具体的编码。FileWrite就不行了。必须要用转换流
OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("gbk_1.txt"));
FileWriter fw = new FileWriter("gbk_1.txt")
osw.write("你好");
osw.close();
//指定编码
OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("utf8.txt","UTF-8"));
}
public static void readText() throws IOException{
FileReader fr = new FileReader("gbk_1.txt");
char[] buf = new char[10];
int len = fr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
fr.close();
FileReader fr = new FileReader("utf8.txt");//读utf-8的会乱码
//修改如下:
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"UTF-8");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
fr.close();
}
十四、File 对象-构造函数&字段
- 用来将文件或者文件夹封装成对象
- 方便对文件与文件夹的属性信息进行操作
- File对象可以作为参数传递给流的构造函数
- 了解File类中的常用方法
public static void constructorDemo(){
//可以将一个已存在的,或者不存在的文件或者目录封装成File对象
File f1 = new File("c:\\a.txt");
File f2 = new File("c:\\","a.txt");
File f = new File("c:\\");
File f3 = new File(f,"a.txt");
File f4 = new File("c:"+System.getProperty("file.separator")+"abc\\a.txt");//分隔符
File f5 = new File("c:"+File.separator+"abc"+File.separator+"a.txt");//分隔符
System.out.println(f4);
System.out.println(f4);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论