黑马程序员——IO流
2015-08-27 16:03
501 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
IO流
一、File类
1、File类:文件和目录路径名的抽象表现形式
2、特点:1)用来将文件或文件夹封装成对象
2)方便于对文件与文件夹的属性信息进行操作
3)File类的实例是不可变的;也就是说,一旦创建,File 对象表示的抽象路径名将永不改变
4)File对象可以作为参数传递给流的构造函数
3、构造方法:
1)File(String pathname):根据指定的路径创建File对象。
2)File(String parent, String child):根据指定的父文件夹和子文件或者文件夹创建File对象
3)File(File parent, String child):根据指定的父文件夹对象和子文件或者文件夹创建File对象
4、功能:
1)创建文件:public boolean createNewFile():如果指定的文件不存在,就创建。如果存在,就不创建。
2)创建文件夹:a、public boolean mkdir():创建指定的目录,如果存在,就不创建。
b、public boolean mkdirs():创建指定的目录,如果存储,就不创建。这个时候,如果父目录不存在,它也会自动创建。
3)删除功能:public boolean delete():既可以删除文件,也可以删除文件夹。取决于你使用的对象。
注意:a、如果你删除的目录下还有内容,那么,必须先把所有内容删除完毕后,在删除目录。
b、java语言的删除不走回收站
4)判断功能:a、boolean exists():判断file对象是否存在
b、boolean isFile():判断file对象是否是文件
c、boolean isDirectory():判断file对象是否是文件夹
d、boolean isAbsolute():判断file对象是否是绝对路径
e、boolean canRead():判断file对象是否可读
f、 boolean canWrite():判断file对象是否可写
g、boolean isHidden():判断file对象是否隐藏
5)获取功能:a、String getAbsolutePath():绝对路径【绝对路径:以盘符开始的路径;相对路径:不以盘符开始的路径】
b、String getPath():相对路径
c、String getName():文件名称
d、long length():文件大小,单位是字节
e、long lastModified():上次修改时间的毫秒值。
6)重要获取功能:a、public static File[] listRoots():列出可用的系统文件根目录
b、public String[] list():返回的是指定目录下所有文件或者文件夹的名称数组
d、public File[] listFiles():返回的是指定目录下所有文件或者文件夹对象数组
5、功能练习:
二. 递归:
1、归定义:方法定义调用方法本身的现象。
如:
2、注意事项:
a、递归一定要有出口。否则就会死递归。
b、递归的次数不要过多。否则内存溢出。
c、做递归的题思路
(1)找递归出口;(2)找递归规律
3、举例:用递归求n!
4、小案例1:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问:第二十个月的兔子对数为多少?
5、小案例2:删除指定的目录。(目录是带有目录或者文件的)
三.
IO流的结构图:
1.字符流
(1)字符输出流
a.构造方法:
①.FileWriter(File
file) 根据给定的 File 对象构造一个 FileWriter 对象。
②.FileWriter(File
file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
【在file对象的文件中追加写入数据,append是true的是后在指定的文件后面追加内容,false则是覆盖原来的内容】
③.FileWriter(String
fileName) 根据给定的文件名构造一个 FileWriter 对象。【名字是一个字符串的路径下文件,但是路径必须存在】
④.FileWriter(String
fileName, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
【在字符串路径的文件中追加写入数据,append是true的是后在
⑤. FileWriter为什么没有无参构造方法?
b.完成一个简单在指定文件中写入数据的操作的步骤:因为写数据的时候,一定要明确写道哪里去
①.创建字符输出流FileWriter对象(并且传入一个你要写入的位置)
FileWriter
fw = new FileWriter("a.txt");
②.调用写数据的功能
fw.write("hello,io,我来了。祝你元旦快乐。");
③.刷新缓冲区
fw.flush();
④.释放资源(jvm不会自动回收流的资源,除非你手动标记该流已成为垃圾)
fw.close();
d.flush()和close()的区别?
flush():只刷新缓冲区,流对象还可以继续使用。
close():先刷新缓冲区,在关闭流对象。流对象不可以继续被使用
(2)字符输入流
①、构造方法总结:
a、FileReader(File file) :在给定从中读取数据的 File[File必须一个明确的文件] 的情况下创建一个新 FileReader
b、FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新FileReder
②、普通字符输入流读取数据步骤:
a、创建字符输入流对象(并且明确你要从哪个文件读数据)
FileReader fr = new FileReader("FileWriterDemo.java");
b、调用读取数据功能,并显示
int ch = 0;
while((ch=fr.read())!=-1){
//如果数据没有了,读取的时候,将返回-1
//如果有数据则返回int类型(字符的int值),并自动移动到下一个数据位置等待读取
System.out.print((char)
ch); }
c、释放资源
fr.close();
③、小案例:把当前项目下的FileWriterDemo.java文件拷贝到,G盘下并改名
(3)高效字符缓冲流:BufferedReader,BufferedWriter
BufferedReader,BufferedWriter是更高级的流,二者的源和目的地必须是字符输入流和字符输出流.如果把字符输入流做为BufferedReader流的源,把字符输出流作为BufferedWriter流的目的地,那么BufferedReader,BufferedWriter将有更强的读、写能力。
int read(char[]
cbuf, int off, int len) //将字符读入数组的某一部分
String readLine()//读取一个文本行
void write(int c)//写入单个字符
void write(String s, int off, int len)//写入字符串的某一部分。
void write(char[]
cbuf, int off, int len)//写入字符数组的某一部分
void newLine()//写入一个行分隔符
d、小案例:
运行结果如下:
2.字节流
(1)字节输出流FileOutputStream
a、创建字节输出流对象
b、调用写数据的方式
c、释放资源
(2)字节输入流FileInputStream
a、创建字节输入流对象
b、调用读取数据的方式,并显示
c、释放资源
(3)示例:
(3)注意:
二进制流数据:图片,视频,音频等...;由于二进制的合并为字符,在我们使用的编码表中没有对应的数据,那么,将来在写回去的时候,就会有数据的丢失,所以,二进制流数据只能使用字节流进行操作。
(4)带缓冲区的输入出输出流
▪BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8M,能够减少访问磁盘的次数,提高文件读取性能;
▪BufferedOutputStream是带缓冲区的输出流,能够提高文件的写入效率。
▪ BufferedInputStream与BufferedOutputStream分别是FilterInputStream类和FilterOutputStream类的子类,实现了装饰设计模式。
(5)小案例:
1:标准的输入输出流
a、public static final InputStream in :“标准”输入流。(System.in)--InputStream(字节输出流)
b、public static final PrintStream out:”标准“输出流 。(System.out)--PrintStream (字节输入流)
2.字节输入流转成字符输入流
a、方法:
字节流通向字符流的桥梁
InputStream ----> InputStreamReader -----> BufferedReader
b、小练习,想自己封装一个键盘录入数据,并存储到文本文件中:
理解:字符流读写数据的底层其实还是调用的字节流,只不过把字节流按照编码顺序排成了字符,从而读出来的是字符所以说既然底层调用的是字节流,那么字节流通过InputStreamReader就可以转成字符流,(其实就是把字节加入了一些编码顺序)所以说 万流之根源乃字节流,根源演变成了其他流,所以说 字节流可以转字符流,而反过来就不可以。
3.字节输出流转字符输出流:
a、方法:OutputStream -- > OutputStreamWriter --> BufferedWriter
b、小案例,需求:把文本文件的数据通过标准输出流的方式显示在控制台。
4:编码问题
流的编码问题:字符流=字节流+编码表
如果你想在IO流中使用指定编码下数据,用转换流。
编码问题其实很简单,永远使用同一种编码即可。
FileReader: FileInputStream+GBK
FileWriter:FileOutputStream+GBK
//写入的时候用的字符流的UTF-8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8"); // 指定编码UTF-8
osw.write("中国");
osw.close();
//那么读出的时候如果用字符流 也应该用 UTF-8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"), "UTF-8");
char[] chs = new char[1024];
int len = isr.read(chs);
String str = new String(chs, 0, len);
System.out.println(str);
如下面代码:
a、PrintStream:字节打印流
b、PrintWriter:字符打印流
1、打印流特点:
a、可以写入任意类型的数据。
b、可以自动刷新。必须先启动,并且是使用println,printf及format方法才有效。
c、可以直接对文件进行写入。
d、注意:打印流只有写数据的,没有读取数据的。
2、构造方法:
a、PrintWriter(String fileName) :使用指定文件创建不具有自动行刷新的新 PrintWriter
b、PrintWriter(OutputStream out):根据现有的OutputStream创建不带自动行刷新的新
PrintWriter
c、
d、PrintWriter(Writer out):创建不带自动行刷新的新PrintWriter。
e、PrintWriter(Writerout,boolean
autoFlush):创建新PrintWriter
3.PrintWriter的 print() 可以写任意类型数据:
PrintWriter 自动刷新功能(必须开启了自动刷新 然后还得是调用println,printf及format方法,才能自动刷新)。
PrintWriter pw = new PrintWriter(new FileWriter("d.txt"), true);PrintWriter的println()能够自动换行。
4.案例:用打印流复制文本文件
a、读取文件的时候,如果是文件夹,会出现:FileNotFoundException: test (拒绝访问。)
b、写文件的时候,如果没有后缀名,它也会自动生成一个文件,而这个种类型的文件通过记事本类型的软件是可以打开的。
6.序列化流
(1)序列化:把对象按照流一样的方式传输或者存储。
ObjectOutputStream
将 Java 对象的基本数据类型和图形写入 OutputStream。
void
writeObject(Object obj)
(2)反序列化:把网络中的流数据或者文件中的流数据还原成对象。
ObjectInputStream:ObjectInputStream
对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化
Object
readObject()
(3)注意问题:
a、如果类的对象想被序列化流操作,请实现序列化接口。
b、看到了类实现了Serializable接口,如果想解决黄色警告线,请点击鼠标。
c、java.io.NotSerializableException
没有实现序列化接口异常
d、类通过实现 java.io.Serializable 接口以启用其序列化功能。该接口被称为序列化接口,是一个标记接口。
没有任何功能需要实现。类一旦实现了该接口,那么,该类的对象就可以被序列化流进行操作。
(4)为什么要写一个序列号?
对象在被序列化流进行操作的时候,会记录一个序列号值。而这个序列号值跟对象的成员相关。
最开始的时候:
Person类的对象 p的id是100
write写到文件中:id -- 100
read读取数据: id -- 100
后来,我们把write方法给注释了,然后修改了Person类,这个类被修改后,成员就发生了改变。
那么,id也就相应的发生改变
Person类的对象id改为了200
那么,直接读取,文件中的id是100,而我们有的id是200,他们不匹配,所以报错了。
那么,在很多情况下,我们的类中的成员可能会发生改变,不可能说每次改变,我都需要把数据重写入一次。
怎么解决这种情况呢?
先写,在读取。没有问题。
先写,后修改了类,在读取,有问题。
很简单,我们想了这样的一个问题:他们之所以出错,就是因为那个id值不匹配,如果我们直接在类中给一个固定的id值。
那么,无论你怎么修改类的成员,这个id值是不变化的,那这样的话,以后的id永远都是一样的。就不会再因为简单的修改出问题。
问题呢?怎么定义这个id值呢?
private static final long serialVersionUID = 333558257579870816L;
(5)
示例:
[java] view
plaincopy
/*
SequenceInputStream
合并流
需求:将三个文本文件中的数据合并到一个文本文件中
思路:1、创建一个Vector集合,将三个文本文件字节流添加到集合中
2、创建Enumeration对象,创建SequnceInputStream对象关联Enumeration
3、输出流关联新文本文件
4、反复读写操作
*/
import java.util.*;
import java.io.*;
class SequenceInputStreamDemo
{
public static void main(String[] args)throws IOException
{
Vector<InputStream> ve=new Vector<InputStream>();//创建vector集合,并添加相关流对象
ve.add(new FileInputStream("1.txt"));
ve.add(new FileInputStream("2.txt"));
ve.add(new FileInputStream("3.txt"));
Enumeration<InputStream> en=ve.elements();//创建枚举对象
SequenceInputStream sis=new SequenceInputStream(en);//合并流
FileOutputStream fos=new FileOutputStream("4.txt");//关联写入文件
//反复读写操作
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
//关流
fos.close();
sis.close();
}
}
练习:
[java] view
plaincopy
/*
切割文件
需求:将一个mp3文件按1M大小切割成几部分
思路:1、使用文件字节流关联mp3文件
2、定义一个容器存储1M大小的数据,当存储满时,写入一个新文件中
*/
import java.util.*;
import java.io.*;
class SplitFile
{
public static void main(String[] args) throws IOException
{
//指定要切割的文件
File file=new File("C:\\Users\\asus\\Desktop\\苏芮 - 一样的月光.mp3");
//将指定文件进行切割
splitFile(file);
//指定要合并到的文件
File file1=new File("E:\\Java Study\\Practice\\day20\\splitFile\\一样的月光.mp3");
//将部分文件进行合并指定文件中
merge(file1);
}
//接收一个文件,将其按1M大小进行切割
public static void splitFile(File file)throws IOException
{
//关联要切割的文件
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=null;
//定义1M大小存储容器
byte[] buf=new byte[1024*1024];
int len=0,x=0;
while ((len=bis.read(buf))!=-1)
{
//每满1M就写入一个新文件中
bos=new BufferedOutputStream(new FileOutputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+(++x)+".part"));
bos.write(buf,0,len);
bos.close();//没写完一个文件要记得关流
}
//关流
bis.close();
}
//将部分文件合并为一个可执行文件
public static void merge(File file)throws IOException
{
//定义一个集合存储这些部分文件关联路径数据
ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
for (int x=1;x<=6 ; x++)
{
al.add(new FileInputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+x+".part"));
}
//因为Enumeration是Vector特有的迭代方法,所以这里创建一个Enumeration类型的匿名内部类
final ListIterator<FileInputStream> it=al.listIterator();
Enumeration<FileInputStream> en=new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
//关联枚举对象
SequenceInputStream sis=new SequenceInputStream(en);
//将合并的文件数据写入指定文件中
FileOutputStream fos=new FileOutputStream(file);
//定义临时存储数据的数组
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);//写数据
}
//关流
fos.close();
sis.close();
}
}
IO流
一、File类
1、File类:文件和目录路径名的抽象表现形式
2、特点:1)用来将文件或文件夹封装成对象
2)方便于对文件与文件夹的属性信息进行操作
3)File类的实例是不可变的;也就是说,一旦创建,File 对象表示的抽象路径名将永不改变
4)File对象可以作为参数传递给流的构造函数
3、构造方法:
1)File(String pathname):根据指定的路径创建File对象。
2)File(String parent, String child):根据指定的父文件夹和子文件或者文件夹创建File对象
3)File(File parent, String child):根据指定的父文件夹对象和子文件或者文件夹创建File对象
4、功能:
1)创建文件:public boolean createNewFile():如果指定的文件不存在,就创建。如果存在,就不创建。
2)创建文件夹:a、public boolean mkdir():创建指定的目录,如果存在,就不创建。
b、public boolean mkdirs():创建指定的目录,如果存储,就不创建。这个时候,如果父目录不存在,它也会自动创建。
3)删除功能:public boolean delete():既可以删除文件,也可以删除文件夹。取决于你使用的对象。
注意:a、如果你删除的目录下还有内容,那么,必须先把所有内容删除完毕后,在删除目录。
b、java语言的删除不走回收站
4)判断功能:a、boolean exists():判断file对象是否存在
b、boolean isFile():判断file对象是否是文件
c、boolean isDirectory():判断file对象是否是文件夹
d、boolean isAbsolute():判断file对象是否是绝对路径
e、boolean canRead():判断file对象是否可读
f、 boolean canWrite():判断file对象是否可写
g、boolean isHidden():判断file对象是否隐藏
5)获取功能:a、String getAbsolutePath():绝对路径【绝对路径:以盘符开始的路径;相对路径:不以盘符开始的路径】
b、String getPath():相对路径
c、String getName():文件名称
d、long length():文件大小,单位是字节
e、long lastModified():上次修改时间的毫秒值。
6)重要获取功能:a、public static File[] listRoots():列出可用的系统文件根目录
b、public String[] list():返回的是指定目录下所有文件或者文件夹的名称数组
d、public File[] listFiles():返回的是指定目录下所有文件或者文件夹对象数组
5、功能练习:
package Test1; import java.io.File; import java.io.IOException; public class FileTest { public static void main(String[] args) throws IOException{ // 创建文件 File file1 = new File("d:\\a.txt"); System.out.println("createNewFile:" + file1.createNewFile()); // 创建文件 忘了写路径名称了,以当前项目路径所在路径为父目录 File file2 = new File("a.txt"); System.out.println("createNewFile:" + file2.createNewFile()); // 创建目录 File file3 = new File("aaa"); System.out.println("mkdir:" + file3.mkdir()); // 如果目录过多,这样做就太麻烦。肿么办呢? File file4 = new File("bbb\\ccc"); System.out.println("mkdirs:" + file4.mkdirs()); // 创建文件 File file5 = new File("b.txt"); System.out.println("mkdir:" + file5.mkdir()); System.out.println("-----------------"); // 判断功能 System.out.println("exists:" + file1.exists());// true System.out.println("isFile:" + file1.isFile());// true System.out.println("isDirectory:" + file1.isDirectory());// false System.out.println("isAbsolute:" + file1.isAbsolute());// true System.out.println("canRead:" + file1.canRead());// true System.out.println("canWrite:" + file1.canWrite());// true System.out.println("isH 24000 idden:" + file1.isHidden());// false System.out.println("-----------------"); //获取功能: File file6 = new File("bbb\\ccc\\a.txt"); System.out.println("getAbsolutePath:" + file6.getAbsolutePath()); System.out.println("getPath:" + file6.getPath()); System.out.println("getName:" + file6.getName()); System.out.println("length:" + file6.length()); System.out.println("lastModified:" + file6.lastModified()); System.out.println("-----------------"); //删除功能 File file7 = new File("a.txt"); System.out.println("delete:" + file7.delete()); // 需求:我要删除aaa File file8 = new File("aaa"); System.out.println("delete:" + file8.delete()); // 需求:我要删除bbb File file9 = new File("bbb"); System.out.println("delete:" + file9.delete()); } }运行结果:
二. 递归:
1、归定义:方法定义调用方法本身的现象。
如:
public void show(){ show(); }
2、注意事项:
a、递归一定要有出口。否则就会死递归。
b、递归的次数不要过多。否则内存溢出。
c、做递归的题思路
(1)找递归出口;(2)找递归规律
3、举例:用递归求n!
package Test1; public class DiGuiTest { public static void main(String[] args) { System.out.println(DiGuiTest.jc(5)); } public static int jc(int n) { if (n == 1) { // 出口 return 1; } else { // 规律 return n * jc(n - 1); } } }
4、小案例1:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问:第二十个月的兔子对数为多少?
package Test1; public class DiGuiTest { public static void main(String[] args) { System.out.println(DiGuiTest.sum(20)); } private static int sum(int i) { if(i ==1){ return 1; }else if(i==2){ return 1; }else if(i==3){ return 2; }else{ return sum(i-3) +sum(i-2)+ sum(i-1); } } }
5、小案例2:删除指定的目录。(目录是带有目录或者文件的)
package Test1; import java.io.File; public class DiGuiTest { public static void main(String[] args) { File f = new File("G:\\CopyFile"); DiGuiTest.deleteFiles(f); } private static void deleteFiles(File file) { // 第1步封装文件夹 File[] fileArray = file.listFiles();// 1,test_deleteFiles; // 2.1,aaa_deleteFiles; // 2.2,bbb_deleteFiles; if (fileArray != null) { // 如果封装的文件夹不为空,那么就进行遍历,获得每一个文件或文件夹 for (File f : fileArray) { if (f.isDirectory()) { // 如果被封装文件夹的子文件还是个文件夹,那么继续封装起来进行判断 deleteFiles(f); } else { // 如果被封装起来的子文件夹正好就是个文件,那么直接删除 System.out.println(f.getName() + "***" + f.delete()); } } } System.out.println(file.getName() + "***" + file.delete()); // 如果文件夹为空,直接删除. 当if语句执行完时,就表示每次封装的目录下的文件被删除完毕。 } }运行结果:
三.
IO流的结构图:
1.字符流
(1)字符输出流
a.构造方法:
①.FileWriter(File
file) 根据给定的 File 对象构造一个 FileWriter 对象。
②.FileWriter(File
file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
【在file对象的文件中追加写入数据,append是true的是后在指定的文件后面追加内容,false则是覆盖原来的内容】
③.FileWriter(String
fileName) 根据给定的文件名构造一个 FileWriter 对象。【名字是一个字符串的路径下文件,但是路径必须存在】
④.FileWriter(String
fileName, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。
【在字符串路径的文件中追加写入数据,append是true的是后在
⑤. FileWriter为什么没有无参构造方法?
b.完成一个简单在指定文件中写入数据的操作的步骤:因为写数据的时候,一定要明确写道哪里去
①.创建字符输出流FileWriter对象(并且传入一个你要写入的位置)
FileWriter
fw = new FileWriter("a.txt");
②.调用写数据的功能
fw.write("hello,io,我来了。祝你元旦快乐。");
③.刷新缓冲区
fw.flush();
④.释放资源(jvm不会自动回收流的资源,除非你手动标记该流已成为垃圾)
fw.close();
d.flush()和close()的区别?
flush():只刷新缓冲区,流对象还可以继续使用。
close():先刷新缓冲区,在关闭流对象。流对象不可以继续被使用
(2)字符输入流
①、构造方法总结:
a、FileReader(File file) :在给定从中读取数据的 File[File必须一个明确的文件] 的情况下创建一个新 FileReader
b、FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新FileReder
②、普通字符输入流读取数据步骤:
a、创建字符输入流对象(并且明确你要从哪个文件读数据)
FileReader fr = new FileReader("FileWriterDemo.java");
b、调用读取数据功能,并显示
int ch = 0;
while((ch=fr.read())!=-1){
//如果数据没有了,读取的时候,将返回-1
//如果有数据则返回int类型(字符的int值),并自动移动到下一个数据位置等待读取
System.out.print((char)
ch); }
c、释放资源
fr.close();
③、小案例:把当前项目下的FileWriterDemo.java文件拷贝到,G盘下并改名
package Test1; import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { // 封装数据源 FileReader fr = new FileReader("FileWriterDemo.java"); // 封装目的地 FileWriter fw = new FileWriter("G:\\Copy.java"); // 读取数据 int ch = 0; while ((ch = fr.read()) != -1) { fw.write(ch); System.out.print((char)ch); fw.flush(); } // 释放资源 fw.close(); fr.close(); } }运行结果如下:
(3)高效字符缓冲流:BufferedReader,BufferedWriter
BufferedReader,BufferedWriter是更高级的流,二者的源和目的地必须是字符输入流和字符输出流.如果把字符输入流做为BufferedReader流的源,把字符输出流作为BufferedWriter流的目的地,那么BufferedReader,BufferedWriter将有更强的读、写能力。
a、构造函数 BufferedReader(Reader in); BufferedWriter(Writer out);
b、常用方法
int read();//读取单个字符
int read(char[]
cbuf, int off, int len) //将字符读入数组的某一部分
String readLine()//读取一个文本行
void write(int c)//写入单个字符
void write(String s, int off, int len)//写入字符串的某一部分。
void write(char[]
cbuf, int off, int len)//写入字符数组的某一部分
void newLine()//写入一个行分隔符
c、使用方法
BufferedReader ino=new BufferedReader(new FileReader("Student.txt"));//参数是字符流
BufferedWriter outo=new BufferedWriter(new FileWrietr("Student.txt"));//参数是字符流
d、小案例:
import java.io.*; //编写一个程序,把指定目录下的所有的带.java文件都拷贝到另一个目录中, //拷贝成功后,把后缀名是.java的改成.txt。 public class Test4 { static int count=0; public static void main(String[] args) throws IOException { File yfile=new File("F:\\"); File mfile=new File("G:\\CopyFile"); copyFile(yfile,mfile); System.out.println("共复制了:"+count+" 个文件"); } public static void copyFile(File yfile,File mfile) throws IOException{ if(yfile==null){ System.out.println("源文件目录为:null."); return; } if(!yfile.exists()){ System.out.println("源文件不存在."); return; } if(!mfile.exists()){ mfile.mkdirs(); System.out.println("目标目录不存在,已经创建"); } File[] fileList=yfile.listFiles(); if(fileList!=null){ for(File f:fileList){ if(f.isFile()&&f.getName().endsWith(".java")){ String name=f.getName(); String newname=name.replaceAll("\\.java$", ".mp3"); Long time=System.currentTimeMillis(); File mf=new File(mfile,time+newname); BufferedReader br=new BufferedReader(new FileReader(f)); BufferedWriter bw=new BufferedWriter(new FileWriter(mf)); String strs=null; while((strs=br.readLine())!=null){ bw.write(strs); bw.newLine(); bw.flush(); } bw.close(); br.close(); count++; } if(f.isDirectory()){ copyFile(f,mfile); } } } } }
运行结果如下:
2.字节流
(1)字节输出流FileOutputStream
a、创建字节输出流对象
b、调用写数据的方式
c、释放资源
(2)字节输入流FileInputStream
a、创建字节输入流对象
b、调用读取数据的方式,并显示
c、释放资源
(3)示例:
package Test1; import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("a.txt"); FileOutputStream fos =new FileOutputStream("G:\\CopyFile.txt"); byte[] bys =new byte[1024]; int len; while((len=fis.read(bys))!=-1){ fos.write(bys,0,len); System.out.println(new String(bys,0,len)); } fos.close(); fis.close(); } }运行结果:
(3)注意:
二进制流数据:图片,视频,音频等...;由于二进制的合并为字符,在我们使用的编码表中没有对应的数据,那么,将来在写回去的时候,就会有数据的丢失,所以,二进制流数据只能使用字节流进行操作。
(4)带缓冲区的输入出输出流
▪BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8M,能够减少访问磁盘的次数,提高文件读取性能;
▪BufferedOutputStream是带缓冲区的输出流,能够提高文件的写入效率。
▪ BufferedInputStream与BufferedOutputStream分别是FilterInputStream类和FilterOutputStream类的子类,实现了装饰设计模式。
(5)小案例:
package Test1; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class FileTest { public static void main(String[] args) throws IOException { //高效字节流一次读写一个字节数组 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\酷狗下载\\信乐团 - 假如.mp3")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("love.mp3")); byte[] bys = new byte[1024]; int len = 0; while((len=bis.read(bys))!=-1){ bos.write(bys,0,len); } bos.close(); bis.close(); System.out.println("复制完成!"); } }3.转换流
1:标准的输入输出流
a、public static final InputStream in :“标准”输入流。(System.in)--InputStream(字节输出流)
b、public static final PrintStream out:”标准“输出流 。(System.out)--PrintStream (字节输入流)
2.字节输入流转成字符输入流
a、方法:
字节流通向字符流的桥梁
InputStream ----> InputStreamReader -----> BufferedReader
b、小练习,想自己封装一个键盘录入数据,并存储到文本文件中:
import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { InputStream is = System.in; InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); // 封装目的地 BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")); String line = null; while ((line = br.readLine()) != null) { // 只要是键盘录入,就的自己定义结束条件 if ("over".equals(line)) { break; } bw.write(line); bw.newLine(); bw.flush(); } bw.close(); br.close(); } }
理解:字符流读写数据的底层其实还是调用的字节流,只不过把字节流按照编码顺序排成了字符,从而读出来的是字符所以说既然底层调用的是字节流,那么字节流通过InputStreamReader就可以转成字符流,(其实就是把字节加入了一些编码顺序)所以说 万流之根源乃字节流,根源演变成了其他流,所以说 字节流可以转字符流,而反过来就不可以。
3.字节输出流转字符输出流:
a、方法:OutputStream -- > OutputStreamWriter --> BufferedWriter
b、小案例,需求:把文本文件的数据通过标准输出流的方式显示在控制台。
import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { // 封装数据源 BufferedReader br = new BufferedReader(new FileReader("bw.txt")); // 封装目的地 OutputStream os = System.out; OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw); // BufferedWriter bw = new BufferedWriter(new // OutStreamWriter(System.out)); String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } System.out.println(); bw.close(); br.close(); } }理解:这句话 "OutputStreamWriter 是字符流通向字节流的桥梁" 难理解,因为不知道为什么字符流是字节流的桥梁,(按字节的流向,字符流读进来文字,按照字符流写入文件,底层还是调用字节流写入文件的,所以是字符流通向字节流的桥梁)不用管OutputStreamWriter是谁到谁的桥梁,我就说上面的是字节流转字符流还是字符流转字节流?肯定是字节流转字符流,反过来就不可以(万流之根源乃字节流,根源演变成了其他流)
4:编码问题
流的编码问题:字符流=字节流+编码表
如果你想在IO流中使用指定编码下数据,用转换流。
编码问题其实很简单,永远使用同一种编码即可。
FileReader: FileInputStream+GBK
FileWriter:FileOutputStream+GBK
//写入的时候用的字符流的UTF-8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8"); // 指定编码UTF-8
osw.write("中国");
osw.close();
//那么读出的时候如果用字符流 也应该用 UTF-8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"), "UTF-8");
char[] chs = new char[1024];
int len = isr.read(chs);
String str = new String(chs, 0, len);
System.out.println(str);
如下面代码:
import java.io.*; public class FileTest { public static void main(String[] args) throws IOException { // OutputStreamWriter(OutputStream out, String charsetName) OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"), "UTF-8");// 指定编码UTF-8 osw.write("中国你好!"); osw.close(); InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"), "UTF-8"); char[] chs = new char[1024]; int len = isr.read(chs); String str = new String(chs, 0, len); System.out.println(str); } }5.打印流
a、PrintStream:字节打印流
b、PrintWriter:字符打印流
1、打印流特点:
a、可以写入任意类型的数据。
b、可以自动刷新。必须先启动,并且是使用println,printf及format方法才有效。
c、可以直接对文件进行写入。
d、注意:打印流只有写数据的,没有读取数据的。
2、构造方法:
a、PrintWriter(String fileName) :使用指定文件创建不具有自动行刷新的新 PrintWriter
b、PrintWriter(OutputStream out):根据现有的OutputStream创建不带自动行刷新的新
PrintWriter
c、
PrintWriter(OutputStream out,boolean autoFlush):通过现有的OutputStream创建新的PrintWriter。
d、PrintWriter(Writer out):创建不带自动行刷新的新PrintWriter。
e、PrintWriter(Writerout,boolean
autoFlush):创建新PrintWriter
3.PrintWriter的 print() 可以写任意类型数据:
PrintWriter 自动刷新功能(必须开启了自动刷新 然后还得是调用println,printf及format方法,才能自动刷新)。
PrintWriter pw = new PrintWriter(new FileWriter("d.txt"), true);PrintWriter的println()能够自动换行。
4.案例:用打印流复制文本文件
public class FileTest { public static void main(String[] args) throws IOException { // 封装数据源 BufferedReader br = new BufferedReader(new FileReader("PrintWriterDemo.java")); // 封装目的地 PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true); String line = null; while ((line = br.readLine()) != null) { pw.println(line); } pw.close(); br.close(); } }5.注意:
a、读取文件的时候,如果是文件夹,会出现:FileNotFoundException: test (拒绝访问。)
b、写文件的时候,如果没有后缀名,它也会自动生成一个文件,而这个种类型的文件通过记事本类型的软件是可以打开的。
6.序列化流
(1)序列化:把对象按照流一样的方式传输或者存储。
ObjectOutputStream
将 Java 对象的基本数据类型和图形写入 OutputStream。
void
writeObject(Object obj)
(2)反序列化:把网络中的流数据或者文件中的流数据还原成对象。
ObjectInputStream:ObjectInputStream
对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化
Object
readObject()
(3)注意问题:
a、如果类的对象想被序列化流操作,请实现序列化接口。
b、看到了类实现了Serializable接口,如果想解决黄色警告线,请点击鼠标。
c、java.io.NotSerializableException
没有实现序列化接口异常
d、类通过实现 java.io.Serializable 接口以启用其序列化功能。该接口被称为序列化接口,是一个标记接口。
没有任何功能需要实现。类一旦实现了该接口,那么,该类的对象就可以被序列化流进行操作。
(4)为什么要写一个序列号?
对象在被序列化流进行操作的时候,会记录一个序列号值。而这个序列号值跟对象的成员相关。
最开始的时候:
Person类的对象 p的id是100
write写到文件中:id -- 100
read读取数据: id -- 100
后来,我们把write方法给注释了,然后修改了Person类,这个类被修改后,成员就发生了改变。
那么,id也就相应的发生改变
Person类的对象id改为了200
那么,直接读取,文件中的id是100,而我们有的id是200,他们不匹配,所以报错了。
那么,在很多情况下,我们的类中的成员可能会发生改变,不可能说每次改变,我都需要把数据重写入一次。
怎么解决这种情况呢?
先写,在读取。没有问题。
先写,后修改了类,在读取,有问题。
很简单,我们想了这样的一个问题:他们之所以出错,就是因为那个id值不匹配,如果我们直接在类中给一个固定的id值。
那么,无论你怎么修改类的成员,这个id值是不变化的,那这样的话,以后的id永远都是一样的。就不会再因为简单的修改出问题。
问题呢?怎么定义这个id值呢?
private static final long serialVersionUID = 333558257579870816L;
(5)
示例:
[java] view
plaincopy
/*
SequenceInputStream
合并流
需求:将三个文本文件中的数据合并到一个文本文件中
思路:1、创建一个Vector集合,将三个文本文件字节流添加到集合中
2、创建Enumeration对象,创建SequnceInputStream对象关联Enumeration
3、输出流关联新文本文件
4、反复读写操作
*/
import java.util.*;
import java.io.*;
class SequenceInputStreamDemo
{
public static void main(String[] args)throws IOException
{
Vector<InputStream> ve=new Vector<InputStream>();//创建vector集合,并添加相关流对象
ve.add(new FileInputStream("1.txt"));
ve.add(new FileInputStream("2.txt"));
ve.add(new FileInputStream("3.txt"));
Enumeration<InputStream> en=ve.elements();//创建枚举对象
SequenceInputStream sis=new SequenceInputStream(en);//合并流
FileOutputStream fos=new FileOutputStream("4.txt");//关联写入文件
//反复读写操作
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
//关流
fos.close();
sis.close();
}
}
练习:
[java] view
plaincopy
/*
切割文件
需求:将一个mp3文件按1M大小切割成几部分
思路:1、使用文件字节流关联mp3文件
2、定义一个容器存储1M大小的数据,当存储满时,写入一个新文件中
*/
import java.util.*;
import java.io.*;
class SplitFile
{
public static void main(String[] args) throws IOException
{
//指定要切割的文件
File file=new File("C:\\Users\\asus\\Desktop\\苏芮 - 一样的月光.mp3");
//将指定文件进行切割
splitFile(file);
//指定要合并到的文件
File file1=new File("E:\\Java Study\\Practice\\day20\\splitFile\\一样的月光.mp3");
//将部分文件进行合并指定文件中
merge(file1);
}
//接收一个文件,将其按1M大小进行切割
public static void splitFile(File file)throws IOException
{
//关联要切割的文件
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos=null;
//定义1M大小存储容器
byte[] buf=new byte[1024*1024];
int len=0,x=0;
while ((len=bis.read(buf))!=-1)
{
//每满1M就写入一个新文件中
bos=new BufferedOutputStream(new FileOutputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+(++x)+".part"));
bos.write(buf,0,len);
bos.close();//没写完一个文件要记得关流
}
//关流
bis.close();
}
//将部分文件合并为一个可执行文件
public static void merge(File file)throws IOException
{
//定义一个集合存储这些部分文件关联路径数据
ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
for (int x=1;x<=6 ; x++)
{
al.add(new FileInputStream("E:\\Java Study\\Practice\\day20\\splitFile\\"+x+".part"));
}
//因为Enumeration是Vector特有的迭代方法,所以这里创建一个Enumeration类型的匿名内部类
final ListIterator<FileInputStream> it=al.listIterator();
Enumeration<FileInputStream> en=new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
//关联枚举对象
SequenceInputStream sis=new SequenceInputStream(en);
//将合并的文件数据写入指定文件中
FileOutputStream fos=new FileOutputStream(file);
//定义临时存储数据的数组
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);//写数据
}
//关流
fos.close();
sis.close();
}
}
相关文章推荐
- 程序员面试笔试宝典——读书笔记2、内存分配、sizeof、指针
- 面试题31连续数组的最大和
- 黑马程序员——java基础——管道流
- 设计模式面试
- 黑马程序员_集合框架(一)
- 面试题30 最小的K个数
- 一道SQL面试例题 if...else 与聚集函数
- 黑马程序员--应用:类的创建练习
- 黑马程序员--手机类的例子
- C# SQL 面试题自我总结
- 面试题29数组中出现次数超过一半的数字
- 程序员面试笔试宝典——读书笔记1、程序设计基础知识
- 黑马程序员——java基础 集合 (复习)
- 程序员为何对苹果 Mac 情有独钟?
- 给程序员推荐一些提高工作效率的工具
- 【原创分享】动态新增行和删除行
- Java面试--io
- 杭州地区IT公司招聘分析与求职建议
- 程序员为何对苹果 Mac 情有独钟?
- 读程序员的自我修养应该弄懂的问题