黑马程序员——【学习笔记】IO技术——IO流
来源:互联网 发布:vb msg 编辑:程序博客网 时间:2024/05/22 00:53
-------android培训、java培训、期待与您交流!----------
1 IO流是用来处理设备之间的数据传输。
这里的设备是指:鼠标,键盘,硬盘(就是文件),显示器,控制台,内存...
Java对数据的操作是通过流的方式处理。Java用与操作流的对象都在包中。
1.1 IO流的分类
①IO流按流方向来说,分为输入流和输出流
输入流和输出流是指用于输入/输出数据的流,这个输入/输出是对于内存来说的。
比如,在键盘打字到显示器上就是键盘输入到内存再输出到显示器上。后续学习到的流很多,不要混淆。
②IO流按操作的数据来说,分为字节流和字符流。
字节流:早期IO包里的都是字节流,因为数据无论是内存中的还是硬盘中的都是字节,最终是以二进制的形式存储。特别是图片、视频等媒体文件。
字符流:后来操作文本数据相当常见,于是创建了字符流。字符流也就是存储abcd等英文及符号的存在,而存入存储的时候就需要编码来对abcd进行编译成二进制输入输出,这就是编码表。中国的编码表是GKB,Unicode是国际通用的码表。字符流可以在内部融合编码表,即可以由调用者指定流内操作的是什么编码表。
PS:Unicode里无论什么字符都用两个字节表示。
其中:
字节流的抽象基类:InputStream,OutputStream,
字符流的抽象基类:Reader,Writer
由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。如FileInputStream FileReader
2 字符流
Reader Writer
2.1 FileWriter
示例:在硬盘创建一个文件并写入文字数据。
首先,创建一个可以往文件中写入字符数据的字符输出流对象FileWriter(是Writer的子类)
在创建对象时,在参数中计入文件存放路径和文件名,如果本来已有文件,则会覆盖,否则新建一个文件。
注意,因为IO操作总是有可能造成IO错误IOException,记得抛出。
用append()或者write()(通常用write)计入数据内容。(跟StringBuffer一样都是用append(),而集合用add()或insert();)
class Day20{public static void main(String[] args) throws IOException{FileWriter fw = new FileWriter("D:\\Demo.txt");fw.append("输出到文件Demo.txt");fw.flush();fw.append("shuchu again");fw.flush();fw.write("haha");fw.flush();}}
FileWriter一旦被创建就必须要有被操作的对象(文件),所以FileWriter没有无参数的构造函数
2.1.1 FileWriter中的flush()和close();
为什么要有flush或close()的操作?
这是因为当我们writer()数据执行完之后实质上只是把数据写入到流里,即内存内,而并没有写到文件中。如果不用这语句,结果是新文件被创造出来并覆盖了旧文件,但是写入的内容并没有被写入。
只有flush()或close()后,被写入到流的数据才会被输出到文件中。
flush()和close()的区别是:flush只刷新,即把流里的数据输出到文件中,但不关闭流。
而close会把数据输出到文件之后关闭流,之后不能再操作此流了。
class Day20{public static void main(String[] args) throws IOException{FileWriter fw = new FileWriter("D:\\Demo.txt");fw.append("输出到文件Demo.txt");//fw.flush();fw.append("\r\nshuchu again");//fw.flush();fw.write("\r\nhaha");//fw.flush();}}
2.1.2 如果在构造函数中加入true,可以实现对文件进行续写。即创建文件时遇到文件存在的话,不会覆盖,只会在数据后续写。
class Day20{public static void main(String[] args) throws IOException{FileWriter fw = new FileWriter("D:\\Demo.txt",true );fw.append("\r\n这是Java输出到文件Demo.txt的数据");//fw.flush();fw.append("\r\nshuchu again");//fw.flush();fw.write("\r\nhaha");fw.flush();}}
2.2 FileReader
示例:读取一个文件,将读取到的字符打印到控制台
FileReader fr = new FileReader(文件);
fr.read();读取流一个字节一个字节地读取,一次读取下一个。如果读到没有字节了就返回-1。返回-1这个反馈给了我们循环的条件。
fr.read(char[] ch);将字符读入数组,返回读取的字符数。然后存一次把字符组打出一次。每次打出的字符个数为fr.read(int[] ch);反回的读入,直到返回数为-1(即没有数据)为止。这样输入流不是一个字节一个字节地读取,而是一次读完数组的长度,再输出。直到没有数。
用StringBuffer()装入read()读取的字符,然后toString输出字符缓冲区。字符一次性读取全部,存入StringBuffer里。
class Day20{public static void main(String[] args){FileReader fr = null;try {fr = new FileReader("D:\\Demo.txt");char[] buf = new char[1024];int num=0;while((num=fr.read(buf))!=-1)System.out.print(new String(buf,0,num));//int ch = 0;//while((ch=fr.read())!=-1)//System.out.print((char)ch);//int ch1 =0;//StringBuffer sb = new StringBuffer();//while((ch1=fr.read())!=-1)//sb.append((char)ch1);//System.out.println(sb.toString());} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{ try {fr.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}
PS:
注意凡是有IO操作的语句肯定会有安全问题,比如读取文件会有FileNotFoundException,IO流操作会有IOException。
数据流操作try catch后记得要finally fr.close();,否则程序一旦catch到了错误,流对象不会关闭。
而fr.close();也是IO操作,也要try catch
示例:将D盘的一个文本文件复制到D盘的另一个不存在的目录。
public class Day21 {public static void main(String[] args){FileReader fr = null;FileWriter fw = null;try{fr = new FileReader("D:\\Demo.txt");fw = new FileWriter("D:\\1\\Demo.txt",true);//第一种方法:数组读取//int num = 0;//char[] ch = new char[1024];////while((num=fr.read(ch))!=-1){//fw.write(ch,0,num);//}//第二种方法:频繁读取int ch = 0;while((ch=fr.read())!=-1)fw.write(ch);}catch(FileNotFoundException e){e.printStackTrace();}catch(IOException e){e.printStackTrace();}finally{try{fr.close();fw.close();}catch(IOException e){e.printStackTrace();}}}}
3 字符流缓冲区
BufferedWriter
BufferedReader
字符缓冲区是为了增强IO流的运行效率。
字符缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象。
字符缓冲区的最终原理就是数组。
public class Day21{public static void main(String[] args) throws IOException{FileWriter fw = null;fw = new FileWriter("D:\\buf.txt");//创建缓冲区,把需要被提高效率的流作为参数输入BufferedWriter bufw = new BufferedWriter(fw);for(int x=0;x<5;x++){bufw.write("bacde"+x);bufw.newLine();}//只要用到缓冲区,就记得刷新。//但是缓冲区只是为了提高效率,真正调用资源的是FileWriter//所以关闭缓冲区,就是关闭缓冲区中的流对象。bufw.flush();bufw.close();}}
PS:
缓冲区只是为了提高效率,
正如上面说的,缓冲区技术最终原理就是用数组存储FileWriter的数据,
所以程序中实际操作数据的是FileWriter,而缓冲区是跟它关联起来了。
所以关闭缓冲区实际上就是关闭流本身。
BufferedReader:
BufferReader的read()可以读取字符流。
而它的readLine();方法更高效,可以直接读取一行,再处理,并返回String(因为一行一行读取的结果就是字符串)。
当readLine();读取到没有数据时,就会返回null。可以用于循环的时候判断。
public class Day21{public static void main(String[] args) throws IOException{FileReader fr = new FileReader("D:\\Demo.txt");BufferedReader bufr = new BufferedReader(fr);String s = new String();while((s=bufr.readLine())!=null)System.out.println(s);bufr.close();}}
LineNumberReader:
LineNumberReader虽然名字没有Buffered,但它是BufferedReader的子类。
它可以获取的数据带行数,而且可以设置行数和获取行数。
跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int i)和getLineNumber(),他们可以分别用于设置和获取当前行号。
setLineNumber();设置起始行数(这个数不包含到数据类,是初始化的数,比如设置0,第一行就是1)
getLineNumber();获取行数
public class Day21{public static void main(String[] args) throws IOException{FileReader fr = new FileReader("D:\\Demo.txt");//BufferedReader bufr = new BufferedReader(fr);LineNumberReader bufr = new LineNumberReader(fr);String s = new String();bufr.setLineNumber(0);while((s=bufr.readLine())!=null)System.out.println(bufr.getLineNumber()+":"+s);bufr.close();}}1:这是文件里的原有数据
2:这是Java输出到文件Demo.txt的数据null
3:shuchu \r\nagain
4:haha
4 装饰类
实际上,BufferedReader和BufferedWriter就是装饰类。
它不是Reader或Writer的子类,但它是为了增强Reader和Writer的功能。
public class Day21{public static void main(String[] args){Animal a = new Animal();NewAnimal na = new NewAnimal(a);na.eat(2);}}class Animal{void eat(){System.out.println("animal eat");}}class NewAnimal{private Animal a;NewAnimal(Animal a){this.a=a;}void eat(int i){System.out.println("newanimal eat better"+i);}}装饰类的特点
装饰类的方法与本类的方法并不是继承关系,装饰类跟本类本身也没有特别的关系(本类的方法eat()不能被调用)。方法间的调用都是认为的。
装饰类必须与本类同继承一个父类或同实现一个接口。
装饰类与父子类关系的区别
按照面向对象的思想,增强程序的运行效率的一个方法是使用缓冲区。
那么,如果用父子类来增强本类,需要:
Writer
——TextWriter操作文本的字符输出流
——BufferedTextWriter运用了缓冲技术的操作文本的字符输出流
——MediaWriter操作媒体的字符输出流
——BufferedMediaWriter运用了缓冲技术的操作媒体的字符输出流
……
这样在以后就会使Writer体系越来越臃肿。
如果使用装饰类,把装饰类设定好之后,则以后使用只需要把需要被增强效率的类传入即可。
即:
Writer
——TextWriter操作文本的字符输出流
——MediaWriter操作媒体的字符输出流
——FileWriter操作文件的字符输出流
……
——BufferedWriter运用了缓冲技术的,默认传入参数是Writer对象的输出流,是一个装饰类,谁需要被装饰就往参数传入谁,以后Writer增加了新的子类,都可以用。5 字节流
InputStream和OutputStream
基本操作与字符流类相同。但它不近可以操作字符流,还可以操作其它媒体文件。
它操作字符流时需要先把字符转化为字节。getByte();
字节流不需要flush即可以把内容传入文本里。但是最后要close关闭资源
fis.available();返回下一次对此输入流调用的方法可以不受阻塞地(读到回车不算阻塞)从此输入流读取的估计剩余字节数
public class Day21{public static void main(String[] args) throws IOException{FileInputStream fis = new FileInputStream("D:\\Demo.txt");//用笃定的字节数组来读//int len =0;//byte[] b = new byte[1024];//while((len=fis.read(b))!=-1)//System.out.println(new String(b,0,len));//一个字节一个字节地读,当文件里有中文的时候需要用到转换里将字节装换成字符//InputStreamReader isr = new InputStreamReader(fis);//int i = 0;//while((i=isr.read())!=-1)//System.out.print((char)i);//估计文本的大小,然后用一个限定长度的数组一次装完。注意如果文件太大,可能会发生内存溢出错误byte[] b = new byte[fis.available()];int i = fis.available();System.out.println(i);fis.read(b);System.out.println(new String(b));fis.close();}}
读取一个键盘录入的数据,并打印在控制台上,
键盘本身就是一个标准的输入设备,对于java而言,对于这种输入设备都有对应的对象
5.1.1 读取键盘录入
System.out:对应的是标准输出设备:控制台
System.in:对应的是标准输入设备:键盘。所有键盘输入的都是字节流。
public class Day22{public static void main(String[] args) throws IOException{InputStream in = System.in;int ch = in.read();System.out.println((char)ch);}}sdfafaf
s
注意:以上程序只读取一个字节,所以就算输入了很多个字节,也只读取一个。
通过System.setIn(InputStream is);和System.setOut(PrintStream sp);可以对默认设备进行设定。
因为InputStream和OutputStream操作的是字节流,所以System.setIn()和System.setOut()的参数都是字节流对象,其中,setIn要求是输入字节流,setOut要求是打印字节流
public class Day22{public static void main(String[] args) throws IOException{System.setIn(new FileInputStream("D:\\Demo.txt"));System.setOut(new PrintStream("D:\\Demo3.txt"));InputStream is = System.in;OutputStream os = System.out;int ch = 0;while((ch=is.read())!=-1)os.write(ch);is.close();os.close();}}
此程序相当于复制本件。因为输入流和输出流都改为文件。
6 转换流
我们直到,读取设备和输出到设备的流都是字节流,但是我们常用的输入是字符,而字符流能更好的运用于各种操作。
所以java给了转换流,作为字节与字符间的桥梁。
6.1 读取转换流
InputStreamReader:它是一个装饰类,可以把输入到内存的字节流转换成字符流。然后用字符流的方法来操作数据。
OutputStreamWriter:它是一个装饰类,可以把内存中的字符流转换成字节流输出到设备中。
就是说:内存内用字符流操作更显高效。但内存外设备只接收字节流。所以转换流就是字节流和字符流间的桥梁。
public class Day22{public static void main(String[] args) throws IOException{BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//使用转换流的同时对转换流使用缓冲技术BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));//<pre name="code" class="java">使用转换流的同时对转换流使用缓冲技术String line = null;while((line=br.readLine())!=null){if("over".equals(line)){break;}bw.write(line);bw.newLine();bw.flush();}}}输入转换流输入的过程,是一个对字节进行解码的过程;
输出转换流输出的过程,是一个对 字符进行编码的过程。
以为一个中文字符由两个字节组成,所以使用字节流读取一个中文字符需要读取两次,容易出乱码,而使用字符流只需要读取一次。
7 流操作的规律
因为学习的流对象太多,开发时不知道用哪个对象合适。可以通过以下四个步骤明确:
①明确数据源和目的
数据源:InputStream Reader
目的:OutputStream Writer
②明确数据是否纯文本数据
是:Reader
否:InputStream
③明确具体设备
源设备:
键盘:File
键盘:System.in(除非System.setIn()成别的设备)
内存:数组
网络:Socket流
目的设备
硬盘:File
控制台:System.out
内存:数组
网络:Socket流
④是否需要提高效率:
需要:设置缓冲区。BufferedReader BufferedWriter BufferedInputStream BufferedOutputStream
8 编码问题
任何Java识别的字符数据使用的都是“Unicode”码表(国际通用),
但是FileWriter写入本地文件使用的是本地编码(中国是“GBK”)
而OutputStreamWriter可以使用指定的编码将要写入流中的字符编码成字节。
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\Demo.txt"),"GBK");
8 File类
File类用来将文件或文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
PS:流只能操作数据,不能操作文件。
File f = new File("d:\\demo\\b.txt")
</pre><pre name="code" class="java">class Day22{public static void main(String[] args){//方法一File f = new File("D:\\Day22.txt");//方法二File f1 = new File("D:\\","Day22.txt");//方法三File f2 = new File("D:\\");File f3 = new File(f2,"Day22.txt");
<span style="white-space:pre"></span>System.out.println(f);<span style="white-space:pre"></span><span style="white-space:pre"></span>System.out.println(f1);<span style="white-space:pre"></span>System.out.println(f2);<span style="white-space:pre"></span>System.out.println(f3);}}D:\Day22.txt
D:\Day22.txt
D:\
D:\Day22.txt
PS:如果想要Windows和linux都成功识别路径分隔符”\“的话,就要用File.separator来代替
public class Day22{public static void main(String[] args){File f = new File("D:\\Day22.txt");File f1 = new File("D:"+File.separator+"Day22.txt");System.out.println(f);System.out.println(f1);}}D:\Day22.txt
D:\Day22.txt
8.1 File类的常用方法
8.1.1 获取
getName();获取文件名
getAbsolutePath();获取绝对路径
getPath();获取路径。实质是File对象引用的路径,当时输入什么,现在就输出什么。
length();获取大小,返回long
lastModified();获取上次修改日期,返回long
getParent();获取父目录。只有引入的时候有写父目录才有,如果当时引入的时候只有文件名,则返回null
getAbsoluteFIle();获取绝对路径,但返回文件类对象的形式。与getAbsolute()可以以toString和new File()相互转换
class Day22{public static void main(String[] args){File f = new File("D:"+File.separator+"Demo.txt");File f1 = new File("Demo.txt");String name = f.getName();String name1 = f1.getName();String abPath = f.getAbsolutePath();String abPath1 = f1.getAbsolutePath();String path = f.getPath();String path1 = f1.getPath();long len = f.length();long len1 = f1.length();long modi = f.lastModified();Date d = new Date(modi);long modi1 = f1.lastModified();String parent = f.getParent();String parent1 = f1.getParent();System.out.println(name);System.out.println(name1);System.out.println(abPath);System.out.println(abPath1);System.out.println(path);System.out.println(path1);System.out.println(len);System.out.println(len1);System.out.println(modi+"..."+d);System.out.println(modi1);System.out.println(parent);System.out.println(parent1);}}Demo.txt
Demo.txt
D:\Demo.txt
E:\Java Workshop\demo\Demo.txt
D:\Demo.txt
Demo.txt
82
0
1444759011857...Wed Oct 14 01:56:51 CST 2015
0
D:\
null
8.1.2 创建和删除
boolean createNewFile(); 创建一个文件,并返回创建结果true或false,如果文件不存在,则创建,如果文件已存在,则不出案件,并返回false创建失败。跟输出流不一样,输出流是覆盖或续写文件。如果目录不存在,报错IOException。
boolean deleteFile(); 删除文件或文件夹,并返回删除结果。当删除文件夹时,如果文件夹中有文件,则删除失败,并返回false
void deleteOnExit();在程序退出时删除。当文件处理的途中报错了,一般程序卡死后deleteFile就没有被执行,文件变成垃圾文件占用空间。用了deleteOnExit就可以在程序关闭时删除指定文件。这个方法没有返回值。
boolean mkdir();创建目录,不可以创建多级目录
boolean mkdirs();创建目录,可以创建多记目录
class Day22{public static void main(String[] args) throws IOException{File f = new File("D:\\2\\3\\4\\5");File f3 = new File("D:\\2\\3\\4\\5\\demo.txt");boolean f2 = f.mkdirs();boolean f1 = f3.createNewFile();System.out.println(f2);System.out.println(f1);}}
8.1.3 判断
boolean exists();判断文件是否存在。可以用于流操作前判断文件是否存在,避免IOException
boolean isFile();判断是否文件
boolean isDirectory();判断是否文件夹
boolean isHidden();判断是否隐藏
boolean isAbsolute();判断是否
PS:记住,因为如果引用的对象本来就不存在,那么判断isFile和isDirectory都会返回false。
所以判断是文件或这是文件夹之前,最好先判断文件是否存在exists();
class Day22{public static void main(String[] args){File f = new File("D:\\haha.txt");boolean b1 = f.exists();boolean b2 = f.isFile();boolean b3 = f.isDirectory();System.out.println(b1);System.out.println(b2);System.out.println(b3);}}false
false
false
8.1.4 重命名
boolean renameTo("新名字");重命名并返回是否命名成功。重命名还有移动文件的功能。
class Day22{public static void main(String[] args){File f = new File("D:\\3.txt");File f1 = new File("D:\\1\\3.txt");f.renameTo(f1);}}
8.1.5 获取目录
String[] list();获取目录下的文件以及文件夹的名称,包含隐藏文件。
调用list方法的File对象中封装的必须是目录,否则会产生NullPointerException
如果访问的是系统级目录也会发生空指针异常
如果目录存在但是没有内容,会返回一个数组,但是长度为0;
File[] listRoot();获取根目录盘符。返回一个File[]列表
File[] listFiles();获取目录,返回File()列表。注意与list区分,一个是获取目录的路径,并返回String。一个是获取目录的对象,返回File[]。
8.2 FilenameFilter
boolean FilenameFilte是一个接口,用于过滤文件名,下面只有一个方法accept(File dir, String name),返回真假表示是否符合要求。
FilenameFilter作为list()的参数加入,在list()的过程中过滤。
class Day22{public static void main(String[] args){File f = new File("E:\\");String[] arr = f.list(new FilenameFilter(){public boolean accept(File f,String name){return name.endsWith(".exe");}});for(String s :arr)System.out.println(s);}}这个FilenameFilter以匿名内部类的形式存在。
示例:获取C盘下的隐藏文件
class Day22{public static void main(String[] args){File f = new File("c:\\");String[] arr = f.list(new FilenameFilter(){public boolean accept(File f,String name){return f.isHidden();}});for(String s :arr)System.out.println(s);}}
8.3 递归
函数自身直接或者间接调用到自身。
一个功能在被重复调用,并每次使用时,参与运算的结果和上一次调用有关,这时可以用递归来解决问题。
PS:
①递归一定要明确结束条件,否则会溢出。
②注意一下递归的次数。
示例:计算1到10的迪递加
public class Day22{public static void main(String[] args){int getSum =getSum(10);System.out.println(getSum);}public static int getSum(int x){if(x==1){return 1;}return x+getSum(x-1);}}
8.4 删除目录
在windows中,删除目录是从里往外删的,
既然是从里往外删除,就需要用到递归
该集合是属于Map——HashTable下的一个子类,
具备Map的特点,存储的都是键值对,而且键值都是String类型
因为配置信息一般都是存储在硬盘类,而它是以Map集合(键值对)的形式存在的,
所以properties集合是集合和IO技术相结合的集合容器。
可以用于存放键值对形式的配置文件。
9.1 Properties的常用方法
存储和取出:
setProperty(String key,Strng value);存入键值对,如果已有该key,value会覆盖
getProperty(key);根据键返回值。
stringPropertyNames();返回一个存储键的Set集合。
list(PrintStream out);把属性列表输出到指定的输出流。
load(InputStream in);从字节输入流读取属性列表。
load(Reader in);从字符输入流读取属性列表。
store(OutputStream out,String comment);
store(Writer out,String commet);将属性列表写入输出流。
ublic class Day22{public static void main(String[] args) throws IOException{Properties prop = new Properties();prop.setProperty("abc","1");prop.setProperty("def","2");prop.setProperty("ghi","2");//prop.list(System.out);FileOutputStream fos = new FileOutputStream("D:\\java prop.txt");prop.store(fos,"haha");fos.close();}}
9.2 Properties存储配置文件
因为Properties里资源更多的时候不是从程序里直接取得(不需要IO流),而是从配置文件里提取,所以Properties必须包含操作文件的方法。
* * 想将文件中的键值数据存到集合中进行操作 */public class Day24{public static void main(String[] args) throws IOException{//1,用一个流和文件关联,//读取一行数据,将该行数据用“=”进行切割//等号左边作为键,右边作为值,存入到Properties集合中即可BufferedReader br = new BufferedReader(new FileReader("E:\\Demotest\\peizhi.txt"));Properties pro = new Properties();String line = null;while((line=br.readLine())!=null){String[] temp=line.split("=");pro.setProperty(temp[0],temp[1]);}br.close();System.out.println(pro);Set<String> s = pro.stringPropertyNames();Iterator<String> it = s.iterator();while(it.hasNext()){String temp = it.next();System.out.println(temp+"=="+pro.getProperty(temp));}}}{赵六=60, 王五=40, 刘七=70, 张三=30, 李四=100}
赵六==60
王五==40
张三==30
刘七==70
李四==100
但是载入Properties不能保证跟源文件顺序一致(因为Properties是HashTable的子类)
实际上这就是Properties的load(InputStream is)或load(Reader r)
10 IO包中的其它类
10.1 打印流
printWriter与PrintStream:可以直接操作输入流和文件。是一个装饰类。
PrintStream为其它输出流添加了功能,除了常规的write(),还有print()方法,能够方便地打印各种数据值表示形式。
与其它输出流不同,PrintSteam永远不会抛出IOException。
PrintStream打印的所有字符都是用平台默认字符编码转换为字节。
在需要
PrintStream
可以对基本数据类型进行直接操作。能保证数据原样性将数据打印出去。(而Write只能输出最低八位)
构造函数可以接收的参数类型:
①File对象。File
②字符串路径。String
③字节输出流。OutputStream
PrintWriter
构造函数可以接收的参数类型:
①File对象。File
②字符串路径。String
③字节输出流。OutputStream
④字符输出流。Writerpublic class Day23{public static void main(String[] args) throws IOException{PrintStream ps = new PrintStream("D:\\ps.txt");//write(int b)方法只能 写入最低八位,变成aps.write(97);//print()方法可以将97先变成字符串保持原样将数据打印到目的地ps.print(97);ps.print("示例文件");ps.close();}}
PrintWriter是非常有用的方法,它可以直接println数据,并且把true作为参数传入可以自动刷新。
(但是File类不可以,因为autoFlush只能用与流,File类不是流,但是用FileWriter(“file”)代替,就可以刷新了)
public class Day23{public static void main(String[] args) throws IOException{BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));PrintWriter out = new PrintWriter(new FileWriter("D:\\haha2。txt"),true);String line = null;while((line=bufr.readLine())!=null){if("over".equals(line))break;out.println(line.toUpperCase());}}}
10.2 序列流
SequenceInputStream:对多个流进行串联合并。它没有对应的OutputStream。
即讲多个流对象拼成一个流对象。
class Day23{public static void main(String[] args) throws IOException{Vector<FileInputStream> v = new Vector<FileInputStream>();//创建集合,与三个需要被合并的文件相关联v.add(new FileInputStream("D:\\1.txt"));v.add(new FileInputStream("D:\\2.txt"));v.add(new FileInputStream("D:\\4.txt"));Enumeration<FileInputStream> e = v.elements(); //迭代器迭代 SequenceInputStream sis = new SequenceInputStream(e);//用序列流将迭代器导入 PrintStream ps = new PrintStream("D:\\5.txt"); byte[] b= new byte[1024]; int len =0; while((len=sis.read(b))!=-1) ps.write(b,0,len); sis.close(); ps.close();}}
注意a97那里,SequenceInputStream是不自动添加换行的,会紧随上个文件的末尾进行续写。
11 文件切割
原理是用固定大小的数组对读取的文件进行“分组”存放到硬盘。
public class Day24 {public static void main(String[] args) throws IOException{splitFile();}public static void splitFile() throws IOException{FileInputStream fis = new FileInputStream("D:\\5.txt");FileOutputStream fos = null;<span style="white-space:pre"></span>//创建输出流用于“分组”输出到文件byte[] buf = new byte[60];int len = 0;int count = 1;while((len=fis.read(buf))!=-1){fos = new FileOutputStream("D:\\"+(count++)+".part"); //输出流运用到循坏内fos.write(buf,0,len);fos.close();}}}
12 文件合并
前面的程序用Vector方法来合并,效率较低。
用ArrayList来存放更高效,
但是序列流SequenceInputStream只能用Enumeration来做参数,所以中间要变换一下。
public class Day24{public static void main(String[] args) throws IOException{//创建更高效的ArrayList来收纳需要被合并的文件ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for(int x=1;x<=3;x++)al.add(new FileInputStream("D:\\"+x+".part"));Iterator<FileInputStream> it = al.iterator();//创建Enumeration对象Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){public boolean hasMoreElements(){return it.hasNext();}public FileInputStream nextElement(){return it.next();}};//创建序列流,但是序列流要输入Enumeration对象做参数SequenceInputStream sis = new SequenceInputStream(en);//创建字符输出流,输出合并后的文件FileOutputStream fos = new FileOutputStream("D:\\3.txt");//用数组操作输出byte[] b = new byte[1024*500];int len = 0;while((len=sis.read(b))!=-1)fos.write(b,0,len);fos.close();}}
13 操作对象
ObjectInputStream和ObjectOutputStream,是可以直接操作对象的流
意义:我们知道流是可以操作数据的,现在对象被封装成一个对象。
对象本身存在于堆内存当中,程序一旦关闭,堆内存就被释放。
这个流就可以把堆内存的对象存放在硬盘上。——这个行为叫做对象的持久化存储,或对象的序列化。Serializable
可以直接操作基本数据类型
13.1 构造方法:
ObjectOutputStream();为完全重新实现ObjectOutputStream的子类提供一种方法,让它不必分配仅由ObjectOutputStream的实现使用的私有数据。
ObjectOutputStream(OutputStream out);创建写入指定OutputStream的dObjectOutputStream。
13.2 常用方法:
writeObject(Object obj);写入一个类。
public class Day24{public static void main(String[] args) throws IOException {writeObj();}public static void writeObj() throws IOException{File f = new File("E:\\Demotest\\oos.txt");ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));oos.writeObject(new Person("zhangsan",16));oos.close();}}class Person{private String name;private int age;Person(String name, int age){this.name=name;this.age=age;}}然而报错了,
Exception in thread "main" java.io.NotSerializableException: demo.Person
13.2 对象序列化
类通过实现Java.io.Serializable接口可以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化(即可把信息保存到硬盘上)。可序列化类的所有子类本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。
就是说Serializable仅仅是一个标识性接口,可简单理解为一个“邮戳”,戳了就视作被“邮局”认可为“信件”了。
class Person implements Serializable{ //序列化private String name;private int age;Person(String name, int age){this.name=name;this.age=age;}}
PS:如果把类序列化之后,类的语句有变化的话,通过系列化后的文件来活得类的方法就会失败。因为该类已经变了。
除非:
①把类语句修正过来。
②给该类设定一个自定义的uid
class Person implements Serializable{private static final long serialVersionUID = 525L; //自定义UIDprivate String name;private int age;Person(String name, int age){this.name=name;this.age=age;}public void print(){System.out.println(name+"..."+age);}}
14 管道流——建议用于多线程
PipedInputStream和PipedOutputStream
输入输出可以直接进行连接。
一般来说读取流和写出流没有直接联系,要想把读取的数据输出到内存外的设备,中间需要字符串或byte数组来承接寄来。
管道流的引入就是为了直接把输入流和输出流连接上,通过结合线城使用。
不建议管道流用于单线程因为这样可能引起死锁:输入流没有数据让输出流输出,输出流等待,因为输出流和输入流是同一线程(单线程),所以输入流无法输入数据。
14.1 PipedInputStream常用方法
connect(PipedOutputStream pos);连接相应的输出管道流。
public class Day24{public static void main(String[] args) throws IOException {PipedInputStream pis = new PipedInputStream();PipedOutputStream pos = new PipedOutputStream();pis.connect(pos);Read r = new Read(pis);Write w = new Write(pos);Thread t1= new Thread(r);Thread t2 = new Thread(w);t1.start();t2.start();}}class Read implements Runnable{ //设置读取流private PipedInputStream pis =null;Read(PipedInputStream pis){this.pis =pis;}public void run(){ //读取线程的工作byte[] buf = new byte[1024];try {int len = pis.read(buf);String s = new String(buf,0,len);System.out.println(s);pis.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class Write implements Runnable{ //设置写入线程private PipedOutputStream pos =null;Write(PipedOutputStream pos){this.pos=pos;}public void run(){ //运行写入线程try {pos.write("piped lai la".getBytes());pos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}这个过程有点像生产者和消费者。其中PipedOutputStream是生产者,PipedInputStream是消费者。
15 此类的实例支持对随机访问文件的读取和写入。
RandomAccessFile
16 操作基本数据类型的类
DataInputStream和DataoutputStream
public class Day24{public static void main(String[] args) throws IOException {DataOutputStream dos = new DataOutputStream(new FileOutputStream("E:\\Demotest\\Demo.txt"));dos.writeBoolean(true);//1个字节dos.writeInt(65);//4个字节dos.writeChar('a') ; //2个字节dos.writeChars("ok"); //4个字节dos.close();}}
17 操作字节数组的类
ByteArrayInputStream和ByteArrayOutputStream
可以直接操作数组中类
特点:作为缓冲区的数组会随着数据不断写入而自动增长(其它流需要设定比如1024字节),可以使用toString()和toString()获取数据
关闭ByteArrayInputStream无效,因为实际上它并没有和其它设备打交道,所以此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部分装了一个可变长度的字节数组。
这就是数据目的地。
public class Day24{public static void main(String[] args) {ByteArrayInputStream bais = new ByteArrayInputStream("abcdefg".getBytes());ByteArrayOutputStream baos = new ByteArrayOutputStream();int by =0; //不用在创建数组,因为数组已经封装在baos内。while((by=bais.read())!=-1)baos.write(by);System.out.println(baos.size());<span style="white-space:pre">System.out.println(baos.toString());</span>}}7
abcdefg
PS:设备总结:
源设备:
键盘:System.in硬盘:File Stream内存:Array Stream
目的设备:
控制台:System.out硬盘:File Stream内存:Array Stream
- 黑马程序员——【学习笔记】IO技术——IO流
- 黑马程序员—IO技术
- 黑马程序员—IO学习笔记
- 黑马程序员 java学习笔记——IO流1
- 黑马程序员 java学习笔记——IO流2
- 黑马程序员 java学习笔记——IO流3
- 黑马程序员java学习笔记——IO流
- 黑马程序员——Java学习笔记--IO流
- 黑马程序员——IO流学习笔记
- 黑马程序员—Java基础学习笔记之IO流
- 黑马程序员java学习—IO流
- 黑马程序员——IO技术
- 黑马程序员—IO
- 黑马程序员—io流
- 黑马程序员—IO流
- 黑马程序员—IO流
- 黑马程序员 — IO流
- 黑马程序员—IO流
- 使用魔术方法实现跨文件调用
- 每天一道算法题(38)——二叉树的非递归遍历
- 记录2
- ant学习笔记
- 代码整洁之道(2)
- 黑马程序员——【学习笔记】IO技术——IO流
- MySQL 行锁深入研究(行锁与表锁的内部优化问题)
- Java Applet 的运行原理
- bzoj1003 物流运输trans
- UISwitch之详解
- java 多线程在swing中的应用
- XML语言学习CRUD3----方立勋JAXP
- Unity Vector3.Slerp() 球形插值详解
- Date、String、Calendar