您的位置:首页 > 职场人生

黑马程序员-java基础(七)-IO流

2015-10-18 15:52 676 查看
-------
android培训、java培训、期待与您交流! ----------

IO流

1  IO流

IO流(Input Output)用来处理设备之间的数据传输。Java对数据的操作是通过流的方式。
按操作数据分:字节流、字符流;字节流抽象基类:InputStream,OutStream;字符流抽象基类:Reader,Writer

按流向分:输入流,输出流

输入输出都是相对于内存而言:
输入:将外设中的数据读取到内存中

输出:将内存的数写入到外设中

p.s.

字符流其实其实就是:字节流读取文字字节数据后,不直接操作而是先查指定的编码表,获取对应的文字。再对这个文字进行操作。简单说:字节流+编码表。

2  字符流

1、创建文件写入字符
FileWriter(String fileName)


          根据给定的文件名构造一个 FileWriter 对象。
import java.io.*;
class  Test
{
public static void main(String[] args) throws IOException
{
FileWriter fw=new FileWriter("Demo.txt"); //创建一个FileWriter对象,该对象一初始化被徐有明确被操作文件,有同名文件会直接覆盖
fw.write("ok,success");				//调用write方法,将字符串写进流,待刷新后写入目的地
fw.close();					//刷新流对象的缓存中的数据到目的地中,也可以用flush刷新,但是close刷新后会关闭流
}
}



2、文件续写

/*
已有文件续写
*/

import java.io.*;
class  Test
{
public static void main(String[] args) throws IOException
{
FileWriter fw=new FileWriter("Demo.txt",true);
fw.write("\r\nWrite again");
fw.close();
}
}




3.IO异常处理:
/*
IO异常处理方式
*/

import java.io.*;
class  FileWriterDemo2
{
public static void main(String[] args) throws IOException
{
FileWriter fw=null ;
try
{
fw=new FileWriter("FileWriter2.txt");
fw.write("ok,success");
}
catch (IOException e)
{
System.out.println("catch:"+e.toString());
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}

4、字符流文件读取
第一种方式:读单个字符reader()
import java.io.*;
class  Test
{
public static void main(String[] args) throws IOException
{
FileReader fr=new FileReader("Demo.txt");	//创建一个文件读取流对象,且保证文件存在
while (true)
{
int ch=fr.read();                    //一次读一个字符,且会自动往下读,读到末尾则返回-1
if (ch==-1)
break;
System.out.println("ch="+(char)ch);//强转为char型
}

fr.close();
}
}
运行结果:



第二种方式:通过字符数组进行reader(char[] cbuf)
import java.io.*;
class Test
{

public static void main(String[] args) throws IOException
{
FileReader fr=new FileReader("Demo.txt");
char[] buf=new char[1024];
int len=0;
while((num=fr.read(buf))!=-1)//read([])返回字符个数
{
System.out.println(new String (buf,0,len));
}
fr.close();
}
}
5、字符流缓冲区(BufferReader、BufferWriter)

在流的基础上对流的功能进行增强,提高对数据的读写效率

P.S.

缓冲区要结合字符流才可以使用

/*
BufferedReader、BufferedWriter
*/

import java.io.*;
class  Test
{
public static void main(String[] args) throws IOException
{
FileWriter fw=new FileWriter("Demo.txt"); //建立字符写入流对象
BufferedWriter bw=new BufferedWriter(fw);//建立字符写入流流对应的缓冲区对象
for(int i=0;i<4;i++){
bw.write("This is BufferedWriter"+i);//用字符缓冲区一次写入一个字符串
bw.newLine();//写入一个行分割符
}
bw.close();

FileReader fr=new FileReader("Demo.txt"); //建立字符读取流对象
BufferedReader br=new BufferedReader(fr);//建立字符读取流对应的缓冲区对象
String line;
while((line=br.readLine())!=null){//一次读取一行
System.out.println(line);
}
br.close();
}
}



P.S.

readLine()方法原理:

【自定义读一行】
/*
自定义读一行 MyReaderDemo
*/
import java.io.*;
class MyReader
{
private FileReader f;
MyReader(FileReader f)//输入流对象
{
this.f=f;
}
public String MyReaderLine() throws IOException//读一行方法
{
StringBuilder sb=new StringBuilder();//定义一个临时容器StringBuilder
int ch=0;
while((ch=f.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')//判断是否到行结尾
return sb.toString();//将字符串输出
sb.append((char)ch);//读一个字符存入容器中
}
if(sb.length()!=0)//防止最后一行没有换行符,造成数据丢失
return sb.toString();
 return null;
}

public void MyClose() throws IOException
{
f.close();
}
}

class MyReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr=new FileReader("demo.txt");
MyReader mr=new MyReader(fr);
String line=null;
while((line=mr.MyReaderLine())!=null)
{
System.out.println(line);//打印一行
}
mr.MyClose();
}
}
6、装饰设计模式
在原有类进行功能改变,增强已有对象的功能(如上:自定义的可以读一行类)

/*
装饰类和继承的区别
*/
class Person{
void chifan(){
System.out.println( "吃饭");
}
}

//采用装饰的方式增强Person类
//这个类的出现是为了增强Person而出现的
class NewPerson{
private Person p;
NewPerson(Person p){
this.p = p;
}
public void chifan(){
p.chifan();
System.out.println( "开胃酒");
System.out.println( "甜点");
}
}

//采用继承的方式增强Person类
class NewPerson2 extends Person{
public void chifan(){
super.chifan();
System.out.println( "开胃酒");
System.out.println( "甜点");
}
}

public class Test{
public static void main(String[] args){
Person p = new Person();
NewPerson np1 = new NewPerson(p);
np1.chifan();
System.out.println( "---------------");
NewPerson2 np2 = new NewPerson2();
np2.chifan();
}
}
运行结果:



P.S.
装饰体系较继承体系灵活,避免了装饰体系的臃肿,降低了类与类之间的关系

3  字节流

1、基本操作与字符流类相同。但它不仅可以操作字符,还可以操作其他媒体文件。
/*
字节流输入、输出基本操作
*/

public class Test{
public static void main(String[] args)throws IOException
{
writeStream();
readStream1();
System.out.println();
System.out.println("--------");
readStream2();
System.out.println();
System.out.println("--------");
readStream3();

fd45
}
public static void writeStream()throws IOException
{
FileOutputStream fos=new FileOutputStream("Demo.txt");
//getBytes()使用平台的默认字符集将此 String 编码为 byte 序列
fos.write("hallo java".getBytes());//字节为最小操作单位不用flush刷新,但要关流
fos.close();
}
//读取方式一:一次读取一个字节
//比较慢
public static void readStream1()throws IOException
{
FileInputStream fis=new FileInputStream("Demo.txt");
int ch;
while((ch=fis.read())!=-1){//从输入流中读取一个字节
System.out.print((char)ch);
}
}
//读取方式二:定义一个1024的字节,一次读1024个字节
//较合理,建议使用
public static void readStream2()throws IOException
{
FileInputStream fis=new FileInputStream("Demo.txt");
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){//从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
System.out.print(new String(buf,0,len));
}
}
//通过available(),获取字节长度,定义一个刚好的字节数组存放
//如果字节长度过长,容易造成溢出
public static void readStream3()throws IOException
{
FileInputStream fis=new FileInputStream("Demo.txt");
byte[] buf=new byte[fis.available()];
fis.read(buf);
System.out.print(new String(buf,0,buf.length));
}
}
运行结果:



/*
复制一个图片
*/
import java.io.*;
public class Test{
public static void main(String[] args)throws IOException
{
FileInputStream fis=new FileInputStream("123.jpg");//创建输入字节流,关联文件
FileOutputStream fos=new FileOutputStream("123_copy.jpg");//创建输出字节流,关联文件
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
fos.write(buf);//每次存储2kb数据
}}}



2、字节流缓冲区:

BufferedOutputStream、BufferedInputStream(和字符流缓冲区使用方法一致)
/*
* 需求:拷贝一个mp3文件,并比较几种方式的效率
* */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test1{
public static void main(String[] args) throws IOException {
long s1=System.currentTimeMillis();
copy_1();//一次读取1kb到缓冲数组中,然后输出
long s2=System.currentTimeMillis();
copy_2();//一次读取一个字节,加入缓冲计数BufferInputStream
long s3=System.currentTimeMillis();
copy_3();//一次读取一个节,不使用缓冲区
long s4=System.currentTimeMillis();
System.out.println("1:"+(s2-s1)+"ms...2:"+(s3-s2)+"ms...2:"+(s4-s3)+"ms");
}

public static void copy_1() throws IOException {
FileInputStream fis = new FileInputStream("1.mp3" );
FileOutputStream fos = new FileOutputStream("1_copy.mp3" );

byte[] buf = new byte[1024];

int len = 0;

while((len = fis.read(buf)) != -1){
fos.write(buf,0,len);
}

fis.close();
fos.close();
}

public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("2.mp3" );
BufferedInputStream bufis = new BufferedInputStream(fis);

FileOutputStream fos = new FileOutputStream("2_copy.mp3" );
BufferedOutputStream bufos = new BufferedOutputStream(fos);

int ch = 0;
//一次读取一个
while((ch = bufis.read()) != -1){
bufos.write(ch);
}

bufis.close();
bufos.close();
}
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("2.mp3" );
FileOutputStream fos = new FileOutputStream("3_copy.mp3" );
int ch = 0;
//一次读取一个
while((ch = fis.read()) != -1){
fos.write(ch);
}
fis.close();
fos.close();
}
}
运行结果





从以上结果,可以很明显看出拷贝效率:加入缓冲的字节数组>使用字节流缓冲区>字节流一次读取一个字节

3、读取键盘输入

System.in:标准输入设备:键盘

System.out:标准输出设备:控制台
/*
获取用户键盘录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。
1、键盘录入只读取一个字节,要判断是否为over,需要将读取的字节拼成字符串
2、定义StringBuilder来存储
3.在用户回车前,转换成字符串并判断
*/
import java.io.*;
public class Test{
public static void main(String[] args)throws IOException
{
InputStream is=System.in;//System.in:返回标准输入流
StringBuilder sb=new StringBuilder();
int ch=0;
while(true){
ch=is.read();
if(ch=='\r')
continue;
if(ch=='\n'){
String s=sb.toString();
System.out.println(s.toUpperCase());//转换为大写输出
if("over".equals(s))//判断是否输入的是over
break;
sb.delete(0, sb.length());//存入一行后要初始化一下
}
else
sb.append((char)(ch));
}}}




4、转换流

字符流和字节流之间的桥梁,方便字节流和字符流之间的转换
转换流:

    InputStreamReader:字节到字符的桥梁,解码。

    OutputStreamWriter:字符到字节的桥梁,编码。

转换流的应用:

    字节流中的数据都是字符时,转成字符流操作更高效。
/*
获取用户键盘录入的数据并将数据变成大写显示在控制台上,如果用户输入的是over,结束键盘录入。
1、键盘录入是字节流,将字节流转换成字符流
2、定义字符流的缓冲区,用readLine方法高效读取
3、判断是否为over
*/
import java.io.*;
public class Test{
public static void main(String[] args)throws IOException
{
InputStream is=System.in;//System.in:返回标准输入流
InputStreamReader isr=new InputStreamReader(is);//将字节输入流转换成字符输入流
BufferedReader br=new BufferedReader(isr);//建立字符输入流的缓冲区
while(true){
String line=br.readLine();
if("over".equals(line))
break;
System.out.println(line.toUpperCase());
}}}
P.S.

    使用字节流读取一个中文字符需要读取两次,因为一个中文字符由两个字节组成,而使用字符流只需读取一次。
import java.io.*;
public class Test{
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;
while((line=br.readLine())!=null){
if("over".equals(line))
break;
bw.write(line.toUpperCase());
bw.newLine();
bw.flush();
}

}}



5、改变标准输入输出设备
System类

static void
setIn(InputStream in)


          重新分配“标准”输入流。
static void
setOut(PrintStream out)


          重新分配“标准”输出流。
/*
改变标准输入、输出设备
*/
import java.io.*;
public class Test{
public static void main(String[] args)throws IOException
{
System.setIn(new FileInputStream("Demo.txt"));//改变标准的输入设备
System.setOut(new PrintStream("Demo_copy.txt"));//改变标准输出设备
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
String line;
while((line=br.readLine())!=null){
if("over".equals(line))
break;
bw.write(line.toUpperCase());
bw.newLine();
bw.flush();
}

}}



P.S.
    字符流继承体系简图



    字节流继承体系简图



  4  File类 

File类用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。

File对象可以作为参数传递给流的构造函数。
import java.io.*;
import java.io.File;

public class Test{
public static void main(String[] args){
constructorDemo();
}

public static void constructorDemo(){
//可以将一个已存在的,或者不存在的文件或者目录封装成file对象
//方式一
File f1 = new File("d:\\demo\\a.txt" );

//方式二
File f2 = new File("d:\\demo" ,"a.txt" );

//方式三
File f = new File("d:\\demo" );

File f3 = new File(f,"a.txt" );

//考虑到Windows和Linux系统通用
File f4 = new File("d:" + File.separator + "demo" + File.separator + "a.txt" );
}
}


P.S
流只能操作数据不能操作文件

File.separator是与系统有关的默认名称分隔符。在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 '\\'。
【例】过滤指定目录中的文件
/*
需求:获取指定目录下后缀名为java的文件。
思路:
用list(FilenameFilter filter)对文件进行过滤
*/
import java.io.*;

public class Test{
public static void main(String[] args){
File dir=new File("f:\\WJava\\Day18");//指定目录
String[] list=dir.list(new FilenameFilter(){//定义内部类获取FilenameFilter对象
public boolean accept(File dir, String name){//dir:目录 name:文件名称,返回真则取出
return name.endsWith(".java");//过滤.java文件
}
});
for(String name:list){
System.out.println(name);}
}
}



递归:

函数自身直接或者间接的调用到了自身。

    一个功能在被重复使用,并每次使用时,参与运算的结果和上一次调用有关。这时可以用递归来解决问题。

 P.S.

1、递归一定明确条件,否则容易栈溢出。

2、注意一下递归的次数。

/*
列出目录下所有内容
*/
import java.io.*;

public class Test{
public static void main(String[] args)
{
File dir=new File("f:\\WJava\\exam");
showFile(dir);
}
public static void showFile(File f)//定义一个打印目录下所文件的方法
{

File[] files=f.listFiles();
for(int i=0;i<files.length;i++){
System.out.println(files[i]);
if(files[i].isDirectory())
showFile(files[i]);//如果是一个目录,递归调用该方法

}

}
}
【例】
/*
需求:利用递归求1到10的和。
*/
import java.io.*;

public class Test{
public static void main(String[] args)
{

System.out.println(getSum(10));

}
public static int getSum(int num)
{
int sum=0;
if(num>=1)
sum=num+getSum(num-1);
return sum;
}
}
运行结果:



【例】 需求:对指定目录进行删除
/*
需求:删除一个带内容的目录
思考:
1、目录里面有内容是无法删除,原理上必须从内往外删
2、定义一个删除指定目录中文件的方法(当存在目录时递归此方法)
*/
import java.io.*;

public class Test{
public static void main(String[] args)
{

File dir=new File("f:\\exam");
removeDir(dir);

}
public static void removeDir(File f)
{
File[] files=f.listFiles();
for(int i=0;i<files.length;i++){
if(files[i].isDirectory())
removeDir(files[i]);
else
System.out.println(files[i].toString()+"::"+files[i].delete());//将文件删除
}
System.out.println(f.toString()+"::"+f.delete());//将文件夹删除
}
}
运行结果:



Properties类

一、概述

Properties是Hashtable的子类,它具备Map集合的特点。而且它里面还有存储的键值对,都是字符串,无泛型定义。是集合中和IO技术想结合的集合容器。

特点:

1)可用于键值对形式的配置文件

2)在加载时,需要数据有固定的格式,常用的是:键=值

二、特有方法

1、设置

Object setProperty(String key,String value);//设置键和值,调用Hashtable的方法put

2、获取

String getProperty(String key);//指定key搜索value

Set<String> stringPropertyName();//返回属性列表的键集,存入Set集合

3、加载流和存入流

void load(InputStream ism);//从输入字节流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

void load(Readerreader);//从输入字符流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。

voidlist(PrintStream out);//将属性列表输出到指定的输出流

void store(OutputStreamout,String comments);//对应load(InputStream )将属性列表(键值对)写入输出流。comments属性列表的描述。

void store(Writerwriter, String comments);//对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。

package cn.swu1;
/*
练习:用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。

分析:
很容易想到的是:计数器。可是该计数器定义在程序中,随着该应用程序的退出,该计数器也在内存中消失了。
所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。键值对数据是map集合。数据是以文件形式存储。使用io技术。那么map+io——>Properties。

思路:1、用读取流关联文本信息文件。如果存在则读取,如果不存在,则创建
2、每次运行,将文件数据存入集合中,读取值,判断次数,如果小于等于5次,则次数增加1次,如果大于则输出提示信息。
3、将值小于等于5次的信息数据存入文件中
*/
import java.util.*;
import java.io.*;

class  APPNum
{
public static void main(String[] args)throws IOException
{
int count=runCount();
if(count>5)//如果程序被使用了超过5次,则终止使用,并提示
{
System.out.println("次数到了,交钱!!!!!");
return ;
}
else
System.out.println("程序第"+count+"次Run!");
}
//获取程序运行的次数
public static int runCount()throws IOException
{
Properties ps=new Properties();//创建集合对象

File file=new File("info.ini");//将文件进行封装
if(!file.exists())//判断是否存在
file.createNewFile();
FileReader fr=new FileReader(file);//将文件于读取流进行关联

ps.load(fr);//加载流中的文件数据到集合中

int count=0;//定义计数器
String value=ps.getProperty("time");//获取次数值

if(value!=null)//如过值不等于null,则将其赋值给count
{
count=Integer.parseInt(value);
}
count++;//每启动一次自增
ps.setProperty("time",count+"");//将次数记录住集合

FileWriter fw=new FileWriter(file);
ps.store(fw,"");//将集合中的数据存入硬盘文件中

fr.close();//关流
fw.close();

return count;//返回程序启动的次数
}
}


运行结果:



6  打印流

概述
1、打印流包括:PrintStream和PrintWriter
2、该流提供了打印方法,可将各种类型的数据都原样打印。

字节打印流:PrintStream

构造方法中可接收的参数类型:

1、File对象。File

2、字符串路径:String
3、字符输出流:OutputStream

字符串打印流:PrintWriter

构造方法中可接受的参数类型
1、File对象:File

2、字符串路径:String
3、字节输出流:OutputStream
4、字符输出流:Writer

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("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();

}
}


总结

1、流的对象很多,开发是具体选择那类流:
a、明确源和目的

         源:InputStream Reader

         目的:OutputStream Writer

b、明确数据是否是纯文本数据

         源:是纯文本:Reader

                否:InputStream

         目的:是纯文本:Writer

                否:OutputStream

         到这里,就可以明确需求中具体要使用哪个体系。

c、明确具体的设备(通过setIn和setOut可以改变默认输入输出设备)

         源设备:硬盘:File        键盘:System.in        内存:数组        网络:Socket流

         目的设备:硬盘:File     控制台:System.out   内存:数组        网络:Socket流

d、是否需要其他额外功能

         是否需要高效(缓冲区):     是,就加上buffer
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息