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

黑马程序员—Java中的IO流

2015-07-08 14:34 621 查看
11. IO流

11.1IO流概述

IO流用来处理设备之间的数据传输。流按操作数据分为两种:字节流与字符流。

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

字节流的抽象基类:InputStream OutputSream

字符流的抽象基类:Reader Writer

11.2字符流

1)FileWriter

import java.io.*;
class FilerWriterDemo
{
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();
}
}

IO异常处理方式:

import java.io.*;
class FilerWriterDemo2
{
public static void main(String[] args)
{
FileWriter fw = null;
try
{
//在此处进行fw初始化,以免出现异常时,finally语句无法读取到fw对象
fw = new FileWriter("demo.txt");
fw.write("abcdefg");
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
/*当上述语句出现异常时,fw不会创建,fw.close()语句将会出现fw类
文件不存在的异常,因此进行一次判定来解决*/
if(fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}

对已有文件的数据续写:

import java.io.*;
class FilerWriterDemo3
{
public static void main(String[] args)
{
FileWriter fw = null;
try
{
/*传递一个true参数,代表不覆盖已有的文件,
并在已有文件的末尾处进行数据的续写*/
fw = new FileWriter("demo.txt",true);
fw.write("\r\nhaha");
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}


2)FileReader

import java.io.*;
class FileReaderDemo
{
public static void main(String[] args)
{
/*创建一个文件读取流对象,和指定名称的文件相关联要保证该文件
是已经存在的,如果不存在会发生异常FileNotFoundException*/
FileReader fr = null;
try
{
fr = new FileReader("demo.txt");

/*读取方法一:调用读取流对象的read方法
read()方法一次读一个字符,而且会自动往下读,已到末尾返回-1*/
int ch = 0;
while ((ch=fr.read())!=-1)
System.out.println((char)ch);

/*读取方法二:定义一个字符串数组,用于存储读到字符。该read(char[])
返回的是读到字符个数*/
char[] buf = new char[1024];
int num = 0;
while ((num=fr.read(buf))!=-1)
{
System.out.println("num="+num+"..."+new String(buf,0,num));
}
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
try
{
if(fr!=null)
fr.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
}
}


3)拷贝文本文件

import java.io.*;
class CopyText
{
public static void main(String[] args)
{
copy_2();
}
//复制方式一:从C盘读一个字符,就往D盘写一个字符
public static void copy_1()
{
//创建目的地
FileWriter fw = null;
//与已有文件关联
FileReader fr = null;
try
{
fw = new FileWriter("RuntimeDemo_copy.txt");
fr = new FileReader("RuntimeDemo.java");
int ch = 0;
while ((ch = fr.read())!=-1)
{
fw.write(ch);
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
try
{
if(fr!=null)
fr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
//复制方式二:从C盘读取一段,再存到D盘中
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
{
try
{
if(fr!=null)
fr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
}


11.3字符流的缓冲区

缓冲区的出现提高了对数据的读写效率,缓冲区要结合流才可以使用,在创建缓冲区之前,必须要先有流对象

1) 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<5 ;x++ )
{
bufw.write("abcd"+x);
bufw.newLine();
bufw.flush();
}
//只要用到缓冲区,就要记得刷新
bufw.flush();
//关闭缓冲区就是在关闭缓冲区中的流对象
bufw.close();
}
}

2) BufferedReader

该缓冲区提供了一个一次读一行的方法,方便于对文本数据的获取: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();
}
}


LineNumberReader类:是BufferedReader类的子类,有两个新方法

getLineNumber():获得当前行号

setLineNumber():设置当前行号

11.4装饰设计模式

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类

装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

装饰模式比继承更灵活,避免了继承体系的臃肿,而且降低了类与类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于一个体系中。

装饰类和被装饰类都是,被装饰类父类的一个子类。创建自定义装饰类时,要继承父类,那么需要复写父类中的抽象方法

11.5字节流

1) FileInputSream (读)

public static void readFile_3() throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
//定义一个刚刚好的缓冲区,不用再循环了,数据不大时使用。
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();
}


2) FileOutputSream (写)

public static void writeFile() throws IOException
{
FileOutputStream fos = new FileOutputStream("fos.txt");

//字节流不能写入字符串,通过String.getBytes()方法将字符串转成byte数组写入
fos.write("abcde".getBytes());
/*不需要flush()。因为字符流也是写入字节,但是需要将写入的字节先临时缓存。
查完表后通过flush()再写入字符。而字节流不需要缓存,直接就将字节写入*/
fos.close();
}


11.6字节流的缓冲区

BufferedOutputStream

BufferedInputStream

//通过缓冲区拷贝mp3文件:
import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws IOException
{
long start = System.currentTimeMillis();
copy_1();
long end = System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
//通过字节流缓冲区完成复制
public static void copy_1() throws IOException
{
BufferedInputStream bufis =
<span style="white-space:pre">			</span>new BufferedInputStream(new FileInputStream("mp3.mp3"));
BufferedOutputStream bufos =
<span style="white-space:pre">			</span>new BufferedOutputStream(new FileOutputStream("mp3_copy.mp3"));
int by = 0;
while ((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
}


11.7读取键盘录入

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

1) InputStreamReader

是字节流通向字符流的桥梁,将字节流对象转成字符流对象。

2) OutputStreamWriter

字符流通向字节流的桥梁。

/*通过键盘录入数据,当录入一行数据后,就将该行数据进行打印
如果录入的数据是over,那么停止录入*/
import java.io.*;
class ReadIn
{
public static void main(String[] args) throws IOException
{
//获取键盘录入对象
InputStream in =System.in;
//将字节流对象转成字符流对象,使用转换流InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
//为了提高效率,将字符流进行缓存区技术高效操作,使用BufferedReader
BufferedReader bufr = new BufferedReader(isr);
//获取输出对象
OutputStream out = System.out;
//将字节流对象转成字符流对象,使用转换流OutputStreamWriter
OutputStreamWriter osw = new OutputStreamWriter(out);
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();
}
}


11.8流操作规律

1) 明确源和目的

源:输入流。InputStream Readerr

目的:输出流 OutputStream Writer

2) 操作的数据是否是纯文本

是:字符流

不是:字节流

3) 当体系明确后,再明确要使用哪个具体的对象

通过设备来进行区分

源设备:内存ArrayStream,硬盘 FileStream,键盘 System.in

目的设备:内存 ArrayStream,硬盘 FileStream,控制台 System.out

改变标准输入输出设备:

//将输入设备由键盘改成PersonDemo.java文件,需要建立一个字节流对象FileInputStream

System.setIn(new FileInputStream(“PersonDemo.java”))

//将输出设备由控制台改成zz.txt文件,需要建立一个字节输出流子类对象PrintStream

System.setOut(new PrintStream(“zz.txt”))

11.9File类

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

File类常见方法:

1) 创建

boolean mkdir():创建文件夹,只能创建一级目录

boolean mkdirs():创建多级目录,包括所有必需但不存在的父目录

boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立就会创建文件,而

且文件已经存在,会覆盖。File类要调用方法才创建文件。

2) 删除

boolean delete():如果此句前出现异常将不会执行到,文件就无法删除

void deleteOnExit():在程序退出时删除。在程序运行出现异常的时候也能删除文件。

3) 判断

boolean canExecute():判断文件是否可执行

boolean exists():判断文件是否存在

boolean isDirectory(): 判断文件对象是否是文件或者目录时,必须要先判断

boolean isFile(): 该文件对象封装的内容是否存在,通过exists判断

boolean isHidden():判断文件是否隐藏

4) 获取信息

getName()

getPath()

getParent() 该方法返回的是绝对路径中的父目录,如果File对象封装的是相对路径,返回null。如果相对路径中有上一层目录那么该目录就是返回结果

getAbsolutePath() 获取绝对路径

long lastModified() 获取文件最后被修改的时间

long length()

boolean renameTo() f1.renameTo(f2),变更文件名,还会将文件剪切到指定目录下

File[] listRoots() 列出可用的系统盘符

String[] list() 列出目录下的文件和子目录。调用list方法的file对象必须是封装了一个目录,该目录还必须存在

File[] listFiles() 列出目录下的文件,返回的是File对象的数组

列出指定目录下文件或者文件夹,包含子目录中的内容(带层次):
import java.io.*;
class FileDemo3
{
public static void main(String[] args)
{
File file = new File("c:\\java001");
showDir(file,0);
}
public static void showDir(File dir,int level )
{
System.out.println(getLevel(level)+dir.getName());
//level只对应于调用的函数中,递归调用时对应的是函数中相应的值
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].getName());
}
}
public static String getLevel(int level)
{
StringBuilder sb = new StringBuilder();
//文件夹及文件名前带|--
sb.append("|--");
for (int x=0;x<level ;x++ )
{
//子级目录缩进
sb.insert(0,"  ");
}
return sb.toString();
}
}

函数自身调用自身,这种表现形式称为递归。使用时需要注意:

1)限定条件

2)要注意递归的次数,尽量避免内存溢出

11.10Properties类

Properties是hashtable的子类,具备map集合的特点,它里面存储的键值对都是字符串。是集合中和IO技术相结合的集合容器。该对象的特点:可以用于键值对形式的配置文件。

Properties中的方法:

setProperty(String key, String value) 向集合中存入键值对

String getProperty(String key) 获取对应键的值

Set<String> stringPropertyNames() 获取Properties对象中键的集合

list(PrintStream out)

list(PrintWriter out)

load(InputStream inStream)

load(Reader reader)

store(OutputStream out, String comments) 将Properties中的键值对存储到配置文件中

store(Writer writer, String comments) 并且加入注释信息comments。

import java.io.*;
import java.util.*;
class  PropertiesDemo
{
public static void main(String[] args) throws IOException
{
//method_1();
PropertiesMethod();
}
//load,list,store方法应用
public static void PropertiesMethod() throws IOException
{
FileInputStream fis = new FileInputStream("Info.txt");
Properties prop = new Properties();
//将流中的数据加载进集合
prop.load(fis);
//设置配置文件中的信息
prop.setProperty("wangwu","39");
FileOutputStream fos = new FileOutputStream("Info.txt");
prop.store(fos,"haha");
//将集合中的数据输出到流中
prop.list(System.out);
fos.close();
fis.close();
}
//load方法原理:将info.txt中键值数据存到集合中进行操作。
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("=");
//调用方法将键和值存入到集合中
prop.setProperty(arr[0],arr[1]);
}
bufr.close();
}
//设置和获取元素
public static void setAndGet()
{
Properties prop = new Properties();
prop.setProperty("zhangsan","30");
prop.setProperty("lisi","39");

String value = prop.getProperty("lisi");
System.out.println(value);
Set<String> names = prop.stringPropertyNames();
for(String s : names)
{
System.out.println(s);
}
}
}


11.11打印流

为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。该流提供了打印方法,可以将各种数据类型的数据都原样打印

字节打印流:

PrintStream

构造函数可以接收的参数类型:

1. file对象 File

2. 字符串路径 String

3. 字节输出流 OutputStream

字符打印流:

PrintWriter

构造函数可以接收的参数类型:

1. file对象 File

2. 字符串路径 String

3. 字节输出流 OutputStream

4. 字符输出流 Writer

PrintWriter(OutputStream out, booleanautoFlush) autoFlush为true时,println方法会

PrintWriter(Writer out, boolean autoFlush) 自动刷新缓存

11.12 序列流SequenceInputStream

SequenceInputStream
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

import java.io.*;
import java.util.*;
class  SequenceDemo
{
public static void main(String[] args) 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<FileInputStream> en = v.elements();
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("4.txt");
byte[] buf = new byte[1024];
int len = 0;
while ((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}


11.13对象的序列化

ObjectInputStream ObjectOutputStream

将堆内存中的对象存入的文件当中,对象内的变量数据也被保存;读取文件的可以读取到所存储的对象,直接调用之前的数据。实现了Serializable接口的对象才能被序列化。

在需要序列化的对象中添加static final long serialVersionUID = 42L语句,即使本地的该对象有所修改,也可以读取被序列化的对象,否则会报出InvalidClassException异常。

static 修饰的变量不能被序列化,因为static修饰的内容存在于方法区;transient修饰的变量也不能被序列化,即使变量存在于堆内存中。

11.14管道流

PipedInputStream PipedOutputStream

管道输入流和管道输出流通过不同线程运行。管道流是多线程和IO流的结合应用

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];
//当没有读取到数据时read方法会阻塞,即线程等待。
int len = in.read(buf);
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
{
out.write("piped lai le".getBytes());
out.close();
}
catch (IOException e)
{
throw new RuntimeException("管道输出流失败");
}
}
}
class  PipedStreamDemo
{
public static void main(String[] args) throws IOException
{
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
in.connect(out);
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}


11.15RandomAccessFile

该类不是IO体系中的子类,而是直接继承自Object。但是它是IO包中的成员,因为它具备读和写功能,内部封装了一个数组,而且通过指针对数组中的元素进行操作。可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。

能够完成读写的原理就是内部封装了字节输入流和输出流。该类只能操作文件,操作模式通常有只读r和读写rw。

如果模式为只读r,不会创建文件,会去读取一个已存在的文件,如果该文件不存在,则会出现异常。

如果模式为rw,操作的文件不存在,会自动创建;如果存在,则不会覆盖。

该类可以通过多个线程同时对一个文件进行读写操作,下载就是利用此原理

import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
//writeFile2();
//writeFile();
//readFile();
}
public static void readFile() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中的指针,来读取想要读取的内容,往前往后都能调整
//raf.seek(8);
//跳过指定的字节数,来读取想要读取的内容,只能往后跳
//raf.skipBytes(8);
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
//从此文件读取一个有符号的 32 位整数
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
//向指定位置写入数据
public static void writeFile2() throws IOException
{
//创建对象时与输出流不同,不会覆盖已有文件
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*3);
raf.write("周期".getBytes());
raf.writeInt(103);
raf.close();
}
public static void writeFile() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
//按4个字节写入int数字
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}


11.16DataStream

可以用于操作基本数据类型的数据的流对象

11.17ByteArrayStream

ByteArrayInputStream : 在构造的时候,需要接收数据源,而且数据源是一个字节数组

ByteArrayOutputStream : 在构造的时候,不用定义数据目的,因为该对象中已经内部封

装了可变长度的字节数组,这就是数据目的地

因为这两个流对象都操作的数组,并没有使用系统资源,所以不用进行close关闭

ByteArrayStream是用流的思想来操作数组

import java.io.*;
class  ByteArrayStream
{
public static void main(String[] args)
{
//数据源
ByteArrayInputStream bis =
new ByteArrayInputStream("ABCDE".getBytes());
//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while ((by=bis.read())!=-1)
{
bos.write(by);
}
System.out.println(bos.toString());
//将字节数组输出流中的数据写入到一个文件当中
bos.writeTo(new FileOutputStream("a.txt"));
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: