IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
流按操作数据分为两种:字节流与字符流 。
流按流向分为:输入流,输出流。
字符流和字节流:
字节流两个基类:
InputStream OutputStream
字符流两个基类:
Reader Writer
注:由这四个类派生出来的子类名称都是 以其父类名作为子类名的后缀。
先学习一下字符流的特点。
既然IO流是用于操作数据的,
那么数据的最常见体现形式是:文件。
那么先以操作文件为主来演示。
需求:在硬盘上,创建一个文件并写入一些文字数据。
找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。 前缀名是该流对象的功能。
创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
其实该步就是在明确数据要存放的目的地。
FileWriter fw = new FileWriter("demo.txt");
调用write方法,将字符串写入到流中。
fw.write("abcde");
刷新流对象中的缓冲中的数据。
将数据刷到目的地中。
fw.flush();
关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
将数据刷到目的地中。
和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。
FileWriter fw = new FileWriter("demo.txt",true);
创建一个文件读取流对象,和指定名称的文件相关联。
要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr = new FileReader("demo.txt");
调用读取流对象的read方法。
read():一次读一个字符。而且会自动往下读。
int ch = fr.read();
fr.close();
第二种方式:通过字符数组进行读取。
定义一个字符数组。用于存储读到字符。
该read(char[])返回的是读到字符个数。
char[] buf = new char[1024];
int num = 0;
while((num=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,num));
}
fr.close();
复制的原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源。
import java.io.*;
class CopyText
{
public static void main(String[] args) throws IOException
{
copy_2();
}
public static void copy_2()
{
FileWriter fw = null;
FileReader fr = null;
try
{
fw = new FileWriter("SystemDemo_copy.txt");
fr = new FileReader("SystemDemo.java");
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1)
{
fw.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
if(fr!=null)
try
{
fr.close();
}
catch (IOException e)
{
}
if(fw!=null)
try
{
fw.close();
}
catch (IOException e)
{
}
}
}
缓冲区的出现是为了提高流的操作效率而出现的。
所以在创建缓冲区之前,必须要先有流对象。
该缓冲区中提供了一个跨平台的换行符:newLine();
创建一个字符写入流对象。
FileWriter fw = new FileWriter("buf.txt");
为了提高字符写入流效率。加入了缓冲技术。
只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x=1; x<5; x++)
{
bufw.write("abcd"+x);
bufw.newLine();
bufw.flush();
}
记住,只要用到缓冲区,就要记得刷新。
bufw.flush();
其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
字符读取流缓冲区:
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。
创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("buf.txt");
为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.print(line);
}
bufr.close();
通过缓冲区复制一个.java文件。
import java.io.*;
class CopyTextByBuf
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));
bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt"));
String line = null;
while((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
找到其参数的共同类型。通过多态的形式。可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
装饰模式比继承要灵活。避免了继承体系臃肿。
而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
演示mp3的复制。通过缓冲区。(也可以自己建立缓冲区看day19 MyBufferedInputStream)
字符流:
FileReader
FileWriter。
BufferedReader
BufferedWriter
字节流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
发现readLine方法其实就是读一行数据的原理。
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流在使用字符流缓冲区的readLine方法呢?
获取键盘录入对象。
InputStream in = System.in;
将字节流对象转成字符流对象,使用转换流。InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReader bufr = new BufferedReader(isr);
键盘的最常见写法。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));键盘录入,数据源
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));控制台,数据目的
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
1,源:键盘录入。
目的:控制台。
2,需求:想把键盘录入的数据存储到一个文件中。
源:键盘。
目的:文件。
3,需求:想要将一个文件的数据打印在控制台上。
源:文件。
目的:控制台。
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘。键盘
目的设备:内存,硬盘,控制台。
1,将一个文本文件中数据存储到另一个文件中。复制文件。
源:因为是源,所以使用读取流。InputStream Reader
是不是操作文本文件。
是!这时就可以选择Reader
这样体系就明确了。
接下来明确要使用该体系中的哪个对象。
明确设备:硬盘。上一个文件。
Reader体系中可以操作文件的对象是 FileReader
是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream Writer
是否是纯文本。
是!Writer。
设备:硬盘,一个文件。
Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
源:因为是源,所以使用读取流。InputStream Reader
是不是操作文本文件。不是!这时就可以选择InputStream
这样体系就明确了。
接下来明确要使用该体系中的哪个对象。
明确设备:硬盘。上一个文件。
InputStream体系中可以操作文件的对象是 FileInputStream
是否需要提高效率:是!。加入InputStream体系中缓冲区 BufferedInputStream.
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
目的:OutputStream Writer
是否是纯文本。
不是!OutputStream
设备:硬盘,一个文件。
OutputStream体系中可以操作文件的对象FileOutputStream
是否需要提高效率:是!。加入OutputStream体系中缓冲区 BufferedOutputStream
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
2,需求:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。
那么分别分析
源:InputStream Reader
是不是纯文本?
设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
但是FileWriter是使用的默认编码表。GBK.
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。
流是用来处理数据的。l
处理数据时,一定要先明确数据源,与数据目的 地(数据汇)。
数据源可以是文件,可以是键盘。
数据目的地可以是文件、显示器或者其他设备。l
而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理.转换处理等。
File类
用来将文件或者文件夹封装成对象l
方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
File类常见方法:
1,创建。
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。
boolean mkdirs():创建多级文件夹。
2,删除。
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回false。
void deleteOnExit();在程序退出时删除指定文件。
3,判断。
boolean exists() :文件是否存在.
记住在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。isFile():
isDirectory();
isHidden();
isAbsolute();
4,获取信息。
getName():
getPath():
getParent():
sop("parent:"+f.getParent());该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。如果相对路径中有上一层目录那么该目录就是返回结果。
getAbsolutePath()
long lastModified()
long length()
调用list方法的file对象必须是封装了一个目录。该目录还必须存在。
列出指定目录下文件或者文件夹,包含子目录中的内容。
也就是列出指定目录下所有内容。
因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。
在列出过程中出现的还是目录的话,还可以再次调用本功能。
也就是函数自身调用自身。
这种表现形式,或者编程手法,称为递归。
递归要注意:
1,限定条件。
2,要注意递归的次数。尽量避免内存溢出。
public static String getLevel(int level)
{
StringBuilder sb = new StringBuilder();
sb.append("|--");
for(int x=0; x<level; x++)
{
//sb.append("|--");
sb.insert(0,"| ");
}
return sb.toString();
}
public static void showDir(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]);
}
}
删除一个带内容的目录。
删除原理:
在window中,删除目录从里面往外删除的。
既然是从里往外删除。就需要用到递归。
File类常见方法:
1,创建。
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。
boolean mkdirs():创建多级文件夹。
2,删除。
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回false。
void deleteOnExit();在程序退出时删除指定文件。
3,判断。
boolean exists() :文件是否存在.
//记住在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。
isFile():
isDirectory();
isHidden();
isAbsolute();
4,获取信息。
getName():
getPath():
getParent():
sop("parent:"+f.getParent());//该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。如果相对路径中有上一层目录那么该目录就是返回结果。
getAbsolutePath()
long lastModified()
long length()
调用list方法的file对象必须是封装了一个目录。该目录还必须存在。
列出指定目录下文件或者文件夹,包含子目录中的内容。
也就是列出指定目录下所有内容。
因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。
在列出过程中出现的还是目录的话,还可以再次调用本功能。
也就是函数自身调用自身。
这种表现形式,或者编程手法,称为递归。
递归要注意:
1,限定条件。
2,要注意递归的次数。尽量避免内存溢出。
public static String getLevel(int level)
{
StringBuilder sb = new StringBuilder();
sb.append("|--");
for(int x=0; x<level; x++)
{
//sb.append("|--");
sb.insert(0,"| ");
}
return sb.toString();
}
public static void showDir(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]);
}
}
删除一个带内容的目录。
删除原理:
在window中,删除目录从里面往外删除的。
既然是从里往外删除。就需要用到递归。
Properties是hashtable的子类。
也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。
是集合中和IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。
FileInputStream fis = new FileInputStream("info.txt");
将流中的数据加载进集合。
prop.load(fis);
练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。
用于记录应用程序运行次数。
如果使用次数已到,那么给出注册提示。
很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了。
下一次在启动该程序,又重新开始从0计数。
这样不是我们想要的。
程序即使结束,该计数器的值也存在。
下次程序启动在会先加载该计数器的值并加1后在重新存储起来。
所以要建立一个配置文件。用于记录该软件的使用次数。
该配置文件使用键值对的形式。
这样便于阅读数据,并操作数据。
键值对数据是map集合。
数据是以文件形式存储,使用io技术。
那么map+io -->properties.
配置文件可以实现应用程序数据的共享。
打印流:
该流提供了打印方法,可以将各种数据类型的数据都原样打印。
字节打印流:
PrintStream
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
字符打印流:
PrintWriter
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流,Writer。
import java.io.*;
import java.util.*;
class SplitFile
{
public static void main(String[] args) throws IOException
{
//splitFile();
merge();
}
public static void merge()throws IOException
{
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=1; x<=3; x++)
{
al.add(new FileInputStream("c:\\splitfiles\\"+x+".part"));
}
final Iterator<FileInputStream> it = al.iterator();
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("c:\\splitfiles\\0.bmp");
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
public static void splitFile()throws IOException
{
FileInputStream fis = new FileInputStream("c:\\1.bmp");
FileOutputStream fos = null;
byte[] buf = new byte[1024*1024];
int len = 0;
int count = 1;
while((len=fis.read(buf))!=-1)
{
fos = new FileOutputStream("c:\\splitfiles\\"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
}
fis.close();
}
}
编码:字符串变成字节数组。
解码:字节数组变成字符串。
String-->byte[]; str.getBytes(charsetName);
byte[] -->String: new String(byte[],charsetName);
注意:
1、服务器中的GBK数据过去,如果iso8859-1不能正确解码,就返编码后用GBK解码。
2、GBK和UTF-8不能互相编译,编译错不能反编码,因为都支持中文。
3、GBK中的数据编码后有可能和UTF-8相同导致被UTF-8解码。比如“联通”。
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。
1,描述学生对象。
2,定义一个可操作学生对象的工具类。
思想:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。
所以可以使用TreeSet。
3,将集合的信息写入到一个文件中。
import java.io.*;
import java.util.*;
class Student implements Comparable<Student>
{
private String name;
private int ma,cn,en;
private int sum;
Student(String name,int ma,int cn,int en)
{
this.name = name;
this.ma = ma;
this.cn = cn;
this.en = en;
sum = ma + cn + en;
}
public int compareTo(Student s)
{
int num = new Integer(this.sum).compareTo(new Integer(s.sum));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public String getName()
{
return name;
}
public int getSum()
{
return sum;
}
public String toString()
{
return "student["+name+", "+ma+", "+cn+", "+en+"]";
}
}
class StudentInfoTool
{
public static Set<Student> getStudents()throws IOException
{
return getStudents(null);
}
public static Set<Student> getStudents(Comparator<Student> cmp)throws IOException
{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
Set<Student> stus = null;
if(cmp==null)
stus = new TreeSet<Student>();
else
stus = new TreeSet<Student>(cmp);
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
String[] info = line.split(",");
Student stu = new Student(info[0],Integer.parseInt(info[1]),
Integer.parseInt(info[2]),
Integer.parseInt(info[3]));
stus.add(stu);
}
bufr.close();
return stus;
}
public static void write2File(Set<Student> stus)throws IOException
{
BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
for(Student stu : stus)
{
bufw.write(stu.toString()+"\t");
bufw.write(stu.getSum()+"");
bufw.newLine();
bufw.flush();
}
bufw.close();
}
}
class StudentInfoTest
{
public static void main(String[] args) throws IOException
{
Comparator<Student> cmp = Collections.reverseOrder();
Set<Student> stus = StudentInfoTool.getStudents(cmp);
StudentInfoTool.write2File(stus);
}
IO包中的其他类
ObjectInputStream
public static void readObj()throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
public static void writeObj()throws IOException
{
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi0",399,"kr"));
oos.close();
}
public static final long serialVersionUID = 42L;
private String name;//私有不能序列化,就是不能被构造函数赋值
transient int age;//不能序列化,就是不能被构造函数赋值
static String country = "cn";//静态不能序列化,就是不能被构造函数赋值
Person(String name,int age,String country)
{
this.name = name;
this.age = age;
this.country = country;
}
public String toString()
{
return name+":"+age+":"+country;
}
管道流
import java.io.*;
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
public void run()
{
try
{
byte[] buf = new byte[1024];
System.out.println("读取前。。没有数据阻塞");
int len = in.read(buf);
System.out.println("读到数据。。阻塞结束");
String s= new String(buf,0,len);
System.out.println(s);
in.close();
}
catch (IOException e)
{
throw new RuntimeException("管道读取流失败");
}
}
}
class Write implements Runnable
{
private PipedOutputStream out;
Write(PipedOutputStream out)
{
this.out = out;
}
public void run()
{
try
{
System.out.println("开始写入数据,等待6秒后。");
Thread.sleep(6000);
out.write("piped lai la".getBytes());
out.close();
}
catch (Exception e)
{
throw new RuntimeException("管道输出流失败");
}
}
}
class PipedStreamDemo
{
public static void main(String[] args) throws IOException
{
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
in.connect(out);
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
RandomAccessFile
该类不是算是IO体系中子类。
而是直接继承自Object。
但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:只读r,,读写rw等。
如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close()
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*0);
raf.write("周期".getBytes());
raf.writeInt(103);
raf.close();
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中指针。
//raf.seek(8*1);
//跳过指定的字节数
raf.skipBytes(8);
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
DataInputStream与DataOutputStream
可以用于操作基本数据类型的数据的流对象。
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utf.txt"));
dos.writeUTF("你好");
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
用于操作字节数组的流对象。
ByteArrayInputStream :在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。
在流操作规律讲解时:
源设备,
键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:
控制台 System.out,硬盘FileStream,内存 ArrayStream。
用流的读写思想来操作数组。
数据源。
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());
数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1)
{
bos.write(by);
}
System.out.println(bos.size());
System.out.println(bos.toString());
bos.writeTo(new FileOutputStream("a.txt"));将内存中的数组缓存转化为硬盘的文件流对象。
flush();方法是把内存中的数据冲出来适用于bufferedoutputstream如果没有这个方法就不能读出数据。
PrintWrite相对于字符串写入流可以不用刷新,字符流刷新是因为在底层调用了字节流,然后将其存入一个数组,因为内部封装了缓冲,所以字符流需要刷新。
OutputStream中flush()存在的意义就是:刷新此输出流并强制写出所有缓存的输出字节。这里所说的缓存是指应用程序与IO设备之间的内存缓存区,这样做的是因为CPU读写内存的速度比读写IO设备要快得多,设置一个内存缓冲区可以提高CPU利用率。没刷新也会存的哦,只不过不是一写就存,而是写到一定的时候再存,这样提高写入提高效率。
flush() 是把缓冲区的数据强行输出,主要用在IO中,即清空缓冲区数据,
例如:在读写流(stream)的时候,数据是先被读到了内存中,再把数据写到文件中,当你数据读完的时候不代表你的数据已经写完了,因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了close()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之前先flush()。
I/O包中常用类层次关系图
1.I/O流
流是字节序列的抽象概念,文件是数据的静态存储方式,而流失指数据传输时的形态。输入输出流是相对程序而言的而不是相对文件而言。输入是输入给程序而不是文件,输出也是输出给程序而不是文件。
IO流类分为两大类:字节流类和过滤流类。程序用于直接操作目标设备所对应的类叫节点流类,节点流类只提供了读写字节的方法,通过它们只能往文件中写入字节或从文件中读取字节。在实际应用中,我们要往文件中写入或读取各种类型的数据,我们就必须先将其他类型的数据转换成字节数组后写入文件或是从文件中读取到的字节数组转换成其他类型,过滤流类就是为此设计的,它提供了专门读取其他类型数据的对应方法。
2.节点流
InputStream和OutputStream是抽象类,还不能表明具体对应那中IO设备,它们下面有许多子类,包括网络,管道,内存,文件等具体的IO设备。
FileInputStream和FIleOutputStream:用来操作磁盘文件。这两个类只提供了对字节数组或字节进行读取的方法,对于字符串的读写,还要用String.getBytes()进行转换。 FIleOutputStream没有使用缓存。
Reader和Writer也是抽象类,和InputStream,OutputStream相对应。它们是用来处理字符的。
FIleReader和FileWriter用来读取文件流中的字符。FileWriter使用了缓存。
PipedInputStream与PipedOutputStream。两个对象连接产生一个通道,这两个类主要用来完成线程之间的通信。
PipedReader与PipedWriter用于字符文本的管道通信。
ByteArrayInputStream与ByteArrayOutputStream,它们的作用是用IO流的方式来完成对字节数组内容的读写,来支持类似内存虚拟文件或内存映像文件的功能。ByteArrayInputStream包含一个内部缓存区,该缓存区存储从流中读取的字节,内部急速器跟踪read方法要提供的下一个字节,关闭ByteArrayInputStream无效,此类中的方法在关闭此流后仍可被调用而不会产生任何IOException。ByteArrayOutputStream实现了一个输出流,其中的数据被写入一个字节数组,缓存区会随着数据的不断写入而自动增长,可使用toByteArray()或toString()检索数据。
StringReader和StringWriter,字符串读写类。
2.过滤流
BufferedInputStream与BufferedOutputStream对其他节点流进行带缓存区的封装以达到性能的改善。
BufferedReader与BufferedWriter提供readLine()和newLine()方法。
ObjectInputStream与putStream:用于存储和读取对象的输入输出流类。
InputStreamReader与OutputStreamWriter:是字节流和字符流之间转换的类。
DataInputStream与DataOutputStream:提供了可以读写各种基本数据类型数据的各种写法。