-------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 block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{ try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.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)方法只能 写入最低八位,变成a
ps.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; //自定义UID
private 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 block
e.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 block
e.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