IO流
概念
IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的类都在IO包中
分类
字节流 : 字节流可以操作任何数据, 计算机任何数据都是按照字节存储的
字符流 : 字符流只能操作字符数据, 比较方便
常用父类
- 字节流的抽象父类
- InputStream
- OutputStream
- 字符流的抽象父类
- Reader
- Writer
操作流程
字节流
FileInputStrem
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class DemoFileInputStream {
public static void main(String[] args) throws IOException {
// demo1();
FileInputStream fis = new FileInputStream("Demo.txt");
int b ;
while((b = fis.read()) != -1) {
System.out.println(b);
}
fis.close();
}
private static void demo1() throws FileNotFoundException, IOException {
// 创建流对象
FileInputStream fis = new FileInputStream("Demo.txt");
// 从硬盘上读取一个字节
int x = fis.read();
int y = fis.read();
System.out.println(x);
System.out.println(y);
// 结束符是-1
// 关流释放资源
fis.close();
}
}
read()方法返回值为什么int
因为字节输入流可以操作任何类型的文件, 比如图片视频等,这些文件底层都是以二进制形式存储的,如果每次读取都返回byte,有可能读到中间时候遇到11111111,而11111111是byte类型-1,程序遇到-1就会终止,所以使用int, 如果在11111111在前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255这样就可以保证整个数据读完,而结束符-1就是int类型
10000001 byte类型-1的原码
11111110 byte类型-1的反码
11111111 byte类型-1的补码
OutputStream
import java.io.FileOutputStream;
import java.io.IOException;
public class DemoFileOutputStream {
public static void main(String[] args) throws IOException {
// 创建字节输出流,如果没有就自动创建,如果有就会将文件清空 true是否追加,
FileOutputStream fos = new FileOutputStream("Test.txt",true);
// 虽然写出的是int类型的数,但是到文件上的是一个字节,会自动取出前三个八位转换成byte类型
fos.write(97);
fos.write(98);
fos.write(99);
fos.close();
}
}
字节数组拷贝
available()方法
- int read(byte[] b): 一次读取一个字节数组
- write(byte [] b): 一次写入一个字节数组
- available() 获取文件中所有的字节个数
# 此方法在copy大文件时可能会导致内存溢出
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DemoCopy {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("Demo.txt");
FileOutputStream fos = new FileOutputStream("Copy.txt");
// 创建与文件相同大小的字节数组
byte [] arr = new byte[fis.available()];
fos.write(fis.read(arr));
fis.close();
fos.close();
System.out.println("Okay");
}
}
小数组copy
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DemoCopy {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("Demo.txt");
FileOutputStream fos = new FileOutputStream("Copy.txt");
// 创建与文件相同大小的字节数组
byte [] arr = new byte[1024 * 8];
int len;
// 如果没有加arr,返回的就不是字节个数,而是字节码标值
while((len = fis.read(arr)) != -1) {
fos.write(arr, 0, len);
}
fis.close();
fos.close();
System.out.println("Okay");
}
}
缓冲区Copy
缓冲区:
- 字节流一次读写一个数组的速度明显快于一个字节的读写速度
- 8192字节
BufferedInputStream
- BufferedInputStream内置了一个缓冲区(数组)
- 从BufferedInputStream中读取一个字节时, BufferedInputStream会一次性性文件中读取8192个字节,存在缓冲区中,返回给程序
- 程序在读时,就不用找文件了,直接从缓冲区中获取
- 直到缓冲区中的字节都获取完,才从新从文件中读取8192个字节
BufferedOutputStream
- BufferedOutputStream内置了一个缓冲区(数组)
- 程序向流中写出字节时,不会直接写到文件, 先写到缓冲区中
- 直到缓冲区写满,BUfferedOutputStream才会把缓冲区中的数据一次性写到文件里
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DemoBufferCopy {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("Demo.txt");
FileOutputStream fos = new FileOutputStream("Copy.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int b ;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();
}
}
比较读写速度
- 小数组的读写速度快于Buffer
- 小数组操作一个数组
- Buffer操作两个数组
flush()方法
用来刷新缓冲区,刷新后再次写出
close()方法
用来关闭流释放资源的, 如果是带缓冲区的流对象的close()方法, 不但会关闭流,还会在关闭流之前刷新缓冲区,将缓冲区中的字节全部刷新到文件上去,关闭后不能再写入
中文问题
字节流读取中文
- 字节流在读中文的时候可能会读半个中文,造成乱码
字节流写出中文
- 字节流直接操作字节,所以写出中文必须将字符串转化成字节数组
- 写出回车换行write("\r\n".getBytes());
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DmeoChinese {
public static void main(String[] args) throws IOException {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("Demo.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("Copy.txt"));
){
byte[] arr = new byte[4];
int b;
while ((b = bis.read(arr)) != -1) {
System.out.println(new String(arr, 0, b));
}
bos.write("你好\r\n".getBytes());
}
}
}
图片加密
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DemoTest1 {
public static void main(String[] args) throws IOException {
try(
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy.png"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("demo.png"));
){
int b;
while((b = bis.read()) != -1) {
// 一个数异或两次就等于本身
bos.write(b ^ 123);
}
}
}
}
字符流
字符流可以直接读写字符的IO流
字符流读取字符,先读取到字节数据,然后转换成字符,如果要写字符,需要把字符转换成字节在写入
FileReader
FileReader类的read()方法可以按照字符大小读取
import java.io.FileReader;
import java.io.IOException;
public class DemoFileReader {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
int b;
while((b = fr.read()) != -1) {
System.out.println((char)b);
}
fr.close();
}
}
FileWrite
FileWrite类的write()方法可以自动把字符转为字节写入
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class DemoFileReader {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
FileWriter fw = new FileWriter("Copy.txt");
int b;
while((b = fr.read()) != -1) {
fw.write(b);
}
fr.close();
fw.close();
}
}
什么情况下使用字符流
字符流也可以拷贝文本文件,但是不推荐使用,因为读取时,会把字节转化为字符,写出时还要把字符转回字节
程序需要读取一段文本,或者需要写出一段文本时候可以使用字符流
读取的时候按照字符的大小进行读取, 不会出现半个中文的情况
写出的时候可以直接将字符串写出,不用转换成字节数组
字符流不可以拷贝非纯文本文件
自定义字符数组拷贝
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class DemoCopy {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("Demo.txt");
FileWriter fw = new FileWriter("Copy.txt");
char [] arr = new char[1024];
int len;
while((len = fr.read(arr)) != -1) {
fw.write(arr,0,len);
}
fr.close();
fw.close();
}
}
带缓冲的字符流
BufferedReader
read()方法
读取字符时,回一次读取若干个字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
readline()方法
可以读取一行字符(不包含换行符)
BufferWriter
writer()方法
写出字符时,会先写到缓冲区,缓冲区写满时才会写到文件,降低文件写入的次数,提高效率
newline()方法
可以输出一个跨平台的换行符号"\r\n"
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class DemoBuffer {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("Demo.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.txt"));
int c;
while((c = br.read()) != -1) {
bw.write(c);
}
br.close();
bw.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class DemoBuffer {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("Demo.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.txt"));
String line;
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
br.close();
bw.close();
}
}
LineNumberReader
LineNumberReader是Buffered对象的子类,具有相同的功能, 并且可以统计行号
方法
- 调用getLineNumber()方法可以获取当先行号
- 调用setLineNumber()方法可以设置当前行号
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class DemoLine {
public static void main(String[] args) throws IOException {
LineNumberReader lnr = new LineNumberReader(new FileReader("Demo.txt"));
String line;
while((line = lnr.readLine()) != null) {
System.out.println(lnr.getLineNumber() + " : " +line);
}
}
}
装饰设计模式
- 耦合性不强, 被装饰类的变化与装饰类的变化无关
public class DemoWarp {
public static void main(String[] args) {
Mephisto mephisto = new Mephisto(new Student());
mephisto.code();
}
}
interface Coder{
public void code();
}
class Student implements Coder{
@Override
public void code() {
System.out.println("javase");
System.out.println("javaweb");
}
}
class Mephisto implements Coder{
// 获取被装饰类的引用
private Student s;
// 在构造方法中传入被装饰类的对象
public Mephisto(Student s) {
super();
this.s = s;
}
// 对原有的功能进行升级
@Override
public void code() {
s.code();
System.out.println("ssh");
System.out.println("ssm");
System.out.println("DB");
System.out.println("soon");
}
}
序列流
可以把多个字节整合成一个, 从序列流中读取数据,将被整合的第一个流开始读, 读完一个之后继续读第二个,一次类推
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
public class DemoSequenceInputStream {
public static void main(String[] args) throws IOException {
SequenceInputStream sis = new SequenceInputStream(new FileInputStream("Demo1.txt"), new FileInputStream("Demo2.txt"));
int b;
FileOutputStream fos = new FileOutputStream("Copy.txt");
while((b = sis.read()) != -1) {
fos.write(b);
}
sis.close();
fos.close();
}
}
内存输出流
该输出流可以想内存中写数据,把内存当做缓冲区,写出之后可以一次性获取所有数据
使用方法
- 创建对象 new ByteArrayOutputStream()
- 写出数据 write(int), write(byte[])
- 获取数据 toByteArray()
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class DemoByteArrayOutputStream {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("Demo1.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte [] arr = new byte[5];
int len;
while ((len = fis.read(arr)) != -1) {
baos.write(arr,0,len);
}
// 将缓冲区的数据全部获取出来
System.out.println(baos);
fis.close();
}
}
随机访问流
概述
RandomAccessFile类不属于流,是Object类的子类,但是它融合了InputStream和OutputStream的功能
支持对随机访问文件的读写
方法
- read()
- write()
- seek()
import java.io.IOException;
import java.io.RandomAccessFile;
public class DemoRandomAccessFile {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("Demo1.txt", "rw");
// raf.write(97);
// int x = raf.read();
// System.out.println(x);
raf.seek(0);
raf.write(97);
raf.close();
}
}
对象操作流
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class DemoObjectOutputStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("Mephisto",18));
list.add(new Person("Mephist",18));
list.add(new Person("Mephis",18));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Demos.txt"));
for (Person person : list) {
oos.writeObject(person);
}
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Demo.txt"));
for (Person person : list) {
System.out.println(person);
}
ois.close();
}
}
数据输入输出流
DataInputStream , DataOutputStream可以按照数据类型的大小读写数据
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DemoDataStream {
public static void main(String[] args) throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream("Demo.txt"));
dos.writeInt(998);
dos.writeChars("你");
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("Demo.txt"));
int x = dis.readInt();
char y = dis.readChar();
System.out.println(x +" "+ y);
dis.close();
}
}
打印流
该流可以很方便的将对象toString()结果输出,并自动加上换行,而且可以使用自动刷出的模式
System.out就是一个PrintStream,其默认向控制台输出信息
PrintStream和PrintWrite分辨是打印字节流和字符流
只操作数据目的
标准输出输入流
System.in是InputStream,标准输入流, 默认可以从键盘输入读取字节数据
System.out是PrintStream,标准输出流, 默认可以想Console中输出字符和字节数据
修改标准输入输出流
- 修改标准输入流 System.setIn(InputerStream)
- 修改标准输出流 System.setOut(OutputStream)
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
public class DemoSystemInOut {
public static void main(String[] args) throws IOException {
// InputStream输入流只有一个
//InputStream is = System.in;
// int x = is.read();
// System.out.println(x);
System.setIn(new FileInputStream("Demo.txt"));
System.setOut(new PrintStream("Copy.txt"));
InputStream is = System.in;
PrintStream ps = System.out;
int b;
while((b = is.read()) != -1) {
ps.write(b);
}
is.close();
ps.close();
}
}
实现键盘录入两种方式
BufferedReader的readLine方法
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Scanner
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
public class DemoSystemIn {
public static void main(String[] args) throws IOException {
// BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// String line1 = br.readLine();
// System.out.println(line1);
// br.close();
Scanner scanner = new Scanner(System.in);
String line2 = scanner.nextLine();
System.out.println(line2);
scanner.close();
}
}
Properties
Properties类表示了一个持久的属性集
可以保存在流中或从流中加载
属性列表中每个键及其对应的值都是一个字符串
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Properties;
public class DemoProperties {
public static void main(String[] args) throws FileNotFoundException, IOException {
// demo1();
// demo2();
Properties properties = new Properties();
properties.load(new FileReader("demo.properties"));
Enumeration<String> enumeration = (Enumeration<String>) properties.propertyNames();
while(enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = properties.getProperty(key);
System.out.println(key + " : " + value);
}
// Properties properties1 = new Properties();
// properties1.store(new FileOutputStream("Demo.txt"),null);
// properties1.setProperty("name", "Mephisto");
}
private static void demo2() {
Properties properties = new Properties();
properties.setProperty("name", "Mephisto");
properties.setProperty("age", "18");
// System.out.println(properties);
Enumeration<String> enumeration = (Enumeration<String>) properties.propertyNames();
while(enumeration.hasMoreElements()) {
System.out.println(properties.getProperty(enumeration.nextElement()));
}
}
private static void demo1() {
Properties properties = new Properties();
properties.put("name", "Mephito");
System.out.println(properties);
}
}