黑马程序员--Java基础之IO流(1)
一、概述
1、IO流用来处理设备之间的数据传输。
流操作数据分为两种:字节流与字符流。字符流的对象中糅合了编码表。
流按流向又分为:输入流,输出流。一般都是成对出现。
2、IO流常用基类
(1)字节流的抽象基类:InputStream,OutputStream
(2)字符流的抽象基类:Reader,Writer
二、常用流
1、字符流FileWriter,对文件操作的字符流,写入流。
FileWriter fw = newFileWriter("demo.txt");//创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。
//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,该同名文件将会被覆盖。
//其实该步骤就是在明确数据要存放的目的地。
fw.write("afgsdhdf");//write方法,将字符串写入到流中。但是这样并没有将字符串真正的写进目的地,而是写进了流中去
//所以要刷新一下才能将缓冲区中的字符串写入到目的地。
fw.flush();//刷新之后还可以再写再刷。
fw.close();//关闭流资源,但是关闭之前会刷新一次内部的缓冲区中的数据,将数据存入到目的地中,关闭之后不能再写。
//close和flush的区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
对已有文件的数据续写:FileWriter的另一个构造函数:FileWriter(StringfileName ,boolean append);
FileWriter fw = newFileWriter("demo.txt",true);
fw.write("sidjfvsldo\r\nvskirfj");//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
2、字符流FileReader,对文件操作的字符流,读取流。
第一种读取方式:通过字符进行读取。
FileReader fr =new FileReader("demo.txt");//创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件是已经存在的,如果不存在,会发生异常:FileNotFoundException。接下来调用read()方法一次读一个字符,读取完毕会返回-1,然后利用循环。
int ch = 0;
while((ch =fr.read()) != -1){
System.out.println((char)ch);}
方式二:通过字符数组进行读取。
原理:定义一个数组缓冲区,将文件中的内容先读取到这个缓冲数组中,然后读出来,接着读取下一轮,下一轮取出的数覆盖原来的数组中的内容。
FileReader fr = new FileReader("demo.txt");
char[] buf = new char[1024];//定义一个字符数组,用于存储读到的字符,一般用1024,这样比较合理,文件内容多了不至于多次循环
int num =0;
while((num = fr.read(buf)) != -1){System.out.print(new String(buf,0,num));}//读几个,打印几个,并且转化成字符串。
fr.close();
3、字符流的缓冲区:BufferedWriter,BufferedRead
缓冲区的出现提高了对数据的读写效率,在流的基础上对流的功能进行了增强。
4、通过缓冲区复制文本文档
package cn.itheima.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyPractice {
public static void main(String[] args)
{ //定义一个写一个读的缓冲区
BufferedWriter bfw = null;
BufferedReader bfr = null;
try
{
FileWriter fw = new FileWriter("BufferDemo.txt");
bfw = newBufferedWriter(fw);
FileReader fr = new FileReader("Test.txt");
bfr = new BufferedReader(fr);
String line = null;
while((line=bfr.readLine())!= null)//一行一行读
{
bfw.write(line);//每读完一行就写一行
bfw.newLine();//写完一行就换行
bfw.flush();//刷新一下
}
}
catch(IOException ie)
{
System.out.println(ie.getMessage());
}
finally
{
if(bfw!= null)//不为空要关闭资源
{
try
{
bfw.close();
}
catch(IOException io)
{}
}
if(bfw!= null)
{
try
{
bfr.close();
}
catch(IOException io)
{}
}
}
}
}
5、装饰设计模式:当想要对已有的对象进行功能增强时,自定义一个类,将已有对象传入,基于已有的功能,提供加强功能,那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接受被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
装饰和继承的区别:要加强某个类的某个功能,可以使用继承的方法,但是如果想要加强方法的类有很多,用继承就会使该继承体系臃肿,可扩展性也差。所以,可以找到其参数的共同类型,通过多态的形式,可以提高扩展性。
6、字节流:FileInputstream,FileOutPutStream,BufferedInputStream,BufferedOutputStream。
练习拷贝图片文件:
package cn.itheima.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyPicture {
public static void main(String[] args){
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fos = new FileOutputStream("2.bmp");
fis = new FileInputStream("1.bmp");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf))!=-1){
fos.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
if(fis!=null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7、转换流:OutputStreamWriter,InputStreamReader。
这是字符流通向字节流的桥梁。
8、流操作的基本规律:
明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStreamWriter
操作的数据是否是纯文本。
是:字符流。
不是:字节流。
再通过设备来明确要使用哪个具体的对象:
源设备:
键盘System.in,硬盘 FileStream, 内存 ArrayStream.
目的设备:
控制台System.out,硬盘 FileStream,内存ArrayStream。
9、File
FIle用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数。
1、File常用方法
*创建。
booleancreateNewFile();//f.createNewFile()在指定位置创建该对象的文件,如果该文件已经存在,则不创建,返回false;这跟输出流不一样,输出流对象一建立就创建文件,而且文件已经存在,会覆盖。
static File createTempFile()//创建临时文件。
boolean mkdir();//创建一级文件夹目录
boolean mkdirs();//创建多级文件夹目录
*删除。
boolean delete();//f.delete()删除失败返回假
void deleteOnExit();//f.deleteOnExit()程序退出时删除文件,常在临时文件时使用。不管有没有异常都删除
*判断。
boolean canExecute();//判断文件是否可以执行
boolean exists();//判断该文件是否存在,未执行创建方法是不存在的
boolean isAbsolute();//也是可以判断出来的创建方法判断是否是绝对路径,这个文件对象不执行
boolean isDirectory();//判断是否为文件目录(文件夹),需要先判断是否存在
boolean isFile();//判断是否为文件,需要先判断是否存在
boolean isHidden;// 判断是否为隐藏文件
*获取信息。
getName();//获取文件对象名字
getPath();//获取文件对象的路径,对象里封装什么路径就得到什么路径,
getParent();//该方法返回的是绝对路径中的父目录,如果获取的是相对路径,返回null,如果相对路径中有上一层目录那么该目录就是返回结果。也就是说,该方法返回的是文件对象中封装的除去本文件的上层目录。
getAbsolutePath();//获取文件的绝对路径,不管对象中有没有封装都获取
long lastModified();//获取上次修改时间
long length(); //获取文件大小。
注意:创建文件对象之后,文件并没有存在,必须执行创建方法,文件才会存在。
2、 File对象功能--文件列表
static File[] listRoots();//File.listRoots返回的是文件数组File[],返回的是电脑中有效盘符。
String[] list();//f.list()//列出当前目录下所有文件,包含隐藏文件。返回的是String[]。
3、 File对象功能--文件列表2
String[] list(FilenameFilter filter);//用于过考虑给定文件夹下的符合条件的文件,FilenameFilter是一个文件过滤接口
File dir = newFile("d:\\java1223\\day18");
String[] arr =dir.list(new FilenameFilter(){//这是一个匿名内部类,实现FilenameFilter接口,
public boolean accept(Filedir,String name){
returnname.endsWith(".bmp");//这个条件,将只能返回d:\\java1223\\day18目录下含有.bmp的文件
}
});
File[] listFiles();//返回的是对象列表,实际开发中,使用这一个。返回指定目录下的文件对象。但是只能拿当前目录下的东西
4、列出目录下所有内容--递归
也就是函数自身调用自身,这种表现形式,或者叫编程手法,成为递归。
递归注意事项:
*限定条件
*要注意递归的次数,尽量避免内存溢出。
递归举例:
//列出指定文件夹下的所有文件,包括子文件夹下的所有文件
public voidshowDir(File dir){
System.out.println(dir);
File[] files = dir.listFiles();
for(int x = 0;x<files.length;x++){
if(files[x].isDirectory())
showDir(files[x]);
else
System.out.println(files[x]);
}
}
******************************************
//sum =1+2+3+4.....+(sum-1)+sum;
public intgetSum(int n){
if(n==1)
return 1;
return n+getSum(n-1);
}
******************************
//十进制转为二进制的代码简化
public voidtoBin(int num){
if(num>0){
toBin(num/2);
System.out.println(num%2);
}
}
*********************************************
//列出目录下所有内容--带层次
public String getLevel(int level){
StringBuilder sb = new StringBuilder();
for(int x = 0; x<level,x++){
sb.append(" ");
}
sb.append("|--");
return sb.toString();
}
//列出指定文件夹下的所有文件,包括子文件夹下的所有文件
public voidshowDir(File dir,int level){
System.out.println(getLevel(level)+dir.getName());
level++;
File[] files = dir.listFiles();
for(int x = 0;x<files.length;x++){
if(files[x].isDirectory())
showDir(files[x],level);
else
System.out.println(getLevel(level)+files[x]);
}
}
删除带内容的目录
删除原理:在windows中,删除目录从里面往外删除的。
既然是从里往外删除,就需要用到递归。
public void removeDir(File dir){
File[] files = dir.listFiles();
for(int x = 0;x<fils.length;x++){
if(files[x].isDirectory()){removeDir(files[x]);}
else
System.out.println(files[x].toString()+":file:"+files[x].delete());
}
System.out.println(dir+":dir:"+dir.delete());
}