JavaIO流

八. IO流

  • I:输入(Input)、输入流(InputStream)

    O:输出(Output)、输出流(OutputStream)

  • IO流的分类:

    1. 按照流的方向分类:

      输入流:从硬盘读取到内存中(Input、Read)

      输出流:从内存写入到硬盘中(Output、Write)

    2. 按照读取数据方式不同分类:

      字节流:按字节方式读取数据,一次读取1字节byte(等同于一次读取8个二进制位)。这种流是万能的,可以读取任何类型的文件。

      字符流:按字符方式读取文件,一次读取一个字符。这种流只能读取文本文件,不能读取图片、音频、视频等文件。

      例如:读取文本文件内容"a字节se"。字节流第一次读取’a’字符,第二次读取’中’字符的一半,第三次读取’中’字符的另一半;字符流第一次读取’a’字符,第二次读取’中’字符。('a’字符在Windows系统中占1个字节,'中’字符在Windows系统中占2个字节)

  • Java中所有的流都在java.io.*;下

    java.io.InputStream 字节输入流

    java.io.OutputStream 字节输出流

    java.io.Reader 字符输入流

    java.io.Writer 字符输出流

    1. 以上四种都是抽象类,都实现了java.io.Closeable接口(可关闭的),都有close()方法。流是内存和硬盘间的通道,占用资源,用完后一定要关闭
    2. 所有输出流都实现了java.io.Flushable接口(可刷新的),都有flush()方法。输出流在最终输出后要调用flush()方法将通道中数据输出完/清空管道(不使用flush()方法可能导致数据丢失)

    类名以Stream结尾的都是字节流;以Reader/Writer结尾的都是字符流

  • java.io包下需要掌握的16个流:

    1. 文件专属:

      java.io.FileInputStream java.io.FileOutputStream

      java.io.FileReader java.io.FileWriter

    2. 转换流(将字节流转换为字符流):

      java.io.InputStreamReader java.io.OutputStreamWriter

    3. 缓冲流专属:

      java.io.BufferedReader java.io.BufferedWriter

      java.io.BufferedInputStream java.io.BufferedOutputStream

    4. 数据流专属:

      java.io.DataInputStream java.io.DataOutputStream

    5. 标准流专属:

      java.io.PrintWriter java.io.PrintStream

    6. 对象流专属:

      java.io.ObjectInputStream java.io.ObjectOutputStream

  • java.io.FileInputStream(掌握)

    int read()方法:返回下一个字节的ASC码,若为空返回-1

FileInputStream fis = null;
try {
	fis = new FileInputStream("C:\\Users\\10434\\Desktop\\a");	//绝对路径
	/*
	IDEA自动把路径中的“\“变成”\\“,也可以采用”/“:
	fis = new FileInputStream("C:/Users/10434/Desktop/a");
	文件a内容:abc
	*/
	for (int i = 0; i < 5; i++) {
		System.out.println(fis.read());		//输出97 98 99 -1 -1
	}
	/*改进版:
	int readData = 0;
	while ((readData = fis.read()) != -1){
		System.out.println(readData);
	}*/
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {		//read()方法的异常
	e.printStackTrace();
} finally {		//finally语句块确保流一定关闭
	if (fis != null) {		//避免空指针异常
		try {
			fis.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

int read(byte[] b)方法:返回读取到的字节数量。一次读取b.length个字节,减少硬盘和内存的交互,提高执行效率

//IDEA默认当前路径是工程Project的根
fis = new FileInputStream("VideoLearn/src/IO/tempFile");	//文件内容:abcdef
byte[] bytes = new byte[4];		//定义长度为4的byte数组
int readCount = fis.read(bytes);
System.out.println(readCount);		//输出4,第一次读取到4个字节
System.out.println(new String(bytes));		//abcd

readCount = fis.read(bytes);
System.out.println(readCount);		//输出2,第二次读取到2个字节
System.out.println(new String(bytes));		//efcd

readCount = fis.read(bytes);
System.out.println(readCount);		//输出-1,没有读取到任何字节返回-1
System.out.println(new String(bytes));		//efcd

//改进版:
public class FileInputStreamTest03 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        byte[] bytes = new byte[4];
        try {
            fis = new FileInputStream("VideoLearn/src/IO/tempFile");
            int readCount = 0;
            while ((readCount = fis.read(bytes)) != -1){
                System.out.print(new String(bytes, 0, readCount));	//构造方法
            }		//输出abcdef
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

int available()方法:返回流中剩余(没有读到的)字节数量

fis = new FileInputStream("VideoLearn/src/IO/tempFile");
/*System.out.println(fis.available());		//输出6
fis.read();
System.out.println(fis.available());*/		//输出5
//这种方法不适合大文件,因为byte[]数组不能太大
byte[] bytes = new byte[fis.available()];
fis.read(bytes);
System.out.println(new String(bytes));		//输出abcdef

long skip(long n)方法:跳过n个字节不读

fis.skip(2);		//前两个字节是97 98
System.out.println(fis.read());		//输出99
  • java.io.FileOutputStream(掌握)

    FileOutputStream(String name)构造方法:将原文件清空,然后写入

    void write(byte[] b)方法:将byte数组全部写出

FileOutputStream fos = null;
byte[] bytes = {97, 98, 99, 100};
try {
	fos = new FileOutputStream("VideoLearn/src/IO/tempFile0");
	fos.write(bytes);		//abcd
	fos.write(bytes, 0, 2);		//abcdab
	fos.flush();		//输出流一定要刷新
}//catch语句块、finally语句块

FileOutputStream(String name, boolean append)构造方法:在原文件末尾追加写入,不清空原有内容

fos = new FileOutputStream("VideoLearn/src/IO/tempFile0", true);
fos.write(bytes);		//abcd
fos.write(bytes);		//abcdabcd
String s = "查拉图斯特拉如是说";
byte[] bs = s.getBytes();		//转换为byte数组
fos.write(bs);		//abcdabcd查拉图斯特拉如是说
  • 文件拷贝
FileInputStream fis = null;
FileOutputStream fos = null;
byte[] bytes = new byte[1024 * 1024];
try {
	fis = new FileInputStream("C:\\Users\\10434\\Desktop/full_site.zip");
	fos = new FileOutputStream("D:/full_site.zip");
	int readCount = 0;
	while ((readCount = fis.read(bytes)) != -1){
		fos.write(bytes, 0, readCount);		//边读边写
	}
	fos.flush();
}	//catch语句块、finally语句块中两个close()方法要分别try/catch
  • java.io.FileReader

    与FileInputStream相同,把byte数组换成char数组

FileReader fr = null;
try {
	fr = new FileReader("VideoLearn/src/IO/tempFile");
	char[] chars = new char[4];
	int readCount = 0;
	while ((readCount = fr.read(chars)) != -1){
		for (char c : chars){
			System.out.print(new String(chars, 0, readCount));		//abcdef
		}
	}
}
  • java.io.FileWriter

    void write(char[] cbuf)方法:写入一个char数组

    void write(String str)方法:写入一个字符串

FileWriter fw = null;
try {
	fw = new FileWriter("VideoLearn/src/IO/tempFile1", true);
	char[] chars = {'非', '常', '棒'};
	String s = "realheisenberg";
	fw.write(chars);
	fw.write(s);
}
  • 字符流文件复制与字节流相同

  • BufferedReader

    带有缓冲区的字符输入流,不需要定义char/byte数组

    BufferedReader(Reader in)构造方法

    String readLine()方法:读一行(不包括换行符)

/*
一个流的构造方法需要传入的流称为节点流
外部负责包装的流称为包装流/处理流
以下程序FileReader是节点流,BufferedReader是包装流
*/
FileReader fr = new FileReader("tempFile");
BufferedReader br = new BufferedReader(fr);
String s;
while ((s = br.readLine()) != null){		//判断条件是String不为空
	System.out.println(s);
}
br.close();		//只需要关闭最外层流,里面的节点流会自动关闭
  • InputStreamReader:将字符流转换为字节流
FileInputStream fis = new FileInputStream("tempFile");
//fis是节点流,isr是包装流
InputStreamReader isr = new InputStreamReader(fis);
//isr是节点流,br是包装流
BufferedReader br = new BufferedReader(isr);
//或者写成:
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("tempFile")));
br.close();		//只需要关闭最外层
  • BufferedWriter

BufferedWriter bf = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("newFile", true)));
bf.write("excited!");
bf.write("\n");
bf.write("吼哇!");
bf.flush();
  • DataOutputStream、DataInputStream

    DataOutputStream:将数据和数据的类型一并写入文件(这个文件不是普通文本文档)

    DataOutputStream写的文件只能用DataInputStream去读,且读取的顺序必须和写入的顺序相同

//构造方法必须传OutputStream类型
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));
byte b = 20;
short s = 50;
int i = 100;
//long、float、double、boolean、char同理
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.flush();
dos.close();

DataInputStream dis = new DataInputStream(new FileInputStream("data"));
byte b = dis.readByte();
short s = dis.readShort();
int i = dis.readInt();
System.out.println(b);
System.out.println(s);
System.out.println(i);
dis.close();
  • PrintStream(掌握)

    标准字节输出流,默认输出到控制台

PrintStream ps = System.out;
ps.println("hello world");		//标准输出流不需要手动关闭
//等同于System.out.println("hello world");

//改变输出方向
PrintStream printStream = new PrintStream(new FileOutputStream("log"));
System.setOut(printStream);
System.out.println("heisenberg");		//输出到“log”文件

public class LogTest01 {
    public static void main(String[] args) {
        MyLog myLog = new MyLog("用户登录");
    }
}

class MyLog{		//日志工具
    public MyLog(String s){
        try {
            PrintStream out = new PrintStream(new FileOutputStream("myLog", true));
            System.setOut(out);
            Date myTime = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
            String strTime = sdf.format(myTime);
            System.out.println(strTime + ":" + s);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • java.io.File

    1. File类和四大家族没有关系,File类不能完成文件的读写
    2. File对象是文件和路径名的抽象表示形式:一个File对象对应的可能是目录,也可能是文件
File file = new File("D:/MyFile");
System.out.println(file.exists());		//false,判断文件/路径是否存在
if (!file.exists()){
	file.createNewFile();		//创建文件,需要try/catch
}
if (!file.exists()){
	file.mkdir();		//创建目录
}

File newFile = new File("D:/real/heisenberg");
if (!newFile.exists()){
	newFile.mkdirs();		//创建多重目录
}

File file = new File("E:/git/Git/bin");
String parent = file.getParent();
System.out.println(parent);		//E:\git\Git

File file1 = file.getParentFile();		//获取上级目录
System.out.println(file1.getAbsolutePath());		//E:\git\Git,获取绝对路径

File file0 = new File("log");
System.out.println(file0.getParent());		//null
System.out.println(file0.getAbsolutePath());		//D:\javase\log

String getName()方法:获取文件名

boolean isDirectory()方法:判断是否为目录

boolean isFile()方法:判断是否为文件

long lastModified()方法:返回文件上次被修改的时间(从1970到当时的毫秒数)

long length()方法:获取文件大小(字节)

File f1 = new File("E:/照片/5班.jpg");
System.out.println(f1.getName());		//5班.jpg

System.out.println(f1.isDirectory());		//false
System.out.println(f1.isFile());		//true

long seconds = f1.lastModified();
Date time = new Date(seconds);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println(sdf.format(time));		//2015-05-30 12:39:21 770

System.out.println(f1.length());		//3190573

File[] listFiles()方法:获取目录下的所有文件

File f2 = new File("E:/音乐");
File[] files = f2.listFiles();
for (File f : files){
	//System.out.println(f.getName());		输出所有文件名
	System.out.println(f.getAbsolutePath());		//输出所有绝对路径
}
  • 作业:拷贝文件夹及其中所有内容
public class CopyTest {
    public static void main(String[] args) {
        File oldFile = new File("E:/照片");
        File newFile = new File("D:/");
        copy(oldFile, newFile);
    }

    private static void copy(File oldFile, File newFile) {
        if (oldFile.isFile()){
            FileInputStream in = null;
            FileOutputStream out = null;
            byte[] bytes = new byte[1024 * 1024];
            int count = 0;
            try {
                in = new FileInputStream(oldFile);
                String path = newFile.getAbsolutePath() + oldFile.getAbsolutePath().substring(3);		//截取字符串获取新路径
                out = new FileOutputStream(path);
                while ((count = in.read(bytes)) != -1){
                    out.write(bytes, 0, count);
                }
                out.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (in != null){
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null){
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return;
        }

        File[] files = oldFile.listFiles();
        for (File file : files){
            if (file.isDirectory()) {
                String s = newFile.getAbsolutePath() + file.getAbsolutePath().substring(3);
                File tempFile = new File(s);
                if (!tempFile.exists()) {
                    tempFile.mkdirs();
                }
            }
            copy(file, newFile);
        }
    }
}
  • ObjectInputStream、ObjectOutputStream

    1. 序列化和反序列化

      序列化:Serialize,将java对象拆分存储为硬盘文件

      反序列化:DeSerialize,将硬盘数据恢复为java对象

    2. 参与序列化和反序列化的对象必须实现Serializable接口

    3. Serializable接口是一个标志接口(接口中没有代码),java虚拟机看到这个接口会自动生成一个序列化版本号

public class Student implements Serializable {
	private int no;
	private String name;
	//set、get方法,无参、有参构造,toString
}
public class StudentTest {
    public static void main(String[] args) throws Exception{
        Student s1 = new Student(256, "zhangsan");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Students"));
        oos.writeObject(s1);
        oos.flush();
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Students"));
        Object obj = ois.readObject();
        System.out.println(obj);		//调用toString方法
        ois.close();
    }
}
  • 一次序列化多个对象:必须使用集合,否则序列化第二个对象时会报错

    参与序列化的集合及集合中的元素都要实现Serializable接口

List<Student> list = new ArrayList<>();
list.add(new Student(1, "zhangsan"));
list.add(new Student(2, "lisi"));
list.add(new Student(3, "heisenberg"));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Students"));
oos.writeObject(list);
oos.flush();
oos.close();

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Students"));
Object obj = ois.readObject();
List<Student> studentList = (List<Student>)obj;
for (Student s : studentList){
	System.out.println(s);
}
ois.close();
  • transient关键字:表示游离的,不参与序列化
private transient String name;		//表示name不参加序列化操作
//再执行上述程序,输出的name属性均为null
  • 序列化版本号

    当某个类的源代码改动后生成新的字节码文件,class文件再运行时java虚拟机生成的序列化版本号也会改变

    java中区分类的机制:先对比类名,类名相同时对比序列化版本号

    自动生成的序列化版本号缺点:修改代码会导致版本号改变(java虚拟机会认为是两个不同的类)

    实现Serializable接口的类建议提供一个固定的序列化版本号

//例如在Student类中增加属性age再进行ObjectInputStream,编译报错:java.io.InvalidClassException
private static final long serialVersionUID = 1L;		//规定版本号
  • IO+Properties的联合使用

    经常改变的数据单独写到文件中,使用程序动态读取,这样不需要修改代码

    这种文件称为配置文件,且当配置文件内容格式为key=value时(也可以使用“:”,等号左右最好不要有空格),称为属性配置文件

    java规范中建议:属性配置文件以.properties结尾(不是必须的)

    属性配置文件中使用井号“#”注释,key或value重复时自动覆盖

FileInputStream in = new FileInputStream("VideoLearn/src/IO/UserInfo");
//文件内容:username=heisenberg	password=123
Properties pro = new Properties();
pro.load(in);		//将文件数据加载到Map集合中,等号左边是key,右边是value
String username = pro.getProperty("username");
System.out.println(username);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值