IO流
- Java对数据的操作是通过流的方式
- IO流用来处理设备之间的数据传输
- Java用于操作流的对象都在IO包中
- 流按操作数据分为两种:字节流与字符流。
- 流按流向分为:输入流,输出流。
- 字节流的抽象基类:
- InputStream ,OutputStream
- 字符流的抽象基类
- Reader, Writer
字符流
Writer
Writer是写入字符流的顶层类。
Writer中定义的方法:
write(): 向流中写入数据,写入的数据可以是单个字符,字符串或字符串的某一部分,字符数组或字符数组的某一部分,
flush(): 刷新流中的数据,将数据写入到目的地。
close(): 关闭流资源,但是在关闭之前会先刷新流。
FileWriter
FileWriter是Writer的间接子类,主要用于操作文本文件。
IO异常处理方式
代码示例:
FileWriter fw = null;
try {
fw = new FileWriter("z://heima.java");
fw.write("java");
fw.flush();
} catch (IOException e) {
System.out.println(e.toString());
} finally {
try {
// 如果指定的路径不存在,则FileWriter初始化失败,fw=null,不能调用close()方法。此处必须进行非空判断。
// 如果有多个流资源的关闭动作,必须对每个流资源的关闭动作进行非空判断。
if (fw != null) {
fw.close();//关闭流资源的动作一定要被执行,所以放在finally块中
}
} catch (IOException e) {
System.out.println(e.toString());
}
}
文本文件的两种读取方式
方式一:一个字符一个字符的读,读取一个字符输出一个字符
int read(): 读取单个字符。返回字符所对应的整数,如果读到流的末尾,则返回-1。
示例代码:
FileReader fr = new FileReader("FileWriterDemo.java");
int ch = 0;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
fr.close();
方式二:每读取一个字符就存入数组中,然后将数组中的字符输出。
int read(char[] cbuf): 将字符读入数组。返回读取的字符数,如果读到流的末尾,则返回-1。
代码示例:
FileReader fr = new FileReader("FileWriterDemo.java");
char[] buffer = new char[1024];
int num = 0;
while ((num = fr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, num));
}
fr.close();
字符流的两种拷贝方式
拷贝方式一:读一个字符就写入一个字符到流中
示例代码:
FileWriter fw = new FileWriter("heima.java");
FileReader fr = new FileReader("FileReaderDemo1.java");
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
fw.close();
fr.close();
拷贝方式二:一直读并存入到缓冲区,再一并写入
示例代码:
FileWriter fw = new FileWriter("heima.java");
FileReader fr = new FileReader("CopyFileDemo.java");
char[] buffer = new char[1024];// 创建一个缓冲
int len = 0;
while ((len = fr.read(buffer)) != -1) {
fw.write(buffer, 0, len);// 写入有效数据
}
fw.close();
fr.close();
拷贝文本文件图列:
字符流的缓冲区
- 缓冲区的出现提高了对数据的读写效率。
- 对应类
- BufferedWriter
- void newLine(): 写入一个行分隔符。
- BufferedReader
- String readLine(): 读取一个文本行。返回包含该行内容的字符串,不包含任何行终止符。如果达到流的末尾则返回null。
- BufferedWriter
- 缓冲区要结合流才能使用。
- 在流的基础上对流的功能进行了增强。
使用缓冲区的注意事项:
1 先有流对象,才有缓冲区。
2 使用缓冲区,必须调用flush()方法进行刷新。
字节流
字节流的拷贝方式
方式一:自定义一个缓冲区 byte数组 的方式读写数据
示例代码:
FileOutputStream fos = new FileOutputStream("d:\\2.jpg");
FileInputStream fis = new FileInputStream("d:\\1.jpg");
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.close();
fis.close();
方式二:通过Java提供的缓冲区对象读写数据
示例代码:
BufferedOutputStream bufos = new BufferedOutputStream(
new FileOutputStream("d:\\3.jpg"));
BufferedInputStream bufis = new BufferedInputStream(
new FileInputStream("d:\\1.jpg"));
int b = 0;
while ((b = bufis.read()) != -1) {
bufos.write(b);
}
bufos.close();
bufis.close();
读取键盘录入
System.in: 对应的是标准的输入设备,键盘。
System.out: 对应的是标准的输出设备,控制台。
示例代码:
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
while (true) {
int ch = in.read();
if (ch == '\r')
continue;
if (ch == '\n') {
String s = sb.toString();
System.out.println(s.toUpperCase());
sb.delete(0, sb.length());
} else {
sb.append((char) ch);
}
}
改变标准输入输出设备
System.setIn(InputStream in): 重新分配标准输入流。
System.setOut(OutputStream out): 重新分配标准输出流。
转换流
InputStreamReader: 是字节流通向字符流的桥梁
OutputStreamWriter : 是字符流通向字节流的桥梁
转换流
转换流
InputStreamReader: 是字节流通向字符流的桥梁
OutputStreamWriter : 是字符流通向字节流的桥梁
//获取键盘录入对象
InputStream in = System.in;
// 将字节流对象转成字符流对象,使用转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(in);
// 为了提高效率,将字符流进行缓冲区技术高效读取,使用BufferedReader
BufferedReader bufr = new BufferedReader(isr);
// 键盘录入最常见写法
BufferedReader bufr1 = new BufferedReader(new InputStreamReader(System.in));
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
// 简写格式:
BufferedWriter bufw1 = 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();
流操作规律
通过三个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备: 内存ArrayStream 硬盘FileStream 键盘System.in
目的设备:内存ArrayStream 硬盘FileStream 控制台System.out
4,如果要提高效率则使用缓冲区
5,如果要用指定编码存储数据则使用转换流
异常的日志信息
示例代码:
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("exeception.log"); ps.println(s); System.setOut(ps); } catch (IOException ex) { throw new RuntimeException("日志文件创建失败"); } e.printStackTrace(System.out); }
File类
File类继承自Object类,是文件和目录路径名的抽象表现形式。
用来将文件或文件夹封装成对象。
方便对文件和文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
File类的字段和构造函数:
String File.separator : 与系统有关的目录分隔符,在windosXP系统中代表“\\”
File(File parent ,String child): 根据 parent 抽象路径名和 child 路径名字符串创建一个新
File
实例
File(String pathname): 通过将给定路径名字符串转换为抽象路径名来创建一个新
File
实例
File(String parent,String child): 根据 parent 路径名字符串和 child 路径名字符串创建一个新
File
实例。
代码示例:
//将a.txt封装成File对象,可以将已有的和未出现的文件或文件夹封装成对象
Fiel f1 = new File("a.txt");
File f2 = new File("c:\\abc","b.txt");
File d = new File("c:\\abc");
File f3 = new File(d,"c.txt");
File类的常见方法:
1,创建
boolean createNewFile():
在指定位置创建文件,如果创建成功,返回true.如果该文件已经存在,则不创建,返回false.
和输出流不一样,输出流对象一建立就创建文件,而且文件已经存在,会覆盖原文件。
static File createTempFile(String prefix,String suffix):
在默认临时文件目录创建一个空文件,使用给定的前缀与后缀生成其名称。
static File createTempFile(String prefix,String suffix,File directory):
在指定目录创建一个空文件,使用给定的前缀与后缀生成其名称。
2,删除
boolean delete(): 删除指定文件,删除成功返回true,删除失败返回false.
void deleteOnExit(): 在程序推出时删除指定文件。即使程序中途出现异常导致程序停止,也会删除。
3,判断
boolean exists(): 判断文件或目录是否存在。
boolean isFile(): 判断是否是文件
boolean isDirectory(): 判断是否是目录
boolean isHidden(): 判断是否是隐藏文件或目录
boolean isAbsolute(): 判断此抽象路径名是否为绝对路径名,无论此文件或文件夹已经被创建。
4,获取信息
String[] list():
返回指定目录中的所有文件和目录的名称,包含隐藏的。调用list方法的file对象必须是封装了一个目录,并且该目录必须存在,否则将引发NullPointException
String[] list(FilenameFilter filter): 返回指定目录中的经过过滤的所有文件和目录的名称
示例代码:列出d盘下Java目录中(不包含子目录)的所有bmp文件
File dir = new File("d:\\Java");
String[] arr = dir.list(new FilenameFilter()
{
public boolean accept(File dir,String name)
return name.endsWith(".bmp");
});
File[] listFiles(): 返回当前目录下所有的文件和目录对象。
示例代码:
File dir = new File("c:\\");
File[] files = dir.listFiles();
for(File f ,files)
{
System.out.println(f.getName()+"::"+f.getLength());
}
static File[] listRoots(): 返回系统根目录。
5,比较
int compareTo(): 按字母顺序比较两个抽象路径名。
打印流
该流提供了打印方法,可以将各种数据类型的数据都原样打印。
可以直接操作流和文件,并可指定是否自动刷新流中的数据。
PrintWriter
构造函数可以接收的参数类型:
- file对象 File
- 字符串路径。String
- 字节输出流。OutputStream
- 字符输出流。Writer
PrintStream
构造函数可以接收的参数类型:
- file对象 File
- 字符串路径。String
- 字节输出流。OutputStream
合并流
SequenceInputStream 对多个流进行合并
SequenceInputStream
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
构造方法:
SequenceInputStream(Enumeration<? extends InputStream> e):
SequenceInputStream(InputStream s1,InputStream s2):
代码示例:
文件合并,将1.txt 2.txt 3.txt文件合并成4.txt文件
通过Vector存储流对象,效率低
Vector<InputStream> v = new Vector<InputStream>();
v.add(new FileInputStream("c:\\1.txt"));
v.add(new FileInputStream("c:\\2.txt"));
v.add(new FileInputStream("c:\\3.txt"));
Enumeration<InputStream> en = v.elements();
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("c:\\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();
通过ArrayList存储流对象
ArrayList<InputStream> al = new ArrayList<InputStream>();
al.add(new FileInputStream("c:\\1.txt"));
al.add(new FileInputStream("c:\\2.txt"));
al.add(new FileInputStream("c:\\3.txt"));
final Iterator<InputStream> it = al.iterator();
Enumeration<InputStream> en = new Enumeration<InputStream>() {
public boolean hasMoreElements() {
return it.hasNext();
}
public InputStream nextElement() {
return it.next();
}
};
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("c:\\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();
序列流
被操作的对象需要实现Serialzzable(标记接口)
ObjectOutputStream
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。
ObjectInputStream
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时可以为应用程序提供对对象图形的持久存储。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时可以为应用程序提供对对象图形的持久存储。
ObjectInputStream 用于恢复那些以前序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。
序列化的作用:
将对象的信息从栈内存中移到硬盘中,实现了对象的持久化存储。
被序列化的对象通常在硬盘中的存储格式:对象名.Object
被transient关键字修饰的非静态成员不能被序列化
静态成员不能被序列化,因为静态成员在方法区,而对象在栈内存中。
当类中自定义了序列化标识:static final long serialVersionUID = 42L; 后
则新的类还能操作曾经被序列化的对象。
管道流
输入输出可以直接进行连接,通过结合线程使用。
- PipedInputStream
- 管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从
PipedInputStream
对象读取,并由其他线程将其写入到相应的PipedOutputStream
。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
- 管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从
- PipedOutputSteam
- 可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入
PipedOutputStream
对象,并由其他线程从连接的PipedInputStream
读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于 毁坏 状态。
- 可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入
RandomAccessFile
- RandomAccessFile类不是IO体系中的成员,而是直接继承自Object.但是它是IO包中的成员,因为它具备读和写功能。
- 内部封装了一个大型数组,而且通过指针对数组的元素进行操作。
- 可以通过getFilePointer获取指针的位置,同时可以通过seek改变指针的位置。
- 随机访问文件,自身具备读写的方法。
- 通过skipBytes(int x),seek(int x)来达到随机访问
- 读写原理:内部封装了字节输入流和输出流
- 局限性:RandomAccessFile类只能操作文件,且只有4种模式
- r: 只读模式,不会创建文件,会读取一个已存在的文件,如果该文件不存在,则会出现异常。
- rw:读写模式,操作的文件不存在,会自动创建,如果文件存在则不会覆盖。
其他流对象
操作基本数据类型
DataInputStream 与DataOutputStream
操作字节数组
ByteArrayInputStream 与ByteArrayOutputStream
代码示例:
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEF".getBytes());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1){
bos.write(by);
}
操作字符数组
CharArrayReader与CharArrayWrite
操作字符串
StringReader 与StringWriter