第十一章 IO流(输入/输出)
目标:
1. 掌握文件操作类File
2. 理解流的概念、掌握IO流的分类
3. 掌握IO流中常用类
4. 掌握处理流
5. 掌握System对IO流的支持
6. 了解对象流
一、 文件操作类File
▲File类常用构造方法
File |
File |
▲File类中两个常量
static String | pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。 Windows ; |
| separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。 Windows \ |
▲File类中常用方法
boolean | createNewFile() 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 |
| delete |
| exists |
getAbsolutePath | |
getName | |
getParent | |
getPath | |
| isDirectory |
| isFile |
| length |
| list |
File | listFiles |
| mkdir |
案例一:在D盘下面创建文件1.txt
package test_12_9;
import java.io.*;
import javax.swing.JOptionPane;
public class Test1 {
public static void main(String[] args) {
File f = new File("D:\\1.txt");
//最好用File f = newFile("D:"+File.separator+"1.txt");
if(!f.exists()){//不存在
try {
f.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}else{
JOptionPane.showMessageDialog(null, "文件已存在!");
}
}
}
注意:为了保证Java程序的跨平台性,做文件操作时最好不要直接用“D:\\1.txt”,因为不同操作系统的路径分隔符不同(eg:windows是\ Linux是/),如果直接使用“D:\\1.txt”那么该程序到Linux平台则无法直接运行,所以最好使用File.separator代替
案例二:输出文件的属性
package test_12_9;
import java.io.*;
public class Test2 {
public static void main(String[] args) {
File f = new File("D:\\abc\\1.txt");
System.out.println("f的绝对路径:"+f.getAbsolutePath());
System.out.println("f的路径:"+f.getPath());
System.out.println("f的父路径:"+f.getParent());
System.out.println("f的文件名:"+f.getName());
System.out.println("f的文件长度:"+f.length());
}
}
案例三:对目录操作,输出指定目录下所有内容
方法一:使用list()
package test_12_9;
import java.io.*;
import javax.swing.JOptionPane;
public class Test3 {
public static void main(String[] args) {
File f = new File("G:\\");
if(f.isDirectory()){
String[] s = f.list();
for(int i = 0;i<=s.length-1;i++){
System.out.println(s[i]);
}
}else{
JOptionPane.showMessageDialog(null, "你指定的不是目录!");
}
}
}
方法二:使用listFiles()
package test_12_9;
import java.io.*;
import javax.swing.JOptionPane;
public class Test3 {
public static void main(String[] args) {
File f = new File("G:\\");
if(f.isDirectory()){
File[] file = f.listFiles();
for(int i = 0;i<=file.length-1;i++){
System.out.println(file[i].getName());
}
}else{
JOptionPane.showMessageDialog(null, "你指定的不是目录!");
}
}
}
案例四:对目录操作,输出指定目录下所有文件(注意目录中还有子目录)
package test_12_9;
import java.io.*;
import javax.swing.JOptionPane;
public class Test3 {
public static void printFile(File f){
if(f!=null){
if(f.isDirectory()){
//是目录
File[] file =f.listFiles();
if(file!=null){
for(int i = 0;i<=file.length-1;i++){
printFile(file[i]);
}
}
}else{
//是文件
System.out.println(f);
}
}
}
public static void main(String[] args) {
File f = new File("D:\\abc");
printFile(f);
}
}
二、 理解流的概念、掌握IO流的分类
Java中IO流是实现输入输出的基础,它可以完成数据的输入输出操作。
流:数据从数据源(文件)到程序(内存)之间经历的路径
IO流分类:
1. 按照数据流动方向:输入流、输出流
输入流:数据从数据源到程序(InputStream、Reader)
输出流:数据从程序到数据源(OutputStream、Writer)
2. 按照读取的方式分类:字节流、字符流
字节流:读写二进制文件(一次读写一个字节8位二进制)(InputStream、OutputStream)
字符流:读写文本文件(一次读写一个字符16位二进制) (Reader、Writer)
3. 按照流的角色分类:节点流、处理流
节点流(低级流):直接连接在数据源上的流
处理流(高级流):包装在节点外面的流
三、 IO流中常用类
IO流中抽象基类:
InputStream(字节输入流)
OutputStream(字节输出流)
Reader(字符输入流)
Writer(字符输出流)
IO流常用类:
★FileInputStream文件输入流
▲常用构造方法
FileInputStream |
FileInputStream |
▲常用方法
| read |
| read |
案例一:字节输入流FileInputStream
/**
* 使用FileInputStream读取G:\abc\1.txt中的内容
*/
package anli;
import java.io.*;
public class Test1 {
FileInputStream fis ;
public Test1(){
try {
//1.在指定的文件上创建一个流
fis = new FileInputStream("G:\\abc\\1.txt");
//2.读
byte[] b = new byte[1024];
int x = 0;//x保存读取的数据长度
while((x=fis.read(b))>0){
String s = new String(b,0,x);//将byte数组中的值转化成字符串并输出
System.out.println(s);
}
fis.close();//关闭流
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String[] args) {
Test1 t = new Test1();
}
}
★FileOutputStream文件输出流
▲常用构造方法
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。 |
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。 |
▲常用方法
| write |
| write |
| write |
案例二:字节输出流FileOutputStream
/**
* 使用FileOutputStream往G:\abc\2.txt中写入内容
*/
package anli;
import java.io.*;
public class Test2 {
FileOutputStream fos;
public Test2(){
try {
//1.在指定文件上创建输出流
fos = new FileOutputStream("G:\\abc\\2.txt");
//2.写入数据
String s = "静夜思\r\n床前明月光\r\n疑是地上霜\r\n";
byte[] b = s.getBytes();
fos.write(b);
//3.关闭流
fos.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test2 t = new Test2();
}
}
案例三:文件拷贝(字节流可以拷贝任何文件 包括图片、视频…)
/**
* 使用FileInputStream和FileOutputStream完成文件拷贝
*/
package anli;
import java.io.*;
public class Test3 {
FileInputStream fis;
FileOutputStream fos;
public Test3(){
try {
//1.创建流
fis = new FileInputStream("G:\\abc\\1.txt");
fos = new FileOutputStream("G:\\abc\\2.txt");
//2.开始读、写
byte[] b = new byte[1024];
int x = 0;
while((x=fis.read(b))>0){//读
fos.write(b, 0, x);//写
}
//3.关闭流
fos.close();
fis.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test3 t = new Test3();
}
}
★FileReader文件输入流、字符流
▲常用构造方法
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader。 |
FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader。 |
▲常用方法
int | read() 读取单个字符。 |
int | read(char[] cbuf, int offset, int length) 将字符读入数组中的某一部分。 |
案例四:字符输入流FileReader
/**
* 使用FileReader读取G:\abc\1.txt中的内容
*/
package anli;
import java.io.*;
public class Test4 {
FileReader fr ;
public Test4(){
try {
//1.在指定的文件上创建一个流
fr = new FileReader("G:\\abc\\1.txt");
//2.读
char[] c = new char[1024];
int x = 0;//x保存读取的数据长度
while((x=fr.read(c))>0){
String s = new String(c,0,x);//将byte数组中的值转化成字符串并输出
System.out.println(s);
}
fr.close();//关闭流
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String[] args) {
Test4 t = new Test4();
}
}
★FileWriter文件输出流、字符流
▲常用构造方法
FileWriter |
FileWriter |
FileWriter |
FileWriter |
▲常用方法
abstract void | flush() 刷新该流的缓冲。 |
void | write(char[] cbuf) 写入字符数组。 |
abstract void | write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 |
void | write(int c) 写入单个字符。 |
void | |
void |
案例五:字符输出流FileWriter
/**
* 使用FileWriter往G:\abc\2.txt中写入内容
*/
package anli;
import java.io.*;
public class Test5 {
FileWriter fw;
public Test5(){
try {
//1.在指定文件上创建输出流
fw = new FileWriter("G:\\abc\\2.txt");
//2.写入数据
fw.write("静夜思\r\n床前明月光\r\n疑是地上霜\r\n");
//3.关闭流
fw.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test5 t = new Test5();
}
}
案例六:文件拷贝(字符流只能拷贝文本文件)
/**
* 使用FileReader和FileWriter完成文件拷贝
*/
package anli;
import java.io.*;
public class Test6 {
FileReader fr;
FileWriter fw;
public Test6(){
try {
//1.创建流
fr = new FileReader("G:\\abc\\1.txt");
fw = new FileWriter("G:\\abc\\2.txt");
//2.开始读、写
char[] c = new char[1024];
int x = 0;
while((x=fr.read(c))>0){//读
fw.write(c, 0, x);//写
}
//3.关闭流
fw.close();
fr.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test6 t = new Test6();
}
}
总结:
1. 拷贝文件时,字节流可以拷贝任何文件,字符流只能拷贝文本文件
2. 读取文本文件时,只能使用字符流
四、 处理流
一般情况下,节点流可以完成任务,但是在有些特殊情况下,节点流无法满足要求,此时可以考虑使用处理流。使用处理流可以方便达到目的. 常用处理流:
1. 打印流:PrintStream、PrintWriter(常用)
2. 缓冲流:BufferedReader
3. 转换流:InputStreamReader
★打印流:
案例一: PrintStream
/**
* PrintStream 打印流可以包装OutputStream
*/
package test6_8;
import java.io.*;
public class Test1 {
FileOutputStream fos;
PrintStream ps;
public Test1(){
try {
//1.创建流对象
fos = new FileOutputStream("G:\\abc\\1.txt");//在1.txt上创建输出流
ps = new PrintStream(fos);//将fos流包装成ps流
//2.写数据
ps.println("你好!");
ps.println("大家好!");
ps.print("才是真的好!");
//3.关闭流
ps.close();
fos.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test1 t = new Test1();
}
}
案例二: PrintWriter
/**
* PrintWriter 打印流可以包装OutputStream、Writer
*/
package test6_8;
import java.io.*;
public class Test2 {
FileWriter fw;
PrintWriter pw;
public Test2(){
try {
//1.创建流
fw = new FileWriter("G:\\abc\\2.txt",true);//在2.txt上创建流
pw = new PrintWriter(fw,true);//将fw包装到pw中 true自动刷新
//2.写数据
pw.println("Hello Wolrd!");
pw.println("你好!");
pw.println("Wlecom tokaifeng!");
//3.关闭流
pw.close();
fw.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test2 t = new Test2();
}
}
★缓冲流:
案例一:BufferedReader
/**
* BufferedReader缓冲流包装Reader
*/
package test6_8;
import java.io.*;
public class Test3 {
FileReader fr;
BufferedReader br;
public Test3(){
try {
//1.创建流对象
fr = new FileReader("G:\\abc\\2.txt");
br = new BufferedReader(fr);
//2.读数据
String s;
while((s=br.readLine())!=null){
System.out.println(s);
}
//3.关闭流
br.close();
fr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Test3 t = new Test3();
}
}
★转换流: InputStreamReader
/**
* InputStreamReader是字节流通向字符流的桥梁
*/
package test6_8;
import java.io.*;
public class Test4 {
FileInputStream fis ;
InputStreamReader isr;
public Test4(){
try {
//1.在指定的文件上创建一个流
fis = new FileInputStream("G:\\abc\\1.txt");
isr = new InputStreamReader(fis);//在fis包装成InputStreamReader字符流,可以正常读写文本文件
//2.读
char[] c = new char[1024];
int x = 0;//x保存读取的数据长度
while((x=isr.read(c))>0){
String s = new String(c,0,x);//将byte数组中的值转化成字符串并输出
System.out.println(s);
}
isr.close();
fis.close();//关闭流
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
public static void main(String[] args) {
Test4 t = new Test4();
}
}
综合案例一:万能拷贝
package test6_9;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyCopy extends JFrame implements ActionListener{
//1.定义组件
JLabel title,l1,l2;
JTextField jtf1,jtf2;
JButton b1,b2;
JPanel jp1,jp2,jp3;
public MyCopy(){
//2.初始化组件
title = new JLabel("万能拷贝器",JLabel.CENTER);
l1 = new JLabel("原始文件地址:");
l2 = new JLabel("目标文件地址:");
jtf1 = new JTextField(20);
jtf2 = new JTextField(20);
b1 = new JButton("拷贝");
b2 = new JButton("取消");
jp1 = new JPanel();
jp2 = new JPanel();
jp3 = new JPanel();
//3.添加组件
this.setLayout(new GridLayout(4,1,0,20));
this.add(title);//第一行
jp1.add(l1);
jp1.add(jtf1);
this.add(jp1);//第二行
jp2.add(l2);
jp2.add(jtf2);
this.add(jp2);//第三行
jp3.add(b1);
jp3.add(b2);
this.add(jp3);//第四行
//4.设置
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
b1.addActionListener(this);//注册监听
b2.addActionListener(this);//注册监听
}
public static void main(String[] args) {
MyCopy mc = new MyCopy();
}
@Override
public voidactionPerformed(ActionEvent e) {
if(e.getSource()==b1){
//拷贝
String s1 = jtf1.getText();
String s2 = jtf2.getText();
try {
//1.创建流
FileInputStream fis = new FileInputStream(s1);//在s1指定的地址上创建输入流
FileOutputStream fos = new FileOutputStream(s2);//在s3指定的地址上创建输出流
//2.读写数据
byte[] b = new byte[1024];
int x = 0;
while((x=fis.read(b))>0){
fos.write(b, 0, x);
}
JOptionPane.showMessageDialog(this, "拷贝成功!");
//3.关闭流
fos.close();
fis.close();
} catch (Exception e1) {
// TODO Auto-generated catchblock
e1.printStackTrace();
}
}else if(e.getSource()==b2){
//取消
jtf1.setText("");
jtf2.setText("");
}
}
}
五、 System类对IO流的支持
| err “标准”错误输出流。 默认输出到屏幕 |
| in “标准”输入流。 默认从键盘读取 |
| out “标准”输出流。 默认输出到屏幕 |
标准重定向:
系统默认指定了输出和输入的往位置,但是我们可以通过如下方法来修改:
| setErr |
| setIn |
| setOut |
案例一:
/**
* setOut() 标准输出重定向
*/
package test_12_11;
import java.io.*;
public class Test5 {
public static void main(String[] args) throws Exception {
PrintStream ps = new PrintStream("D:\\abc\\2.txt");
System.setOut(ps);//标准输出流重定向
System.out.println("Hello World!");
System.out.println("大家好!");
}
}
案例二:
/**
* setErr()标准错误输出流重定向
*/
package test_12_11;
import java.io.*;
import java.util.Date;
public class Test6 {
PrintStream ps;
FileOutputStream fis;
public Test6(){
try {
fis = new FileOutputStream("D:\\abc\\log.txt",true);
ps = new PrintStream(fis);
int i = 10/0;
} catch (Exception e) {
System.setErr(ps);//标注错误输出流重定向
Date d = new Date();
System.err.println(d.toLocaleString()+" "+e);
}
}
public static void main(String[] args) {
new Test6();
}
}
案例三:标准键盘输入
package test_12_11;
import java.io.*;
public class Test7 {
public static void main(String[] args) throws Exception {
int a,b,sum;
InputStreamReader isr = new InputStreamReader(System.in);//System.in是字节输入流将他转化成字符输入流 isr
BufferedReader br = new BufferedReader(isr);
System.out.println("请输入a的值:");
a = Integer.parseInt(br.readLine());
System.out.println("请输入b的值:");
b = Integer.parseInt(br.readLine());
sum = a+b;
System.out.println(a+"+"+b+"="+sum);
}
}
六、 对象流
对象流用来传输对象,要想实现对象的传输必须首先将对象实现序列化。
对象序列化的目的是将对象保存到磁盘中,或实现网络中直接传输对象,对象序列化机制允许将内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点。其他程序一旦获得了这种二进制流(无论是从磁盘获取还是从网络获取),都可以将这种二进制流恢复成原来的Java对象
如果要让某个对象支持序列化,必须让他的类是可序列化的,为了让某个类可序列化,该类必须实现如下两个接口中的一个:
Serializable(推荐)
Externalizable
Java中很多类已经实现类Serializable,该接口知识一个标记接口,实现该接口无需实现任何方法。
案例:
package test6_9;
import java.io.Serializable;
public class Dog implements Serializable{
public String name;
public String var;
public String color;
public int age;
public Dog() {
}
public Dog(String name, String var,String color, int age) {
this.name = name;
this.var = var;
this.color = color;
this.age = age;
}
public void tell(){
System.out.println("昵称:"+name+",品种:"+var+",颜色:"+color+",年龄:"+age);
}
}
/**
* 将对象写入到文件中
*/
package test6_9;
import java.io.*;
public class Test1 {
Dog d = new Dog("大黄","牧羊犬","黄色",3);
FileOutputStream fos;
ObjectOutputStream oos;
public Test1(){
try {
//1.创建对象
fos = new FileOutputStream("G:\\abc\\dog.txt");
oos = new ObjectOutputStream(fos);
//2.写数据
oos.writeObject(d);
//3.关闭流
oos.close();
fos.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test1 t = new Test1();
}
}
/**
* 从文件中读取对象
*/
package test6_9;
import java.io.*;
public class Test2 {
Dog d;
FileInputStream fis;
ObjectInputStream ois;
public Test2(){
try {
//1.创建流
fis = new FileInputStream("G:\\abc\\dog.txt");
ois = new ObjectInputStream(fis);
//2.读取数据
d = (Dog)ois.readObject();
System.out.println(d.name);
d.tell();
//3.关闭流
ois.close();
fis.close();
} catch (Exception e) {
// TODO Auto-generated catchblock
e.printStackTrace();
}
}
public static void main(String[] args) {
Test2 t = new Test2();
}
}