Java程序设计学习笔记(三)—— IO
时间:2016-3-24 11:02
——IO流(Input/Output)
IO流用来处理设备之间的数据传输。
Java对数据的操作是通过流的方式。
Java对于操作流的对象都在IO包中。
流按操作数据分为两种:字节流与字符流。
字符流为了处理文本数据,可以在内部融合编码表,编码表可以自己指定,处理文字时不会出现乱码。
图片等文件使用字节流。
首个中国编码表——GB2312
后来进行了一次扩容——GBK
再后来为了方便使用,将世界上各个国家的编码表融合——Unicode,在Unicode编码表中无论什么字符,都用两个字节来表示。
后期Unicode进行了优化,产生了UTF-8,在UTF-8中,能用一个字符就用一个字符,减少空间浪费。
字符流的由来:
不同编码表之间的字符会产生乱码,为了避免此现象的发生,Java在流技术上,就基于字节流产生了字符流,字符流可以在字
符流内部指定编码表,从而解决不同编码表之间的乱码问题。
通用字节流,字符流是基于字节流的。
流按流向分为:输入流和输出流。
——字符流
FileReader、FIleWriter、BufferedReader、BufferedWriter
1、IO流常用基类(这四个类都是抽象类)
字节流的抽象基类:
InputStream,OutputStream
字符流的抽象基类:
Reader,Writer
注:由这四各类派生出来的子类名称都是以其父类名作为自类名的后缀。
如:InputStream的子类FileInputStream
如:Reader的子类FileReader
后缀名是父类名,前缀名是该流对象的功能。
2、Writer类
是抽象类,构造方法是protected,也就是只能在子类中使用。
Writer类的共性方法:
Writer append(char c)
abstract void close()
关闭流,并刷新流。
当流关闭后,不能再使用write方法。
abstract void flush()
刷新流
void write(int c)
写入单个字符。
write()方法仅仅是将数据写入到内存当中,也就是写入到流中,想要将数据写入文件,需要刷新流。flush()方法刷新流。
void write(char[] cbuf)
写入字符数组。
void write(String str)
写入字符串。
void write(String str,int off,int len)
写入字符串的某一部分。
str--字符串 off--相对初始写入字符的偏移量(从哪开始) len--要写入的字符数。
既然IO流是用于操作数据的,数据的最常见体现形式是:文件,那么就以操作文件为主来演示。
需求:
在硬盘上,创建一个文件并写入一些文字数据。
找到一个专门用于操作文件的Writer子类对象:FileWriter,前缀名是功能,后缀名是父类名。
(可以发现,Writer实现的子类特点:前缀名是功能,后缀名是父类名)
3、FileWriter
构造方法:
FileWriter(String fileName)
根据给定的文件名构造一个FileWriter流对象。
FileWriter(String fileName, boolean append)
根据给定的文件名以及指示是否附加写入数据的boolean值来构造一个FileWriter流对象。
append:一个boolean值,如果为true,则将数据写入文件末尾处,而不是写入文件开始处。
也就是实现了文件的续写,不再从文件开头写数据了。
如果文件不存在,先创建文件再续写,如果文件存在,直接续写。
在Windows操作系统中,换行符是:\r\n
在Linux操作系统中,换行符是:\n
继承自Writer的方法:
abstract void close()
关闭流,并且刷新流。
abstract void flush()
刷新该流的缓冲。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String str,int off,int len)
写入字符串的某一部分。
void wrie(cha[] cbuf)
写入字符数组。
abstract void write(char[ ] cbuf,int off,int len)
写入字符数组的某一部分。
4、IO异常处理
凡是能和设备产生关系的操作都会发生异常。
FileWriter fw = null; //需要将fw定义在try外部,否则finally无法访问。
5、FileReader
用来读取字符文件的便捷类,此类的构造方法有默认的字符编码和默认字节缓冲区。
默认编码可以通过getProperties()方法获取当前系统的编码。,该编码表是可变化的。
将文本读入流中。
构造方法:
该类的对象一被创建就必须明确要操作的文件,所以该类没有无参构造方法。
FileReader(String fileName)
在给定从中读取数据的文件名的情况下创建一个新的FileReader流对象。
并且该文件会被创建到指定目录下,如果该目录下已有同名文件,将覆盖原文件。
FileWriter(File file)
指定文件。
继承自Reader的方法:
abstract void close()
关闭流并释放与之关联的所有资源。
并不刷新流。
int read()
读取单个字符,返回的是int型的ASCII码值。
read方法一次读取一个字符,而且会自动往下读。
如果读到文件的末尾,会返回-1。
//一次读取一个字符
public static void main(String[] args)throws IOException
int read(char [ ] buff)
将字符读入数组,返回的是int型的字符个数。
没有直接读字符串的方法。
通常字符数组的长度定义为1024或者是1024的整数倍。
代码示例:
//使用字符数组读取字符
public static void main(String[] args)throws IOException
//另一个String方法来输出字符数组:String(char[].offset,len)
//功能是将指定长度的字符数组转换为字符串。
//使用循环读取字符
publicstatic void main(String[] args) throws IOException
{
FileReaderfr = new FileReader("Demo.txt");
char[]ch = new char[3];
int num;
while((num= fr.read(ch)) != -1)
{
System.out.println("num= " + num + ".." + new String(ch,0,num));
}
fr.close();
}
abstract int read(char[ ],int off,int len)
从数组指定位置开始读入字符,从off处开始读取字符,要读取的最多字符数为len。
while((num = fr.read(buf)) != -1)
7、字符流的缓冲区
缓冲区的出现提高了对数据的读写效率。
对应类
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用,因为缓冲区的为了提高流的操作效率。
在流的基础上对流的功能进行了增强。
8、BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
读取流缓冲区在对象建立时需要有被缓冲的对象,也就是在缓冲区构造方法中传入FIleReader的对象。
构造方法:
BufferedReader(Read in)
创建一个默认大小输入缓冲区的缓冲字符输入流。
in是一个流对象。
继承自Reader的方法:
void close()
关闭该流并释放与之关联的所有资源。
int read()
读取单个字符。
int read(char[ ],int off,int len)
从指定位置开始读入,并且指定长度。
特有方法:
String readLine()
读取一个文本行。遇到'\n'或者'\r'就认为某行终止。
读完一行会自动跳到下一行。
包含该行内容的字符串,不包含任何行终止符,如果达到流的末尾,则返回null。
因为不会读取到换行符,所以在读取过程中一般配合newLine使用。
readLine底层其实就是read方法。
9、BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。(意思就是全部存完一次写入)
该类的构造方法必须有参数,因为有流才有缓冲区。
所以在创建缓冲区之前,必须有流对象。一般开发都会加入缓冲区技术,对性能和效率进行增强。
构造方法:
BufferedWriter(Writer out)
创建一个使用默认大小输出缓冲区的缓冲字符输出流。
out是一个Writer流对象。
继承自Writer的方法:
void close()
关闭此流并刷新。
void flush()
刷新该流的缓冲。
void write(char [ ],int off,int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String s,int off,int len)
写入字符串的某一部分。
特有方法:
void newLine()
写入一个行分隔符,换行。
因为在Windows中换行符是“\r\n”,而Linux中换行符为“\n”,会出现兼容性问题,所以newLine的出现提高了Java的跨平台性。
/**
11、LineNumberReader
跟踪行号的缓冲字符输入流。
该类继承自BufferedReader,所以可以直接调用BufferedReader的方法。
此类定义了方法setLineNumber(int)和getLineNumber(),用于设置行号和获取行号,默认情况下,行号从0开始。
setLineNumber(int):设置起始行号。
getLineNumber() :获取当前行号。
12、输入输出举例
包含了FileReader,FileWriter,BufferedReader,BufferedWriter的用法。
13、装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类可以
成为装饰类。
例如
BufferedReader中的读取一行:readLine();
装饰类通常会通过构造方法接受被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
装饰模式比继承要灵活,避免了集成体系的臃肿,而且降低了类与类之间关系。
装饰类因为增强已有对象,具备的功能和已有的功能是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于同一
个体系中。
/**
——字节流
1、FileOutputStream
如果想要操作图片数据,就需要用到字节流。
字符串转成字节数组的方法:
byte[] String.getBytes()
字符串转成字符数组的方法:
String[] toCharArrays()
int available()
返回剩余字符数。
可以通过available()的返回值来确定数组大小,但是虚拟机启动时默认是64MB,所以需要注意数组大小。
2、字节流缓冲区
BufferedInputStream 和 BufferedOutputStream
10、IOStream
字符流需要刷新数据,而字节流不需要刷新,因为字符流中有一个字符数组,临时存储字符数据,刷新后才将数组内容写入。
字节流如果不使用缓冲区,就不需要刷新操作。
12、用BufferedI/OStream复制一个图片
InputStreamReader,专门用于操作字节流的字符流对象。
可以将字节流转换成字符流。
在构造时需要接收一个字节流对象。
InputStreamReader(InputStream in)
方法继承自Reader。
/*
通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。
也就是readLine()方法。
那么能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流缓冲区Buff而恶毒Reader类中的方法。
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?
*/
16、总结规律
流操作的基本规律:
最痛苦的就是对象有很多,不知道该用哪一个。
选择对象可以通过三个步骤来完成:
1、明确源和目的
源:输入流
InputStream
Reader
目的:输出流
OutputStream
Writer
2、操作的数据是否是纯文本
如果是,就选择字符流。
如果不是,就选择字节流。
3、当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘。
目的设备:内存,硬盘,控制台。
复制文件:将一个文本文件中的数据存储到另一个文件中。
源:因为是源,所以使用读取流:InputStream和Reader
是不是操作文本文件?
是!就可以选择字符流Reader。
明确设备:硬盘中的文件。
Reader体系中可以操作文件的对象是FileReader。
是否需要提高效率?
是!使用BufferedReader缓冲区。
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
目的:因为是目的,所以使用输出流:OutputStream和Writer
是否纯文本?
是!使用字符流Writer
设备:硬盘中的一个文件。
Writer体系中可以操作文件的对象是FIleWriter。
是否需要提高效率?
是!使用BufferedWriter缓冲区。
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
17、指定编码表
想要把录入的数据按照指定的编码表(utf-8),将数据存储到文件中。
目的:OutputStream和Writer
是否存储为文本文件?
是!使用Writer
设备:硬盘中的一个文件
使用Writer体系中可以操作文件的FIleWriter
但是FIleWriter是使用默认的编码表GBK,存储时需要加入指定的编码表utf-8,而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter,并且该转换流对象要接收一个字节输出流对象(FileOutputStream),还可以接收指定
的编码表。
OutputStreamWriter osw = new OutputStreamWritrr(new FileOutputStream("a.txt"),"utf-8");
是否需要提高效率?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,转换流什么时候使用?
通常涉及到字符编码转换时,需要用到转换流。
18、改变标准输入输出设备
System中的setIn和setOut方法,可以改变输入输出设备。
如果setIn方法参数是一个文本文件,setOut方法参数也是一个文本文件,就会复制文件。
最新文章
- MySQL 分组后,统计记录条数
- map erase iterator
- (整理) JQuery中的AJAX
- [AngularJS] jQuery时代
- 快速理解Java中的五种单例模式
- 业务代码中(java class)中如何实现多线程,并且将子线程中的值随方法返回返回值
- python爬虫中文网页cmd打印出错问题解决
- .net学习笔记---IIS 处理模型及ASP.NET页面生命周期
- [ArcEngine]IFeatureBuffer使用
- scala模拟一个timer
- C++析构函数定义为虚函数(转载)
- uCGUI字符串显示过程分析和uCGUI字库的组建
- EasilyUI的一个简单的拖拽功能
- 成都OpenPart——DevOps专场活动参与感
- lucene拼写检查模块
- C# DateTime的ToString()方法的使用
- First release of mlrMBO - the toolbox for (Bayesian) Black-Box Optimization
- Core知识整理
- 测试中,重现偶发的BUG问题。
- Codeforces 514E Darth Vader and Tree 矩阵快速幂