黑马程序员——Java基础---IO、File类、Properties类、打印流、序列流(合并流)

时间:2021-05-02 19:39:58

-----------android培训java培训、java学习型技术博客、期待与您交流!------------


第一讲     IO概述

一、概述

()IO流:即InputOutput的缩写。

()特点:

1. IO流用来处理设备间的数据传输。

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

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

4. 流按操作数据分为两种:字节流和字符流。

5. 流按流向分为:输入流和输出流。

注意:流只能操作数据,而不能操作文件。

()IO流的常用基类:

1. 字节流的抽象基流:InputStream和OutputStream

2. 字符流的抽象基流:Reader和Writer

注:此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能;如InputStream子类FileInputStream;Reader子类FileReader

第二讲    字符流

一、简述

1. 字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。

2. 字符流只用于处理文字数据,而字节流可以处理媒体数据。

3. 既然IO流是用于操作数据的,那么数据的最常见体现形式是文件。查看API,找到一个专门用于操作文件的Writer子类对象:FileWriter。   后缀是父类名。前缀名是流对象的功能。该流对象一被初始化,就必须有被操作的文件存在。

二、字符流的读写

()写入字符流步骤

a、创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。且该目录下如果已有同名文件,则同名文件将被覆盖。其实该步就是在明确数据要存放的目的地。

b、调用write(String s)方法,将字符串写入到流中。

c、调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。

d、调用close()方法,关闭流资源。但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中。

close()和flush()区别:

flush()刷新后,流可以继续使用;

而close()刷新后,将会关闭流,不可再写入字符流。

注意:

1. 其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。

2. 文件的数据的续写是通过构造函数 FileWriter(Strings,boolean append),在创建对象时,传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。(windows系统中的文件内换行用\r\n两个转义字符表示,在linux系统中只用\n表示换行)

3. 由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理。

示例:

package io;

import java.io.*;

class FileWriterDemo {

public static void main(String[] args) {

writerDate("abcde");// 写入数据

writerFrom("zheshi:\r\nshenma");// 续写数据,并附换行操作

}

// 在硬盘上创建一个文件并写入指定数据

public static void writerDate(String s) {

FileWriter fw = null;

try {

fw = new FileWriter("demo.txt");// 创建文件

fw.write(s);// 将数据写入流

catch (IOException e) {

throw new RuntimeException("写入失败");

finally {

if (fw != null)

try {

fw.close();// 将写入流的数据刷到指定文件内,并关闭流资源

catch (IOException e) {

}

}

}

// 对已有文件的数据续写指定数据

public static void writerFrom(String s) {

FileWriter fw = null;

try {

fw = new FileWriter("demo.txt"true);// 传递一个true参数,代表不覆盖已有的文件。

fw.write(s);

catch (IOException e) {

throw new RuntimeException("写入失败");

finally {

try {

if (fw != null)

fw.close();

catch (IOException e) {

}

}

}

}

()读取字符流步骤

1. 创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。

2. 调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。

第一种方式:读取单个字符。

第二种方式:通过字符数组进行读取。

3. 读取后要调用close方法将流资源关闭。

示例:

package io;

import java.io.*;

class FileReaderDemo {

public static void main(String[] args) {

// 第一种读取方式

singleCharReader();

// 第二种读取方式

arrayCharReader();

}

// 第一种读取方式,单个字符读取

public static void singleCharReader() {

FileReader fr = null;

try {

fr = new FileReader("demo.txt");// 读取文件位置和文件名

for (int ch = 0; (ch = fr.read()) != -1;) {

System.out.print((char) ch);

}

catch (IOException e) {

throw new RuntimeException("读取失败");

finally {

try {

if (fr != null)

fr.close();// 关闭读取流资源

catch (IOException e) {

}

}

}

// 第二种读取方式,通过字符数组进行读取

public static void arrayCharReader() {

FileReader fr = null;

char[] arr = new char[1024];// 定义一个字符数组,用于临时存储读取的字符

try {

// 读取一个.java文件,并打印在控制台上。

fr = new FileReader("FileReaderDemo.java");

for (int len = 0; (len = fr.read(arr)) != -1;) {

// 显示字符数组的字符个数

System.out.print(new String(arr, 0, len));

}

catch (IOException e) {

throw new RuntimeException("读取失败");

finally {

if (fr != null)

try {

fr.close();

catch (IOException e) {

}

}

}

}

注意:

1. 定义文件路径时,可以用/”或者\\”

2. 在创建一个文件时,如果目录下有同名文件将被覆盖。

3. 在读取文件时,必须保证该文件已存在,否则出异常。

小练习:文本文件的拷贝

package io;

/* 

 需求: 

 c盘一个文本文件复制到e盘、 

 复制的原理: 

 其实就是将c盘下的文件数据存储到e盘的一个文件中。 

 步骤: 

 1、在e盘创建一个文件。用于存储c盘文件中的数据。 

 2、定义读取流和c盘文件关联。 

 3、通过不断的读写完成数据存储。 

 4、关闭资源。 

 */

import java.io.*;

class ReaderWriterTest {

public static void main(String[] args) {

// 调用复制方法

// copy_1();

copy_2();

}

// 用第一种读取方式进行复制

public static void copy_1() {

FileWriter fw = null;

FileReader fr = null;

try {

// 关联读取和写入的文件

fw = new FileWriter("E:\\HelloWorld.java");

fr = new FileReader("F:\\HelloWorld.java");

for (int ch = 0; (ch = fr.read()) != -1;) {

fw.write(ch);// 一个字符一个字符写入

}

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) {

}

}

}

// 第二种读取方式进行复制

public static void copy_2() {

FileWriter fw = null;

FileReader fr = null;

try {

// 关联读取和写入的文件

fw = new FileWriter("E:\\HelloWorld.java");

fr = new FileReader("F:\\HelloWorld.java");

char[] arr = new char[1024];

for (int len = 0; (len = fr.read(arr)) != -1;) {

fw.write(arr, 0, len);// 利用数组一次存入数组中的字符

}

catch (IOException e) {

throw new RuntimeException("读写失败");

finally {

try {

if (fr != null)

fr.close();

catch (IOException e) {

finally {

if (fw != null)

try {

fw.close();

catch (IOException e) {

}

}

}

}

}

三、字符流的缓冲区——BufferedReader和BufferedWriter

()缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象。即先将流对象初始化到构造函数中。 

()缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。

()写入流缓冲区BufferedWriter的步骤:

1. 创建一个字符写入流对象。

如:FileWriter fw=newFileWriter("buf.txt");

2. 为了提高字符写入流效率。加入缓冲技术。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。

如: BufferedWriter bufw =new BufferedWriter(fw);

3. 调用write方法写入数据到指定文件

如:bufw.write("adfg");

记住,只要用到缓冲区,就要记得刷新。(关闭流同样会刷新,但为了排除意外事故,保证数据存在,建议写入一次就刷新一次)

如:bufw.flush();

4. 其实关闭缓冲区,就是在关闭缓冲区中的流对象。

如: bufw.close();

小知识:BufferedWriter缓冲区中提供了一个跨平台的换行符:newLine();可以在不同操作系统上调用,用作数据换行。

        如:bufw.newLine();

5. 读取流缓冲区BufferedReader

该缓冲区提供了一个一次读一行的方法readLine,方便于堆文本数据的获取,当返回null时表示读到文件末尾。readLine方法返回的时候,只返回回车符之前的数据内容。并不返回回车符。

readLine方法原理:

无论是读一行。或者读取多个字符。其实最终都是在在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。

步骤:

(1)创建一个读取流对象和文件相关联

如: FileReader fr=newFileReader("buf.txt");

(2)为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲区对象的构造函数。

如: BufferedReader bufr=new BufferedReader(fr);

(3)调用该缓冲区提供的readLine方法一行一行读取,如果到达文件末尾,则返回null

如: String s=bufr.readLine();

(4)关闭流资源

如: bufr.close();、

示例:通过缓冲区复制一个文本文件。

package io;

/* 

 需求:使用缓冲技术copy一个文本文件 

 */

import java.io.*;

class BufferedCopyDemo {

public static void main(String[] args) {

BufferedWriter bfw = null;

BufferedReader bfr = null;

try {

// 创建写缓冲对象

bfw = new BufferedWriter(

new FileWriter("ReaderWriterTest_copy.txt"));

// 创建读缓冲对象

bfr = new BufferedReader(new FileReader("ReaderWriterTest.java"));

// 利用BufferedReader提供的readLine方法获取整行的有效字符。直到全部获取

for (String line = null; (line = bfr.readLine()) != null;) {

bfw.write(line);// 写入指定文件中

bfw.newLine();// 换行

bfw.flush();// 将缓冲区数据刷到指定文件中

}

catch (IOException e) {

throw new RuntimeException("文件copy失败");

finally {

if (bfw != null)

try {

bfw.close();// 关闭写入流

catch (IOException e) {

throw new RuntimeException("写入流关闭失败");

}

if (bfr != null)

try {

bfr.close();// 关闭读取流

catch (IOException e) {

throw new RuntimeException("读取流关闭失败");

}

}

}

}

6. 自定义BufferedReader

原理:

可根据BufferedReader类中特有一行一行读取方法readLine()的原理,自定义一个类中包含相同功能的方法

步骤:

1. 初始化自定义的类,加入流对象。

2. 定义一个临时容器,原BufferedReader封装的是字符数组,此类中可定义一个StringBuilder的容器,最终可实现字符串的提取。

代码:

package io;

/* 

 需求:根据readLine方法原理,模拟BufferedReader写一个自己的MyBufferedReader 

 */

import java.io.*;

//自定义缓冲类  

class MyBufferedReader extends Reader {

private Reader r;// 定义接收的流对象

MyBufferedReader(Reader r) {

this.r = r;

}

// 自定义整行读取

public String myReadLine() throws IOException {

// 创建一个容器,用来存储一行的字符

StringBuilder sb = new StringBuilder();

// 一个字符一个字符读取

for (int ch = 0; (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 int read(char[] cbuf, int off, int len) throws IOException {

return r.read(cbuf, off, len);

}

// 复写父类的close方法

public void close() throws IOException {

r.close();

}

}

// 测试MyBufferedReader

class MyBufferedReaderDemo {

public static void main(String[] args) {

MyBufferedReader mbr = null;

try {

mbr = new MyBufferedReader(new FileReader("BufferedCopyDemo.java"));

for (String line = null; (line = mbr.myReadLine()) != null;) {

System.out.println(line);// 显示效果

}

catch (IOException e) {

throw new RuntimeException("读取数据失败");

finally {

try {

if (mbr != null)

mbr.close();

catch (IOException e) {

throw new RuntimeException("读取流关闭失败");

}

}

}

}

7. LineNumberReader

在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:

setLineNumber();//设置初始行号

getLineNumber();//获取行号

示例:

package io;

import java.io.*;

/* 

 需求:利用LineNumberReader的特有方法去设置和获取文件中数据的行号 

 */

class LineNumberReaderDemo {

public static void main(String[] args) {

LineNumberReader lnr = null;

try {

// 将读取流对象传入

lnr = new LineNumberReader(new FileReader(

"LineNumberReaderDemo.java"));

lnr.setLineNumber(100);// 设置开始行号

for (String line = null; (line = lnr.readLine()) != null;) {

System.out.println(lnr.getLineNumber() + ":" + line);// 打印每行行号和字符

}

catch (IOException e) {

throw new RuntimeException("读取数据失败");

finally {

try {

if (lnr != null)

lnr.close();

catch (IOException e) {

throw new RuntimeException("读取流关闭失败");

}

}

}

}

四、装饰设计模式

()简述

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

()特点

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

()装饰和继承的区别:

1. 装饰模式比继承要灵活。避免了继承体系的臃肿,且降低了类与类之间的关系。

2. 装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。

3. 从继承结构转为组合结构。

注:在定义类的时候,不要以继承为主;可通过装饰设计模式进行增强类功能。灵活性较强,当装饰类中的功能不适合,可再使用被装饰类的功能。

示例:上面讲到的MyBufferedReader的例子就是最好的装饰设计模式的例子。

第三讲     字节流

一、概述

()字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。

()由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作。

()读写字节流:

InputStream   输入流(读)

OutputStream  输出流(写)

()为何不用进行刷流动作:

因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。所以可直接将字节数据写入到指定文件中。

()InputStream特有方法:

int available();//返回文件中的字节个数

注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。

代码演示

package io;

/* 

 练习: 

 复制一个图片 

 思路: 

 1、用字节读取流对象和图片关联。 

 2、用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。 

 3、通过循环读写,完成数据的存储。 

 4、关闭资源。 

 */

import java.io.*;

class CopyPic {

public static void main(String[] args) {

// 常用方法复制

byteArrayCopy();

// 利用输入流的available方法进行复制

availableCopy();

// 利用available方法对复制文件大小有限制,慎用

}

// 使用available方法进行复制

public static void availableCopy() {

FileInputStream fis = null;

FileOutputStream fos = null;

try {

// 关联要复制的文件

fis = new FileInputStream("C:/Users/asus/Desktop/1.jpg");

// 指定复制的路径

fos = new FileOutputStream("C:/Users/asus/Desktop/3.jpg");

// 利用available方法指定数组长度

byte[] b = new byte[fis.available()];

fis.read(b);// 复制关联文件数据

fos.write(b);// 粘贴到指定路径

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("写入字节流关闭失败");

}

}

}

// 使用读数组方式进行复制

public static void byteArrayCopy() {

FileInputStream fis = null;

FileOutputStream fos = null;

try {

// 关联要复制的文件

fis = new FileInputStream("C:/Users/asus/Desktop/1.jpg");

// 指定复制的路径

fos = new FileOutputStream("C:/Users/asus/Desktop/2.jpg");

// 利用数组的读取方式

byte[] b = new byte[1024];

int len = 0;

while ((len = fis.read(b)) != -1)// 复制文件的全部数据

{

fos.write(b, 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("写入字节流关闭失败");

}

}

}

}

二、字节流缓冲区

同样是提高了字节流的读写效率。

(一) 读写特点:

read():会将字节byte型值提升为int型值

write():会将int型强转为byte型,即保留二进制数的最后八位。

()原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。

1. 先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区。

2. 循环这个动作,直到最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素。

3. 每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增。

4. 取出的时候,数组中的元素在减少,取出一个,就减少一个,直到减到0即元素取完。

5. 当文件中的全部数据都被读取出时,read()方法就返回-1。

()自定义读取字节流缓冲区

需求:根据字节流缓冲区的原理,自定义一个字节流缓冲区。

注意:

1. 字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。

因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。

       所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。

2. byte类型的-1提升为int类型时还是-1。原因:因为在bit8个1前面补的全是1导致的。如果在bit8个1前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&0xff即255即可。

代码:

package io;

/* 

 自定义字节流读取缓冲区 

 思路: 

 1、定义一个固定长度的数组 

 2、定义一个指针和计数器用于读取数组长度,和计数数组元素是否取完为

 3、每次将字节数据存入元素要先将数组中的元素取完 

 */

import java.io.*;

class MyBufferedInputStream {

private InputStream in;

private byte[] by = new byte[1024];

private int count = 0, pos = 0;

MyBufferedInputStream(InputStream in) {

this.in = in;

}

// 自定义读方法,一次读一个字节

public int myRead() throws IOException {

// 通过in对象读取硬盘上数据,并存储by中。

// 存储在数组中的数据被读取完,再通过in对象从硬盘上读取数据

if (count == 0) {

count = in.read(by);

if (count < 0)// 文件数据全部被读取出来了

return -1;

pos = 0;// 初始化指针

byte b = by[pos];

count--;// 每被读一个字节,表示数组中的字节数少一个

pos++;// 指针加1

return b & 255;// 返回的byte类型提升为int类型,字节数增加,且高24位被补1,原字节数据改变。

// 通过与上255,主动将byte类型提升为int类型,将高24位补0,原字节数据不变。

// 而在输出字节流写入数据时,只写该int类型数据的最低8位。

else if (count > 0)// 如果数组中的数据没被读取完,则继续读取

{

byte b = by[pos];

count--;

pos++;

return b & 0xff;

}

return -1;

}

// 自定义关闭资源方法

public void close() throws IOException {

in.close();

}

}

// 测试自定义输入字节流缓冲区

class MyBufferedCopyMp3 {

public static void main(String[] args) {

long start = System.currentTimeMillis();

// 利用字节流的缓冲区进行复制

copy_2();

long end = System.currentTimeMillis();

System.out.println("复制共用时:" + (end - start) + "毫秒");

}

// 使用字节流的缓冲区进行复制

public static void copy_2() {

BufferedOutputStream bout = null;

MyBufferedInputStream bin = null;

try {

// 关联复制文件输入流对象到缓冲区

bin = new MyBufferedInputStream(new FileInputStream(

"C:/Users/asus/Desktop/一样的月光.mp3"));

// 指定文件粘贴位置的输出流对象到缓冲区

bout = new BufferedOutputStream(new FileOutputStream(

"C:/Users/asus/Desktop/一样的月光2.mp3"));

int by = 0;

while ((by = bin.myRead()) != -1) {

bout.write(by);// 将缓冲区中的数据写入指定文件中

}

catch (IOException e) {

throw new RuntimeException("MP3复制失败");

finally {

try {

if (bin != null)

bin.close();// 关闭输入字节流

catch (IOException e) {

throw new RuntimeException("读取字节流关闭失败");

}

try {

if (bout != null)

bout.close();// 关闭输出字节流

catch (IOException e) {

throw new RuntimeException("写入字节流关闭失败");

}

}

}

}

第四讲     流操作规律

一、键盘录入

()标准输入输出流

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

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

System.in的类型是InputStream.

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

()整行录入

当使用输入流进行键盘录入时,只能一个字节一个字节进行录入。为了提高效率,可以自定义一个数组将一行字节进行存储。当一行录入完毕,再将一行数据进行显示。这种正行录入的方式,和字符流读一行数据的原理是一样的。也就是readLine方法。

那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。

那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?这就需要用到转换流了。

()转换流

1. 转换流的由来:

a、字符流与字节流之间的桥梁

b、方便了字符流与字节流之间的操作

转换流的应用:

字节流中的数据都是字符时,转成字符流操作更高效。

2. InputStreamReader将字节流通向字符流

a、获取键盘录入对象。

InputStream in=System.in;

b、将字节流对象转成字符流对象,使用转换流。

InputStreamReaderisr=new InputStreamReader(in);

c、为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader

BufferedReaderbr=new BufferedReader(isr);

       d键盘录入最常见写法

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

3. OutputStreamWriter字符流通向字节流

字符通向字节:录入的是字符,存到硬盘上的是字节。步骤和InputStreamReader转换流一样。

示例:

package io;

/* 

 需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束 

 源:键盘录入。 

 目的:控制台。 

 */

import java.io.*;

class TransStreamDemo {

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

// 获取键盘录入对象。

// InputStream in=System.in;

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

// InputStreamReader isr=new InputStreamReader(in);

// 为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader

// BufferedReader br=new BufferedReader(isr);

// 键盘录入最常见写法

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

// 字符流通向字节流

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(

System.out));

String s = null;

while ((s = in.readLine()) != null) {

if ("over".equals(s))

break;

bw.write(s.toUpperCase());// 写入数据

bw.newLine();// 换行

bw.flush();// 刷新

}

bw.close();// 关闭流资源

in.close();

}

}

二、流操作规律

1.

源:键盘录入。

目的:控制台。

2. 需求:想把键盘录入的数据存储到一个文件中。

源:键盘

目的:文件。

使用字节流通向字符流的转换流(桥梁):InputStreamReader

3. 需求:想要将一个文件的数据打印在控制台上。

源:文件

目的:控制台

使用字符流通向字节流的转换流(桥梁):OutputStreamWriter

4. 流操作的基本规律:

最痛苦的就是流对象有很多,不知道该用哪一个。

通过三个明确来完成:

(1)明确源和目的。

源:输入流。InputStream  Reader

目的:输出流。OutputStream  Writer

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

是:字符流

否:字节流

(3)当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:

源设备:内存,硬盘,键盘

目的设备:内存,硬盘,控制台

5. 规律体现

(1)将一个文本文件中数据存储到另一个文件中。复制文件。

①源:因为是源,所以使用读取流:InputStream和Reader

明确体系:是否操作文本:是,Reader

明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader

是否需要提高效率:是,加入Reader体系中缓冲区 BufferedReader.

FileReader fr = new FileReader("a.txt");

BufferedReader bufr = new BufferedReader(fr);

       ②目的:输出流:OutputStream和Writer

明确体系:是否操作文本:是,Writer

明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。

是否需要提高效率:是,加入Writer体系中缓冲区 BufferedWriter

FileWriter fw = new FileWriter("b.txt");

BufferedWriter bufw = new BufferedWriter(fw);

练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。

(1)源:输入流,InputStream和Reader

是否是文本?否,InputStream

源设备:硬盘上的一个文件。InputSteam体系中可以操作文件的对象是FileInputSteam

是否需要提供效率:是,BufferedInputStream

              BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));

(2)目的:输出流,OutputStream和Writer

是否是文本?否,OutputStream

源设备:硬盘上的文件,FileOutputStream

是否需要提高效率:是,加入BufferedOutputStream

BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));

6.  需求:将键盘录入的数据保存到一个文件中。

(1)源:InputStream和Reader

是不是纯文本?是,Reader

设备:键盘。对应的对象是System.in——为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成Reader。用Reader体系中转换流,InputStreamReader

InputStreamReaderisr = new InputStreamReader(System.in);

需要提高效率吗?需要,BufferedReader

BufferedReaderbufr = new BufferedReader(isr);

(2)目的:OutputStream  Writer

是否是存文本?是!Writer。

设备:硬盘。一个文件。使用 FileWriter。

FileWriter fw = newFileWriter("c.txt");

需要提高效率吗?需要。

BufferedWriter bufw = new BufferedWriter(fw);

7. 扩展:想要把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。

目的:OutputStream  Writer

是否是存文本?是!Writer。

设备:硬盘上的一个文件。使用 FileWriter。——但是FileWriter是使用的默认编码表:GBK。而存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。

该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream

OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");

需要高效吗?需要,BufferedWriter

BufferedWriter bufw = new BufferedWriter(osw);

记住:

转换流什么使用?

字符和字节之间的桥梁。通常,涉及到字符编码转换时,需要用到转换流。

练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。

(1)源:InputStreamReader

是文本?是:Reader

设备:硬盘。上的文件:FileReader

是否需要提高效率?是:BufferedReader

BufferedReader br=new BufferedReader(newFileReader("1.txt"));

       (2)目的:OutputStream Writer

是文本?是:Writer

设备:控制台。对应对象System.out由于System.out对应的是字节流,所以利用OutputSteamWriter转换流

是否提高效率?是:BufferedWriter

BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));

示例:

package io;

/* 

 2、需求:想把键盘录入的数据存储到一个文件中。 

 源:键盘 

 目的:文件 

 把录入的数据按照指定的编码表(UTF-8),将数据存到文件中。 

 3、需求:想要将一个文件的数据打印在控制台上。 

 源:文件 

 目的:控制台 

 */

import java.io.*;

class TransStreamDemo2 {

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

// 键盘录入

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

// 存入文件中,按照指定的编码表(UTF-8

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(

new FileOutputStream("readin1.txt"), "UTF-8"));

String line = null;

while ((line = br.readLine()) != null) {

if ("over".equals(line))

break;

bw.write(line);

bw.newLine();

bw.flush();

}

/*

 * //录入文件数据 BufferedReader br=new BufferedReader(new

 * InputStreamReader(new FileInputStream("TransStreamDemo2.java")));

 * 

 * //显示在控制台 BufferedWriter bw=new BufferedWriter(new

 * OutputStreamWriter(System.out));

 * 

 * String line=null; while((line=br.readLine())!=null) {

 * if("over".equals(line)) break; bw.write(line); bw.newLine();

 * bw.flush(); }

 */

}

}

小知识:

1. 异常的日志信息:

当程序在执行的时候,出现的问题是不希望直接打印给用户看的,是需要作为文件存储起来,方便程序员查看,并及时调整的。

示例

package io;

import java.io.*;

import java.text.*;

import java.util.*;

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(

"yyyyMMdd HH:mm:ss");

String s = sdf.format(d);

PrintStream ps = new PrintStream("info.log");// 打印流对象

System.setOut(ps);// 修改输出流设备

ps.println(s);// 输出时间

catch (IOException ex) {

throw new RuntimeException("文件创建失败");

}

e.printStackTrace(System.out);// 将异常信息输出指定输出流

}

}

}

2. 系统属性信息存入文本

        获取系统信息:

                 Properties getProperties()

        将信息输出到指定输出流中

                 void list(PrintStream out)

        将输出流中数据存入指定文件中

                  new PrintStream("systeminfo.txt")

示例:

package io;

//将系统属性信息保存到指定文本中  

import java.util.*;

import java.io.*;

class SystemInfo {

public static void main(String[] args) {

PrintStream ps = null;

try {

// 获取系统信息:

Properties pop = System.getProperties();

// 创建输出流对象,将输出流中数据存入指定文件中

ps = new PrintStream("systeminfo.txt");

// 将属性列表输出到指定的输出流

pop.list(ps);

catch (Exception e) {

throw new RuntimeException("获取系统信息失败。");

}

}

}

3. 通过System类的setInsetOut方法可以对默认设备进行改变

        System.setIn(newFileInputStream(“1.txt”));//将源改成文件1.txt。

        System.setOut(newFileOutputStream(“2.txt”));//将目的改成文件2.txt

流的基本应用小结:

流是用来处理数据的。

处理数据时,一定要先明确数据源,与数据目的地(数据汇)。

数据源可以是文件,可以是键盘。

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

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

字符流继承体系简图

黑马程序员——Java基础---IO、File类、Properties类、打印流、序列流(合并流)

字节流继承体系简图

 黑马程序员——Java基础---IO、File类、Properties类、打印流、序列流(合并流)

第五讲     递归

一、递归

1. 定义

       当函数内每一次循环还可以调用本功能来实现,也就是函数自身调用自身。这种表现形式,或者编程手法,称为递归。

2. 递归注意事项

        a、限定条件。是来结束循环调用,否则是死循环。

        b、注意递归的次数,尽量避免内存溢出。因为每次调用自身的时候都会先执行下一次调用自己的方法,所以会不断在栈内存中开辟新空间,次数过多,会导致内存溢出。

示例一:

package digui;

/* 

 需求:列出指定目录下文件或文件夹,包含子目录,即列出指定目录下所有内容(带层次的)。 

 分析,因为目录中还有目录,只有使用同一个列出目录功能的函数完成即可,在列出过程中出现的还是目录的话,还可以再调用本功能,这就是利用递归原理。 

 */

import java.io.*;

class RecursionDemo {

public static void main(String[] args) {

// 关联指定路径

File dir = new File("e:\\Java Study\\Practice");

// 列出关联路径中所有的.java文件

allFileList(dir, 0);

}

// 列出指定目录下的所以内容

public static void allFileList(File dir, int level) {

// 有层次的输出

System.out.println(getLevel(level) + dir);

level++;

File[] fileArr = dir.listFiles();// 获取本目录下的所以文件和目录的抽象路径

// 遍历

for (File file : fileArr) {

if (file.isDirectory()) {

// 如果目录下还是目录,则继续调用本函数

allFileList(file, level);

else

System.out.println(getLevel(level) + file);// 显示(列出)文件

}

}

// 带层次的列表

public static String getLevel(int level) {

StringBuilder sb = new StringBuilder();

sb.append("|--");

// 每多一级目录,就多输出指定字符

for (int x = level; x > 0; x--) {

// sb.append("|--");

sb.insert(0, "|  ");

}

return sb.toString();

}

}

示例二:

package digui;

/* 

 删除一个带内容的目录。 

 删除原理: 

 windows中,删除目录从里面往外面删除的。 

 既然是从里往外删除。就需要用到递归。 

 */

import java.io.*;

class RemoveDir {

public static void main(String[] args) {

// 指定目录

File dir = new File("e:\\1");

// 删除目录

removeDir(dir);

}

// 删除传入目录

public static void removeDir(File dir) {

File[] files = dir.listFiles();// 列出目录下的所以文件和文件夹

// 遍历

for (File file : files) {

// 如果还是目录且非隐藏

if (!file.isHidden() && file.isDirectory())

removeDir(file);// 继续删除目录里的内容

else

System.out

.println(file.toString() + ":-file-:" + file.delete());// 删除文件

}

System.out.println(dir + ":::dir:::" + dir.delete());// 删除目录

}

}

示例三:

package digui;

/*

 练习: 

 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。建立一个java文件列表的文件。 

 思路: 

 1、对指定的目录进行递归。 

 2、获取递归过程所有的java文件的路径。 

 3、将这些路径存储到集合中。 

 4、将集合中的数据写入到一个文件中。 

 */

import java.util.*;

import java.io.*;

class JavaFileList {

public static void main(String[] args) {

// 指定目录

File dir = new File("e:/Java Study/Practice");

// 定义一个List集合,用于存储.java文件的File对象

List<File> list = new ArrayList<File>();

// 调用获取文件路径方法

fileToList(dir, list);

// 指定写入文件

File file = new File(dir, "javafilelist.txt");

// 调用写入文件方法

writeToFile(list, file);

}

// 获取指定文件夹内的所有java文件的绝对路径,并存入集合中

public static void fileToList(File dir, List<File> list) {

File[] files = dir.listFiles();// 列出dir路径下的所以文件和目录,

// 遍历

for (File file : files) {

// 如果是目录,则继续获取

if (file.isDirectory()) {

list.add(file.getAbsoluteFile());// 把父目录路径也存入

fileToList(file, list);

}

// 将是.java文件的绝对路径存入

else if (file.getName().endsWith(".java"))

list.add(file);

}

}

// 将集合中元素写入到一个文本文件中

public static void writeToFile(List<File> list, File file) {

BufferedWriter bw = null;

try { // 使用字符流缓冲区对象关联写入的文件

bw = new BufferedWriter(new FileWriter(file));

for (File file0 : list) {

bw.write(file0.getAbsolutePath());// 写入

bw.newLine();// 换行

bw.flush();// 刷新

}

catch (IOException e) {

throw new RuntimeException("写入文件失败");

finally {

try {

if (bw != null)

bw.close();// 关流

catch (IOException e) {

throw new RuntimeException("流资源关闭失败");

}

}

}

}

第六讲      Properties类

一、概述

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

()特点:

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

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

二、特有方法

()设置

Object setProperty(String key,String value);

 //设置键和值,调用Hashtable的方法put

()获取

String getProperty(String key);

//指定key搜索value

Set<String> stringPropertyName();

//返回属性列表的键集,存入Set集合

()加载流和存入流

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 properties;

import java.io.*;

import java.util.*;

public class PropertiesDemo {

// 演示,如何将流中的数据存储到集合中。

// 想要将info.txt中键值数据存到集合中进行操作。

/*

 * 1,用一个流和info.txt文件关联。 2,读取一行数据,将该行数据用"="进行切割。

 * 3,等号左边作为键,右边作为值。存入到Properties集合中即可。

 */

// 将文件数据存储进Properties集合方法

public static void method() throws IOException {

// 使用字符读取缓冲流关联文件

BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));

String line = null;

// 定义Properties集合

Properties prop = new Properties();

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

String[] arr = line.split("=");// 将一行数据以“=”号进行分割

// =左边作为键存入,将=右边作为值存入

prop.setProperty(arr[0], arr[1]);

}

bufr.close();// 关流

System.out.println(prop);

}

}

练习:

package properties;

/* 

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

 分析: 

 很容易想到的是:计数器。可是该计数器定义在程序中,随着该应用程序的退出,该计数器也在内存中消失了。 

 所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。键值对数据是map集合。数据是以文件形式存储。使用io技术。那么map+io——>Properties 

 思路:1、用读取流关联文本信息文件。如果存在则读取,如果不存在,则创建 

 2、每次运行,将文件数据存入集合中,读取值,判断次数,如果小于等于5次,则次数增加1次,如果大于则输出提示信息。 

 3、将值小于等于5次的信息数据存入文件中 

 */

import java.util.*;

import java.io.*;

class RunCount {

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;// 返回程序启动的次数

}

}

第七讲    打印流

一、概述

       打印流包括:PrintStream和PrintWriter

       该流提供了打印方法,可将各种类型的数据都原样打印。

二、字节打印流:PrintStream

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

        1. File对象。File

        2. 字符串路径:String

        3. 字符输出流:OutputStream

三、字符串打印流:PrintWriter

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

        1. File对象:File

        2. 字符串路径:String

        3. 字节输出流:OutputStream

        4. 字符输出流:Writer

示例

package printstream;

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

}

}

第八讲    序列流

一、概述

SequenceInputStream对多个流进行合并。也被称为合并流。

常用构造函数

SequenceInputStream(Enumeration<?extends FileInputStream> e)

二、常见合并多个流文件步骤

        1.创建集合,并将流对象添加进集合

        2.创建Enumeration对象,将集合元素加入。

        3.创建SequenceInputStream对象,合并流对象

        4.创建写入流对象,FileOutputStream关联写入文件

        5.利用SequenceInputStream对象和FileOutputStream对象读数据进行反复读写操作。

示例:

package xulie;

/* 

 SequenceInputStream 

 合并流 

 需求:将三个文本文件中的数据合并到一个文本文件中 

 思路:1、创建一个Vector集合,将三个文本文件字节流添加到集合中 

 2、创建Enumeration对象,创建SequnceInputStream对象关联Enumeration 

 3、输出流关联新文本文件 

 4、反复读写操作 

 */

import java.util.*;

import java.io.*;

class SequenceInputStreamDemo {

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

Vector<InputStream> ve = new Vector<InputStream>();// 创建vector集合,并添加相关流对象

ve.add(new FileInputStream("1.txt"));

ve.add(new FileInputStream("2.txt"));

ve.add(new FileInputStream("3.txt"));

Enumeration<InputStream> en = ve.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();

}

}

练习:

package xulie;

/* 

 切割文件 

 需求:将一个mp3文件按1M大小切割成几部分 

 思路:1、使用文件字节流关联mp3文件 

 2、定义一个容器存储1M大小的数据,当存储满时,写入一个新文件中 

 */

import java.util.*;

import java.io.*;

class SplitFile {

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

// 指定要切割的文件

File file = new File("C:\\Users\\asus\\Desktop\\苏芮 - 一样的月光.mp3");

// 将指定文件进行切割

splitFile(file);

// 指定要合并到的文件

File file1 = new File(

"E:\\Java Study\\Practice\\day20\\splitFile\\一样的月光.mp3");

// 将部分文件进行合并指定文件中

merge(file1);

}

// 接收一个文件,将其按1M大小进行切割

public static void splitFile(File file) throws IOException {

// 关联要切割的文件

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(

file));

BufferedOutputStream bos = null;

// 定义1M大小存储容器

byte[] buf = new byte[1024 * 1024];

int len = 0, x = 0;

while ((len = bis.read(buf)) != -1) {

// 每满1M就写入一个新文件中

bos = new BufferedOutputStream(new FileOutputStream(

"E:\\Java Study\\Practice\\day20\\splitFile\\" + (++x)

".part"));

bos.write(buf, 0, len);

bos.close();// 没写完一个文件要记得关流

}

// 关流

bis.close();

}

// 将部分文件合并为一个可执行文件

public static void merge(File file) throws IOException {

// 定义一个集合存储这些部分文件关联路径数据

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

for (int x = 1; x <= 6; x++) {

al.add(new FileInputStream(

"E:\\Java Study\\Practice\\day20\\splitFile\\" + x

".part"));

}

// 因为EnumerationVector特有的迭代方法,所以这里创建一个Enumeration类型的匿名内部类

final ListIterator<FileInputStream> it = al.listIterator();

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

// 定义临时存储数据的数组

byte[] buf = new byte[1024];

int len = 0;

while ((len = sis.read(buf)) != -1) {

fos.write(buf, 0, len);// 写数据

}

// 关流

fos.close();

sis.close();

}

}

-----------android培训java培训、java学习型技术博客、期待与您交流!------------