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

黑马程序员—java基础之IO流

2013-06-06 02:35 204 查看

黑马程序员—java基础之IO流

------- android培训java培训、期待与您交流!
----------

请记住: 这个世界上没有任何力量能够阻碍我们走向成功。如果有,就是我们自己。

IO流概念

IO流用来处理设备之间的数据传输

Java对数据的操作是通过流的方式

Java用于操作流的对象都在IO包中

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

流按操作类型分为两种:字节流与字符流。 字节流可以操作任何数据,字符流只能操作纯字符数据,比较方便。

IO流常用基类

字节流的抽象基类:

InputStream ,OutputStream

字符流的抽象基类:

Reader , Writer

由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

如:InputStream的子类FileInputStream。

如:Reader的子类FileReader。

注意: InputStreamReader 是字符流, 可以从字节流中读取字符

IO流使用步骤:

使用前,导入IO包中的类

使用时,进行IO异常处理

使用后,释放资源

IO流(IO异常处理方式)

1.凡是能和设备上的数据发生关联的,调用底层资源的都会发生IOException。

2."K:\\demo.txt"没有K盘,会抛出出FileNotFoundException(系统找不到指定的路径)异常。

FileNotFoundException是IOException的子类。

3.初始化抛出异常,说明初始化失败,fw还为空,故不可调用对象的close()方法,

所以抛出NullPointerException异常,finally中一定要对关闭的流对象进行不等于空的判断。

字符流读写文件

读取文件

定义字符流关联指定文件

FileReader reader = new FileReader("Test.txt");

读取一个字符,返回int,该字符的码表值

int ch = reader.read();

关闭流,释放资源

reader.close();

写出文件

定义字符输出流关联指定文件

FileWriter writer = new FileWriter("Test.txt");

写出一个字符,接收int码表值

writer.write(97);

关闭流,释放资源

writer.close();

注意事项

文件路径

定义文件路径时Windows中的目录符号为“\”,但这个符号在Java中是特殊字符,需要转义。

可以用“\\”或“/”表示。

读取文件

读取文件时必须保证文件存在,否则将抛出FileNotFoundException。

写出文件

写出时文件如不存在时程序会创建新文件,如文件已存在则会清空原文件内容重新写入。

如需追加内容可调用FileWriter构造函数FileWriter(String fileName, boolean append),传入true之后则不会清空原有文件

练习

拷贝一个文件

字符流缓冲区读写

自定义缓冲区读写

为什么定义缓冲区

由于单个字符读写需要频繁操作文件,所以效率非常低。

我们可以定义缓冲区将要读取或写出的数据缓存,减少操作文件次数。

缓冲区读取

先定义一个数组,然后调用FileReader读取一个数组的方法。

int read(char[] cbuf)

缓冲区写出

将要写出的数据存放在数组中,调用FileWriter方法,一次写出一个数组。

void write(char[] cbuf,int off, int len)

内置缓冲区的BufferedReader和BufferedWriter

Java提供了带缓冲功能的Reader和Writer类:BufferedReader,BufferedWriter

这两个类都是提供包装功能,需要提供其他流来使用,给其他流增加缓冲功能

当我们调用BufferedReader读取数据时,程序会从文件中一次读取8192个字符用来缓冲

当我们调用BufferedWriter写出数据时,程序会先将数据写出到缓冲数组,直到写满8192个才一次性刷出到文件中,

练习:

现有一个程序是一个试用程序,

每次启动提示剩余次数,如果执行了10次.提示已到期.

代码:

package com.itheima.io.exercise;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public
class
Exercise4{
public
static void
main(String[] args)throws IOException {
BufferedReader br =
new
BufferedReader(newFileReader("times.txt")); //定义BufferedReader
String line = br.readLine(); //从times.txt读取一行文本
br.close();
// 关闭流,
释放资源
int times =Integer.parseInt(line); //把String转为int,
Integer.parseInt()
if (times > 0) { //如果次数大于0
System.out.println("欢迎试用Xxx软件,剩余使用次数:
" + --times +"次"); //打印次数,并且-1
FileWriter fw = newFileWriter("times.txt");//定义FileWriter
fw.write(times+ ""); //把次数转为String,写出次数.
fw.close();
// 关闭流
}else
System.out.println("软件已到期!");
}
}

装饰设计模式(Decorator)

当我们需要对一个类的功能进行改进、增强的时候会使用装饰设计模式.

格式:

含有被装饰类的引用

通过构造函数传入被装饰类对象

和被装饰类含有同样的方法,其中调用被装饰类的方法,对其进行改进、增强

和被装饰类继承同一个类或实现同一个接口,可以当做被装饰类来使用

BufferedReader、BufferedWriter都是装饰类,他们可以装饰一个Reader或Writer,给被装饰的Reader和Writer提供缓冲的功能。

就像我们用BufferedReader、BufferedWriter装饰FileReader和FileWriter,使用的读写功能还是FileReader和FileWriter的,但给这两个类的读写添加了缓冲功能

字节流

基本操作与字符流相同

字节流可以操作任意类型数据

练习:拷贝一个Jpg文件

package com.itheima.io.test

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

public
class
Demo5_CopyByArray {

public
staticvoid
main(String[]args)throws IOException {

FileInputStream fis = new FileInputStream("ts.jpg");

FileOutputStream fos = new FileOutputStream("F:/ts.jpg");

byte[] buffer =newbyte[1024]; // 定义一个较小的缓冲区(1KB), 提高效率又不太占内存

int len; // 该变量用来记录每次拷贝的字节个数

while ((len = fis.read(buffer)) != -1) { // 从文件中读取数据到数组,len记住个数, 判断如果不是文件末尾(-1)

fos.write(buffer,0, len); //将数组中的数据写出,从0号索引写出len

}

fis.close();

fos.close();

}

}

字节流缓冲区读写

自定义缓冲区读写

原理和字符流相同,都是为了提高效率

定义数组缓冲数据,一次读取一个数组,一次写出一个数组,减少操作文件的次数

BufferedInputStream、BufferedOutputStream

和BufferedReader、BufferedWriter原理相同,都是包装类

BufferedInputStream、BufferedOutputStream包装InputStream和OutputStream提供缓冲功能

读取键盘输入:

1读取键盘录入:

System.out:对应的是标准输出设备,控制台。

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

2.in

public static final InputStream in

“标准”输入流。此流已打开并准备提供输入数据。

通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。

3.需求:

通过键盘录入数据。

当录入一行数据后,就将该行数据进行打印。

如果录入的数据是over,那么停止录入。

4.注意:

ctrl+c :可以强行停止程序,其原理是因为它调用封装了回车换行标记。

10.IO流(读取转换流)

1.通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。

也就是readLine方法。

能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

readLine方法是字符流BufferedReader类中的方法。

而键盘录入的read方法是字节流InputStream的方法。

那么能不能将字节流转成字符流,再使用字符流缓冲区的readLine方法呢?

2.转换流

Reader ---> InputStreamReader(java.io包)

1)InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。

它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。

要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。

为了达到最高效率,可要考虑在 BufferedReader 内包装InputStreamReader。例如:

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

2)构造方法:

InputStreamReader(InputStream in)

创建一个使用默认字符集的 InputStreamReader。

3)特有方法:

<1> void close()

关闭该流并释放与之关联的所有资源。

<2>String getEncoding()

返回此流使用的字符编码的名称。

<3>int read()

读取单个字符。

<4>int read(char[] cbuf, int offset, int length)

将字符读入数组中的某一部分。

<5>boolean ready()

判断此流是否已经准备好用于读取。

3.BufferedReader中有readLine()方法,而且BufferedReader可作用于Reader及其子类。

InputStreamReader是Reader子类,故可以使用BufferedReader对其进行缓冲区技术高效操作.

4.代码:

//获取键盘录入对象。

InputStream in = System.in;

//将字节流对象转成字符流对象,使用转换流InputStreamReader.

InputStreamReader isr = new InputStreamReader(in);

//为了提高效率,将字符串进行。使用BufferedReader.

BufferedReader bufr = new BufferedReader(isr);

String line = null;

while ((line = bufr.readLine()) != null)

{

if("over".equals(line))

break;

System.out.println(line.toUpperCase());

}

bufr.close();

标准输入输出流

System类中的成员变量:in,out。

它们各代表了系统标准的输入和输出设备。

默认输入设备是键盘,输出设备是显示器。

System.in的类型是InputStream.

System.out的类型是PrintStream是OutputStream的子类FilterOutputStream 的子类.

练习:通过修改标准输入输出流,使用System.in和System.out拷贝文件

流基本应用小节

流是用来处理数据的。

处理数据时,一定要先明确数据源,或者数据目的地

数据源可以是文件,可以是键盘或者其他设备。

数据目的地可以是文件、显示器或者其他设备。

而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。

IO流(写入转换流)

1.写入转换流:

Writer ---> OutputStreamWriter(java.io包)

1)public class OutputStreamWriterextends WriterOutputStreamWriter 是字符流通向字节流的桥梁:

可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,

否则将接受平台默认的字符集。

每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,

得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。

注意,传递给 write() 方法的字符没有缓冲。

为了获得最高效率,可考虑将 OutputStreamWriter 包装到BufferedWriter 中,

以避免频繁调用转换器。例如:

Writer out

=new BufferedWriter(new OutputStreamWriter(System.out));

2)构造函数:

OutputStreamWriter(OutputStream out)

创建使用默认字符编码的 OutputStreamWriter。

3)方法摘要

<1>void close()

关闭此流,但要先刷新它。

<2>void flush()

刷新该流的缓冲。

<3>String getEncoding()

返回此流使用的字符编码的名称。

<4>void write(char[] cbuf, int off, int len)

写入字符数组的某一部分。

<5>void write(int c)

写入单个字符。

<6>void write(String str, int off, int len)

写入字符串的某一部分。

转换流

用来将文件或者文件夹路径封装成对象

方便对文件与文件夹进行操作。

File对象可以作为参数传递给流的构造函数。

了解File类中的常用方法。

File类常见方法:

1.创建:

boolean createNewFile()

throws IOException

在指定位置创建文件,如果该文件已经存在,则不创建,返回false.

和输出流不一样,输出流对象一建立创建文件,而且文件已经存在,会覆盖。

boolean mkdir():创建文件夹。

boolean mkdirs():创建多级文件夹。

2.删除:

<1>boolean delete()

删除此抽象路径名表示的文件或目录。删除失败返回false.

<2>void deleteOnExit()

在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。在程序退出时删除指定文件。

注意:若前面的代码发生异常,则此时后面的删除操作就执行不到,此时就会产生垃圾。

就算是在finally中删除有时也未必有效。因为正在被流操作的文件会报提示无法删除。

此时用deleteOnExit()退出时删除方法就很容易解决这个问题了。

3.判断:

<1>boolean canExecute()

测试应用程序是否可以执行此抽象路径名表示的文件。

<2>boolean canRead()

测试应用程序是否可以读取此抽象路径名表示的文件。

<3>boolean canWrite()

测试应用程序是否可以修改此抽象路径名表示的文件。

<4>int compareTo(File pathname)

按字母顺序比较两个抽象路径名。

<5>boolean exists()

测试此抽象路径名表示的文件或目录是否存在。

<6>boolean isAbsolute()

测试此抽象路径名是否为绝对路径名。

<7>boolean isDirectory()

测试此抽象路径名表示的文件是否是一个目录。

<8>boolean isFile()

测试此抽象路径名表示的文件是否是一个标准文件。

<9>boolean isHidden()

测试此抽象路径名指定的文件是否是一个隐藏文件。

注意:文件夹也可能叫a.txt

4.获取信息:

<1>String getName()

返回由此抽象路径名表示的文件或目录的名称。

<2>String getParent()

返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。

<3>File getParentFile()

返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。

<4>String getPath()

将此抽象路径名转换为一个路径名字符串。

<5>String getAbsolutePath()

返回此抽象路径名的绝对路径名字符串。

<6>long lastModified()

返回此抽象路径名表示的文件最后一次被修改的时间。

<7>long length()

返回由此抽象路径名表示的文件的长度。

<8>boolean renameTo(File dest)

重新命名此抽象路径名表示的文件。

5.文件列表

文件列表功能:

1)static File[] listRoots()

列出可用的文件系统根。

2)String[] list()

返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。

3)String[] list(FilenameFilter filter)

返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。

6.文件列表2:

1)String[] list(FilenameFilter filter)

返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。

2)File[] listFiles()

返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。

3)File[] listFiles(FileFilter filter)

返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。

4)File[] listFiles(FilenameFilter filter)

返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。

递归

函数自己调用自己。

应用场景:

当某一功能要重复使用时。

递归要注意:

1)限定条件。递归时一定要明确结束条件。

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

递归演示:

package com.itheima.recursion

public
class
Demo4_Recursion{
private
static int
x;
public
static void
main(String[] args) {
System.out.println(count(5)); // 15
System.out.println(count(100)); // 5050
}
/*
* count(5) 从1加到5我们发现是:
* = count(4) + 5 //1--4的和加5
* = count(3) + 4 //1--3的和加4
* = count(2) + 3 //1--2的和加3
* = count(1) + 2 //1--1的和加2
* = 1 //1 因此,对于这样的嵌套式,产生了递归的解决办法
*/
public
static int
count(int n) {
//控制循环的条件为n-1
return n == 1 ? 1 :count(n- 1) + n;
}

练习:

1.打印出一个文件中所有带.java的文件

package com.itheima.io.file.exercise;
import java.io.File;
public
class
Exercise3{
public
static void
main(String[] args) {
File dir = newFile("F:/Itcast/20130322_JavaSE/Code/day22");
print(dir);
}
public
static void
print(File dir) {//打印出该文件夹中所有.java文件的文件名
File[] subFiles = dir.listFiles(); //获取所有子文件
for (File subFile : subFiles) { //遍历
if (subFile.isFile()&& subFile.getName().endsWith(".java")) //如果是文件并且文件名以.java结尾
System.out.println(subFile.getName()); //打印
if (subFile.isDirectory()) //如果是文件夹
print(subFile); //递归
}
}
}

2.删除嵌套文件夹
package com.itheima.io.file.exercise;
import java.io.File;
public
class
Exercise4{
public
static void
main(String[] args) {
File file = newFile("F:/Itcast/20130322_JavaSE/Code/day22/day22");
deleteDir(file);
}
public
static void
deleteDir(File dir) {
File[] subFiles = dir.listFiles(); //获取子文件
for (File subFile : subFiles) { //循环遍历所有子文件
if (subFile.isFile()) //如果是文件
subFile.delete();
// 直接删除
else //是文件夹
deleteDir(subFile);
// 递归删除该文件夹下所有内容
}
dir.delete();
// 所有子文件的遍历删除了,删除当前文件夹
}
}

IO流(Properties存取配置文件)

1.Properties的方法:
1)voidlist(PrintStream out)
将属性列表输出到指定的输出流。
2)voidlist(PrintWriter out)
将属性列表输出到指定的输出流。
3)voidload(InputStream inStream)
从输入流中读取属性列表(键和元素对)。
4)void load(Readerreader)
按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
5)voidstore(OutputStream out, String comments)//comments为注释
以适合使用load(InputStream) 方法加载到Properties 表中的格式,
将此 Properties 表中的属性列表(键和元素对)写入输出流。
6)void store(Writerwriter, String comments)
以适合使用load(Reader) 方法的格式,将此Properties 表中的属性列表(键和元素对)
写入输出字符。

2.演示:
如何将流中的数据存储到集合中。
想要将info.txt中键值数据存到集合中进行操作。
1)用一个流和info.txt文件关联。
2)读取一行数据,将该行数据用“=”进行切割。
3)等号左边作为键,右边作为值。存入到Properties集合中即可
配置文件有两种:
1)以“.properties”结尾的。
如:name=zhangsan
age=20;

2)以".xml"结尾的
如:
<persons>
<person id="001">
<name>zhangsan</name>
<age>30</age>
<address>bj</address>
</person>
<person>
<name>lisi</name>
</person>
</persons>
所以要想使用某个软件不续费而继续使用的话,可以删除配置文件即可,
如果配置文件放在了C:\WINDOWS\sytem32中,则删除也无用。

IO包中的其他类

序列流

SequenceInputStream

可以将多个字节输入流整合成一个流,在使用这个流读取的时候,读到第一个流的末尾时继续读第二个,第二个读到末尾则继续读第三个,以此类推,直到读到最后一个流的末尾返回-1

操作对象

ObjectOutputStream

可以将实现了Serializable的接口的对象转成字节写出到流中

ObjectInputStream

可以从流中读取一个ObjectOutputStream流写出的对象

打印流

PrintStream 、PrintWriter

相比普通的OutputStream和Writer增加了print()和println()方法,这两个方法可以输出实参对象的toString()方法返回值

这两个类还提供自动flush()的功能

操作内存缓冲数组

ByteArrayInputStream:可以从一个字节数组中读取字节。

ByteArrayOutputStream: 写出到字节数组(内存)中,可以获取写出的内容装入一个字节数组。通常我们用这个流来缓冲数据。

CharArrayReader:可以从一个字符数组中读取字符。

CharArrayWriter:写出字符到字符数组(内存)中,可以获取写出的内容装入一个字符数组。

管道流

PipedInputStream:管道输入流,可以从管道输出流中读取数据

PipedOutputStream:管道输出流,可以向管道输入流中写出数据

操作基本数据类型

DataInputStream、DataOutputStream

可以按照基本数据类型占用空间大小读写数据

随机访问文件

RandomAccessFile

今日本帖练习:

1. 分割文件(从键盘接收一个文件的路径,然后将文件分为五份,存入一个和文件同名的文件夹内)

package com.itheima.io.exercise;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public
class
Exercise1{
public
static void
main(String[] args)throws IOException {
File file = getFile();
cut(file);
}
/*
* 把文件分成5份,装入一个同名的文件夹
*/
private
static void
cut(File file)throws IOException {
// 获取文件大小,计算每一分的大小
long length = file.length()/ 5 + 1;
// 建立一个临时文件夹
File dir = newFile(".temp");
dir.mkdir();
// 循环5次
BufferedInputStream bis =
new
BufferedInputStream(newFileInputStream(file));
for (int i = 0; i < 5; i++) {
// 每次从目标文件中读取数据,写出到新文件
File newFile = newFile(dir, i +"");
BufferedOutputStream bos =newBufferedOutputStream(newFileOutputStream(newFile));
int b;
for (int j = 0; j < length&& (b = bis.read()) != -1; j++) {
bos.write(b);
}
bos.close();
}
bis.close();
// 删除源文件,把文件夹改名
file.delete();
dir.renameTo(file);
}
private
static
File getFile() throws IOException {
System.out.println("请输入要分割的文件路径:");
BufferedReader br =
new
BufferedReader(newInputStreamReader(System.in));
while (true) {
File file = newFile(br.readLine()); //
从键盘接收一个字符串,封装成File对象
if (!file.exists()) { //如果不存在
System.out.println("您输入的路径不存在,请重新输入:"); //提示
}elseif(file.isDirectory()) { //如果是文件夹
System.out.println("您输入的是文件夹路径,请输入一个文件路径:"); //提示
}else{ //存在,且不是文件夹,那么就是一个文件
return file;
}
}
}
}

2合并文件(从键盘中接收一个文件夹路径,练习一中分割的,

然后将切割的文件合并,要求还原为分割以前的文件.)

packagecom.itheima.io.exercise;

importjava.io.BufferedReader;

importjava.io.File;

importjava.io.FileInputStream;

importjava.io.FileOutputStream;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.InputStreamReader;

importjava.io.SequenceInputStream;

importjava.util.ArrayList;

importjava.util.Collections;

public classExercise2 {

public static void main(String[] args)throws IOException {

File dir = getDir();

merge(dir);

}

private static void merge(File dir)throws IOException {

// 遍历所有子文件, 每个子文件创建输入流, 装入集合

ArrayList<InputStream>al = new ArrayList<>();

for (File subFile :dir.listFiles())

al.add(newFileInputStream(subFile));

// 从集合获取Enumeration, 创建SequenceInputStream合并流

SequenceInputStream sis = newSequenceInputStream(Collections.enumeration(al));

// 从SequenceInputStream读取数据写到一个临时文件中

File file = newFile(".temp");

FileOutputStream fos = newFileOutputStream(file);

byte[] buffer = newbyte[1024];

int len;

while ((len =sis.read(buffer)) != -1)

fos.write(buffer, 0,len);

sis.close();

fos.close();

// 删除文件夹, 把文件改名

for (File subFile :dir.listFiles())

subFile.delete();

dir.delete();

file.renameTo(dir);

}

private static File getDir() throwsIOException {

System.out.println("请输入要合并的文件夹路径:");

BufferedReader br = newBufferedReader(new InputStreamReader(System.in));

while (true) {

File file = newFile(br.readLine()); //从键盘接收一个字符串, 封装成File对象

if (!file.exists()){ // 如果不存在

System.out.println("您输入的路径不存在, 请重新输入:"); // 提示

} else if(file.isFile()) { //如果是文件夹

System.out.println("您输入的是文件路径, 请输入一个文件夹路径:"); // 提示

} else { //存在, 且不是文件夹, 那么就是一个文件

returnfile;

}

}

}

}

------- android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: