IO流
概述
IO流是用来处理设备上数据的一种技术。该技术都被封装成不同的类,存放在java.io包中,供开发者使用。注意:流中所有的方法都有可能抛出异常,因此需解决异常问题。
1) 流按操作数据分为两种:字节流(通用流)与字符流
2) 流按流向分为两种:输入流,输出流
拓展:
字符流要用到编码表,写程序的时候可以指定编码表,这样就不至于乱码。编码表主要的特点就是映射。
在外国用的是ASCII码表:将字母与01二进代码对应起来
在中过用到是GB2312码表(之后扩容GBK):将汉子与01二进制代码对应起来
把各国的文字统一起来,就有了国际标准码表,叫做Unicode码表。
该码表的特点:Unicode里面的无论什么字符都用两个字节表示,虽然能表示很多个字符,但是,有些字符只要8为就可以搞定,因此有点浪费空间,这时就出来一个UTF-8码表,即一个字节能装的下就用一个字节装,一个装不下就用两个表,按需分配。
IO流常用的基类:
字节流的抽象基类:InputStream(读)、OutputStream(写)。
字符流的抽象基类:Reader(读)、Writer(写)
Ps:这四个类派生出来的子类名称都是以其父类名作为子类的后缀。如:
InputStream的子类FileInputStream
Reader的子类FileReader
温馨提示:流对象对同一个文件进行读和写操作,要注意是否需要将读方法和写方法对应(比如readInt()和writeInt())从而保证读出来的内容和写进去的内容一样。字符流中的读写方法是以字符为单位的,字节流中的读写方法是以字节为单位的。
字符流
一、FileWriter
1、往文件中写入文字
在硬盘上,创建一个文件并写入一些文字数据。
import java.io.*;
class FileWriterDemo
{
/*
所用到的方法:
FileWriter(),write(),flush(),close()
都有可能发生I/O错误,都会抛出I/O异常。
*/
public static void main(String[] args)throws IOException
{
/*
创建一个FileWriter对象。
该对象一被初始化就必要确定被操作的文件。
而且该文件会被创建到指定目录下。
如果该目录下已有同名文件,将被覆盖。
其实该步就是在明确数据要存放的目的地。
现在是在当前目录下。
*/
FileWriter fw=new FileWriter("demo.txt");
/*
调用write(),将字符串写入到流缓冲区中。
*/
fw.write("hello java");
/*
刷新流缓冲区中的数据,
将数据刷新到目的地。
*/
fw.flush();//可以省略不写。
/*
关闭流,但是关闭之前会刷新一次内部缓冲区中
的数据,将数据刷新到目的地中。它和flush的区
别在于,flush刷新后,流还可以继续使用,close
刷新后,会将流关闭。
若流多时,需要一个一个关闭。
*/
fw.close();
}
}
//其实,IO流在调用windows底层资源,
//具体的工作都是windows底层自己做,
//很好地体现了平台无关性。
2、异常处理
|--IOException
|--FileNoFoundException
代码如下:
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw=null;
try
{
fw=new FileWriter("demo.txt");
fw.write("agasfsdgs");
}
catch (IOException e)
{
System.out.println("catch1="+e.toString());
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.println("catch2="+e.toString());
}
}
}
}
3、文本文件的续写
续写是指,我们进行第二次编译运行时,如果指定的目录
存在,则不去覆盖,而是接着已有内容写入数据。
代码如下:
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args)throws IOException
{
/*
创建流对象时,往里边再传递一个参数true,
代表不覆盖已有的文件。并在已有的文件的末尾
处进行数据的续写。
续写有两种方式:
方式一:
通过改动write()方法中的参数。
方式二:
通过连续调用write(),较麻烦。
*/
FileWriter fw=new FileWriter("demo.txt",true);
fw.write("haofahfao;\r\nfjw");
fw.close();
}
}
ps:我们知道,创建一个写入文本文件流对象之后,我们通过调用write(String str),指定的内容可能先是被写入到了流缓冲区中,然后通过调用flush()或者是close()才将缓冲区中的内容刷到目的地。目前还没有学习到BufferedWriter,因此认为指定的内容都是被写入到目的地。
4、BufferedWriter
该类称之为字符流的缓冲区。缓冲区的出现提供了对数据
写入速度。要结合流才可以使用,在流的基础上对流的功 能进行了增强。它同时提供了一个跨平台的换行符: newline()。引入了缓冲区之后,一些写入、关流、刷 新 等方法 都由缓冲区对象进行调用。需要注意的是, 用到 缓冲区, 就要记得刷 新, 防止断电,数据流 失。
示例:
import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args)throws IOException
{
/*
创建一个字符写入流对象。
*/
FileWriter fw=new FileWriter("buf.txt");
/*
为了提高字符写入流效率,加入缓冲区技术。
只要将需要被提高效率的流对象作为参数传递给缓冲区
的构造函数即可。
*/
BufferedWriter bufw=new BufferedWriter(fw);
for(int x=1;x<=10;x++)
{
bufw.write("abcd"+x);
bufw.newLine();
bufw.flush();
}
bufw.flush();
bufw.close();
}
}
二、FileRead
1、文本文件的读取
方式一:
import java.io.*;
class FileReadDemo
{
public static void main(String[] args)throws IOException
{
/*
创建一个FileRead对象,
和指定名称的文件相关联,
要保证该文件是已经存在的,
如果不存在会发生异常FileNotFoundException。
*/
FileReader fr=new FileReader("demo.txt");
/*
调用read(),
该方法一次读取一个字符,
而且会自动往下读;
该方法返回值类型是int类型,到达流的末尾返回-1,
将读取到的字符以整数形式
展现。
*/
int ch = fr.read();
System.out.println("ch="+(char)ch);
int ch1 = fr.read();
System.out.println("ch1="+(char)ch1);
int ch2 = fr.read();
System.out.println("ch2="+(char)ch2);
fr.close();
}
}
优化:
import java.io.*;
class FileReadDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("demo.txt");
int ch=0;
while((ch=fr.read())!=-1)
{
System.out.println((char)ch);
}
/*
while(true)
{
if((ch=fr.read())!=-1)
System.out.println((char)ch);
else
break;
}
*/
fr.close();
}
}
方式二:
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("demo.txt");
/*
定义一个字符数组,用于存储读到的字符。
*/
char[] buf=new char[1024];
/*
通过int read(char[] buf),
将读到字符放进数组,
返回值表示读到的字符个数,
这个个数由数组的长度(2)和流中剩余字符个数(1)决定。
自动往下读,
当读到流的末尾,返回-1。
再通过创建String类型,
将字符数组中的内容换成
字符串打印输出。
*/
int num=0;
while((num=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,num) );
}
fr.close();
}
}
2、文本文件读取练习
读取一个文件,并打印到控制台上。
import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("SystemDemo.java");
char[] buf=new char[1024];
int num=0;
while((num=fr.read(buf))!=-1)
{
System.out.println(new String(buf,0,num) );
}
fr.close();
}
}
运行结果如下图所示:
发现,打印出来的文件排版都没有变化。但是,我们想,当我们的文本文件大于2kB, 那么打印出来的排版格式一定会发生改变,因为我们不是接着打印的。因此我们通常将 System.out.println()换成System.out.print()。
ps:我们知道,创建读取文本文件流对象,我们通过调用read(),或者是read(char[] buf)读取数据。当到达流末尾时,返回的是-1.
3、BufferedReader
该类称之为字符读取缓冲区。缓冲区的出现提供了对数据
读出速度。要结合流才可以使用,在流的基础上对流的功 能进行了增强,通过提供一个一次读一行的方法:String readLine():当返回null时,表示读到文件末尾,该 方 法返回的时候只返回回车符之前的数据内容,并不返 回回 车符。引入了缓冲区之后,一些写入、关流、刷 新 等 方 法 都由缓冲区对象进行调用。
示例:
import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args)throws IOException
{
/*
创建一个读取流对象和文件相关联。
*/
FileReader fr=new FileReader("buf.txt");
/*
为了提高效率。加入缓冲技术。
将字符读取流对象作为参数传递给缓冲对象的构造函数。
*/
BufferedReader bufr=new BufferedReader(fr);
String line=null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
}
拓展:
|--BufferedReader
|--LineNumberReader
① 跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可 分别用于设置和获取当前行号。
② 默认情况下,行编号从 0 开始。该行号随数据读取 在每个行结束符处递增,并且可以通过调用 setLineNumber(int) 更改行号。但要注意的是, setLineNumber(int) 不会实际更改流中的当前位 置;它只更改将由 getLineNumber() 返回的值。
③ 可认为行在遇到以下符号之一时结束:换行符 ('\n')、回车符('\r')、回车后紧跟换行符。
示例:
import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("SystemDemo.java");
LineNumberReader lnr=new LineNumberReader(fr);
String line=null;
lnr.setLineNumber(6);
while((line=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+"::"+line);
}
}
}
运行结果如下图所示:
三、FileWriter和FileReader的搭配应用
需求:将C盘下的一个文本文件复制到D盘下。
方式一:简洁版
import java.io.*;
class CopyText
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("C:\\java.txt");
FileWriter fw=new FileWriter("D:\\java_copy.txt");
int ch=0;
while((ch=fr.read())!=-1)
{
fw.write((char)ch);
}
fr.close();
fw.close();
}
}
方式二:复杂版
import java.io.*;
class CopyText
{
public static void main(String[] args)
{
FileReader fr=null;
FileWriter fw=null;
try
{
fr=new FileReader("C:\\java.txt");
fw=new FileWriter("D:\\java_copy.txt");
int num=0;
char[] buf=new char[1024];
while((num=fr.read(buf))!=-1)
{
fw.write(buf,0,num);
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
try
{
if(fr!=null)
fr.close();
}
catch (IOException e)
{
}
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
}
}
}
}
四、BufferedReader和BufferedWriter的搭配使用
方式一:(简洁版)
import java.io.*;
class CopyTextBybuf
{
public static void main(String[] args)throws IOException
{
FileReader fr=new FileReader("C:\\java.txt");
FileWriter fw=new FileWriter("D:\\java_copy.txt");
BufferedReader bufr=new BufferedReader(fr);
BufferedWriter bufw=new BufferedWriter(fw);
String line=null;
while((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
}
}
方式二:(复杂版)
import java.io.*;
class CopyTextBybuf
{
public static void main(String[] args)
{
FileReader fr=null;
FileWriter fw=null;
BufferedReader bufr=null;
BufferedWriter bufw=null;
try
{
fr=new FileReader("C:\\java.txt");
fw=new FileWriter("D:\\java_copy.txt");
bufr=new BufferedReader(fr);
bufw=new BufferedWriter(fw);
String line=null;
while((line=bufr.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("创建失败");
}
finally
{
if(bufr!=null)
try
{
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
if(bufw!=null)
try
{
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
装饰设计模式
一、什么是装饰设计模式
当想要对已有的对象进行功能增强时,又不想对已有功能 进行修改。此时可以定义一个新的类,建立新类的对象, 将已有对象传入,通过构造方法接收该已有对象,基于 已有对象的功能,提供更强的功能。那么自定义类称为装 饰类。
如:
class Person
{
public void chiFan()
{
System.out.println("吃饭");
}
}
//自定义一个类,作为装饰类。
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p=p;
}
public void superChiFan()
{
System.out.println("开胃酒");
p.chiFan();
System.out.println("甜点");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p=new Person();
SuperPerson sp=new SuperPerson(p);
sp.superChiFan();
}
}
二、装饰和继承的区别
想要给已有对象提供更强的功能:
1.如果是用继承来做
|--MyReader
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
2.如果是用装饰来做
|--MyReader
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
多态思想:
class MyBufferReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
3、总结
综上所述,装饰模式比继承要灵活,避免了继承体系的臃肿。单独描述一下缓冲内容,将需要被缓冲的对象传递进来。
三、自定义装饰类
import java.io.*;
class MyBuffererReader extends Reader
{
private Reader r;
MyBufferedReader(Reader r)
{
this.r=r;
}
public String myReadLine()throws IOException
{
StringBuffer sb=new StringBuffer();
int ch=0;
while((ch=r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString()
else
sb.append(ch);
}
if(sb.length()!=-1)
return sb.toString();
return null;
}
public void myClose()throws IOException
{
r.close();
}
//复写Read类中的抽象方法
public void close()throws IOException
{
r.close();
}
public int read(char[] cbuf,int off,int len)throws IOException
{
return r.read(cbuf,off,len);
}
}
字节流
字节流对象在写如数据时,无论什么情况下都不需要刷 新,直接往目的地里,因为它是最小的单位。处理图片都 需要用字节流。
一、FileInputStream
1、需求:
读取一个图片文件。
代码如下:
import java.io.*;
class FileInputStreamDemo
{
public static void main(String[] args)throws IOException
{
readFile_3();
}
//方法一:
/*
通过int read():从此输入流中读取下一个数据字节。
返回一个 0 到 255 范围内的 int 字节值。
如果因为已经到达流末尾而没有字节可用,则返回 -1。
*/
public static void readFile_1()throws IOException
{
FileInputStream fis=new FileInputStream("C:\\123.jpg");
int ch=0;
while ((ch=fis.read())!=-1)
{
System.out.print((char)ch);
}
fis.close();
}
//方法二:
/*
通过int read(byte[] b) :
从此输入流中将 byte.length 个字节的数据读入一个 byte 数组中。
如果因为已经到达流末尾而没有更多的数据,则返回 -1。
*/
public static void readFile_2()throws IOException
{
FileInputStream fis=new FileInputStream("C:\\123.jpg");
int num=0;
byte[] buf=new byte[1024];
while ((num=fis.read(buf))!=-1)
{
System.out.print(new String(buf,0,num));
}
fis.close();
}
//方法三:
/*
通过调用available():获取文件中总的字节数。
根据该数目定义一个刚刚好的缓冲区,不用再循环。
*/
public static void readFile_3()throws IOException
{
FileInputStream fis=new FileInputStream("C:\\123.jpg");
byte[] buf=new byte[fis.available()];
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
}
2、BufferedInputStream
与BufferedReader功能类似,可以参照学习,
此处不做过多阐述。
二、FileOutputStream
1、需求:
写入一个文件。
代码如下:
import java.io.*;
class FileOutputStreamDemo
{
public static void main(String[] args)throws IOException
{
FileOutputStream fos=new FileOutputStream("C:\\fos.txt");
fos.write("coanoafds".getBytes());
fos.close();
}
}
2、BufferedOutputStream
与BufferedWriter功能类似,可以参照学习,
此处不做过多阐述。
三、FileInputStream和FileOutputStream的搭配应用
需求:将C盘下的图片文件复制到D盘下。
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
fis=new FileInputStream("C:\\123.jpg");
fos=new FileOutputStream("D:\\123_copy.jpg");
int ch=0;
while((ch=fis.read())!=-1)
{
fos.write((char)ch);//也可以不强转
}
}
catch (IOException e)
{
throw new RuntimeException("复制失败");
}
finally
{
if(fis!=null)
try
{
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("读取流关闭失败");
}
if(fos!=null)
try
{
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("写入流关闭失败");
}
}
}
}
四、BufferedInputStream和BufferedOutputStream的搭配应用
1、需求:将C盘下的图片文件复制到D盘下。
import java.io.*;
class CopyPicBybuf
{
public static void main(String[] args)
{
long star=System.currentTimeMillis();
copy();
long end=System.currentTimeMillis();
System.out.println("time="+(end-star));
}
public static void copy()
{
FileInputStream fis=null;
FileOutputStream fos=null;
BufferedInputStream bufis=null;
BufferedOutputStream bufos=null;
try
{
fis=new FileInputStream("C:\\123.jpg");
fos=new FileOutputStream("D:\\123_copy.jpg");
bufis=new BufferedInputStream(fis);
bufos=new BufferedOutputStream(fos);
int ch=0;
while ((ch=bufis.read())!=-1)
{
bufos.write((char)ch);
}
}
catch (IOException e)
{
throw new RuntimeException("复制失败");
}
finally
{
if(bufis!=null)
try
{
bufis.close();
}
catch (IOException e)
{
throw new RuntimeException("读取流关闭失败");
}
if(bufos!=null)
try
{
bufos.close();
}
catch (IOException e)
{
throw new RuntimeException("写入流关闭失败");
}
}
}
}
运行结果如下图所示:
2、自定义读取字节流的缓冲区
import java.io.*;
class CopyPicByselfbuf
{
public static void main(String[] args)throws IOException
{
long star=System.currentTimeMillis();
copy();
long end=System.currentTimeMillis();
System.out.println();
}
public static void copy()throws IOException
{
FileInputStream fis=new FileInputStream("C:\\123.jpg");
FileOutputStream fos=new FileOutputStream("D:\\123_copy.jpg");
MyBufferedInputStream mbufis=new MyBufferedInputStream(fis);
BufferedOutputStream bufos=new BufferedOutputStream(fos);
int ch=0;
while((ch=mbufis.myRead())!=-1)
{
bufos.write(ch);
}
mbufis.myClose();
bufos.close();
}
}
class MyBufferedInputStream
{
private FileInputStream in;
private byte[] buf=new byte[1024];
private int pos=0;//字节数组的角标
private int count=0;//字节数组的元素个数
MyBufferedInputStream(FileInputStream in)
{
this.in=in;
}
public int myRead()throws IOException
{
//通过in对象读取硬盘上的数据,并存储buf中。
if(count==0)
{
count=in.read(buf);
if(count<0)
return -1;
pos=0;
byte b=buf[pos];
count--;
pos++;
return b;
}
else if(count>0)
{
byte b=buf[pos];
count--;
pos++;
return b;
}
else
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
编译运行之后,发现,图片副本内容为空。
分析:
我们知道,在内存中文件存在的形式是以01二进制代码形式存在的。myRead()方法 每次返回的是一个int类型,我们想,如果第一个字节b的二进制形式是1111 1111, 那么它 被提升之后,1111 1111 1111 1111 1111 1111 1111 1111(-1),那 么copy()中的while()循环条件成立,流直接关闭,没有做任何事情,导致图片副 本为空。因此我们想在前面补0,这样既可以保留原字节数据不变,又可以避免-1的出 现,可以这样做,将返回值b与上一个255。
这样返回去的值就是int类型的值,想说读到的值和写入副本中的值不就不一样了
吗?这就要谈一谈write()的特点了,它本身具有强转功能,只写入int类型的
最低8位。这样就保证了只有到达流的末尾才返回-1。
读取键盘录入
一、说明
System.out:对应的是标准的输出设备,控制台。
System.in:对应的是标准输入设备,键盘。
InputStream in=System.in;//获取键盘对象。
ps:无论从键盘录入什么,包括回车(有两个字符组成 “\r\n”),读入之后打印到控制台上都是一整数形式出 现的。
二、需求:
1、通过键盘录入数据。
import java.io.*;
class ReadIn
{
public static void main(String[] args)throws IOException
{
InputStream in=System.in;
int by=in.read();
int by1=in.read();
int by2=in.read();
System.out.println(by);
System.out.println(by1);
System.out.println(by2);
}
}
运行过程如下图所示:
敲入字符按回车:
敲入字符,按ctrl+c:
温馨提示:这里的流对象不需要手动关闭,因为它是系统的,程序结束,就自动结 束了。
2、当录入一行数据后,就将该行数据进行打印。如 果 录入的数据是over,那么就停止录入。
import java.io.*;
class ReadIn
{
public static void main(String[] args)throws IOException
{
InputStream in=System.in;
StringBuffer sb=new StringBuffer();
while(true)
{
/*
in.read(),发挥两个作用:
1、开放了从键盘敲入字符的一个接口。
2、敲完字符进去以后,按下回车,就开始读取录入的字符。
*/
int ch=in.read();
if(ch=='\r')
continue;
if(ch=='\n')
{
String s=sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
}
}
转换流
一、将字节流转化成字符流,通过InputStreamReader
通过刚才的键盘录入一行数据并打印其大写的练习,我们可以发现其实就是读一行数据的原理,也就是readLine()方法。但是readLine()是BufferedReader中的方法,是属于字符流的范畴,而键盘录入是输入字节流的范畴,因此我们想到转换。
import java.io.*;
class TransStreamDemo
{
}
二、将字节流转化成字符流,通过OutputStreamWriter
同理,我们也可以将写入字节流转换成字符流。
import java.io.*;
class ReadIn
{
public static void main(String[] args)throws IOException
{
StringBuffer sb=new StringBuffer();
InputStream in=System.in;
OutputStream out=System.out;
OutputStreamWriter osw=new OutputStreamWriter(out);
BufferedWriter bufw=new BufferedWriter(osw);
while(true)
{
int ch=in.read();
if(ch=='\r')
continue;
if(ch=='\n')
{
String s=sb.toString();
if("over".equals(s))
break;
bufw.write(s.toUpperCase());
bufw.newLine();
bufw.flush();
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
}
}
Ps:
转化流可以指定编码:
OutputStreamWriter osw=new OutputStreamWriter(
new FileOutputStream(“gbk.txt”),"gbk");
osw.write("你好");
OutputStreamWriter osr=new OutputStreamWriter(
new FileOutputStream("utf.txt"),"utf");
osw.write("你好");
这是我们打开gbk.txt、utf.txt之后,里边的内容都是 “你好”,但是gbk编码的只需4个字节,而utf编码的
是6个字节。
以上就是不同编码的一点区别。
三、流的操作:弄清“源”和“目”
流操作规律:(三个明确)
① 明确“源”和“目”。
源:输入流。InputStream、Reader
目:输出流。OutputStream、Writer
② 操作的数据是否是纯文本。
是:字符流
不是:字节流
ps:如果“源”和“目”不同是,则字符优先。
不是字符的一方必须应用转换流转化成字符流。
③ 明确具体的流对象。通过设备来区分:
源设备:硬盘、键盘
目的设备:硬盘、控制台
1、把从键盘录入的字符,存放到控制台中。
1) 明确1:
“源”:InputStream、Reader
“目”:OutputStream、Writer
2)明确2:
不是:字节流
“源”:InputStream
“目”:OutputStream
3)明确3:
“源”:键盘
InputStream in=System.in;
“目”:控制台
OutputStream out=System.in
2、把从键盘录入的字符,存放到磁盘文件中。
1) 明确1:
“源”:InputStream、Reader
“目”:OutputStream、Writer
2)明确2:
以字符为准。
“源”:Reader
“目”:Writer
3)明确3:
“源”:键盘
InputStream in=System.in;
InputStreamReader isr=new InputStreamReader(in);
“目”:硬盘
FileWriter fw=new FileWriter(".....");
3、把磁盘文件中的内容,存放到控制台中。
1) 明确1:
“源”:InputStream、Reader
“目”:OutputStream、Writer
2)明确2:
以字符为准。
“源”:Reader
“目”:Writer
3)明确3:
“源”:硬盘
FileReader fr=new FileReader(".........");
“目”:控制台
OutputStream out=System.out;
OutputStreamWriter osw=new OutputStreamWriter(out);
l ps:转化流可以指定编码表
InputStreamReader(InputStream in, Charset cs)
创建使用给定字符集的 InputStreamReader。
OutputStreamWriter(OutputStream out, Charset cs)
创建使用给定字符集的 OutputStreamWriter
“源”和“目”只需指定一方。
l 练习:一般情况下,我们开发时,按照下列模板做。利用 转化流。
(1)把键盘录入的数据,存放到控制台中。
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
InputStream in=System.in;
OutputStream out=System.out;
InputStreamReader isr=new InputStreamReader(in);
OutputStreamWriter osw=new OutputStreamWriter(out);
BufferedReader bufr=new BufferedReader(isr);
BufferedWriter bufw=new BufferedWriter(osw);
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
}
}
(2)把键盘录入的数据,放到C盘下的一个文件中。
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
InputStream in=System.in;
FileWriter fw=new FileWriter("C:\\687180q.txt");
InputStreamReader isr=new InputStreamReader(in);
BufferedReader bufr=new BufferedReader(isr);
BufferedWriter bufw=new BufferedWriter(fw);
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
}
}
l 改变标准输入输出设备。
(1)static viod setIn(InputStream in):重新分配“标 准” 输入流。
(2)static void setOut(PrintStream out):重新分 配“标准”输出流。
示例:
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
System.setIn(new FileInputStream("SystemDemo.java"));
System.setOut(new PrintStream("C:\\z.txt"));
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
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();
bufw.close();
}
}
可以选择注释:
System.setIn(new FileInputStream("SystemDemo.java"));
System.setOut(new PrintStream("C:\\z.txt"));
来改变“源”和“目”
拓展:
(1)异常的日志信息
import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionInfo
{
public static void main(String[] args)
{
try
{
int[] arr=new int[2];
System.out.println(arr[3]);
}
catch (Exception e)
{
try
{
Date d=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String s=sdf.format(d);
PrintStream ps=new PrintStream("exception.log");
ps.println(s);
System.setOut(ps);//改变输出流设备,位置任意。
}
catch (IOException ex)
{
throw new RuntimeException("日志文件创建失败");
}
e.printStackTrace(System.out);
}
}
}
(2)系统信息
import java.util.*;
import java.io.*;
class SystemInfo
{
public static void main(String[] args)throws IOException
{
Properties pro=System.getProperties();
System.out.println(pro);//不换行打印在控制台上。
pro.list(System.out);//换行打印在控制台上。
pro.list(new PrintStream("Systeminfo.txt"));//打印在文本文件中
}
}
四、转化流的字符编码
字符编码:
字符的出现为了方便操作字符,更重要的是加入编码转化
,通过了子类转化流来完成。InputStreamReader和
OutputStream。
编码表的由来:
计算机只能识别二进制数据,早期由来是电信号。为了方
便应用计算机,让它可以识别各个国家的文字,就将各个
国家的文字用数字来表示,并一一对应,形成一张表。这
就是编码表。
常见编码表:
ASCII:美国标准信息交换码。用一个字节的7位可以表
示。
ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表
示。不识别中文。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符
号。
Unicode:国际标准码表,融合了多种文字。所有文字都 用两个字节来表示,java语言使用的就是unicode。
UTF-8:最多用三个字节来表示,具体按需分配。
示例:
(1):在转化流中
import java.io.*;
class EncodeDemo
{
public static void main(String[] args)throws IOException
{
//writeText();
readText();
}
public static void writeText()throws IOException
{
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("C:\\utf.txt"),"gbk");//不写的话,默认为GBK。
osw.write("你好!");
osw.close();
}
public static void readText()throws IOException
{
InputStreamReader isr=new InputStreamReader(new FileInputStream("C:\\utf.txt"),"gbk");
int ch=0;
while((ch=isr.read())!=-1)
{
System.out.print((char)ch);
}
}
}
ps:要注意对应关系。
(2)在一般情况中
字符串->字节:编码
String->byte[]:str.getBytes(charsetName);
字节->字符串:解码
byte[]->String:new String(byte[],charsetName);
import java.io.*;
import java.util.*;
class EncodeDemo
{
public static void main(String[] args)throws Exception
{
String s="你好";
/*编码*/
byte[] b1=s.getBytes("gbk");
System.out.println(Arrays.toString(b1));
/*解码*/
String s1=new String(b1,"gbk");
System.out.println(s1);
}
}
运行结果:[-60、-29、-70、-61]
你好
ps:要注意对应关系。
注意:如果不是对应关系,则就会出现解错码的问题,那
么有两种办法可以解决:
(1)更换编码表,是对应。
(2)再编一次。
如:
String s="你好";
byte[] b1=s.getBytes("gbk");
System.out.println(Arrays.toString(b1));
String s1=new String(b1,"ISO8859-1");
System.out.println("s1="+s1);
byte[] b2=s1.getBytes("ISO8859-1");
System.out.println(Arrays.toString(b2));
String s2=new String(b2,"gbk");
System.out.println("s2="+s2);
拓展:“联通”
现象:新建一个文本文档,往里边写入“你好”二字,
保存后关闭,再次打开还是“你好”二字。新建一个
文本文档,往里边写入“联通”二字,保存后关闭,
再次打开却不是“联通”二字。
这就是所谓的编码问题。
记事本也有编码解码过程。
练习:
/*
有5个学生,每个学生有3门课的成绩。
从键盘输入以上数据(包括姓名、三门课的成绩)
输入格式:如:zhangsan,30,40,50
计算出总成绩
并把学生信息和计算出的总分数按高低顺序村房子磁盘文件“stud.txt”中
*/
//思路:
/*
1、描述学生对象。
*/
class Student implements Comparable<Student>
{
private String name;
private int math,chinese,english;
private int sum;
Student(String name,int math,int chinese,int english)
{
this.name=name;
this.math=math;
this.chinese=chinese;
this.english=english;
sum=math+chinese+enlish;
}
public String getName()
{
return name;
}
public int getSum()
{
return sum;
}
public int compareTo(Student s)
{
int num=new Integer(this.sum)compareTo(new Integer(s.num));
if(num==0)
return this.name.compareTo(s.name);
return num;
}
public int hashCode()
{
return name.hashCode()+sum*87;
}
public boolean equals(Object obj)
{
if(!(obj instanceof(Student)))
throws new ClassCastException("类型不匹配");
Student s=(Student)obj;
return this.name.equals(s.name)&&this.sum==s.sum;
}
public String toString()
{
return "Student["+name+","+math+","+chinese+","+english;
}
}
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)
stu=new TreeSet<Student>();
else
stu=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]));
stu.add(stu);
}
bufr.close();
return stu;
}
public static void writeToFile(Set<Student> stus)throws IOException
{
BufferedWriter bufw=new BufferedWriter(new FileWriter("C:\\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=Collection.reverseOrder();
Set<Student> stus=StudentInfoTool.getStudents(cmp);
StudentInfoTool.writeToFile(stus);
}
}
File
一、概述
1、用来将文件或者文件夹封装成对象。
2、方便对文件与文件夹的属性信息进行操做。
3、File对象可以作为参数传递给流的构造函数,
示例:
import java.io.*;
class FileDemo
{
public static void main(String[] args)
{
File f1=new File("a.txt");
System.out.println(f1);
File f2=new File("C:\\abc");
System.out.println(f2);
File f3=new File("C:\\abc\\c.txt");
System.out.println(f3);
File f4=new File(f2,"b.txt");
System.out.println(f4);
File f5=new File("C:\\abc","b.txt");
System.out.println(f5);
}
}
运行结果:
ps:封住之后,它们并没有真正存在。也可以封装已经存在的。
有一个跨平台的技巧:
可以将\\换成File.separator(与系统有关的默认名称分隔符,为了方便,它被表示为 一个字符串。)
二、功能
File类中常见的方法如下:
1、创建:
① static File createTempFile(String prefix,String suffix):在默认临时文件 目录中创建一个空文件(文件内容为空),使用给定前缀和后缀字符串生成其名称。
此文件类型不明确。
② static File createTempFile(String prefix,String suffix,File directory):
在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。directory必须是已经存在的,否则会抛异常。此文件类型不明确。
③ boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建, 返回true。
如:创建一个不明类型的abc文件
File f=new File("C:\\abc");
f.createNewFile();
如:创建一个.txt文件
File f=new File("C:\\abc.txt");
f.createNewFile();
ps:这里只能封装一级目录。
④ boolean mkdir():创建单级或者多级文件夹。可以覆盖。
⑤ boolean mkdirs():创建单级或者多级文件夹。不可以覆盖。注意,此操作失 败时也可能已经成功地创建了一部分必需的父目录。
2、删除:
下列方法所属的对象都可以是已存在的也可以是还未存在的。
① boolean delete():删除失败返回false(程序可能中途发生异常,导致执行不 到)、文件在被占用时,无法删除,返回false。
② void deleteOnExit();在程序退出时删除指定文件。
3、判断:
下列方法所属的对象都可以是已存在的也可以是还未存在的。
若不存在,对象不管封装的是什么,都返回false
若存在,根据对象封装的是什么来判断。
上述规则不适用于isAbsolute()。
① boolean exists():判断File对象是否存在。
② boolean isFile():判断File对象是否是文件。
③ boolean isDirectory():判断File对象是不是目录。
④ boolean isHidden():判断File对象是不是隐藏文件。
⑤ boolean isAbsolute():判断File对象是不是绝对路径。(如:C:\\abc\\c.txt)
⑥ boolean canExecute():判断File文件是否可以运行。
4、获取:
下列方法所属的对象都可以是已存在的也可以是还未存在的。
① String getName():返回最后一个名称。
② String getPath():返回File对象初始化内容。
③ String getParent():getPath()-getName()。
④ String getAbsolutePath():返回File对象初始化内容,即绝对路径。
⑤ String toString():返回File对象初始化内容。
⑥ long lastModified():返回File对象表示的文件或者文件夹最后一次被修改 的时间。
⑦ long length():返回File对象表示的文件或者文件夹的长度(单位:字节)。
三、功能的应用
1、文件列表
(1)String[] list():列出指定目录下的内容。
import java.io.*;
class FileDemo
{
public static void main(String[] args)throws IOException
{
File f=new File("F:\\JavaSDK");
String[] names=f.list();//f必须是已存在的目录,否则抛异常(无指向)。
for(String name:names)
{
System.out.println(name);
}
}
}
运行结果:
(2)Static File[] listRoots():列出可用的文件系统根。
import java.io.*;
class FileDemo
{
public static void main(String[] args)throws IOException
{
File[] files=File.listRoots();
for(File f:files)
{
System.out.println(f);
}
}
}
运行结果:
(3)列出指定目录下的指定内容。过滤。
import java.io.*;
class FileDemo
{
public static void main(String[] args)throws IOException
{
File dir=new File("F:\\JavaSDK\\day00\\");
String[] arr=dir.list(new FilenameFilter()
{
public boolean accept(File dir,String name)
{
System.out.println("dir:"+dir+"-------name:"+name);
return name.endsWith(".java");
}
});
System.out.println("len:"+arr.length);
for(String name:arr)
{
System.out.println(name);
}
}
}
(4)String[] list()和File[] listFiles()
1)
import java.io.*;
class FileDemo
{
public static void main(String[] args)throws IOException
{
File f=new File("F:\\JavaSDK");
String[] arr=f.list();
for(String name:arr)
{
System.out.println(name);
}
}
}
运行结果:
2)
import java.io.*;
class FileDemo
{
public static void main(String[] args)throws IOException
{
File f=new File("F:\\JavaSDK");
File[] arr=f.listFiles();
for(File name:arr)
{
System.out.println(name);
}
}
}
运行结果:
因此:一般我们优先采用File[] listFiles()。
(5)列出指定目录下的所有内容,知道每一项没有内 容可列。--递归
递归要注意:
1)限定条件
2)要注意递归的次数。尽量避免内存溢出
import java.io.*;
class FileDemo
{
public static void main(String[] args)throws IOException
{
File dir=new File("C:\\abc");
showDir(dir);
}
public static void showDir(File dir)
{
System.out.println(dir);
File[] files=dir.listFiles();
for(int x=0;x<files.length;x++)
{
if(files[x].isDirectory())
showDir(files[x]);
System.out.println(files[x]);
}
}
}
拓展:带有层次
import java.io.*;
class FileDemo
{
public static void main(String[] args)throws IOException
{
File dir=new File("C:\\abc");
showDir(dir,0);
}
public static String getLevel(int level)
{
StringBuffer sb=new StringBuffer();
sb.append("|--");
for(int x=0;x<level;x++)
{
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);
System.out.println(getLevel(level)+files[x]);
}
}
}
运行结果:
递归的其他应用:
将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。建立一个java 文件列表文件。
/*
思路:
1、对指定目录进行递归,
2、获取递归过程中所有的java文件的路径。
3、将这些路径存储到集合中。
4、将集合中的数据写入到一个文件中。
*/
import java.io.*;
import java.util.*;
class FileListDemo
{
public static void main(String[] args)throws IOException
{
File dir=new File("F:\\JavaSDK");
List<File> list=new ArrayList<File>();
fileToList(dir,list);
File file=new File(dir,"javaList.txt");
writeToFile(list,file.toString());
}
public static void fileToList(File dir,List<File> list)throws IOException
{
File[] files=dir.listFiles();
for(File file:files)
{
if(file.isDirectory())
fileToList(file,list);
else
{
if(file.getName().endsWith(".java"))
list.add(file);
}
}
}
public static void writeToFile(List<File>list ,String javaListFile)throws IOException
{
BufferedWriter bufw=new BufferedWriter(new FileWriter(javaListFile));
for(File f:list)
{
String path=f.getAbsolutePath();
bufw.write(path);
bufw.newLine();
bufw.flush();
}
bufw.close();
}
}
集合与IO相结合的容器:Propertie
一、如何使用
Properties是HashTable的子类,即是Map的子类,并 且集合中存的都是字符串。
重点掌握以下几个方法:
① Properties pro=new Properties();
② Object setProperties(String key,String value);
③ String getProperties(String key);
④ Set<String> StringPropertyNames();返回键集。
⑤ void load(InputStream inStream);将流关联的文件 加载到集合中去。
⑥ void store(OutputStream outStream,String comments):使集合和对应的流同步。
ps:键相同,则覆盖。
二、应用
1、“存”&“取”
import java.util.*;
class PropertiesDemo
{
public static void main(String[] args)
{
Properties pro=new Properties();
pro.setProperty("zhangsan","20");
pro.setProperty("zhangsan","30");
System.out.println(pro);
pro.setProperty("Lisi",10+"");
String value=pro.getProperty("zhangsan");
System.out.println(value);
Set<String> names=pro.stringPropertyNames();
for(String name:names)
{
System.out.println(name+"::"+pro.getProperty(name));
}
}
}
2、存取配置文件
假设现在有一个配置文件info.txt。
//现在想将info.txt中的键值数据存到集合中进行操作。
/*
思路:
1、将字符读取流与文件相关联。为提高效率,用字符缓冲读取流。
2、读取一行数据,将改行数据用“=”进行切割。
3、等号左边作为键,右边作为值,存入到Propertie集合中即可。
*/
import java.util.*;
import java.io.*;
class PropertiesDemo
{
public static void main(String[] args)throws IOException
{
Properties pro=new Properties();
BufferedReader bufr=new BufferedReader(new FileReader("G:\\info.txt"));
String line=null;
while((line=bufr.readLine())!=null)
{
String[] arr=line.split("=");
pro.setProperty(arr[0],arr[1]);
}
bufr.close();
System.out.println(pro);
}
}
运行结果:
优化:
通过查阅Properties的API,发现有一个方法已经帮我 们实现了这个功能:void load(FileInputStream inStream);
import java.util.*;
import java.io.*;
class PropertiesDemo
{
public static void main(String[] args)throws IOException
{
Properties pro=new Properties();
FileInputStream in=new FileInputStream("G:\\info.txt");
pro.load(in);
System.out.println(pro);
//pro.list(System.out);
}
}
现在我们对集合进行一些设置,比如:
import java.util.*;
import java.io.*;
class PropertiesDemo
{
public static void main(String[] args)throws IOException
{
Properties pro=new Properties();
FileInputStream in=new FileInputStream("G:\\info.txt");
pro.load(in);
pro.setProperty("zhangsan","30");
System.out.println(pro);
}
}
运行结果:
但是在看一下文件:
想让文件也有同样的变化,则:
//现在想将info.txt中的键值数据存到集合中进行操作。
/*
思路:
1、将字符读取流与文件相关联。为提高效率,用字符缓冲读取流。
2、读取一行数据,将改行数据用“=”进行切割。
3、等号左边作为键,右边作为值,存入到Propertie集合中即可。
*/
import java.util.*;
import java.io.*;
class PropertiesDemo
{
public static void main(String[] args)throws IOException
{
Properties pro=new Properties();
FileInputStream in=new FileInputStream("G:\\info.txt");
pro.load(in);
pro.setProperty("zhangsan","30");
pro.store(new FileOutputStream("G:\\info.txt"),"haha");
pro.list(System.out);
}
}
运行结果:
现在,再看一下文件:
拓展:
配置文件有两种形式,一种是Properties,一种是xml文件。由于Properties形式的 配置文件有他的局限性,所以之后又出来了xml文件。
name=zhangsan
age=20
name=wangwu
age=30
<Persons
<person
<name>zhangsan</name>
<age>20</age>
/person>
<person
<name>wangwu</name>
<age>30<>
/person>
/Persons>
3、Properties练习
需求:记录应用程序运行的次数。如果使用次数已到,那 么给出注册提示。
/*
很容易想到的是计数器。
它的特点是:
该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
随着该应用程序的退出,该计数器也在内存中消失,下一次再启动时,重新计数。
这不是我们想要的,我们希望,程序即使结束,该计数器的值也存在。
下次程序启动时会先加载该计数器的值并加1后重新存储起来。
因此,我们想到用配置文件来记录该软件的使用次数。
该配置文件使用键值对的形式,这样便于阅读数据,并操作数据。
键值对数据是Map集合,数据是以文件的形式存储的,使用io技术,
那么io+Map=Properties。
*/
import java.util.*;
import java.io.*;
class RunCount
{
public static void main(String[] args)throws IOException
{
Properties prop=new Properties();
File file=new File("C:\\count.txt");
FileInputStream fis=new FileInputStream(file);
prop.load(fis);
int count=0;
String value=prop.getProperty("time");
if(value!=null)
{
count=Integer.parseInt(value);
if(count>5)
{
System.out.println("您好,使用次数已到,请注册!");
return;
}
}
count++;
prop.setProperty("time",count+"");
FileOutputStream fos=new FileOutputStream(file);
prop.store(fos,"");
fis.close();
fos.close();
}
}
第一次编译运行后:
第二次运行后:
其他
一、PrintWriter
打印流。它提供了多种重载形式的打印方法,可以将各种 数据类型的数据打印到指定的位置。
import java.util.*;
import java.io.*;
class PrintStreamDemo
{
public static void main(String[] args)throws IOException
{
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
PrintWriter out=new PrintWriter(new FileWriter("C:\\a.txt"),true);
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line.toUpperCase());
//out.flush();out初始化时,boolean参数为true,自动刷新
}
out.close();
bufr.close();
}
}
其实,我们可以把PrintWriter看成是FileWriter,PrintStream看成是 FileOutputStream来学习。那么这里的println()就可以看成是write()。只需要 特别注意一下PrintWriter构造函数所接受的参数即可,它可以接收的参数类型比较广 泛一些。
PrintStream构造函数可以接收的参数类型:
a. File对象
b. 字符串路径
c. 字节输出流对象
PrintWriter构造函数可以接收的参数类型:
a. File对象
b. 字符串路径
c. 字节输出流对象
d. 字符输出流对象
二、合并流(SequenceInputStream)
|--InputStream
|--SequenceInputStream
SequenceInputStream 表示其他输入流的逻辑串联。它从 输入流的有序集合开始,并从第一个输入流开始读取,直 到到达文件末尾,接着从第二个输入流读取,依次类推, 直到到达包含的最后一个输入流的文件末尾为止。
SequenceInputStream(Enumeration<?extends InputStream> e) :通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
练习:将1.txt、2.txt、3.txt中的内容写入到一个文件 中。
import java.util.*;
import java.io.*;
class SequenceDemo
{
public static void main(String[] args)throws IOException
{
Vector<FileInputStream> v=new Vector<FileInputStream>();
v.add(new FileInputStream("C:\\1.txt"));
v.add(new FileInputStream("C:\\2.txt"));
v.add(new FileInputStream("C:\\3.txt"));
Enumeration en=v.elements();
SequenceInputStream sis=new SequenceInputStream(en);
FileOutputStream fos=new FileOutputStream("C:\\a.txt");
byte[] buf=new byte[1024];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
三、切割文件&合并文件
需求:将a.txt切割成三个文件,分别是1.txt、2.txt、 3.txt。 再将三个文件合并成一个文件b.txt。
import java.util.*;
import java.io.*;
class SplitFile
{
public static void main(String[] args)throws IOException
{
//splitFile();
merge();
}
public static void splitFile()throws IOException
{
FileInputStream fis=new FileInputStream("C:\\a.txt");
FileOutputStream fos=null;
byte[] buf=new byte[16];
int len=0;
int count=1;
while((len=fis.read(buf))!=-1)
{
fos=new FileOutputStream("C:\\"+(count++)+".txt");
fos.write(buf,0,len);
fos.close();
}
fis.close();
}
public static void merge()throws IOException
{
/*
Vector<FileInputStream> v=new Vector<FileInputStream>();
v.add(new FileInputStream("1.txt"));
v.add(new FileInputStream("2.txt"));
v.add(new FileInputStream("3.txt"));
Enumeration en=v.elements();
*/
//由于Vector太低效,所以我们用ArrayList。
ArrayList<FileInputStream> al=new ArrayList<FileInputStream>();
for(int x=1;x<=3;x++)
{
al.add(new FileInputStream("C:\\"+x+".txt"));
}
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:\\hebin.txt");
byte[] buf=new byte[8];
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
sis.close();
fos.close();
}
}
四、对象的序列化
什么是对象的序列化?
把对象存放在硬盘上叫做对象的持久化存储(序列化)。
该对象所属的类必须实现Serializable接口。(该接口 没有方法,仅用于标识可序列化的语义。)
更细节划分操作对象的流:
ObjectInputStream/ObjectOutputStream
涉及到的方法:
① ObjectOutputStream oos=new ObjectOutputSteam(OutputStream out);
② void writeObject(Object obj):将指定的对象写入ObjectOutputStream;
③ objectInputStream ois=new ObjectInputStream(InputStream in);
④ void readObject();从 ObjectInputStream 读取对象
练习:将自定义类Person的对象存入到磁盘文件中,再 从文件中读取出来。
/*
实现了Serializable之后的类,编译后都会产生一个对应的uid。
它是根据类中不同的成员来算这个值的,给类做标记。
使对象所属的类文件始终保持是同一个uid。
通过:public static fianl long serialVersionUID=42L;
好处:
当我们往文件obj.txt中存储对象之后,我们改动了类中的成员变量,
比如,加一个新的修饰符。此时我们想从obj.txt中读取数据,编译运行后,
运行失败。原因就是序列号不统一。但是我们只要通过上面一行代码,我们
就可以解决这个问题。并且可以使obj.txt中的内容同步。
还有一点就是:静态不可以序列化,即存不进去,我们会将相应的该数据类型
默认初始化值代替初始化值存入进去。若非静态也不想序列化,我们可以通过
关键字transient修饰。
*/
import java.io.*;
class Person implements Serializable
{
public static final long serialVersionUID=42L;
private String name;
private int age;
private String country;
Person(String name,int age,String country)
{
this.name=name;
this.age=age;
this.country=country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}
class ObjectStreamDemo
{
public static void main(String[] args)throws Exception
{
//writeObj();
readObj();
}
public static void writeObj()throws Exception
{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("C:\\t.txt"));
oos.writeObject(new Person("zhangsn",20,"cn"));
oos.writeObject(new Person("wangwwu",23,"ke"));
oos.close();
}
public static void readObj()throws Exception
{
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("C:\\t.txt"));
Person p=(Person)ois.readObject();
System.out.println(p);//打印zhangsn:20:cn
ois.close();
}
}
五、管道流
管道流:
PipedInputStream/PipedOutputStreamm
PipedReader/PipedWriter
PipedInputStream in=new PipedInputStream();
PipedOutputStream out=new PipedOutputStream();
in.connect(out);
管道输入流应该连接到管道输出流,管道输入流提供要写 入管道输出流的所有数据。通常,数据由某个线程从管道 输入流对象读取,并由其他线程将其写入到相应的管道输 出流。
示例:
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("pipe lai le".getBytes());
out.close();
}
catch (Exception e)
{
throw new RuntimeException("管道输出流失败");
}
}
}
class PipedStreamDemo
{
public static void main(String[] args)throws Exception
{
PipedInputStream in=new PipedInputStream();
PipedOutputStream out=new PipedOutputStream();
in.connect(out);
Read r=new Read(in);
Write w=new Write(out);
new Thread(w).start();
new Thread(r).start();
}
}
六、RandomAccessFile
简而言之,该类可以根据模式(r,rw)的不同对磁盘上 的文件进行随机的读和写。该类的对象就相当于一个文件 指针,通过调用类中的方法可以改变指针的所指的位置从 而实现随机。
重点掌握以下几个方法:
① RandomAccessFile(File file,String mode):创建从中读取和向其中写入(可选) 的随机访问文件流,该文件由File参数指定。
② RandomAccessFile(String name,String mode):创建从中读取和向其中写入 (可选)的随机访问文件流,该文件具有指定名称。
③ void seek(long pos):设置到此文件开头测量到的文件指针偏移量,在该位置 发生下一个读取或写入操作。
④ int skipBytes(int n):尝试跳过输入的n个字节以丢弃跳过的字节。
⑤ int readInt():从此文件读取一个有符号的32位整数。
⑥ int read():从此文件中读取一个数据字节。
⑦ int read(byte[] b):将最多b.lenggh个数据字节从此文件读入byte。
⑧ void write(int b):向此文件写入指定的字节。
⑨ void write(byte[] b):将 b.length 个字节从指定 byte 数组写入到此文件, 并从当前文件指针开始。
⑩ void writeInt(int v):按四个字节将int写入该文件,先写高字节。
示例:
import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] arags)throws IOException
{
writeFile();
//readFile();
}
public static void writeFile()throws IOException
{
RandomAccessFile raf=new RandomAccessFile("C:\\t.txt","rw");
raf.write("lisi".getBytes());
/*
write()/具有强转的功能,为保证数据的原样性,只写最低八位。
而writeInt()会写32位。且写入的其实是a,通过编码表查找。
*/
raf.writeInt(97);
//raf.writeBytes("\r\n");
//raf.seek(8);
raf.write("wangwu".getBytes());
raf.writeInt(258);
raf.close();
}
public static void readFile()throws IOException
{
RandomAccessFile raf=new RandomAccessFile("C:\\t.txt","r");
byte[] buf=new byte[4];
raf.read(buf);
String name=new String(buf);
//raf.seek(8);
//raf.skipBytes(4);
int age=raf.readInt();
System.out.println("name="+name+",age="+age);
}
}
总结:这个类也可以看成是FileWriter/FileWriter来 学习。就seek()和skipBytes()特殊一点。
七、操作基本数据类型的流对象DataStream
DataInputStream/DataOutputStream
DataInputStream dis=new DataInputStream(InputStream in);
DataOutputStream dos=new DataOutputStream(OutputStream out);
readUTF();
writeUTF();
readInt();
writeInt();
readBoolean();
writeBoolean();
readDouble();
writeDouble();
温馨提示:这个类可以看成FileWriter和FileReader来 对照学习。
八、ByteArrayStream
ByteArrayInputStream/ByteArrayOutputStream
ByteArrayStream(byte[] buf):
创建一个ByteArrayInputStream,使用buf作为其缓冲区 数组。
Ps:在构造的时候,需要接收数据源,而且数据源是一个 字节数组。
ByteArrayStream():创建一个新的byte数组输出流。
ps:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节 数组。
因此,这两个流对象都操作的数组,并没有使用系统资源,
不用进行close()关闭。
还需要掌握的方法:
ByteArrayInputStream:
read();
ByteArrayOutputStream:
write();
size();
toString();
write(OutputStream out);
2015-12-16至
2015-12-20著