JavaSE学习笔记_18:Java-IO流

来源:互联网 发布:程序侠 源码 编辑:程序博客网 时间:2024/04/28 07:10

IO流

 

概述

IO流是用来处理设备上数据的一种技术。该技术都被封装成不同的类,存放在java.io包中,供开发者使用。注意:流中所有的方法都有可能抛出异常,因此需解决异常问题。

1) 流按操作数据分为两种:字节流(通用流)与字符流

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

 

拓展:

字符流要用到编码表,写程序的时候可以指定编码表,这样就不至于乱码。编码表主要的特点就是映射。

 

在外国用的是ASCII码表:将字母与01二进代码对应起来

在中过用到是GB2312码表(之后扩容GBK):将汉子与01二进制代码对应起来

 

把各国的文字统一起来,就有了国际标准码表,叫做Unicode码表。

该码表的特点:Unicode里面的无论什么字符都用两个字节表示,虽然能表示很多个字符,但是,有些字符只要8为就可以搞定,因此有点浪费空间,这时就出来一个UTF-8码表,即一个字节能装的下就用一个字节装,一个装不下就用两个表,按需分配。

 

 

IO流常用的基类:

字节流的抽象基类:InputStream(读)、OutputStream(写)。

字符流的抽象基类:Reader(读)、Writer(写)

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

InputStream的子类FileInputStream

Reader的子类FileReader

 

 

温馨提示:流对象对同一个文件进行读和写操作,要注意是否需要将读方法和写方法对应(比如readInt()和writeInt())从而保证读出来的内容和写进去的内容一样。字符流中的读写方法是以字符为单位的,字节流中的读写方法是以字节为单位的。

 

字符流

一、FileWriter

1、往文件中写入文字

在硬盘上,创建一个文件并写入一些文字数据。

import java.io.*;

class FileWriterDemo

{

/*

所用到的方法:

FileWriter(),write(),flush(),close()

都有可能发生I/O错误,都会抛出I/O异常。

*/

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

{

/*

创建一个FileWriter对象。

该对象一被初始化就必要确定被操作的文件。

 

而且该文件会被创建到指定目录下。

如果该目录下已有同名文件,将被覆盖。

 

其实该步就是在明确数据要存放的目的地。

现在是在当前目录下。

*/

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

 

/*

调用write(),将字符串写入到流缓冲区中。

*/

fw.write("hello java");

 

/*

刷新流缓冲区中的数据,

将数据刷新到目的地。

*/

fw.flush();//可以省略不写。

 

/*

关闭流,但是关闭之前会刷新一次内部缓冲区中

的数据,将数据刷新到目的地中。它和flush的区

别在于,flush刷新后,流还可以继续使用,close

刷新后,会将流关闭。

若流多时,需要一个一个关闭。

*/

fw.close();

}

}

 

//其实,IO流在调用windows底层资源,

//具体的工作都是windows底层自己做,

//很好地体现了平台无关性。

 

 

2、异常处理

|--IOException

|--FileNoFoundException

代码如下:

import java.io.*;

class FileWriterDemo

{

public static void main(String[] args)

{

FileWriter fw=null;

try

{

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

 

fw.write("agasfsdgs");

 

}

catch (IOException e)

{

System.out.println("catch1="+e.toString());

}

finally

{

try

{

if(fw!=null)

fw.close();

}

catch (IOException e)

{

System.out.println("catch2="+e.toString());

}

}

}

}

 

 

3、文本文件的续写

续写是指,我们进行第二次编译运行时,如果指定的目录

存在,则不去覆盖,而是接着已有内容写入数据。

代码如下:

import java.io.*;

class FileWriterDemo

{

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

{

/*

创建流对象时,往里边再传递一个参数true,

代表不覆盖已有的文件。并在已有的文件的末尾

处进行数据的续写。

 

续写有两种方式:

方式一:

通过改动write()方法中的参数。

 

方式二:

通过连续调用write(),较麻烦。

*/

FileWriter fw=new FileWriter("demo.txt",true);

 

fw.write("haofahfao;\r\nfjw");

 

fw.close();

}

}

 

ps:我们知道,创建一个写入文本文件流对象之后,我们通过调用write(String str),指定的内容可能先是被写入到了流缓冲区中,然后通过调用flush()或者是close()才将缓冲区中的内容刷到目的地。目前还没有学习到BufferedWriter,因此认为指定的内容都是被写入到目的地。

 

 

 

 

4、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<=10;x++)

{

bufw.write("abcd"+x);

bufw.newLine();

bufw.flush();

}

 

bufw.flush();

 

bufw.close();

}

}

 

 

 

二、FileRead

1、文本文件的读取

方式一:

import java.io.*;

class FileReadDemo

{

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

{

/*

创建一个FileRead对象,

和指定名称的文件相关联,

要保证该文件是已经存在的,

如果不存在会发生异常FileNotFoundException。

*/

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

/*

调用read(),

该方法一次读取一个字符,

而且会自动往下读;

该方法返回值类型是int类型,到达流的末尾返回-1,

将读取到的字符以整数形式

展现。

*/

int ch = fr.read();

System.out.println("ch="+(char)ch);

 

int ch1 = fr.read();

System.out.println("ch1="+(char)ch1);

 

int ch2 = fr.read();

System.out.println("ch2="+(char)ch2);

 

fr.close();

}

}

优化:

import java.io.*;

class FileReadDemo

{

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

{

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

int ch=0;

 

 

while((ch=fr.read())!=-1)

{

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

}

 

/*

while(true)

{

if((ch=fr.read())!=-1)

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

else

break;

}

*/

fr.close();

}

}

 

方式二:

import java.io.*;

class FileReaderDemo

{

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

{

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

 

/*

定义一个字符数组,用于存储读到的字符。

*/

char[] buf=new char[1024];

 

/*

通过int read(char[] buf),

将读到字符放进数组,

返回值表示读到的字符个数,

这个个数由数组的长度(2)和流中剩余字符个数(1)决定。

自动往下读,

当读到流的末尾,返回-1。

 

再通过创建String类型,

将字符数组中的内容换成

字符串打印输出。

*/

int num=0;

while((num=fr.read(buf))!=-1)

{

System.out.println(new String(buf,0,num) );

}

 

fr.close();

}

}

 

2、文本文件读取练习

读取一个文件,并打印到控制台上。

import java.io.*;

class FileReaderDemo

{

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

{

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

 

char[] buf=new char[1024];

 

int num=0;

 

while((num=fr.read(buf))!=-1)

{

System.out.println(new String(buf,0,num) );

}

 

fr.close();

}

}

运行结果如下图所示:

 

发现,打印出来的文件排版都没有变化。但是,我们想,当我们的文本文件大于2kB, 那么打印出来的排版格式一定会发生改变,因为我们不是接着打印的。因此我们通常将 System.out.println()换成System.out.print()。

 

 

 

ps:我们知道,创建读取文本文件流对象,我们通过调用read(),或者是read(char[] buf)读取数据。当到达流末尾时,返回的是-1.

 

 

 

3、BufferedReader

该类称之为字符读取缓冲区。缓冲区的出现提供了对数据

读出速度。要结合流才可以使用,在流的基础上对流的功 能进行了增强,通过提供一个一次读一行的方法:String 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();

}

}

 

 

拓展:

|--BufferedReader

|--LineNumberReader

① 跟踪行号的缓冲字符输入流。此类定义了方法  setLineNumber(int) 和getLineNumber(),它们可  分别用于设置和获取当前行号。

② 默认情况下,行编号从 0 开始。该行号随数据读取  在每个行结束符处递增,并且可以通过调用  setLineNumber(int) 更改行号。但要注意的是,  setLineNumber(int) 不会实际更改流中的当前位  置;它只更改将由 getLineNumber() 返回的值。

③ 可认为行在遇到以下符号之一时结束:换行符 ('\n')、回车符('\r')、回车后紧跟换行符。

示例:

import java.io.*;

class LineNumberReaderDemo

{

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

{

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

 

LineNumberReader lnr=new LineNumberReader(fr);

 

String line=null;

lnr.setLineNumber(6);

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

{

System.out.println(lnr.getLineNumber()+"::"+line);

}

}

}

运行结果如下图所示:

 

 

 

 

三、FileWriter和FileReader的搭配应用

需求:将C盘下的一个文本文件复制到D盘下。

方式一:简洁版

import java.io.*;

class CopyText

{

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

{

FileReader fr=new FileReader("C:\\java.txt");

 

FileWriter fw=new FileWriter("D:\\java_copy.txt");

 

int ch=0;

while((ch=fr.read())!=-1)

{

fw.write((char)ch);

}

 

fr.close();

fw.close();

}

}

 

方式二:复杂版

import java.io.*;

class CopyText

{

public static void main(String[] args)

{

FileReader fr=null;

FileWriter fw=null;

try

{

fr=new FileReader("C:\\java.txt");

fw=new FileWriter("D:\\java_copy.txt");

 

int num=0;

char[] buf=new char[1024];

while((num=fr.read(buf))!=-1)

{

fw.write(buf,0,num);

}

}

catch (IOException e)

{

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

}

finally

{

try

{

if(fr!=null)

fr.close();

}

catch (IOException e)

{

}

 

try

{

if(fw!=null)

fw.close();

}

catch (IOException e)

{

}

}

}

}

 

 

四、BufferedReader和BufferedWriter的搭配使用

方式一:(简洁版)

import java.io.*;

class CopyTextBybuf

{

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

{

FileReader fr=new FileReader("C:\\java.txt");

FileWriter fw=new FileWriter("D:\\java_copy.txt");

 

BufferedReader bufr=new BufferedReader(fr);

BufferedWriter bufw=new BufferedWriter(fw);

 

String line=null;

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

{

bufw.write(line);

bufw.newLine();

bufw.flush();

}

 

bufr.close();

bufw.close();

}

}

 

方式二:(复杂版)

import java.io.*;

class CopyTextBybuf

{

public static void main(String[] args)

{

FileReader fr=null;

FileWriter fw=null;

BufferedReader bufr=null;

BufferedWriter bufw=null;

 

try

{

fr=new FileReader("C:\\java.txt");

fw=new FileWriter("D:\\java_copy.txt");

bufr=new BufferedReader(fr);

bufw=new BufferedWriter(fw);

String line=null;

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

{

bufw.write(line);

bufw.newLine();

bufw.flush();

}

}

catch (IOException e)

{

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

}

finally

{

if(bufr!=null)

try

{

bufr.close();

}

catch (IOException e)

{

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

}

if(bufw!=null)

try

{

bufw.close();

}

catch (IOException e)

{

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

}

}

}

}

 

 

 

装饰设计模式

一、什么是装饰设计模式

当想要对已有的对象进行功能增强时,又不想对已有功能 进行修改。此时可以定义一个新的类,建立新类的对象, 将已有对象传入,通过构造方法接收该已有对象,基于   已有对象的功能,提供更强的功能。那么自定义类称为装 饰类。

如:

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

}

}

 

class PersonDemo

{

public static void main(String[] args)

{

Person p=new Person();

SuperPerson sp=new SuperPerson(p);

sp.superChiFan();

}

}

 

 

 

二、装饰和继承的区别

想要给已有对象提供更强的功能:

1.如果是用继承来做

|--MyReader

|--MyTextReader

|--MyBufferTextReader

|--MyMediaReader

|--MyBufferMediaReader

|--MyDataReader

|--MyBufferDataReader

2.如果是用装饰来做

|--MyReader

|--MyTextReader

|--MyMediaReader

|--MyDataReader

|--MyBufferReader

 

class MyBufferReader

{

MyBufferReader(MyTextReader text)

{}

MyBufferReader(MyMediaReader media)

{}

}

多态思想:

class MyBufferReader

{

private MyReader r;

MyBufferReader(MyReader r)

{}

}

 

3、总结

综上所述,装饰模式比继承要灵活,避免了继承体系的臃肿。单独描述一下缓冲内容,将需要被缓冲的对象传递进来。

 

三、自定义装饰类

import java.io.*;

class MyBuffererReader extends Reader

{

private Reader r;

MyBufferedReader(Reader r)

{

this.r=r;

}

public String myReadLine()throws IOException

{

StringBuffer sb=new StringBuffer();

int ch=0;

while((ch=r.read())!=-1)

{

if(ch=='\r')

continue;

if(ch=='\n')

return sb.toString()

else

sb.append(ch);

}

if(sb.length()!=-1)

return sb.toString();

return null;

}

public void myClose()throws IOException

{

r.close();

}

 

//复写Read类中的抽象方法

public void close()throws IOException

{

r.close();

}

public int read(char[] cbuf,int off,int len)throws IOException

{

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

}

}

 

 

 

字节流

字节流对象在写如数据时,无论什么情况下都不需要刷 新,直接往目的地里,因为它是最小的单位。处理图片都 需要用字节流。

一、FileInputStream

1、需求:

读取一个图片文件。

代码如下:

import java.io.*;

class FileInputStreamDemo

{

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

{

readFile_3();

}

 

//方法一:

/*

通过int read():从此输入流中读取下一个数据字节。

返回一个 0 到 255 范围内的 int 字节值。

如果因为已经到达流末尾而没有字节可用,则返回 -1。

*/

public static void readFile_1()throws IOException

{

FileInputStream fis=new FileInputStream("C:\\123.jpg");

 

int ch=0;

while ((ch=fis.read())!=-1)

{

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

}

 

fis.close();

}

 

//方法二:

/*

通过int read(byte[] b) :

    从此输入流中将 byte.length 个字节的数据读入一个 byte 数组中。

如果因为已经到达流末尾而没有更多的数据,则返回 -1。

*/

public static void readFile_2()throws IOException

{

FileInputStream fis=new FileInputStream("C:\\123.jpg");

 

int num=0;

byte[] buf=new byte[1024];

while ((num=fis.read(buf))!=-1)

{

System.out.print(new String(buf,0,num));

}

 

fis.close();

}

 

//方法三:

/*

通过调用available():获取文件中总的字节数。

根据该数目定义一个刚刚好的缓冲区,不用再循环。

*/

public static void readFile_3()throws IOException

{

FileInputStream fis=new FileInputStream("C:\\123.jpg");

 

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

fis.read(buf);

System.out.println(new String(buf));

 

fis.close();

}

}

 

 

2、BufferedInputStream

与BufferedReader功能类似,可以参照学习,

此处不做过多阐述。

 

 

二、FileOutputStream

1、需求:

写入一个文件。

代码如下:

import java.io.*;

class FileOutputStreamDemo

{

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

{

FileOutputStream fos=new FileOutputStream("C:\\fos.txt");

 

fos.write("coanoafds".getBytes());

 

fos.close();

}

}

 

2、BufferedOutputStream

与BufferedWriter功能类似,可以参照学习,

此处不做过多阐述。

 

三、FileInputStream和FileOutputStream的搭配应用

需求:将C盘下的图片文件复制到D盘下。

import java.io.*;

class CopyPic

{

public static void main(String[] args)

{

FileInputStream fis=null;

FileOutputStream fos=null;

 

try

{

fis=new FileInputStream("C:\\123.jpg");

fos=new FileOutputStream("D:\\123_copy.jpg");

 

int ch=0;

while((ch=fis.read())!=-1)

{

fos.write((char)ch);//也可以不强转

}

}

catch (IOException e)

{

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

}

finally

{

if(fis!=null)

try

{

fis.close();

}

catch (IOException e)

{

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

}

if(fos!=null)

try

{

fos.close();

}

catch (IOException e)

{

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

}

}

}

}

 

 

 

四、BufferedInputStream和BufferedOutputStream的搭配应用

1、需求:将C盘下的图片文件复制到D盘下。

import java.io.*;

class CopyPicBybuf

{

public static void main(String[] args)

{

long star=System.currentTimeMillis();

copy();

long end=System.currentTimeMillis();

System.out.println("time="+(end-star));

}

public static void copy()

{

FileInputStream fis=null;

FileOutputStream fos=null;

 

BufferedInputStream bufis=null;

BufferedOutputStream bufos=null;

 

try

{

fis=new FileInputStream("C:\\123.jpg");

fos=new FileOutputStream("D:\\123_copy.jpg");

 

bufis=new BufferedInputStream(fis);

bufos=new BufferedOutputStream(fos);

 

int ch=0;

while ((ch=bufis.read())!=-1)

{

bufos.write((char)ch);

}

}

catch (IOException e)

{

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

}

finally

{

if(bufis!=null)

try

{

bufis.close();

}

catch (IOException e)

{

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

}

if(bufos!=null)

try

{

bufos.close();

}

catch (IOException e)

{

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

}

}

}

}

运行结果如下图所示:

 

 

 

2、自定义读取字节流的缓冲区

import java.io.*;

class CopyPicByselfbuf

{

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

{

long star=System.currentTimeMillis();

copy();

long end=System.currentTimeMillis();

System.out.println();

}

public static void copy()throws IOException

{

FileInputStream fis=new FileInputStream("C:\\123.jpg");

FileOutputStream fos=new FileOutputStream("D:\\123_copy.jpg");

 

MyBufferedInputStream mbufis=new MyBufferedInputStream(fis);

BufferedOutputStream bufos=new BufferedOutputStream(fos);

 

int ch=0;

while((ch=mbufis.myRead())!=-1)

{

bufos.write(ch);

}

 

mbufis.myClose();

bufos.close();

}

}

class MyBufferedInputStream

{

private FileInputStream in;

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

private int pos=0;//字节数组的角标

private int count=0;//字节数组的元素个数

MyBufferedInputStream(FileInputStream 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;

}

else if(count>0)

{

byte b=buf[pos];

count--;

pos++;

return b;

}

else

return -1;

 

}

public void myClose()throws IOException

{

in.close();

}

 

}

编译运行之后,发现,图片副本内容为空。

分析:

我们知道,在内存中文件存在的形式是以01二进制代码形式存在的。myRead()方法 每次返回的是一个int类型,我们想,如果第一个字节b的二进制形式是1111 1111, 那么它 被提升之后,1111 1111   1111 1111    1111 1111  1111 1111(-1),那 么copy()中的while()循环条件成立,流直接关闭,没有做任何事情,导致图片副 本为空。因此我们想在前面补0,这样既可以保留原字节数据不变,又可以避免-1的出 现,可以这样做,将返回值b与上一个255。

 

这样返回去的值就是int类型的值,想说读到的值和写入副本中的值不就不一样了

吗?这就要谈一谈write()的特点了,它本身具有强转功能,只写入int类型的

最低8位。这样就保证了只有到达流的末尾才返回-1。

 

 

 

读取键盘录入

一、说明

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

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

InputStream in=System.in;//获取键盘对象。

 

ps:无论从键盘录入什么,包括回车(有两个字符组成 “\r\n”),读入之后打印到控制台上都是一整数形式出 现的。

 

 

 

二、需求:

1、通过键盘录入数据。

import java.io.*;

class ReadIn

{

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

{

InputStream in=System.in;

 

int by=in.read();

int by1=in.read();

int by2=in.read();

System.out.println(by);

System.out.println(by1);

System.out.println(by2);

}

}

运行过程如下图所示:

 

敲入字符按回车:

 

敲入字符,按ctrl+c:

 

温馨提示:这里的流对象不需要手动关闭,因为它是系统的,程序结束,就自动结 束了。

 

 

2、当录入一行数据后,就将该行数据进行打印。如 果 录入的数据是over,那么就停止录入。

import java.io.*;

class ReadIn

{

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

{

InputStream in=System.in;

StringBuffer sb=new StringBuffer();

 

while(true)

{

/*

in.read(),发挥两个作用:

1、开放了从键盘敲入字符的一个接口。

2、敲完字符进去以后,按下回车,就开始读取录入的字符。

*/

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

}

}

}

 

 

转换流

一、将字节流转化成字符流,通过InputStreamReader

通过刚才的键盘录入一行数据并打印其大写的练习,我们可以发现其实就是读一行数据的原理,也就是readLine()方法。但是readLine()是BufferedReader中的方法,是属于字符流的范畴,而键盘录入是输入字节流的范畴,因此我们想到转换

import java.io.*;

class TransStreamDemo

{

}

 

 

二、将字节流转化成字符流,通过OutputStreamWriter

同理,我们也可以将写入字节流转换成字符流。

import java.io.*;

class ReadIn

{

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

{

StringBuffer sb=new StringBuffer();

 

InputStream in=System.in;

OutputStream out=System.out;

 

OutputStreamWriter osw=new OutputStreamWriter(out);

BufferedWriter bufw=new BufferedWriter(osw);

 

while(true)

{

int ch=in.read();

if(ch=='\r')

continue;

if(ch=='\n')

{

String s=sb.toString();

if("over".equals(s))

break;

 

bufw.write(s.toUpperCase());

bufw.newLine();

bufw.flush();

 

sb.delete(0,sb.length());

}

else

sb.append((char)ch);

}

}

}

 

Ps:

转化流可以指定编码:

OutputStreamWriter osw=new OutputStreamWriter(

new FileOutputStream(“gbk.txt”),"gbk");

osw.write("你好");

 

OutputStreamWriter osr=new OutputStreamWriter(

new FileOutputStream("utf.txt"),"utf");

osw.write("你好");

这是我们打开gbk.txt、utf.txt之后,里边的内容都是 “你好”,但是gbk编码的只需4个字节,而utf编码的

是6个字节。

 

以上就是不同编码的一点区别。

三、流的操作:弄清“源”和“目”

流操作规律:(三个明确)

① 明确“源”和“目”。

源:输入流。InputStream、Reader

目:输出流。OutputStream、Writer

 

② 操作的数据是否是纯文本。

是:字符流

不是:字节流

ps:如果“源”和“目”不同是,则字符优先。

不是字符的一方必须应用转换流转化成字符流。

 

③ 明确具体的流对象。通过设备来区分:

源设备:硬盘、键盘

目的设备:硬盘、控制台

 

1、把从键盘录入的字符,存放到控制台中。

1)  明确1:

“源”:InputStream、Reader

“目”:OutputStream、Writer

2)明确2:

不是:字节流

“源”:InputStream

“目”:OutputStream

3)明确3:

“源”:键盘

InputStream in=System.in;

“目”:控制台

OutputStream out=System.in

 

 

2、把从键盘录入的字符,存放到磁盘文件中。

1) 明确1:

“源”:InputStream、Reader

“目”:OutputStream、Writer

2)明确2:

以字符为准。

“源”:Reader

“目”:Writer

3)明确3:

“源”:键盘

InputStream in=System.in;

InputStreamReader isr=new InputStreamReader(in);

“目”:硬盘

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

 

 

 

3、把磁盘文件中的内容,存放到控制台中。

1) 明确1:

“源”:InputStream、Reader

“目”:OutputStream、Writer

2)明确2:

以字符为准。

“源”:Reader

“目”:Writer

3)明确3:

“源”:硬盘

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

“目”:控制台

OutputStream out=System.out;

OutputStreamWriter osw=new OutputStreamWriter(out);

 

l ps:转化流可以指定编码表

InputStreamReader(InputStream in,Charset cs)
创建使用给定字符集的 InputStreamReader。

 

OutputStreamWriter(OutputStream out,Charset cs)
创建使用给定字符集的 OutputStreamWriter

 

“源”和“目”只需指定一方。

 

 

练习:一般情况下,我们开发时,按照下列模板做。利用 转化流。

(1)把键盘录入的数据,存放到控制台中。

import java.io.*;

class TransStreamDemo

{

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

{

InputStream in=System.in;

OutputStream out=System.out;

 

InputStreamReader isr=new InputStreamReader(in);

OutputStreamWriter osw=new OutputStreamWriter(out);

 

BufferedReader bufr=new BufferedReader(isr);

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

 

}

}

(2)把键盘录入的数据,放到C盘下的一个文件中。

import java.io.*;

class TransStreamDemo

{

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

{

InputStream in=System.in;

FileWriter fw=new FileWriter("C:\\687180q.txt");

 

InputStreamReader isr=new InputStreamReader(in);

 

BufferedReader bufr=new BufferedReader(isr);

BufferedWriter bufw=new BufferedWriter(fw);

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

 

}

}

 

 

l 改变标准输入输出设备。

(1)static viod setIn(InputStream in):重新分配“标 准” 输入流。

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

示例:

import java.io.*;

class TransStreamDemo

{

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

{

System.setIn(new FileInputStream("SystemDemo.java"));

System.setOut(new PrintStream("C:\\z.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();

bufw.close();

}

}

可以选择注释:

System.setIn(new FileInputStream("SystemDemo.java"));

System.setOut(new PrintStream("C:\\z.txt"));

来改变“源”和“目”

 

拓展:

(1)异常的日志信息

import java.io.*;

import java.util.*;

import java.text.*;

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("yyyy-MM-dd hh:mm:ss");

String s=sdf.format(d);

 

PrintStream ps=new PrintStream("exception.log");

ps.println(s);

System.setOut(ps);//改变输出流设备,位置任意。

}

catch (IOException ex)

{

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

}

e.printStackTrace(System.out);

}

}

}

(2)系统信息

import java.util.*;

import java.io.*;

class SystemInfo

{

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

{

Properties pro=System.getProperties();

System.out.println(pro);//不换行打印在控制台上。

pro.list(System.out);//换行打印在控制台上。

pro.list(new PrintStream("Systeminfo.txt"));//打印在文本文件中

}

}

 

四、转化流的字符编码

字符编码:

字符的出现为了方便操作字符,更重要的是加入编码转化

,通过了子类转化流来完成。InputStreamReader和

OutputStream。

 

编码表的由来:

计算机只能识别二进制数据,早期由来是电信号。为了方

便应用计算机,让它可以识别各个国家的文字,就将各个

国家的文字用数字来表示,并一一对应,形成一张表。这

就是编码表。

 

常见编码表:

ASCII:美国标准信息交换码。用一个字节的7位可以表

示。

ISO8859-1:拉丁码表。欧洲码表,用一个字节的8位表

示。不识别中文。

GB2312:中国的中文编码表。

GBK:中国的中文编码表升级,融合了更多的中文文字符

号。

Unicode:国际标准码表,融合了多种文字。所有文字都 用两个字节来表示,java语言使用的就是unicode。

UTF-8:最多用三个字节来表示,具体按需分配。

 

示例:

(1):在转化流中

import java.io.*;

class EncodeDemo

{

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

{

//writeText();

readText();

}

public static void writeText()throws IOException

{

OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("C:\\utf.txt"),"gbk");//不写的话,默认为GBK

 

osw.write("你好!");

osw.close();

}

public static void readText()throws IOException

{

InputStreamReader isr=new InputStreamReader(new FileInputStream("C:\\utf.txt"),"gbk");

 

int ch=0;

while((ch=isr.read())!=-1)

{

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

}

}

}

ps:要注意对应关系。

 

(2)在一般情况中

字符串->字节:编码

String->byte[]:str.getBytes(charsetName);

字节->字符串:解码

byte[]->String:new String(byte[],charsetName);

 

import java.io.*;

import java.util.*;

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,"gbk");

System.out.println(s1);

}

}

运行结果:[-60、-29、-70、-61]

  你好

 

ps:要注意对应关系。

 

注意:如果不是对应关系,则就会出现解错码的问题,那

么有两种办法可以解决:

(1)更换编码表,是对应。

(2)再编一次。

 如:

String s="你好";

byte[] b1=s.getBytes("gbk");

System.out.println(Arrays.toString(b1));

 

String s1=new String(b1,"ISO8859-1");

System.out.println("s1="+s1);

 

byte[] b2=s1.getBytes("ISO8859-1");

System.out.println(Arrays.toString(b2));

 

String s2=new String(b2,"gbk");

System.out.println("s2="+s2);

 

 

拓展:“联通”

现象:新建一个文本文档,往里边写入“你好”二字,

保存后关闭,再次打开还是“你好”二字。新建一个

文本文档,往里边写入“联通”二字,保存后关闭,

再次打开却不是“联通”二字。

这就是所谓的编码问题。

 

记事本也有编码解码过程。

 

练习:

/*

有5个学生,每个学生有3门课的成绩。

从键盘输入以上数据(包括姓名、三门课的成绩)

输入格式:如:zhangsan,30,40,50

计算出总成绩

并把学生信息和计算出的总分数按高低顺序村房子磁盘文件“stud.txt”中

*/

 

//思路:

/*

1、描述学生对象。

*/

class Student implements Comparable<Student>

{

private String name;

private int math,chinese,english;

private int sum;

 

Student(String name,int math,int chinese,int english)

{

this.name=name;

this.math=math;

this.chinese=chinese;

this.english=english;

sum=math+chinese+enlish;

}

public String getName()

{

return name;

}

public int getSum()

{

return sum;

}

public int compareTo(Student s)

{

int num=new Integer(this.sum)compareTo(new Integer(s.num));

if(num==0)

return this.name.compareTo(s.name);

return num;

}

public int hashCode()

{

return name.hashCode()+sum*87;

}

public boolean equals(Object obj)

{

if(!(obj instanceof(Student)))

throws new ClassCastException("类型不匹配");

Student s=(Student)obj;

return this.name.equals(s.name)&&this.sum==s.sum;

}

public String toString()

{

return "Student["+name+","+math+","+chinese+","+english;

}

}

 

class StudentInfoTool

{

public static Set<Student> getStudents()throws IOException

{

return getStudents(null);

}

public static Set<Student> getStudents(Comparator<Student>cmp)throws IOException

{

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

 

String line=null;

 

set<Student> stus=null;

 

if(cmp==null)

stu=new TreeSet<Student>();

else

stu=new TreeSet<Student>(cmp);

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

{

if("over".equals(line))

break;

String[] info=line.split(",");

 

Student stu=new Student(info[0],Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3]));

stu.add(stu);

}

bufr.close();

return stu;

}

public static void writeToFile(Set<Student> stus)throws IOException

{

BufferedWriter bufw=new BufferedWriter(new FileWriter("C:\\stuinfo.txt"));

 

for(Student stu:stus)

{

bufw.write(stu.toString()+"\t");

bufw.write(stu.getSum()+"");

bufw.newLine();

bufw.flush();

}

bufw.close();

}

}

class StudentInfoTest

{

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

{

Comparator<Student> cmp=Collection.reverseOrder();

Set<Student> stus=StudentInfoTool.getStudents(cmp);

 

StudentInfoTool.writeToFile(stus);

}

}

 

 

                                                                                 

 

File

一、概述

1、用来将文件或者文件夹封装成对象。

2、方便对文件与文件夹的属性信息进行操做。

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

示例:

import java.io.*;

class FileDemo

{

public static void main(String[] args)

{

File f1=new File("a.txt");

System.out.println(f1);

 

File f2=new File("C:\\abc");

System.out.println(f2);

 

File f3=new File("C:\\abc\\c.txt");

System.out.println(f3);

 

File f4=new File(f2,"b.txt");

System.out.println(f4);

 

File f5=new File("C:\\abc","b.txt");

System.out.println(f5);

}

}

运行结果:

 

ps:封住之后,它们并没有真正存在。也可以封装已经存在的。

 

有一个跨平台的技巧:

可以将\\换成File.separator(与系统有关的默认名称分隔符,为了方便,它被表示为 一个字符串。)

 

二、功能

File类中常见的方法如下:

1、创建:

① static File createTempFile(String prefix,String suffix):在默认临时文件 目录中创建一个空文件(文件内容为空),使用给定前缀和后缀字符串生成其名称。

此文件类型不明确。

② static  File createTempFile(String prefix,String suffix,File directory)

在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。directory必须是已经存在的,否则会抛异常。此文件类型不明确。

③ boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建, 返回true。

如:创建一个不明类型的abc文件

File f=new File("C:\\abc");

f.createNewFile();

如:创建一个.txt文件

File f=new File("C:\\abc.txt");

f.createNewFile();

ps:这里只能封装一级目录。

④ boolean mkdir():创建单级或者多级文件夹。可以覆盖。

⑤ boolean mkdirs():创建单级或者多级文件夹。不可以覆盖。注意,此操作失 败时也可能已经成功地创建了一部分必需的父目录。 

2、删除:

下列方法所属的对象都可以是已存在的也可以是还未存在的。

① boolean delete():删除失败返回false(程序可能中途发生异常,导致执行不 到)、文件在被占用时,无法删除,返回false。

② void deleteOnExit();在程序退出时删除指定文件。

3、判断:

下列方法所属的对象都可以是已存在的也可以是还未存在的。

若不存在,对象不管封装的是什么,都返回false

若存在,根据对象封装的是什么来判断。

上述规则不适用于isAbsolute()。

① boolean exists():判断File对象是否存在。

② boolean isFile():判断File对象是否是文件。

③ boolean isDirectory():判断File对象是不是目录。

④ boolean isHidden():判断File对象是不是隐藏文件。

⑤ boolean isAbsolute():判断File对象是不是绝对路径。(如:C:\\abc\\c.txt)

⑥ boolean canExecute():判断File文件是否可以运行。

4、获取:

下列方法所属的对象都可以是已存在的也可以是还未存在的。

① String getName():返回最后一个名称。

② String getPath():返回File对象初始化内容。

③ String getParent():getPath()-getName()。

④ String getAbsolutePath():返回File对象初始化内容,即绝对路径。

⑤ String toString():返回File对象初始化内容。

⑥ long lastModified():返回File对象表示的文件或者文件夹最后一次被修改 的时间。

⑦ long length():返回File对象表示的文件或者文件夹的长度(单位:字节)。

 

三、功能的应用

1、文件列表

(1)String[] list():列出指定目录下的内容。

import java.io.*;

class FileDemo

{

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

{

File f=new File("F:\\JavaSDK");

String[] names=f.list();//f必须是已存在的目录,否则抛异常(无指向)

for(String name:names)

{

System.out.println(name);

}

}

}

运行结果:

 

(2)Static File[] listRoots():列出可用的文件系统根。

import java.io.*;

class FileDemo

{

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

{

File[] files=File.listRoots();

for(File f:files)

{

System.out.println(f);

}

}

}

运行结果:

 

(3)列出指定目录下的指定内容。过滤。

import java.io.*;

class FileDemo

{

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

{

File dir=new File("F:\\JavaSDK\\day00\\");

String[] arr=dir.list(new FilenameFilter()

{

public boolean accept(File dir,String name)

{

System.out.println("dir:"+dir+"-------name:"+name);

return name.endsWith(".java");

}

});

System.out.println("len:"+arr.length);

for(String name:arr)

{

System.out.println(name);

}

}

}

(4)String[] list()和File[] listFiles()

1)

import java.io.*;

class FileDemo

{

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

{

File f=new File("F:\\JavaSDK");

 

String[] arr=f.list();

 

for(String name:arr)

{

System.out.println(name);

}

}

}

运行结果:

 

2)

import java.io.*;

class FileDemo

{

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

{

File f=new File("F:\\JavaSDK");

 

File[] arr=f.listFiles();

 

for(File name:arr)

{

System.out.println(name);

}

}

}

运行结果:

 

因此:一般我们优先采用File[] listFiles()。

 

(5)列出指定目录下的所有内容,知道每一项没有内 容可列。--递归

递归要注意:

1)限定条件

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

 

import java.io.*;

class FileDemo

{

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

{

File dir=new File("C:\\abc");

 

showDir(dir);

}

public static void showDir(File dir)

{

System.out.println(dir);

 

File[] files=dir.listFiles();

 

for(int x=0;x<files.length;x++)

{

if(files[x].isDirectory())

showDir(files[x]);

System.out.println(files[x]);

}

}

}

 

 

拓展:带有层次

import java.io.*;

class FileDemo

{

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

{

File dir=new File("C:\\abc");

 

showDir(dir,0);

}

public static String getLevel(int level)

{

StringBuffer sb=new StringBuffer();

sb.append("|--");

for(int x=0;x<level;x++)

{

sb.insert(0,"|  ");

}

return sb.toString();

}

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

System.out.println(getLevel(level)+files[x]);

}

}

}

运行结果:

 

 

递归的其他应用:

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

/*

思路:

1、对指定目录进行递归,

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

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

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

*/

import java.io.*;

import java.util.*;

class FileListDemo

{

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

{

File dir=new File("F:\\JavaSDK");

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

fileToList(dir,list);

 

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

writeToFile(list,file.toString());

}

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

{

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=new BufferedWriter(new FileWriter(javaListFile));

for(File f:list)

{

String path=f.getAbsolutePath();

bufw.write(path);

bufw.newLine();

bufw.flush();

}

bufw.close();

}

}

 

 

集合与IO相结合的容器:Propertie

一、如何使用

Properties是HashTable的子类,即是Map的子类,并 且集合中存的都是字符串。

重点掌握以下几个方法:

① Properties pro=new Properties();

② Object setProperties(String key,String value);

③ String getProperties(String key);

④ Set<String> StringPropertyNames();返回键集。

⑤ void load(InputStream inStream);将流关联的文件 加载到集合中去。

⑥ void store(OutputStream outStream,String comments):使集合和对应的流同步。

ps:键相同,则覆盖。

二、应用

1、“存”&“取”

import java.util.*;

class PropertiesDemo

{

public static void main(String[] args)

{

Properties pro=new Properties();

 

pro.setProperty("zhangsan","20");

pro.setProperty("zhangsan","30");

System.out.println(pro);

 

pro.setProperty("Lisi",10+"");

 

String value=pro.getProperty("zhangsan");

System.out.println(value);

 

Set<String> names=pro.stringPropertyNames();

for(String name:names)

{

System.out.println(name+"::"+pro.getProperty(name));

}

}

}

2、存取配置文件

假设现在有一个配置文件info.txt。

 

 

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

/*

思路:

1、将字符读取流与文件相关联。为提高效率,用字符缓冲读取流。

2、读取一行数据,将改行数据用“=”进行切割。

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

*/

import java.util.*;

import java.io.*;

class PropertiesDemo

{

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

{

Properties pro=new Properties();

 

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

 

String line=null;

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

{

String[] arr=line.split("=");

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

}

bufr.close();

System.out.println(pro);

}

}

运行结果:

 

 

优化:

通过查阅Properties的API,发现有一个方法已经帮我 们实现了这个功能:void load(FileInputStream inStream);

import java.util.*;

import java.io.*;

class PropertiesDemo

{

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

{

Properties pro=new Properties();

 

FileInputStream in=new FileInputStream("G:\\info.txt");

 

pro.load(in);

 

System.out.println(pro);

//pro.list(System.out);

}

}

现在我们对集合进行一些设置,比如:

import java.util.*;

import java.io.*;

class PropertiesDemo

{

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

{

Properties pro=new Properties();

 

FileInputStream in=new FileInputStream("G:\\info.txt");

 

pro.load(in);

 

pro.setProperty("zhangsan","30");

 

System.out.println(pro);

}

}

运行结果:

 

但是在看一下文件:

 

想让文件也有同样的变化,则:

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

/*

思路:

1、将字符读取流与文件相关联。为提高效率,用字符缓冲读取流。

2、读取一行数据,将改行数据用“=”进行切割。

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

*/

import java.util.*;

import java.io.*;

class PropertiesDemo

{

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

{

Properties pro=new Properties();

 

FileInputStream in=new FileInputStream("G:\\info.txt");

 

pro.load(in);

 

pro.setProperty("zhangsan","30");

 

pro.store(new FileOutputStream("G:\\info.txt"),"haha");

 

pro.list(System.out);

}

}

运行结果:

现在,再看一下文件:

 

 

拓展:

配置文件有两种形式,一种是Properties,一种是xml文件。由于Properties形式的 配置文件有他的局限性,所以之后又出来了xml文件。

name=zhangsan

age=20

name=wangwu

age=30

 

<Persons

<person

<name>zhangsan</name>

<age>20</age>

/person>

 

<person

<name>wangwu</name>

<age>30<>

/person>

/Persons>

3、Properties练习

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

/*

 很容易想到的是计数器。

 它的特点是:

 该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。

 随着该应用程序的退出,该计数器也在内存中消失,下一次再启动时,重新计数。

 

 这不是我们想要的,我们希望,程序即使结束,该计数器的值也存在。

 下次程序启动时会先加载该计数器的值并加1后重新存储起来。

 

 因此,我们想到用配置文件来记录该软件的使用次数。

 

 该配置文件使用键值对的形式,这样便于阅读数据,并操作数据。

 

 键值对数据是Map集合,数据是以文件的形式存储的,使用io技术,

 那么io+Map=Properties。

*/

import java.util.*;

import java.io.*;

class RunCount

{

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

{

Properties prop=new Properties();

 

File file=new File("C:\\count.txt");

 

FileInputStream fis=new FileInputStream(file);

 

prop.load(fis);

 

int count=0;

 

String value=prop.getProperty("time");

 

if(value!=null)

{

count=Integer.parseInt(value);

if(count>5)

{

System.out.println("您好,使用次数已到,请注册!");

return;

}

}

count++;

prop.setProperty("time",count+"");

 

FileOutputStream fos=new FileOutputStream(file);

prop.store(fos,"");

 

fis.close();

fos.close();

}

}

第一次编译运行后:

 

第二次运行后:

 

 

 

其他

一、PrintWriter

打印流。它提供了多种重载形式的打印方法,可以将各种 数据类型的数据打印到指定的位置。

import java.util.*;

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("C:\\a.txt"),true);

 

String line=null;

 

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

{

if("over".equals(line))

break;

out.println(line.toUpperCase());

//out.flush();out初始化时,boolean参数为true,自动刷新

}

out.close();

bufr.close();

}

}

其实,我们可以把PrintWriter看成是FileWriter,PrintStream看成是 FileOutputStream来学习。那么这里的println()就可以看成是write()。只需要 特别注意一下PrintWriter构造函数所接受的参数即可,它可以接收的参数类型比较广 泛一些。

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

a. File对象

b. 字符串路径

c. 字节输出流对象

 

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

a. File对象

b. 字符串路径

c. 字节输出流对象

d. 字符输出流对象

 

 

二、合并流(SequenceInputStream)

|--InputStream

|--SequenceInputStream

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

SequenceInputStream(Enumeration<?extends InputStream> e) :通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。

 

练习:将1.txt、2.txt、3.txt中的内容写入到一个文件 中。

import java.util.*;

import java.io.*;

class SequenceDemo

{

public static void main(String[] args)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 en=v.elements();

 

SequenceInputStream sis=new SequenceInputStream(en);

 

FileOutputStream fos=new FileOutputStream("C:\\a.txt");

 

byte[] buf=new byte[1024];

int len=0;

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

{

fos.write(buf,0,len);

}

fos.close();

sis.close();

}

}

 

三、切割文件&合并文件

需求:将a.txt切割成三个文件,分别是1.txt、2.txt、 3.txt。 再将三个文件合并成一个文件b.txt。

import java.util.*;

import java.io.*;

class SplitFile

{

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

{

//splitFile();

merge();

}

 

public static void splitFile()throws IOException

{

FileInputStream fis=new FileInputStream("C:\\a.txt");

 

FileOutputStream fos=null;

 

byte[] buf=new byte[16];

int len=0;

int count=1;

while((len=fis.read(buf))!=-1)

{

fos=new FileOutputStream("C:\\"+(count++)+".txt");

fos.write(buf,0,len);

fos.close();

}

fis.close();

}

public static void merge()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 en=v.elements();

*/

//由于Vector太低效,所以我们用ArrayList。

 

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

for(int x=1;x<=3;x++)

{

al.add(new FileInputStream("C:\\"+x+".txt"));

}

final Iterator<FileInputStream> it=al.iterator();

 

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("C:\\hebin.txt");

 

byte[] buf=new byte[8];

 

int len=0;

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

{

fos.write(buf,0,len);

}

sis.close();

fos.close();

}

}

 

 

四、对象的序列化

什么是对象的序列化?

把对象存放在硬盘上叫做对象的持久化存储(序列化)。

该对象所属的类必须实现Serializable接口。(该接口 没有方法,仅用于标识可序列化的语义。)

更细节划分操作对象的流:

ObjectInputStream/ObjectOutputStream

 

涉及到的方法:

① ObjectOutputStream oos=new ObjectOutputSteam(OutputStream out);

② void writeObject(Object obj):将指定的对象写入ObjectOutputStream;

③ objectInputStream ois=new ObjectInputStream(InputStream in);

④ void readObject();从 ObjectInputStream 读取对象

 

练习:将自定义类Person的对象存入到磁盘文件中,再 从文件中读取出来。

/*

实现了Serializable之后的类,编译后都会产生一个对应的uid。

它是根据类中不同的成员来算这个值的,给类做标记。

 

使对象所属的类文件始终保持是同一个uid。

通过:public static fianl long serialVersionUID=42L;

好处:

当我们往文件obj.txt中存储对象之后,我们改动了类中的成员变量,

比如,加一个新的修饰符。此时我们想从obj.txt中读取数据,编译运行后,

运行失败。原因就是序列号不统一。但是我们只要通过上面一行代码,我们

就可以解决这个问题。并且可以使obj.txt中的内容同步。

 

还有一点就是:静态不可以序列化,即存不进去,我们会将相应的该数据类型

默认初始化值代替初始化值存入进去。若非静态也不想序列化,我们可以通过

关键字transient修饰。

 

*/

import java.io.*;

class Person implements Serializable

{

public static final long serialVersionUID=42L;

    private String name;

private int age;

private  String country;

Person(String name,int age,String country)

{

this.name=name;

this.age=age;

this.country=country;

}

public String toString()

{

return name+":"+age+":"+country;

}

}

class ObjectStreamDemo

{

public static void main(String[] args)throws Exception

{

//writeObj();

readObj();

}

public static void writeObj()throws Exception

{

ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("C:\\t.txt"));

 

oos.writeObject(new Person("zhangsn",20,"cn"));

oos.writeObject(new Person("wangwwu",23,"ke"));

 

oos.close();

}

public static void readObj()throws Exception

{

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("C:\\t.txt"));

 

Person p=(Person)ois.readObject();

System.out.println(p);//打印zhangsn:20:cn

 

ois.close();

}

}

 

 

五、管道流

管道流:

PipedInputStream/PipedOutputStreamm

PipedReader/PipedWriter

 

   PipedInputStream in=new PipedInputStream();

   PipedOutputStream out=new PipedOutputStream();

   in.connect(out);

 

管道输入流应该连接到管道输出流,管道输入流提供要写 入管道输出流的所有数据。通常,数据由某个线程从管道 输入流对象读取,并由其他线程将其写入到相应的管道输 出流。

 

示例:

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

System.out.println("读取前。。。没有数据阻塞");

int len=in.read(buf);

System.out.println("读取后。。。阻塞结束");

 

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

{

System.out.println("开始写入数据,等待6秒后");

Thread.sleep(6000);

out.write("pipe lai le".getBytes());

out.close();

}

catch (Exception e)

{

throw new RuntimeException("管道输出流失败");

}

}

}

class PipedStreamDemo

{

public static void main(String[] args)throws Exception

{

PipedInputStream in=new PipedInputStream();

PipedOutputStream out=new PipedOutputStream();

 

in.connect(out);

 

Read r=new Read(in);

 

Write w=new Write(out);

 

new Thread(w).start();

new Thread(r).start();

}

}

 

 

六、RandomAccessFile

简而言之,该类可以根据模式(r,rw)的不同对磁盘上 的文件进行随机的读和写。该类的对象就相当于一个文件 指针,通过调用类中的方法可以改变指针的所指的位置从 而实现随机。

 

重点掌握以下几个方法:

① RandomAccessFile(File file,String mode):创建从中读取和向其中写入(可选) 的随机访问文件流,该文件由File参数指定。

② RandomAccessFile(String name,String mode):创建从中读取和向其中写入 (可选)的随机访问文件流,该文件具有指定名称。

③ void seek(long pos):设置到此文件开头测量到的文件指针偏移量,在该位置 发生下一个读取或写入操作。

④ int skipBytes(int n):尝试跳过输入的n个字节以丢弃跳过的字节。

⑤ int readInt():从此文件读取一个有符号的32位整数。

⑥ int read():从此文件中读取一个数据字节。

⑦ int read(byte[] b):将最多b.lenggh个数据字节从此文件读入byte。

⑧ void write(int b):向此文件写入指定的字节。

⑨ void write(byte[] b):将 b.length 个字节从指定 byte 数组写入到此文件, 并从当前文件指针开始。

⑩ void writeInt(int v):按四个字节将int写入该文件,先写高字节。

 

 

示例:

import java.io.*;

class RandomAccessFileDemo

{

public static void main(String[] arags)throws IOException

{

writeFile();

//readFile();

}

 

public static void writeFile()throws IOException

{

RandomAccessFile raf=new RandomAccessFile("C:\\t.txt","rw");

raf.write("lisi".getBytes());

 

/*

write()/具有强转的功能,为保证数据的原样性,只写最低八位。

而writeInt()会写32位。且写入的其实是a,通过编码表查找。

*/

raf.writeInt(97);

 

//raf.writeBytes("\r\n");

//raf.seek(8);

 

raf.write("wangwu".getBytes());

raf.writeInt(258);

 

raf.close();

 

}

public static void readFile()throws IOException

{

RandomAccessFile raf=new RandomAccessFile("C:\\t.txt","r");

 

byte[] buf=new byte[4];

raf.read(buf);

String name=new String(buf);

 

//raf.seek(8);

//raf.skipBytes(4);

 

int age=raf.readInt();

 

System.out.println("name="+name+",age="+age);

}

}

 

总结:这个类也可以看成是FileWriter/FileWriter来 学习。就seek()和skipBytes()特殊一点。

 

 

七、操作基本数据类型的流对象DataStream

DataInputStream/DataOutputStream

 

DataInputStream dis=new DataInputStream(InputStream in);

 

DataOutputStream dos=new DataOutputStream(OutputStream out);

 

readUTF();

writeUTF();

 

readInt();

writeInt();

 

readBoolean();

writeBoolean();

 

readDouble();

writeDouble();

 

温馨提示:这个类可以看成FileWriter和FileReader来 对照学习。

 

 

八、ByteArrayStream

ByteArrayInputStream/ByteArrayOutputStream

 

ByteArrayStream(byte[] buf):

创建一个ByteArrayInputStream,使用buf作为其缓冲区 数组。

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

ByteArrayStream():创建一个新的byte数组输出流。

ps:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节 数组。

因此,这两个流对象都操作的数组,并没有使用系统资源,

不用进行close()关闭。

 

还需要掌握的方法:

ByteArrayInputStream:

read();

 

ByteArrayOutputStream:

write();

size();

toString();

write(OutputStream out);

 

 

 

2015-12-16至

2015-12-20著

 

0 0
原创粉丝点击