IO流
IO流:
一、字节流:
1、InputStream 读入字节数据。
2、OutputStream 写出字节数据。
二、字符流:
1、Reader 读入字符数据。
2、Writer 写入字符数据。
字符流:
FileWriter练习:
public class WriterTest{
//创建文件流对象,必须处理或者抛出异常。
public static void main(String[] args){
//在外部创建文件流引用,不指定对象,方便内部处理异常。
FileWriter fw = null;
try{
//创建一个文件流对象,并初始化路径。必须初始化,FileWriter没有空数构造函数。
fw = new FileWriter("d:\\WriterTest.txt");
fw.write("fanbotest");//向流中写入数据。些时写入流中,并没写入指定文件中。
fw.flush();//刷新流中的数据至指定文件中。
}catch(IOException e){
System.out.println(e.toString());
}finally{
try{
if(fw !=null)
fw.close();//关闭流,并将流中数据刷新到指定文件中。
}catch(IOExceptione){
System.out.println(e.toString());
}
}
}
}
此例中,fw = new FileWriter("d:\\WriterTest.txt");如果此路径下已存在指定文件,那么此时创建的FileWriter流对象会将原有的文件覆盖掉。
要在原文件中续写内容,需要修改构造参数,如:
fw = new FileWriter("d:\\WriterTest.txt",true); 这时续写的内容会自动添加到原有文件的末尾。
注意:在向文件中续写内容时,要想换行,Windows只识别\r\n。
FileRader:
read()、read(char[] cbuf)、read(char[] int off,int len)、read(CharBuffer target):read方法的返回值都是int。参数是字符数组的,返回值是字符个数。当文件读到尾末时,返回值为-1。
文件读入方式一:读一个打印一个。
例如:
// 创建文件读入流,并指定路径需读的文件。
FileReader fr = new FileReader("F:\\abc.txt");
//循环读,直到读到尾末返回-1时结束,此时文件读完。
while(fr.read()!=-1){
System.out.println((char)fr.read());
}
fr.close();
文件读入方式二:读一个存一个,存满了一次全打印。
例如:
// 创建文件读入流,并指定路径需读的文件。
FileReader fr = new FileReader("F:\\abc.txt");
//定义一个字符数组,将取到的字符存入。
char[] ch =new char[1024];
int len = 0;
//循环读,直到读到末尾返回-1。
while((len =fr.read(ch))!=-1){
//将字符数组中的字符变成字符串,一次打印。
System.out.println(len+" "+new String(ch,0, len));
}
fr.close();
文件的拷贝练习:
//练习拷贝一个txt文件。
package IO_Test;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class ReaderTest_01 {
public static void main(String[] args) {
//创建读和写的文件流引用。
FileWriter fw = null;
FileReader fr = null;
try{
//创建目的文件和源文件。
fw = new FileWriter("D:\\copytest.txt");
fr = new FileReader("F:\\abc.txt");
//字符数组用来存储源文件中的字符数据。
char[] ch =new char[1024];
int len = 0;
while((len = fr.read(ch))!=-1){
fw.write(ch, 0, len);//将字符数组数据写入目的文件。
}
}catch(IOException e){
//捕到异常时,说明文件读写失败了,这时就让程序停止,抛出去让程序员处理。
throw new RuntimeException("文件错误。");
}finally{
if(fw !=null){
//注意:读入流和写出流在关闭时,要分别try。
try{
fw.close();
}catch(IOException e){
}
}
if( fr !=null){
try{
fr.close();
}catch(IOException e){
}
}
}
}
}
BufferedWriter:
缓冲区的出现是为了提高效率。
缓冲区在创建之前,必须先有流对象,也就是缓冲区没有空参数的构造函数。
只将流对象传入缓冲区的构造参数即可。
记住:只要用到缓冲区,就要刷新。
关闭缓冲区时,同时也关闭了流对象。
缓冲区多了一个newLine()方法:行分隔符,在Windows和linux里通用,跨平台的。Windows只识别\r\n,linux只识别\n。
BufferedReader:
BufferReader有一个readLind方法,可以一次读取一行数据。非常效率。返回值为String,返回的数据只包括回车符以前的数据,并不包含量终止符。如果读到末尾,返回null。
示例:
//创建读入流对象,指定操作文件。
FileReader fr = new FileReader("d:\\BufferedTest.txt");
//定义缓冲区,将流传入构造参数。
BufferedReader br = new BufferedReader(fr);
String str = null;
//读出缓冲区数据,一行一行的读。
while((str = br.readLine())!=null){
System.out.println(str);
}
br.close();
装饰设计模式:
对已有的对象进行功能增强。
定义类,将已有的对象传入,基于已有的功能,添加更强的功能。这个自定义的类叫做装饰类。
装饰类通常通过构造方法,接收被装饰的对象。
示例:
package ZhuangShiSheJi;
public class ZhuangShiTest {
public static void main(String[] args) {
//建立原有类对象。
Person p = new Person();
//建立装饰类对象,将原类对象传进来。
SuperPerson sp = new SuperPerson(p);
sp.superEat();//调用新类新方法。
}
}
//原有的类。
class Person{
public void eat(){
System.out.println("吃饭");
}
}
//装饰类。
class SuperPerson{
//建立原有类引用。
private Personp;
//将原类引用作为参数传进来。
SuperPerson(Person p){
this.p = p;
}
//建立新的装饰函数。比原有类的函数功能强大。
public void superEat(){
System.out.println("喝点小酒");
p.eat();
System.out.println("吃点心");
System.out.println("抽根烟");
}
}
装饰类和继承的区别:
装饰设计模式比继承更灵活,辟免了继承体系的臃肿,而且降低了类与类之间的关系。
装饰类因为增强已有对象,具备的功能和已有对象是相同的,只不过提供了更强功能,所以装饰类和被装饰类通常都是属于一个体系中的。
如:
MyReader//专门用于读取数据的类。
|--MyTestReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
MyReader//专门用于读取数据的类。
|--MyTestReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
LineNumberReader:
它是一个装饰类,跟踪行号的缓冲字符输入流。
setLineNumber(int):设置当前行号,从传入的参数int开始计起。
getLineNumber():获取当前行号。返回值为int。
// 创建读入流对象。
FileReader fr = new FileReader("D: \\ExceptionTest.java");
//创建行号跟踪流对象,并将读入流对象传入。
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
//设置行号从100开始递加。
lnr.setLineNumber(100);
while((line = lnr.readLine())!=null){
//打印出指定文件的每行内容,并将行号打印在前面。
System.out.println(lnr.getLineNumber()+" "+line);
}
lnr.close();
}
字节流:
InputStream读入 OutputStream写出
在使用字节流写出数据时,不用刷新,不关闭流,也可以写出数据。
OutputStream:
//创建字节文件写出流,并指定目的文件。
FileOutputStream os = new FileOutputStream("d:\\OutputStreamTest.txt");
//将字符串转换成字节数组,并写入指定文件中。
os.write("fanbo_test_OutputStream".getBytes());
os.close();
使用字节流读入文件有三种方式:
一、一个字节一个字节的读。
//创建文件读入流,并指定源文件。
FileInputStream fis = new FileInputStream("d:\\OutputStreamTest.txt");
int ch = 0;
//一个字节一个字节的读。
while((ch=fis.read())!=-1){
//将读到的每个字节转换成字符。
System.out.println((char)ch);
}
fis.close();
二、将源文件按字节读入数组中,再一次取出。
//创建文件读入流,并指定源文件。
FileInputStream fis = new FileInputStream("d:\\OutputStreamTest.txt");
//定义一个字节数组,将源文件的数据读到这里面。
byte[] b =new byte[1024];
int len = 0;//用len取得读到数据的长度。
while((len = fis.read(b))!=-1){
//将读入b字节数组的数据转换成字符串打印出。
System.out.println(new String(b,0,len));
}
fis.close();
三、用available方法定义一个刚好够的字节数组。
//创建文件读入流,并指定源文件。
FileInputStream fis = new FileInputStream("d:\\OutputStreamTest.txt");
//avalable()方法可以返回源文件中的字节数。
int num = fis.available();
//将字节数组的长度定义为源文件数据的长度。
byte[] b =new byte[num];
//直接把刚好长度的数组读进流。
fis.read(b);
//再把数据变成字符串打印出来。
System.out.println(new String(b));
fis.close();
此第三种方法,虽然最简便,但是如果操作一个巨大的文件,比如1G多的电影,这时内存会溢出。不安全。
所以,这三种读入方法,第二种最适用。
拷贝图片:
用字节流,用字符流也可以拷贝,但是很可能打不开,凡同操作媒体数据,都用字节流,不要用字符流。
代码:
package IO_Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyPictureTest {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try{
fis = new FileInputStream("e:\\fanbo.jpg");//源图片。
fos = new FileOutputStream("d:\\樊波.jpg");//目的图片。
byte[] b =new byte[1024];
int len = 0;
//读取图片,并写到目的。
while((len = fis.read(b))!=-1){
fos.write(b,0,len);
}
}catch(IOException e){
throw new RuntimeException("拷贝图片失败");
}finally{
try{
if(fis!=null)
fis.close();
}catch(IOException e){
System.out.println("读取流关闭失败");
}
try{
if(fos!=null)
fos.close();
}catch(IOException e){
System.out.println("写出流关闭失败");
}
}
}
}
拷贝Mp3:
通过字节流缓冲区拷贝。
部分代码:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:\\流浪记.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\copytest.mp3"));
int ch = 0;
while((ch = bis.read())!=-1){
bos.write(ch);
}
bis.close();
bos.close();
read和write的特点:
read在做类型提升,write在做强转,保证了原数据的不变化。
读取键盘录入:
System.out:对应的是标准输出设备,控制台。
System.in:对应的标准输入设备,键盘。返回值类型为:InputStream
回车符:\r-->13 \n-->10
练习:需求:通过键盘录入数据,录入一行打印一行,如果录入over,就停止录入。
import java.io.IOException;
import java.io.InputStream;
public class SystemInTest {
public static void main(String[] args)throws IOException {
//创建读入流,指定键盘录入。
InputStream is = System.in;
//创建缓冲区,记录读到的数据。
StringBuilder sb = new StringBuilder();
//循环操作读到的数据。
while(true){
int ch = is.read();//一个一个读。
if(ch=='\r')
continue;//如果读到\r继续循环读。
if(ch=='\n'){
String s = sb.toString();//读到\n时,就将读到的所有数据变成字符串。
if("over".equals(s))
break;//判断一下,如果字符串是over,就跳出结束。
System.out.println(s.toUpperCase());//打印字符串。
sb.delete(0, sb.length());//清空缓冲区。
}
else
sb.append((char)ch);//将读到的数据存到缓冲区里。
}
}
}
此方法较复杂,可以通过使用转换流InputStreamReader,使用readLine方法,更简便。
例如:
//创建键盘录入对象。
InputStream is = System.in;
//创建读入转换流,字节流转换为字符流。
InputStreamReader isr = new InputStreamReader(is);
//建立字符流缓冲区,它有readLine方法。
BufferedReader br = new BufferedReader(isr);
String line = null;
//循环录入数据,并读入。
while((line = br.readLine())!=null){
if("over".equals(line))//判断over结束。
break;
System.out.println(line.toUpperCase());//打印出来。
}
br.close();
}
写出流转换:
public class OutputStreamWriterTest {
public static void main(String[] args)throws IOException {
//字符流缓冲区-->字节读入转换流-->键盘录入。源:键盘。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//字符流缓冲区-->字节写出转换流-->输出。目的:控制台。
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
//循环录入一行,输出一行。
while((line=br.readLine())!=null){
if("over".equals(line))//结束标记。
break;
bw.write(line.toUpperCase());//写出。
bw.newLine();//换行。
bw.flush(); //刷新。
}
bw.close();
br.close();
}
}
键盘的最常见写法:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
操作流的基本规律:
三个明确:
一、明确源和目的。
源:输入流,InputStream Reader
目的:输出流,OutputStream Writer
二、操作的数据是不是纯文本。
是:字符流。
不是:字节流。
三、当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存、硬盘、键盘。
目的设备:内存、硬盘、控制台。
是否要提高效率?是:加入缓冲区。
需求:将键盘录入的数据保存到一个文件中。
分析:
源:InputStream Reader
是否为纯文件?是,Reader
设备:键盘,对应的对象是System.in。
System.in对应的是字节流,选Reader是为了操作方便,将字节流转成字符流。
那么就将System.in转成Reader。用到InputStreamReader。
InputStreamReaderisr = new InputStreamReader(System.in);
要提高效率吗?要。BufferedReader
BufferedReader= br = new BufferedReader(isr);
目的:OutputStream Writer
是否为纯文件?是。Writer
设备:硬盘。使用FileWriter。
FileWriter fw = newFileWriter("d:\\cc.txt");
需要提高效率:
BufferedWriter bw = new BufferedWriter(fw);
扩展:
要想把录入的数据按指定的编码表(utf-8),将数据存到文件中。
FileWriter是使用的默认编码表,GBK。
这时要使用的对象是转换流。OutputStreamWriter,只有转换流,在传参数时,才可以指定编码表。
OutputStreamWriter osw = new OutputStreamWriter("d:\\aa.txt","utf-8");
记住:转换流是字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流。
当使用转换流,将字定编码的流数据写入到某文件时,要想读取此文件,也得指定同样的编码才可以读取。同样,通过转换流指定编码:InputStreamWriter osw =new InputStreamWriter("d:\\aa.txt","utf-8");
System中有两不常用的方法:(介绍)
一、System.setIn(InputStream in):将键盘录入更改为指定读取流对象。
例:System.setIn(“d:\\fanbo.txt”);这时,数据就不在是键盘录入了,而是fanbo.txt中的数据内容。
二、System.setOut(printStream ps):将控制台更改为指定写入流对象。
例:System.setOut(“e:\\cc.txt”):这时,原来打印在控制台的数据,就存到了cc.txt里面了。
异常的日志信息:
系统信息:
File类
separator :跨平台的分隔符,在windows的绝对路径中\\表示分隔符,如c:\\fanbo\\fan.txt。但是\\不跨平台。可以用separator代替。如”c:”+File.separator+”fanbo”+File.separator+”fan.txt”。
创建File对象:
1、File f1 = newFile("c:\\abc\\a.txt");
2、File f2 = newFile("c:\\abc","a.txt");
3、File d = newFile("c:\\abc");
File f3 = new File(d,"a.txt");
1、2、3种创建方式都指向同一个路径。打印f1、f2、f3,得到所指的路径。
File类常见方法:
一、创建
booleancreatNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流相反,输出流对象一建立就创建文件,如果文件存在,会覆盖。
static File createTempFile(String XX,StringXX):创建临时文件。
二、删除
booleandelete():删除文件或者目录,目录必须为空才能删除,失败则返回false。
voiddeleteOnExit():在程序退出时删除指定文件。
三、判断
booleancanExecute():判断文件是否可以执行。
booleanexists():文件是否存在。
booleanmkdir():创建一级目录,就是创建文件夹,只能创建一级。"abc\\ab"就可以,"fdsk\\fdf\\dfsf\\fdfsd"就不可以。
booleanmkdirs():创建多级文件夹。
booleanisDirectory():是否是目录。
booleanisFile():是否是文件。
对一个文件或者目录判断时,必须要判断该文件对象是否存在,通过exists判断。(很容易出错) 注意:目录也可以是xxx.txt,它就不是文件。
booleanisHidden():是否是隐藏文件。
booleanisAbsolute():判断是不是绝对路径,文件不存在也可以判断。
四、获取
StringgetName():获取文件名字。
StringgetPath():获取相对路径。封装的什么路径,得到的就是什么路径。文件不存在也可以获取。
StringgetParent():获取父目录。返回的是绝对路径中的父目录,如果是相对路径,返回null。就是上一层目录,只返回一级。
StringgetAbsolutePath():获取绝对路径。文件不存在也可以获取。
longlastModified():返回时间,最后一次修改时间。
longlength():返回文件大小。
booleanrenameTo():改名字,类似剪切。
示例:
Filef1 = new File("c:\\Test.java");
Filef2 = new File("c:\\haha.java");
f1.renameTo(f2);将Test.java的名字改为haha.java
如果File f2 = new File("d:\\haha.java");这时f1调用renameTo方法,就将Test.java改名后,移到了d盘,就是又改名又移动,类似剪切。
File[] listRoots():返回的是File[],可以列出电脑里的有效盘符。
例:File[] f2 =File.listRoots();
for(File f :f2){
sop(f);
}
String[] list():列出指定目录下的所有文件或文件夹名称,包含隐藏文件,调用它必须是该对象封装的是目录。
例:File f1 = new File("e:");
String[] str = f1.list();
for(String s : str){
sop(s);
}
String[] list(FilenameFilter filter)过滤器文件名。
例:File f1 = new File("f:");
String[] str = f1.list(new FilenameFilter(){
public boolean accept(File dir,String name){
return name.endsWith("docx");
}
});
File[] listFiles():返回的是文件,可以对文件进行操作,String[] list()只是返回的文件名,不能进行操作。
例:File f1 = new File("f:");
File[] str = f1.listFiles();
for(File s : str){
sop(s.getName()+" "+s.length());
}
练习:列出目录下所有内容。(递归)
递归要注意:
1、限定条件。
2、要注意递归的次数。尽量避免内存溢出。
package IO_Test;
import java.io.File;
public class DiGuiTest {
public static void main(String[] args) {
Filef = newFile("E:");//指定要列出所有目录的父目录。
getDrictory(f);
}
//创建获取方法:获取指定目录下的所有文件。
public static void getDrictory(File dir){
File[]files = dir.listFiles();//将指定目录的子目录存到File数组中。
System.out.println(dir);//打印父目录。
//循环打印子目录及文件。
for(int x=0; x<files.length; x++){
if(files[x].isDirectory())//如果有子目录,就使用递归,将子目录的文件打印。
getDrictory(files[x]);
else
System.out.println(files[x]);//打印子目录。
}
}
}
练习:删除带内容的目录。(用到递归)
package IO_Test;
import java.io.File;
public class DeleteTest {
public static void main(String[] args) {
File file = new File("d:\\fanbo");//指定要删除的目录及里面的所有内容。
deleteFile(file);//调删除方法。
}
//定义删除方法。
public static void deleteFile(File f){
//传入要删除的目录,再将些目录的子目录装进File[]数组。
File[] files = f.listFiles();
for(int x=0; x<files.length;x++){
//判断,如果是目录,就递归。
if(files[x].isDirectory()){
deleteFile(files[x]);
}
Else
System.out.println(files[x]+"::--::"+files[x].delete());//删除文件。
}
System.out.println(f+"::--::"+f.delete());//将已删掉子文件的空目录也删掉。
}
}
练习:创建java文件列表。(和毕老师的代码不一样)
package IO_Test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class GetFileDirectoryTest {
public static void main(String[] args)throws IOException {
//定义要创建列表的目录。
File file = new File("E:\\中关村-黑马程序员\\Java零基础辅导班 - 李若亮");
//将目录存到当前的txt中。
FileWriter fw = new FileWriter("E:\\中关村-黑马程序员\\Java零基础辅导班 - 李若亮\\li.txt");
getFileDirectory(file,fw);//调用自定义获取方法。
}
//制作获取方法,参数为要创建的目录,和要写出的数据流,
public static void getFileDirectory(File file,FileWriter fw)throws IOException{
File[] files = file.listFiles();
for(File f : files){
if(f.isDirectory())//如果是目录,就递归。
getFileDirectory(f,fw);
fw.write(f.getAbsolutePath()); //获取绝对路径,并写到目的txt文件中。
fw.write("\r\n");//存一个就换行。
}
}
}
Propertise
Properties是hashtable的子类。
它具备map集合的特点,它里面存储的健值对都是字符串。就不用泛型了。
是集合和IO技术相结合的集合容器。
该对象的特点,可以用于键值对形式的配置文件ini(就是软件的一些属性,比如设置颜色corlor=50)。(文本=红色,背景=绿色)
Properties的存取方法:
setProperty(Stringstr1,String str2):直接存入键和值。
getProperty(Stringstr):str为键。返回value值。
stringPropertyNames():返回值的是Set集合。将键存入到Set集合中。
Properties已经封装好了将配置文件(键值对的文件)中的数据通过流加载进集合。
在加载数据时,需要数据有固定格式:键=值。
直接用load(InputStream is)和load(Reader read)两个方法就能将流数据加载进集合。
list(PrintStream ps)将属性列表输出到指定的输出流。如:XXX.list(System.out)就将键值信息打印到控制台上。
store(OutputStream out, String comments)方法,能将集合中修改过的数据存储到流的目的文件中。
练习:用于记录应用程序运行次数,使用次数已到,那么给出注册提示。
打印流:
PrintStream:字节打印流。
构造函数可以接收的参数类型:
1、file对象。File
2、字符串路径。String
3、字节输出流。OutputStream
PrintWriter:字符打印流。很常用。
构造函数可以接收的参数类型:
1、file对象。File
2、字符串路径。String
3、字节输出流。OutputStream
4、字符输出流。Writer
打印流:该流提供了打印方法,可以将各种数据类型都原样打印。
示例:
public class PrintStream {
public static void main(String[] args)throws IOException {
//创建键盘录入。
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
//PrintWriter pw = newPrintWriter(System.out);//打印流,直接传入控制台,需要刷新。
//PrintWriter pw = newPrintWriter(System.out,true);//加true参数,可以不用刷新,直接输出。
//PrintWriter pw = newPrintWriter("d:\\fanbo.txt");//直接传入文件,不能刷新。
PrintWriter pw = new PrintWriter(new FileWriter("d:\\fanbo.txt"),true);//加上文件写出流,就可以加true自动刷新了。
String line = null;
while((line = bfr.readLine())!=null){
if("over".equals(line))
break;
pw.println(line.toUpperCase());
pw.flush();
}
bfr.close();
pw.close();
}
合并流:SequenceInputStream
将多个流合并成一个流。例如:把三个txt文档的内容合到一个txt文档中,先建立3个读取流,再使用SequenceInputStream流把3个读取流合成一个流。
它有两种构造函数:
1、SequenceInputStream(InputStream s1,InputStream s2):将2读取流合成一个流。
2、SequenceInputStream(Enumeration<InputStream> e):把多个读取流合成一个,参数是枚举,就是先将多个流存到Vector集合中,然后再合成一个。
示例:三流合一流。
public class SequenceInputStreamTest {
public static void main(String[] args)throws IOException {
//建立Vector集合,泛型指定存入文件读入流对象。
Vector<FileInputStream> v = new Vector<FileInputStream>();
//添加3个文件读入流对象。
v.add(new FileInputStream("d:\\完美国际名字.txt"));
v.add(new FileInputStream("d:\\题谷公司地址.txt"));
v.add(new FileInputStream("d:\\MySQL路径.txt"));
//枚举。
Enumeration<FileInputStream> en = v.elements();
//定义合并流,将en传入,三流变成一流。
SequenceInputStream sis = new SequenceInputStream(en);
//定义文件写出流,并指定目的。
FileOutputStream bis = new FileOutputStream("d:\\三合一.txt");
byte[] b =new byte[1024];
int len = 0;
while((len = sis.read(b))!=-1){
bis.write(b,0,len);
}
sis.close();
bis.close();
}
}
切割文件:
和合并流相反,将某文件切割成几份。
示例:
public class SplitFileTest {
public static void main(String[] args)throws IOException {
//定义读取流,确定要被切割的源文件。
FileInputStream fis = new FileInputStream("d:\\许佳慧-预谋.mp3");
FileOutputStream fos = null;//先声明一个文件写出流。
byte[] b =new byte[1024*1024];//定义一个1M的容器。
int len = 0;
int count = 1;
while((len = fis.read(b))!=-1){
//将刚好1M的容器数据写入到指定目的。计数器count自增后,下次就另存一个目的文件。
fos = new FileOutputStream("d:\\"+(count++)+".part");
fos.write(b, 0, len);
fos.close();
}
fis.close();
}
}
IO包中的其他类:
ObjectInputStream ObjectOutputStream
ObjectOutputStream构造时必须有一个流对象。
writeObject和readerObjet成对使用。
要序列化的对象必须实现Serializable接口,Serializable是一个标记接口,没有方法。
当某个对象已经持久化存到硬盘了,再改动过代码,再存,这时就读不出改过后的对象了,因为改动前已经有一个序列号UID,改动后生成了新的UID,不一样就识别不出来。UID是根据成员属性来生成的序列号。
如果你想改动后仍是原来的对象,那么自定义固定的UID,不让虚拟机自动生成。
如:public static final long serialVersionUID= 42L;
如果对象中有静态成员,将不能被序列化。
如果不是静态成员,又不想被序列化,就加transient修辞。
一般将对象序列存为xxx.object。
写出示例:
//创建对象写出流。
ObjectOutputStreamoos =new ObjectOutputStream(new FileOutputStream("d:\\person.object"));
oos.writeObject(new Person("樊波",20));//指定要写出的对象。
oos.close();//关流。
读取示例:
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\person.object"));
Person p = (Person)ois.readObject();//读取对象。
System.out.println(p);
ois.close();
管道流:
PipedInputStream和PipedOutputStream
涉及到多线程的IO流,输出流和输入流同时进行。
如何连接?
1、构造函数就对方的流传进来。如:PipedInputStream(PipedOutputStream src);
2、通过方法connect(PipedOutputStreamsrc)连接。
示例:
import java.io.*;
class Read implements Runnable
{
private PipedInputStreamin;
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 PipedOutputStreamout;
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 PipedStreamTest
{
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,操作的文件不存,会自动创建,如果存在则不会覆盖。
可以对数据进行分段存储,比如三条线程,分段写进某一文件。
示例:
import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] args)throws IOException
{
//writeFile_2();
//readFile();
//System.out.println(Integer.toBinaryString(258));
}
public static void readFile()throws IOException
{
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();
}
public static void writeFile_2()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*0);
raf.write("周期".getBytes());
raf.writeInt(103);
raf.close();
}
public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}
DataStream:
DataInputStream和DataOutputStream
操作基本数据类型的流对象。
示例:
import java.io.*;
class DataStreamDemo
{
public static void main(String[] args)throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(newFileOutputStream("gbk.txt"),"gbk");
// osw.write("你好");
// osw.close();
// readUTFDemo();
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");
dos.close();
}
public static void readData()throws IOException
{
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();
}
public static void writeData()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
ObjectOutputStream oos = null;
oos.writeObject(new O());
}
}
ByteArrayStream:
初始化时必须有源数据。关闭此流无效,此类中的方法在关闭此流后仍可被使用,而不会产生任何IOException。
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());
与之对应的类还有:
1、CharArrayReader、CharArrayWriter
功能同理。
转换流的字符编码:
//编码。向指定流文件中写入“你好”,按UTF-8编码。
OutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
osw.write("你好");
osw.close();
//解码。读取流文件中的数据,按gbk解码。
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
char[] buf =new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
表编码后再用另一种码表解码,会出现乱码。
表:2个字节一个字符。
8码表:3个字节一个字符。
字符编码:
编码:字符串变成字节数组。
解码:字节数组变成字符串。
String-->byte[]; str.getBytes(charsetName);字符串变成字节数组,按参数charseName编码。
byte[] -->String: new String(byte[],charsetName);字节数组变成字符串,按参数charseName解码。
注意:
用GBK编码,若用iso8859-1解码,无凝出现乱码,这时将乱码用iso8859-1编码,再用GBK解码,能获得原有数据,情况出现在网页向Tomcat服务器传数据。
若用GBK编码,再用UTF-8解码,无凝出现????乱码,再将乱码用UTF-8编码,再用GBK解码,不能获得原有数据。GBK和UTF-8都能识别中文。
String s = "你好";//原数据。
byte[] b1 = s.getBytes("GBK");//用GBK编码。
System.out.println(Arrays.toString(b1));
String s1 = new String(b1,"utf-8");//用UTF-8解码。
System.out.println("s1="+s1);
byte[] b2 = s1.getBytes("utf-8");//用UTF-8编码。
System.out.println(Arrays.toString(b2));
String s2 = new String(b2,"gbk");//和GBK解码。
System.out.println("s2="+s2);
联通:
以“联通”两个字打头存入记事本,这时是以默认GBK码表存入数据。当再次打开记事本时,这时去以UTF-8码表解码,出现乱码。
原因:“联通”这两个字在编码时存入时的二进制数字第一位为110****,第二位10****,符合了UTF-8码表的头识别,在打开记事本时,就自动用UTF-8解码了。
练习:
/*
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。
1,描述学生对象。
2,定义一个可操作学生对象的工具类。
思想:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。
所以可以使用TreeSet。
3,将集合的信息写入到一个文件中。
*/
package IO_Test;
import java.io.*;
import java.util.*;
public class SaveStudentsTest {
public static void main(String[] args)throws NumberFormatException,IOException {
TreeSet<Student> trse = new TreeSet<Student>();//创建TreeSet集合,用来存学生。
StudentTool.setStudent(trse);//调用添加学生信息的函数。
StudentTool.saveStudent(trse);//调用将学生信息存到指定文档中的函数。
}
}
//学生工具类,有添加学生信息的功能和将学生信息存到指定文档中的功能。
class StudentTool{
//添加学生信息的函数
public static void setStudent(TreeSet<Student> ts)throwsNumberFormatException, IOException{
//键盘录入。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = br.readLine())!=null){
if("over".equals(line))//over为结束标记。
break;
String[] s = line.split(",");//将录入的信息用,号分割。
//将录入的学生信息包成对象。
Student stu = new Student(s[0],Integer.parseInt(s[1]),Integer.parseInt(s[2]),Integer.parseInt(s[3]));
ts.add(stu);//将学生对象存入集合。
}
}
//将学生信息存到指定文档中的函数
public static void saveStudent(TreeSet<Student> ts)throws IOException{
//指定写出流对象,指定要写入的文档。
BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\student.txt"));
for(Student s : ts){
bw.write("姓名:"+s.getName()+" 总分:"+s.getSum());//向文档中写入学生姓名和总分。
bw.newLine();
bw.flush();
}
bw.close();
}
}
//学生类。
class Studentimplements Comparable<Student>{
//属性有姓名,数学分数、语文分数、英语分数,总分。
private Stringname;
private int math;
private int chinese;
private int english;
private int sum;
//构造函数。
Student(String na,int ma,int ch,int en){
this.name = na;
this.math = ma;
this.chinese = ch;
this.english = en;
this.sum = ma+ch+en;
}
public String getName(){
return name;
}
public int getSum(){
return sum;
}
public int hashCode(){
return name.hashCode()+sum*22;
}
public boolean equals(Object obj){
if(!(objinstanceof Student))
throw new ClassCastException("类型不符");
Student s = (Student)obj;
return this.name.equals(s.name) &&this.sum==s.sum;
}
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;
}
}