I/O流 技术 学习笔记
本文查阅方法:
1、查阅目录 —— 查阅本文目录,确定想要查阅的目录标题
2、快捷“查找” —— 在当前浏览器页面,按键 “Ctrl+F” 按键组合,开启浏览器的查找功能,
在查找搜索框中 输入需要查阅的 目录标题,便可以直接到达 标题内容 的位置。
3、学习小结 —— 文中的学习小结内容,是笔者在学习之后总结出的,开发时可直接参考其进行应用开发的内容, 进一步加快了本文的查阅 速度。(水平有限,仅供参考。)
本文目录
学习小结
1、IO(Input Output)流 概述
(1)IO流的简介
(2)IO流常用基类
(3)IO程序的书写流程
2、字符流-- FileWriter
3、处理IO输出流异常的完整代码
4、文件的续写
5、字符流--FileReader读取文件的两种方式
(1)第一种方式:逐个字符地从文件中读取数据
(2)第二种方式:通过字符数组进行读取。
(3)注意事项:
6、拷贝文本文件
7、字符流的缓冲区 BufferedWriter 和 BufferedReader
(1)BufferedWriter -- 字符写入流缓冲区
(2)BufferedReader -- 字符读取流缓冲区:
(3)通过缓冲区复制文本文件
(4)readLine的原理图解
(5)自定义ReadLine()方法
8、采用“装饰设计模式”的思想来改造IO流类----增强其功能。
(1)装饰设计模式 介绍
(2)装饰和继承的区别
(3)以LineNumberReader 类为基础装饰设计模式应用Demo
9、字节流FileInputStream / FileOutputStream 对文件的读写操作
(1)InputStream / OutputStream 的基本操作 Demo
(2)拷贝图片
10、字节流的缓冲区: BufferedOutputStream / BufferedInputStream
(1)复制mp3:演示缓冲区使用流程
(2)自定义字节流的缓冲区-- 探寻read()和write()的特点
11、读取键盘录入: System.in
12、读取/写入 转换流:InputStreamReader与OutputStreamWriter
(1)标准输入输出
(2)改变标准的输入输出设备
13、众多IO流对象的选用规律
14、保存“异常信息”到日志文件
15、File对象的创建、删除、判断、获取等功能。
16、File对象 ———— 实现文件列表功能常用方法
17、File对象 —— 操作 文件列表/目录
(1)列出目录下所有内容——递归方法(含带层次展现)
(2)删除带内容的目录
(3)创建展现多层级的Java文件列表并输出到文件
18、Properties 存取配置文件
19、PrintStream 以及 PrintWriter
20、文件的 切割操作
21、文件的合并操作 ----- SequenceInputStream 合并文件
22、对象的序列化:ObjectOutputStream 和反序列化:ObjectInputStream
23、RandomAccessFile 随机读写访问文件
24、操作基本数据类型 DataInputStream与DataOutputStream
25、ByteArrayStream 操作字节数组的流对象。
26、字符的编码和解码
(1)编码 和 解码 的练习
(2)转换流的字符编码
学习小结
(1)字符流的输入与输出
public void test() //此种写法虽然麻烦,但是效能高,也处理了异常,非常健壮,建议拷贝使用。
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try{
bufr = new BufferedReader(new FileReader("Text1.java"));
bufw = new BufferedWriter(new FileWriter("Text2.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("写入关闭失败");
}
}
}
(2)字节流的输入与输出
//采用了Buffered缓冲区,但是未考虑详细异常的字节流读取
public static void copy_1()throws IOException{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
int by = 0;
while((by=bufis.read())!=-1){
bufos.write(by);
}
bufos.close();
bufis.close();
}
//未采用字节流缓冲区Buffered,但是处理了异常的字节流读取。
public static void main(String[] args){
FileOutputStream fos = null;
FileInputStream fis = null;
try{
fos = new FileOutputStream("c:\\2.bmp");
fis = new FileInputStream("c:\\1.bmp");
//=============== 开发时常用代码片段====================
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
//=============== 开发时常用代码片段====================
}catch (IOException e){
throw new RuntimeException("复制文件失败");
}finally{
try{
if(fis!=null)
fis.close();
}catch (IOException e){
throw new RuntimeException("读取关闭失败");
}
try{
if(fos!=null)
fos.close();
}catch (IOException e){
throw new RuntimeException("写入关闭失败");
}
}
}
(3)Properties 读取配置文件
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
prop.load(fis); //将流中的数据加载进集合。
String value = prop.getProperty("lisi"); //取出属性值的操作
fis.close();
(4)File对象的创建、判断
FileInputStream fis = null;//注:该输入流 根据实际情况 赋值。
File dir = new File("abc\\kkk\\a");//你讲文件放置到该目录下面。
if(!dir.exists()){ //判断该目录是否已实际存在
dir.mkdirs(); //若不存在,则创建出实际目录
}
File newFile=new File(dir,"newText.jpg"); //将目标目录与拟存放后的新文件名创建新的文件绝对路径
FileOutputStream fos =new FileOutputStream(newFile);
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len); //向新的文件中写入数据。
}
1、IO(Input Output)流 概述
(1)IO流的简介
简介:IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中
分类:流按操作数据分为两种:字节流与字符流。
流按流向分为:输入流,输出流。
(2)IO流常用基类
字节流的抽象基类:
InputStream ,OutputStream。
字符流的抽象基类:
Reader ,Writer 。
注:由这四个类派生出来的子类名称都是
以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。
(3)IO程序的书写流程
导入IO包中的类
编写IO代码
进行IO异常处理
在finally 中对流进行关闭
2、字符流-- FileWriter
FileWriter是专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。 前缀名是该流对象的功能。
语法:
创建流对象,建立数据存放文件
• FileWriter fw = new FileWriter(“Test.txt”);
调用流对象的写入方法,将数据写入流
• fw.write(“text”);
关闭流资源,并将流中的数据清空到文件中。
• fw.close();
Demo如下:
class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
//其实该步就是在明确数据要存放的目的地。
FileWriter fw = new FileWriter("demo.txt");
//调用write方法,将字符串写入到流中。
fw.write("abcde");
//刷新流对象中的缓冲中的数据。
//将数据刷到目的地中。
//fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
//将数据刷到目的地中。
//和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();
}
}
3、处理IO输出流异常的完整代码
这是处理写文件时控制异常的完整代码,建议我们在实际开发中,尽量写这种代码,将会是代码更加的严谨和健壮。
FileWriter fw = null;
try{
fw = new FileWriter("Test.txt");
fw.write("text");
}
catch (IOException e){
System.out.println(e.toString());
}
finally{
If(fw!=null)
try{
fw.close();
}catch (IOException e){
System.out.println(e.toString());
}
}
4、文件的续写
在开发中,向已经存在的文件的末尾,继续续写数据的功能非常重要,下面两个构造函数可以实现:
FileWriter(String fileName, boolean append)
FileOutputStream(String name, boolean append)
根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter/FileOutputStream对象
Demo样例:
FileWriter fw=new FileWriter("demo.txt",true);
fw.write("nihao\r\n xiexie"); //在文件中写入换行显示的数据,
fw.close(); //windows的记事本里识别的换行符,仅支持 \r\n,
//而linux等系统以及 EditPlus 中则支持\n换行。
5、字符流--FileReader读取文件的两种方式
(1)第一种方式:逐个字符地从文件中读取数据
Demo:
class FileReaderDemo
{
public static void main(String[] args) throws IOException{
//创建一个文件读取流对象,和指定名称的文件相关联。
//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr = new FileReader("demo.txt");
//调用读取流对象的read方法。
//read():一次读一个字符。而且会自动往下读。
int ch = 0;
while((ch=fr.read())!=-1){
System.out.println(
}
/*
while(true){
int ch = fr.read();
if(ch==-1)
break;
System.out.println("ch="+(char)ch);
}
*/
fr.close();
}
}
逐个字符读取文件原理图解
(2)第二种方式:通过字符数组进行读取。
Demo:
class FileReaderDemo2
{
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("demo.txt");
//定义一个字符数组。用于存储读到字符。
//该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();
}
}
字符数组进行读取文件图解
(3)注意事项:
a.定义文件路径时,可以用“/”或者“\\” 。
b.在创建一个文件时,如果目录下有同名文件将被覆盖。
c.在读取文件时,必须保证该文件已存在,否则出异常。
6、拷贝文本文件
拷贝文本文件原理:是有两个过程完成的。
(1)读取数据:读取源文件中的数据到内存中。
(2)写入数据:将读到的数据从内存中写入到目标位置一个文件中。
示例剖析:将C盘一个文本文件复制到D盘。
复制的原理:
其实就是将C盘下的文件数据存储到D盘的一个文件中。
步骤:
1,在D盘创建一个文件。用于存储C盘文件中的数据。
2,定义读取流和C盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源。
示例Demo:
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){ }
}
}
//从C盘读一个字符,就往D盘写一个字符。
public static void copy_1()throws IOException
{
//创建目的地。
FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");
//与已有文件关联。
FileReader fr = new FileReader("RuntimeDemo.java");
int ch = 0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
fw.close();
fr.close();
}
}
文件复制过程图例
7、字符流的缓冲区 BufferedWriter 和 BufferedReader
缓冲区的出现提高了对数据的读写效率,要结合流才可以使用。是在流的基础上对流的功能进行了增强。
对应类
BufferedWriter -- 字符写入流缓冲区
BufferedReader -- 字符读取流缓冲区
缓冲区是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象。
(1)BufferedWriter -- 字符写入流缓冲区
备注:该缓冲区中提供了一个跨平台的换行符。newLine();
示例Demo:
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<5; x++){
bufw.write("abcd"+x);
bufw.newLine();//若要在写入的文本中实现换行格式的效果,就需要调用newLine()方法写入换行符。
bufw.flush();
}
//记住,只要用到缓冲区,就要记得刷新。
//bufw.flush();
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();
}
}
(2)BufferedReader -- 字符读取流缓冲区:
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
备注:readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
示例Demo:
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.print(line);
}
bufr.close();
}
}
(3)通过缓冲区复制文本文件
示例Demo:
//需求:通过缓冲区复制一个.java文件。
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(); //若要在写入的文本中实现换行格式的效果,就需要调用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("写入关闭失败");
}
}
}
}
(4)readLine的原理图解
(5)自定义ReadLine()方法
明白了BufferedReader类中特有方法readLine的原理后,可以自定义一个MyBufferedReader类中包含一个功能和readLine一致的方法。
来模拟一下BufferedReader
示例Demo:
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Reader r){
this.r = r;
}
//可以一次读一行数据的方法。
public String myReadLine()throws IOException{
//定义一个临时容器。原BufferReader封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
/*
覆盖Reader类中的抽象方法。
*/
public int read(char[] cbuf, int off, int len) throws IOException{
return r.read(cbuf,off,len) ;
}
public void close()throws IOException{
r.close();
}
public void myClose()throws IOException{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader myBuf = new MyBufferedReader(fr);
String line = null;
while((line=myBuf.myReadLine())!=null){
System.out.println(line);
}
myBuf.myClose();
}
}
8、采用“装饰设计模式”的思想来改造IO流类----增强其功能。
(1)装饰设计模式 介绍
装饰设计模式:当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类,这种增强类功能的方式称之为“装饰设计模式”。
装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
示例Demo:
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("甜点");
System.out.println("来一根");
}
}
class PersonDemo
{
public static void main(String[] args){
Person p = new Person();
//p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
(2)装饰和继承的区别
我们使用装饰类增强原有类功能的思想,与继承的方式很相似,但是二者在实际的使用中,是有着一定区别的
下面以模拟 自定义增强Reader体系中的各种装饰类 继承关系 来分析 二者的区别
MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
Demo:
class MyBufferReader
{
MyBufferReader(MyTextReader text){
}
MyBufferReader(MyMediaReader media){
}
}
上面这个类扩展性很差,倘若找到其参数的共同类型,通过多态的形式,可以提高扩展性。
Demo:
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r){
}
}
MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
以前是通过继承将每一个子类都具备缓冲功能,那么继承体系会复杂,并不利于扩展。现在优化思想,单独描述一下缓冲内容,将需要被缓冲的对象,传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区,这样继承体系就变得很简单,优化了体系结构。
装饰模式比继承要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。
(3)以LineNumberReader 类为基础装饰设计模式应用Demo
示例Demo:LineNumberReader (跟踪行号的缓冲字符输入流) 与 自定义MyLineNumberReader
//原 LineNumberReader 功能Demo
class LineNumberReaderDemo
{
public static void main(String[] args)throws IOException {
FileReader fr = new FileReader("PersonDemo.java");
LineNumberReader lnr = new LineNumberReader(fr);//跟踪行号的缓冲字符输入流
String line = null;
lnr.setLineNumber(100);// 设置当前行号。
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+":"+line);//获得当前行号。
}
lnr.close();
}
}
//自定义 类似LineNumberReader 功能的类Demo
class MyLineNumberReader
{
private Reader r;
private int lineNumber;
MyLineNumberReader(Reader r){
this.r = r;
}
public String myReadLine()throws IOException{
lineNumber++;
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void setLineNumber(int lineNumber){
this.lineNumber = lineNumber;
}
public int getLineNumber(){
return lineNumber;
}
public void myClose()throws IOException{
r.close();
}
}
//采用了装饰模式的 MyLineNumberReader Demo
class MyLineNumberReader extends MyBufferedReader
{
private int lineNumber;
MyLineNumberReader(Reader r){
super(r);
}
public String myReadLine()throws IOException{
lineNumber++;
return super.myReadLine();
}
public void setLineNumber(int lineNumber){
this.lineNumber = lineNumber;
}
public int getLineNumber(){
return lineNumber;
}
}
class MyLineNumberReaderDemo
{
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("copyTextByBuf.java");
MyLineNumberReader mylnr = new MyLineNumberReader(fr);
String line = null;
mylnr.setLineNumber(100);
while((line=mylnr.myReadLine())!=null){
System.out.println(mylnr.getLineNumber()+"::"+line);
}
mylnr.myClose();
}
}
9、字节流FileInputStream / FileOutputStream 对文件的读写操作
字节流:InputStream / OutputStream 的基本操作与字符流类相同
但它不仅可以操作字符,还可以操作其他媒体文件需求。例如想要操作图片数据,这时就要用到字节流。
(1)InputStream / OutputStream 的基本操作 Demo
Demo样例:
class FileStream
{
public static void main(String[] args) throws IOException{
readFile_3();
}
public static void readFile_3()throws IOException{ //使用available()函数定义正好的缓冲区
FileInputStream fis = new FileInputStream("fos.txt");
// int num = fis.available();
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区,不用在循环了,但有可能因文件太大造成内存溢出。
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
public static void readFile_2()throws IOException{ //定义一定容量的缓冲区进行循环的读取/写入操作, 推荐使用的方式,高效又安全。
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fis.close();
}
public static void readFile_1()throws IOException{ //逐个字节地读取并写入数据到文件。
FileInputStream fis = new FileInputStream("fos.txt");
int ch = 0;
while((ch=fis.read())!=-1){
System.out.println((char)ch);
}
fis.close();
}
public static void writeFile()throws IOException{ //将数据写入到文件
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("abcde".getBytes());
fos.close();
}
}
(2)拷贝图片
思路:
a.用字节读取流对象和图片关联。
b.用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
c.通过循环读写,完成数据的存储。
d.关闭资源。
示例Demo:
class CopyPic
{
public static void main(String[] args){
FileOutputStream fos = null;
FileInputStream fis = null;
try{
fos = new FileOutputStream("c:\\2.bmp");
fis = new FileInputStream("c:\\1.bmp");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
}catch (IOException e){
throw new RuntimeException("复制文件失败");
}finally{
try{
if(fis!=null)
fis.close();
}catch (IOException e){
throw new RuntimeException("读取关闭失败");
}
try{
if(fos!=null)
fos.close();
}catch (IOException e){
throw new RuntimeException("写入关闭失败");
}
}
}
}
10、字节流的缓冲区: BufferedOutputStream / BufferedInputStream
字节流的缓冲区同样是提高了字节流的读写效率。 下面就以一个Demo来介绍一下。
(1)复制mp3:演示缓冲区使用流程
示例Demo:
class CopyMp3
{
public static void main(String[] args) throws IOException{
long start = System.currentTimeMillis();
copy_2();
long end = System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
public static void copy_2()throws IOException
{
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\9.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\3.mp3"));
int by = 0;
//System.out.println("第一个字节:"+bufis.myRead());
while((by=bufis.myRead())!=-1){
bufos.write(by);
}
bufos.close();
bufis.myClose();
}
//通过字节流的缓冲区完成复制。
public static void copy_1()throws IOException{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
int by = 0;
while((by=bufis.read())!=-1){
bufos.write(by);
}
bufos.close();
bufis.close();
}
}
(2)自定义字节流的缓冲区-- 探寻read()和write()的特点
示例Demo:
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024*4];
private int pos = 0,count = 0;
MyBufferedInputStream(InputStream 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&255;
}else if(count>0){
byte b = buf[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
public void myClose()throws IOException{
in.close();
}
}
下面我们来分析一下:字节流的读一个字节的read方法为什么返回值类型不是byte,而是int
11111111-111111110000000000101001001010100101010010101001010 //返回的一串二进制代码的前8位有可能是8个1;
byte: -1 ---> int : -1;
00000000 00000000 00000000 11111111 255
11111111 11111111 11111111 11111111 //此处是为了与我们的校验值 -1(int) 进行比对,进行自动补位而类型提升。
11111111 -->提升了一个int类型 那不还是-1吗?是-1的原因是因为在8个1前面补的是1导致的。
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
怎么补0呢?
11111111 11111111 11111111 11111111
&00000000 00000000 00000000 11111111
------------------------------------
00000000 00000000 00000000 11111111
0000-0001
1111-1110
000000001
1111-1111 -1
结论: 。
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.
那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
而在写入数据时,只写该int类型数据的最低8位。
注:这两种操作都被封装:
“返回数据时自动补位24个0” ———— 封装在read()方法里面;
“写入数据时只写该int类型数据的最低8位” ———— 封装在write()方法里面。
11、读取键盘录入: System.in
java.lang.System类中给我们提供了两个对象。
System.out -- InputStream:对应的是标准输出设备,控制台。
System.in -- PrintStream:对应的标准输入设备:键盘。
我们根据其所属的数据类型,就可以当作一种输入流 或是 输出流 ,就可以进行键盘的读取操作与控制台的输出。
示例Demo:
//需求:通过键盘录入数据,当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么停止录入。
class ReadIn
{
public static void main(String[] args) throws IOException
{
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while(true){
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);
}
}
}
12、读取/写入 转换流:InputStreamReader与OutputStreamWriter
转换流:InputStreamReader , OutputStreamWriter
转换流的由来
a.字符流与字节流之间的桥梁
b.方便了字符流与字节流之间的操作
转换流的应用
字节流中的数据都是字符时,转成字符流操作更高效。
转换流的使用之一:作为字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。
(1)标准输入输出
示例Demo1:
class TransStreamDemo
{
public static void main(String[] args) throws IOException
{
//获取键盘录入对象。
//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();
}
}
(2)改变标准的输入输出设备
示例Demo1:
//System.setIn(InputStream in); --- 改变标准的输入设备
//System.setOut(PrintStream out); --- 改变标准的输出设备
class TransStreamDemo2
{
public static void main(String[] args) throws IOException
{
//现将标准的输入和输出设备都换成文件。
System.setIn(new FileInputStream("PersonDemo.java"));
System.setOut(new PrintStream("zzz.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();
}
}
13、众多IO流对象的选用规律
通过四个步骤来确认完成。
a.明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer。
b.操作的数据是否是纯文本。
是:字符流。
不是:字节流。
c.明确设备:当体系明确后,再明确要使用哪个具体的对象。
源设备:内存,硬盘,键盘。
目的设备:内存,硬盘,控制台。
d.是否需要提高效率:
选择使用 :BufferedReader 和 BufferedWriter
Demo1需求:复制文件 -- 将一个文本文件中数据存储到另一个文件中。
关于源的分析
a.源:因为是源,所以使用读取流。InputStream Reader
b.是不是操作文本文件。
是!这时就可以选择Reader
这样体系就明确了。接下来明确要使用该体系中的哪个对象。
c.明确设备:硬盘。上一个文件。
Reader体系中可以操作文件的对象是 FileReader
d.是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
关于目的分析
a.目的:OutputStream Writer
b.是否是纯文本。
是!Writer。
c.设备:硬盘,一个文件。
Writer体系中可以操作文件的对象FileWriter。
d.是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
Demo2需求:将键盘录入的数据保存到一个文件中。
关于源的分析
a.源:InputStream Reader
b.是不是纯文本?是!Reader
c.设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
d.需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
关于目的分析
a.目的:OutputStream Writer
b.是否是存文本?是!Writer。
c.设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
d.需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
Demo3需求:扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
关于目的分析
a.目的:OutputStream Writer
b.是否是存文本?是!Writer。
c.设备:硬盘。一个文件。使用 FileWriter。
可是FileWriter是使用的默认编码表:GBK,而在存储时,需要加入指定编码表utf-8,但指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。该转换流对象要接收一个字节输出流,那么可以操作文件的字节输出流是:FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
d.需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以记住:转换流什么时候使用:作为字符和字节之间的桥梁。通常涉及到字符编码转换时,需要用到转换流。
14、保存“异常信息”到日志文件
注:本例仅作思路参考,开发时 大多使用log4j 这个开源的插件。
示例Demo1:保存异常信息
class ExceptionInfo
{
public static void main(String[] args)throws IOException
{
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("exeception.log");
ps.println(s);
System.setOut(ps);
}
catch (IOException ex)
{
throw new RuntimeException("日志文件创建失败"); //这是给上一层调用人员使用的
}
e.printStackTrace(System.out); //这是将异常的日志信息保存到文件中。
}
}
}
示例Demo2:将系统信息保存到文件中
class SystemInfo
{
public static void main(String[] args) throws IOException
{
Properties prop = System.getProperties();
//System.out.println(prop);
prop.list(new PrintStream("sysinfo.txt"));
}
}
15、File对象的创建、删除、判断、获取等功能。
File对象的作用:
a.用来将文件或者文件夹封装成对象
b.方便对文件与文件夹的属性信息进行操作。
c.File对象可以作为参数传递给流的构造函数。
File类常见方法:
(1)构造方法
File(File parent, String child)
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
File(String parent, String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
File(URI uri)
通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。
(2)创建。
boolean - createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean - mkdir():创建文件夹。
boolean - mkdirs():创建多级文件夹。
(3)删除。
boolean - delete():删除失败返回false。如果文件正在被使用,则删除不了返回false。
void - deleteOnExit();在程序退出时删除指定文件。
(4)判断。
boolean - exists():文件是否存在.
boolean - isFile():测试此抽象路径名表示的文件是否是一个标准文件。
boolean - isDirectory();测试此抽象路径名表示的文件是否是一个目录。
boolean - isHidden(); 测试此抽象路径名指定的文件是否是一个隐藏文件。
boolean - isAbsolute(); 测试此抽象路径名是否为绝对路径名。
(5)获取信息。
String - getName(): 返回由此抽象路径名表示的文件或目录的名称。
String - getPath():将此抽象路径名转换为一个路径名字符串。
String - getParent():返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。
String - getAbsolutePath(): 返回此抽象路径名的绝对路径名字符串。
long - lastModified(): 返回此抽象路径名表示的文件最后一次被修改的时间。
long - length(): 返回由此抽象路径名表示的文件的长度。
示例Demo:
class FileDemo
{
public static void main(String[] args) throws IOException
{
method_5();
}
public static void method_5()
{
File f1 = new File("c:\\Test.java");
File f2 = new File("d:\\hahah.java");
sop("rename:"+f2.renameTo(f1));
}
public static void method_4()
{
File f = new File("file.txt");
sop("path:"+f.getPath());
sop("abspath:"+f.getAbsolutePath());
sop("parent:"+f.getParent());//该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。
//如果相对路径中有上一层目录那么该目录就是返回结果。
}
public static void method_3()throws IOException
{
File f = new File("d:\\java1223\\day20\\file2.txt");
//f.createNewFile();
//f.mkdir();
//记住在判断文件对象是否是文件或者目的时,必须要先判断该文件对象封装的内容是否存在。
//通过exists判断。
sop("dir:"+f.isDirectory());
sop("file:"+f.isFile());
sop(f.isAbsolute());
}
public static void method_2()
{
File f = new File("file.txt");
//sop("exists:"+f.exists());
//sop("execute:"+f.canExecute());
//创建文件夹
File dir = new File("abc\\kkk\\a\\a\\dd\\ee\\qq\\aaa");
sop("mkdir:"+dir.mkdirs());
}
public static void method_1()throws IOException
{
File f = new File("file.txt");
//sop("create:"+f.createNewFile());
//sop("delete:"+f.delete());
}
//创建File对象
public static void consMethod()
{
//将a.txt封装成file对象。可以将已有的和为出现的文件或者文件夹封装成对象。
File f1 = new File("a.txt");
//
File f2 = new File("c:\\abc","b.txt");
File d = new File("c:\\abc");
File f3 = new File(d,"c.txt");
sop("f1:"+f1);
sop("f2:"+f2);
sop("f3:"+f3);
File f4 = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
16、File对象 ———— 实现文件列表功能常用方法
能够实现文件列表的一些方法与接口:
(1)File[] listFiles(): 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
(2)String[] listFiles(FilenameFilter filter)
返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
FilenameFilter接口的知识
接口:java.io.FilenameFilter :实现此接口的类实例可用于过滤器文件名,
方法:boolean accept(File dir,String name):测试指定文件是否应该包含在某一文件列表中。
参数:dir - 被找到的文件所在的目录。
name - 文件的名称。
返回:当且仅当该名称应该包含在文件列表中时返回 true;否则返回 false。
(3)String[] list():返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。
(4)static File[] listRoots():列出可用的文件系统根。
示例Demo:
class FileDemo2
{
// 使用 File[] listFiles()方法
public static void main(String[] args)
{
File dir = new File("c:\\");
File[] files = dir.listFiles();
for(File f : files)
{
System.out.println(f.getName()+"::"+f.length());
}
}
// 使用 String[] listFiles(FilenameFilter filter) 方法
public static void listDemo_2()
{
File dir = new File("d:\\java1223\\day18");
String[] arr = dir.list(new FilenameFilter()
{
public boolean accept(File dir,String name)
{
//System.out.println("dir:"+dir+"....name::"+name);
/*
if(name.endsWith(".bmp"))
return true;
else
return false;
*/
return name.endsWith(".bmp");
}
});
System.out.println("len:"+arr.length);
for(String name : arr)
{
System.out.println(name);
}
}
// 使用 String[] list()方法
public static void listDemo()
{
File f = new File("c:\\abc.txt");
String[] names = f.list();//调用list方法的file对象必须是封装了一个目录。该目录还必须存在。
for(String name : names)
{
System.out.println(name);
}
}
// 使用static File[] listRoots()方法
public static void listRootsDemo()
{
File[] files = File.listRoots();
for(File f : files)
{
System.out.println(f);
}
}
}
17、File对象 —— 操作 文件列表/目录
(1)列出目录下所有内容——递归方法(含带层次展现)
需求分析:列出指定目录下文件或者文件夹,包含子目录中的内容,也就是列出指定目录下所有内容。因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身。
递归算法:这种函数自身调用自身的表现形式,或者编程手法,称为递归。
递归要注意:a.限定条件。
b.要注意递归的次数,尽量避免内存溢出。
示例Demo:
class FileDemo3
{
public static void main(String[] args)
{
File dir = new File("d:\\testdir");
showDir(dir,0);
System.out.println(dir.delete());
}
//根据文件所在层级书写层标“|--”
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();
}
//采用递归算法,列出所有 Java文件列表
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]);
}
}
}
(2)删除带内容的目录
删除原理:在window中,删除目录从里面往外删除的。既然是从里往外删除,就需要用到递归。
示例Demo:
class RemoveDir
{
public static void main(String[] args)
{
File dir = new File("d:\\testdir");
removeDir(dir);
}
public static void removeDir(File dir)
{
File[] files = dir.listFiles();
for(int x=0; x<files.length; x++)
{
if(files[x].isDirectory())
removeDir(files[x]);
else
System.out.println(files[x].toString()+":-file-:"+files[x].delete());
}
System.out.println(dir+"::dir::"+dir.delete());
}
}
(3)创建展现多层级的Java文件列表并输出到文件
需求:将一个指定目录下的java文件的绝对路径,存储到一个文本文件中,建立一个java文件列表文件。
思路:
a.对指定的目录进行递归。
b.获取递归过程所以的java文件的路径。
c.将这些路径存储到集合中。
d.将集合中的数据写入到一个文件中。
示例Demo:
class JavaFileList
{
public static void main(String[] args) throws IOException
{
File dir = new File("d:\\java1223");
List<File> list = new ArrayList<File>();
fileToList(dir,list);
//System.out.println(list.size());
File file = new File(dir,"javalist.txt");
writeToFile(list,file.toString());
}
public static void fileToList(File dir,List<File> list)
{
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 = null;
try
{
bufw = new BufferedWriter(new FileWriter(javaListFile));
for(File f : list)
{
String path = f.getAbsolutePath();
bufw.write(path);
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw e;
}
finally
{
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw e;
}
}
}
}
18、Properties 存取配置文件
Properties是hashtable的子类,也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串,是集合中与IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。
示例Demo:
class PropertiesDemo
{
public static void main(String[] args) throws IOException
{
//method_1();
loadDemo();
}
//这是 读取 properties 类型配置文件 最标准的 流程,可以直接在开发中应用。
public static void loadDemo()throws IOException
{
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
//将流中的数据加载进集合。
prop.load(fis);
//取出属性值的操作
String value = prop.getProperty("lisi");
//该从配置文件中读取的“lisi” 的 属性值,就可以被我们使用了。
//给属性赋值的操作。
prop.setProperty("wangwu","39");
//将修改过属性值的集合 重新写回配置文件中:更新
FileOutputStream fos = new FileOutputStream("info.txt");
prop.store(fos,"haha");
// System.out.println(prop);
prop.list(System.out);
fos.close();
fis.close();
}
//下面演示,自定义方法:将流中的数据存储到集合中。
//想要将info.txt中键值数据存到集合中进行操作。
/*
1,用一个流和info.txt文件关联。
2,读取一行数据,将该行数据用"="进行切割。
3,等号左边作为键,右边作为值。存入到Properties集合中即可。
*/
public static void method_1()throws IOException
{
BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));
String line = null;
Properties prop = new Properties();
while((line=bufr.readLine())!=null)
{
String[] arr = line.split("=");
///System.out.println(arr[0]+"...."+arr[1]);
prop.setProperty(arr[0],arr[1]);
}
bufr.close();
System.out.println(prop);
}
// 设置和获取元素。
public static void setAndGet()
{
Properties prop = new Properties();
prop.setProperty("zhangsan","30");
prop.setProperty("lisi","39");
// System.out.println(prop);
String value = prop.getProperty("lisi");
//System.out.println(value);
prop.setProperty("lisi",89+"");
Set<String> names = prop.stringPropertyNames();
for(String s : names)
{
System.out.println(s+":"+prop.getProperty(s));
}
}
}
19、PrintStream 以及 PrintWriter
打印流:该流提供了打印方法,可以将各种数据类型的数据都原样打印。
PrintStream - 字节打印流:
构造函数可以接收的参数类型:
a.file对象。File --------- PrintStream(File file)
b.字符串路径。String ----- PrintStream(String fileName)
c.字节输出流。OutputStream PrintStream(OutputStream out)
PrintWriter - 字符打印流
构造函数可以接收的参数类型:
a.file对象。File ----------- PrintWriter(File file)
b.字符串路径。String ------- PrintWriter(String fileName)
c.字节输出流。OutputStream -- PrintWriter(OutputStream out)
d.字符输出流,Writer。 ----- PrintWriter(Writer out)
【备注:在构造函数中可以 添加 true/false, 以代表是否需要自动刷新。】
示例Demo:
class PrintStreamDemo
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);
String line = null; //自动刷新
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line.toUpperCase()); //自带平台适应的行分隔符(等同于换行符)
//out.flush();
}
out.close();
bufr.close();
}
}
20、文件的 切割操作
示例Demo:
@Test
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();
}
21、文件的合并操作 ----- SequenceInputStream 合并文件
SequenceInputStream:表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
构造方法:SequenceInputStream(Enumeration<? extends InputStream> e)
通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
SequenceInputStream 的两种装载方式:
a. Vector方式:操作方便,但是效能远低于ArrayList。
b. ArrayList方式:操作稍显麻烦,但是ArrayList的性能要比Vector高很多。
示例Demo:
//第一种 SequenceInputStream 的装载方式 ———— Vector
@Test
public static void merge1() 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<FileInputStream> en = v.elements();//仅Vector结合对象能得到Enumeration类型。
SequenceInputStream sis = new SequenceInputStream(en); //将多个文本的输入流 组装到SequenceInputStream中
FileOutputStream fos = new FileOutputStream("c:\\4.txt");
byte[] buf = new byte[1024];
int len =0;
while((len=sis.read(buf))!=-1) //把 SequenceInputStream 当作一个 普通输入流 读取数据。
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
//第二种 SequenceInputStream 的装载方式 ———— ArrayList
@Test
public static void merge2()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();
//将 it 装载入 Enumeration 里面
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
{
public boolean hasMoreElements()
{
return it.hasNext();
}
public FileInputStream nextElement()
{
return it.next();
}
};
//上面的文件合并功能实现方式 若稍显麻烦(但是ArrayList的性能要比Vector高很多),
// 可以参见“#SquenceInputSteam 合并文件” 中的演示。
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();
}
22、对象的序列化:ObjectOutputStream 和反序列化:ObjectInputStream
ObjectOutputStream
作用:可将对象写入流中(对象类需实现Serializable接口);
也可将 Java 对象的基本数据类型和图形写入 OutputStream。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
示例Demo:
class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
//writeObj();
readObj();
}
public static void readObj()throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject(); //该Person类必须是实现了Serializable 接口
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")); //将对象保存到硬盘文件"obj.txt" 中。
oos.close(); //该Person类必须是实现了Serializable 接口
}
}
23、RandomAccessFile 随机读写访问文件
RandomAccessFile:该类不是算是IO体系中子类,而是直接继承自Object,但是它是IO包中成员,因为它具备读和写功能。其内部封装了一个数组,而且通过指针对数组的元素进行操作。
我们可以通过getFilePointer()获取指针位置,同时可以通过seek()改变指针的位置,其实完成读写的原理就是内部封装了字节输入流和输出流。
【备注:可实现多线程并发读写同一文件:如迅雷技术。】
构造函数:
RandomAccessFile(File file, String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
RandomAccessFile(String name, String mode)
创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
mode 参数指定用以打开文件的访问模式。允许的值及其含意为:
值 含意
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式:只读r,读写rw等。
如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。
示例Demo:
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
//writeFile_2();
//readFile();
//System.out.println(Integer.toBinaryString(258));
}
//测试打开一个只读的RandomAccessFile对象
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();
}
//测试打开一个 可读也可写的RandomAccessFile对象
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();
}
}
24、操作基本数据类型 DataInputStream与DataOutputStream
DataInputStream与DataOutputStream 可以用于操作基本数据类型的数据的流对象,减少字节位数 转换的麻烦。
示例Demo:
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("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(); //读入一个已使用 UTF-8 修改版格式编码的字符串。
System.out.println(s);
dis.close();
}
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");//将表示长度信息的两个字节写入输出流,后跟字符串 s 中每个字符的 UTF-8 修改版表示形式。
dos.close();
}
public static void readData()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt(); // 读取 int 类型的数据
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); //写入 int 类型的数据
dos.writeBoolean(true);//写入布尔类型的数据
dos.writeDouble(9887.543);// 写入双精度类型的数据
dos.close();
ObjectOutputStream oos = null;
oos.writeObject(new O()); //写入对象数据
}
}
25、ByteArrayStream 操作字节数组的流对象。
用于操作字节数组的流对象。
ByteArrayInputStream : 在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源,所以不用进行close关闭。
在流操作规律讲解时:
源设备,
键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:
控制台 System.out,硬盘FileStream,内存 ArrayStream。
用流的读写思想来操作数据。
示例Demo:
class ByteArrayStream
{
public static void main(String[] args)
{
//数据源。
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"));
}
}
26、字符的编码和解码
编码:字符串变成字节数组:String-->byte[]; str.getBytes(charsetName);
解码:字节数组变成字符串:byte[] -->String: new String(byte[],charsetName);
(1)编码 和 解码 的练习
示例Demo:
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,"utf-8");
System.out.println("s1="+s1);
//对s1进行iso8859-1编码。
byte[] b2 = s1.getBytes("utf-8");
System.out.println(Arrays.toString(b2));
String s2 = new String(b2,"gbk");
System.out.println("s2="+s2);
}
}
(2)转换流的字符编码
示例Demo:
class EncodeStream
{
public static void main(String[] args) throws IOException
{
//writeText();
readText();
}
public static void readText()throws IOException
{
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();
}
public static void writeText()throws IOException
{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
osw.write("你好");
osw.close();
}
}
附图片: 编码1.jpg 和 编码2.jpg
编码1.jpg
编码2.jpg
IO流 学习笔记