文件
文件是保存数据的地方
文件流:文件在程序中是以流的形式来操作的,流是指数据源(文件)和程序(内存)之间经历的路径
输入流:文件(磁盘) \(\rightarrow\)
Java程序(内存)
输出流:Java程序(内存) \(\rightarrow\) 文件(磁盘)
常用文件操作
1 2 3 4 5
| new File(String pathname) new File(File parent, String child) new File(String parent, String child) file.createNewFile();
|
1 2 3 4 5 6 7
| getName getAbsolutePath getParent length() exists isFile isDirectory
|
1 2 3
| //mkdir创建一级目录、mkdirs创建多级目录、delete删除空目录或文件 返回boolean 在java编程中,目录也被当做文件
|
IO流原理及流的分类
io流概览
Java IO 流原理
- I/O技术是非常实用的技术,用于处理数据传输。如读写文件,网络通讯等。
- Java 程序中,对于数据的输入、输出操作以“流(stream)”的方式进行。
java.io
包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
流的分类
- 按操作数据单位不同分为:字节流(8bit)二进制文件,字符流(按字符)文本文件
(一个汉字3个字节)
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:节点流,处理流/包装流
| 输入流 |
InputStream |
Reader |
| 输出流 |
OutputStream |
Writer |
Java 的 IO
流共涉及40多个类,实际上非常规则,都是如上4个抽象基类派生的,由这四个类派生出来的子类名称都是以其父类名作为子类名前缀。
1 2 3 4 5 6
| InputStream抽象类是所有 类字节输入流 的超类
InputStream常用子类: - FileInputStream:文件输入流 - BufferedInputStream:缓存字节输入流 (直接父类FilterInputStream) - ObjectInputStream:对象字节输入流
|
1 2 3 4 5 6 7 8 9
| 构造器 FileInputStream(File file) FileInputStream(FileDescriptor fdObj) FileInputStream(String name)
方法 read() 从此输入流中读取一个数据字节 read(byte[] b) 从此输入流中将最多b.length个字节的数据读入一个byte数组中 close() 关闭此文件输入流并释放于此流有关的所有系统资源
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Test public void readFile01() { int readDate = 0; FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream("./hhh.txt"); while ((readDate = fileInputStream.read()) != -1) { System.out.print((char)readDate); } } catch (IOException e) { e.printStackTrace(); } finally { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Test public void readFile02() { int readLen = 0; byte[] b = new byte[9]; FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream("./hhh.txt"); while ((readLen = fileInputStream.read(b)) != -1) { System.out.print(new String(b, 0, readLen)); }
} catch (IOException e) { e.printStackTrace(); } finally { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
FileOutputStream
1 2 3 4 5 6 7 8
| 构造器 FileOutputStream(File file) FileOutputStream(String name, boolean append)
方法 write(byte[] b) write(byte[] b, int off, int len) write(int b)
|
1 2
| String str = "hello, world!"; fileOutputStream.write(str.getBytes());
|
文件拷贝 = 输入流 + 输出流
FileReader 和 FileWriter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| FileReader 和 FileWriter 是字符流,即按照字符来操作io
FileReader方法 (直接父类InputStreamReader, 父类的父类Reader) new FileReader(File/String) read() read(char[]) new String(char[]) new String(char[], off, len) FileWriter方法 (直接父类OutputStreamWriter, 父类的父类Writer) new FileWriter(File/String) new FileWriter(File/String, true) write(int/char[]/string) String.toCharArray() 注意:FileWriter使用后必须close或flush, 否则写入不到指定文件!
|
节点流和处理流
- 节点流可以从一个特定的数据源读取数据,如
FileReader、 FileWriter
- 处理流也叫包装流,是连接在已存在的流(节点流或处理流)之上,为程序提供更为强大的读取功能,如
BufferedReader、BufferedWriter
BufferedReader 是 Reader 的子类,有属性
private Reader in;
,即可以封装一个节点流,该节点流可以是任意的,只要是 Reader
的子类;同理 BufferedWriter 也一样。
节点流和处理流
区别与联系
- 节点流是底层流/低级流,直接跟数据源相接。
- 处理流包装节点流既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
- 处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
处理流的功能主要体现在:
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.company.file;
public class BufferedReader_ { public static void main(String[] args) {
BufferReader_ bufferReader_ = new BufferReader_(new FileReader_());
bufferReader_.readFile(); BufferReader_ bufferReader_1 = new BufferReader_(new StringReader_());
} }
abstract class Reader_ { public void fileRead() {} public void stringRead() {} }
class FileReader_ extends Reader_{ public void fileRead() { System.out.println("读取文件~~~"); } }
class StringReader_ extends Reader_ { public void stringRead() { System.out.println("读取字符串~~~"); } }
class BufferReader_ extends Reader_ { private Reader_ reader_;
public BufferReader_(Reader_ reader_) { this.reader_ = reader_; }
public void readFile() { reader_.fileRead(); }
public void readFiles(int num) { while (num-- != 0) { reader_.fileRead(); } }
public void readStrings(int num) { while (num -- != 0) { reader_.stringRead(); } } }
|
处理流 BufferedReader
和 BufferedWriter
BufferedReader 和 BufferedWriter
属于字符流,是按照字符来读取数据的,关闭时,只需要关闭外层流即可。不要去操作二进制文件(音频,
视频, pdf, doc),可能造成文件损坏。
1 2 3 4 5 6 7 8 9 10 11 12 13
| BufferedReader bufferedReader = new BufferedReader(new FileReader()); String line; while ((line = bufferedReader.readLine()) != null) System.out.println(line);
bufferedReader.close();
BufferedWriter bw = new BufferedWriter(new FileWriter("./ok.txt")); bw.write("hello, world!"); bw.newLine(); bw.write("hello, java!"); bw.newLine(); bw.close();
|
BufferedInputtStream 是字节流,在创建
BufferedInputStream 时,会创建一个内部缓存区数组。
BufferedOutputStream
也是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每个字节写入调用底层系统。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class BufferedCopy { public static void main(String[] args) throws IOException { BufferedInputStream bis = null; BufferedOutputStream bos = null; int readLen = 0; byte[] b = new byte[1024]; try { bis = new BufferedInputStream(new FileInputStream("./demo.png")); bos = new BufferedOutputStream(new FileOutputStream("./tmp/demo1.png")); while ((readLen = bis.read(b)) != -1) { bos.write(b, 0, readLen); } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (bis != null) { bis.close(); } if (bos != null) { bos.close(); } } } }
|
能够将基本数据类型或者对象进行序列化和反序列化操作,ObjectInputStream
提供反序列化功能,ObjectOutputStream 提供序列化功能。
- 序列化就是在保存数据时,保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
1 2
| Serializable Externalizable
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| writeInt() writeBoolean() writeChar() writeDouble() writeUTF("你好") oos.writeObject(new Dog("kimi"));
...
Object dog = ois.readObject();
Dog dog2 = (Dog)dog; dog2.getName();
|
注意事项:
- 读写顺序要一致
- 要求序列化或反序列化对象,需要实现
Serializable
- 序列化的类中建议添加
SerialVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将里面所有属性都进行序列化,但除了
static 或 transient 修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化
标准输入输出流
System.in |
InputStream |
BufferedInputStream |
键盘 |
System.out |
PrintStream |
PrintStream |
显示器 |
字节流转成字符流:解决文件乱码问题
1 2
| InputStreamReader(InputStream, Charset) OutputStreamReader(OutputStream, Charset)
|
InputStreamReader: Reader 的子类,可以将
InputStream (字节流)包 装成Reader(字符流)
OutputStreamWriter: Writer 的子类,实现将
OutputStream (字节流) 包装成
Writer(字符流)
- 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
- 可以在使用时指定编码格式(比如utf 8, gbk,gb2312, IS08859-1等)
1 2 3 4 5 6 7 8 9 10 11
| InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk"); BufferedReader br = new BufferedReader(isr); String s = br.readLine(); System.out.println("读取内容: " + s); br.close();
String filePath = "./tmmp.txt"; String charSet = "utf-8"; OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet)); osw.write("你好"); osw.close();
|
打印流
PrintStream和PrintWriter
打印流只有输出流,没有输入流
字节流 PrintStream 的父类
OutputStream,字符流 PrintWriter 的父类
Writer
1 2 3 4 5 6 7
| PrintStream out = System.out; out.print("hello");
out.close();
System.setOut(new PrintStream("./tmp.txt")); System.out.println("hello!");
|
1 2 3 4
| PrintWriter pw = new PrintWriter(new FileWriter("./tmp.txt")); pw.print("hello, world"); pw.close();
|
Properties 类
配置文件的读取和写入
1 2 3 4 5 6 7 8
| 1. 专门用于读写配置文件的集合类 (Hashtable的子类) 格式:键=值 (键值对不需要有空格,值不需要用引号引起来,默认类型是String) 2. 常见方法 load: 加载配置文件的键值对到Properties对象 list: 将数据显示到指定设备/流对象 getProperty(key): 根据键获取值 setProperty(key, value): 设置键值对到Properties对象 store: 将Properties中的键值对存储到配置文件 (中文unicode)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Properties properties = new Peoperties();
properties.load(new FileReader("./sql.properties"));
properties.list(System.out);
properties.getProperty(xxx);
Properties properties1 = new Peoperties(); properties1.setProperty("user", "kuhn");
properties1.store(new FileOutputStream("./mysql.properties"), "注解");
|