Java I/O 对文件操作,其实是通过操作系统进行操作的。
File
统计文件数量及大小
package file;
import java.io.File;
// 统计某路径(文件夹)下文件数、文件夹数及所占空间大小(Byte)
public class DirCount {
private String path;
private File file;
private long len;
private long dirCount = -1; // 去掉文件夹自身
private long fileCount;
public DirCount(String path) {
this.path = path;
this.file = new File(path);
count(file, 0);
}
private void count(File file, int deep) {
for(int i = 0; i < deep; i++) {
System.out.print("-");
}
if(null == file || !file.exists()) {
return;
}else if(file.isDirectory()) {
dirCount++;
System.out.println(file.getAbsolutePath());
for(File dir : file.listFiles()) {
count(dir, deep+1);
}
}else {
System.out.println(file.getAbsolutePath());
len += file.length();
fileCount++;
}
}
public long getLen() {
return len;
}
public long getDirSize() {
return dirCount;
}
public long getFileSize() {
return fileCount;
}
public static void main(String[] args) {
DirCount dirCount = new DirCount(".");
System.out.println(dirCount.getLen());
System.out.println(dirCount.getDirSize());
System.out.println(dirCount.getFileSize());
}
}
字符集及编码解码
package file;
import java.io.UnsupportedEncodingException;
public class ContentDecode {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "您好a";
byte[] bytes = s.getBytes(); // 编码:字符 -> 字节
System.out.println(bytes.length); // 7 (UTF-8,汉字占 3 字节,数字和英文字母占 1 字节)
s = new String(bytes, 0, bytes.length, "UTF-8");
System.out.println(s); // 您好a
s = new String(bytes, 0, bytes.length-1, "UTF-8");
System.out.println(s); // 您好
s = new String(bytes, 0, bytes.length-2, "UTF-8");
System.out.println(s); // 乱码(字节数量有问题)
s = new String(bytes, 0, bytes.length-2, "UTF-16");
System.out.println(s); // 乱码(编码和解码所用字符集不一致)
}
}
I/O
I/O 标准步骤
四大 I/O 流类,InputStream/OutputStream(字节) + Reader/Writer(字符)
// 输入流
package io;
import java.io.*;
public class IOTest01 {
public static void main(String[] args) {
// 1. 找到源
File file = new File("xxx.txt"); // xxx.txt 在工程根目录下。。
InputStream inputStream = null;
try {
// 2. 选择流
inputStream = new FileInputStream(file);
// int tmp;
// // 3. 读/写
// while ((tmp = inputStream.read()) != -1) { // 每次读取 1 字节
// System.out.println((char)tmp);
// }
byte[] tmp = new byte[3]; // 每次读 3 字节
// 3. 读/写
while (inputStream.read(tmp) != -1) {
String s = new String(tmp, 0, tmp.length);
System.out.println(s);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != inputStream) {
// 4. 关闭(告诉操作系统可以关闭文件了)
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件拷贝(字节流)
package io;
import java.io.*;
public class IOTest02 {
public static void main(String[] args) {
File src = new File("src.txt"); // src.txt 在工程根目录下
File out = new File("out.txt");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(src);
outputStream = new FileOutputStream(out, true); // 是否是追加方式写
byte[] tmp = new byte[3]; // 这么读取中文,不同字符集编码方式可能乱码
while (inputStream.read(tmp) != -1) {
outputStream.write(tmp, 0, tmp.length);
}
outputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != outputStream) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件拷贝(字符流)
package io;
import java.io.*;
public class IOTest02 {
public static void main(String[] args) {
File src = new File("src.txt"); // src.txt 在工程根目录下
File out = new File("out.txt");
Reader reader = null;
Writer writer = null;
try {
reader = new FileReader(src);
writer = new FileWriter(out, true); // 是否是追加方式写
char[] tmp = new char[3]; // 不会中文乱码
while (reader.read(tmp) != -1) {
writer.write(tmp, 0, tmp.length);
}
writer.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(null != writer) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != reader) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ByteArrayOutputStream/ByteArrayInputStream
字节数组输入输出流是和内存打交道,不能直接读写文件,这有别于文件输入输出流(跟文件打交道,其实是通过操作系统)。
装饰器设计模式
package io;
/**
* 装饰器模式(以对原味咖啡加糖、加牛奶为例,方法增强)
* 1. 抽象组件
* 2. 具体组件(继承抽象组件)
* 3. 抽象装饰器(继承抽象组件,并组合进抽象组件)
* 4. 具体装饰器(继承抽象装饰器)
*/
abstract class Drink {
abstract String info();
abstract double cost();
}
class Coffee extends Drink {
public String info() {
return "原味咖啡";
}
public double cost() {
return 10.0;
}
}
abstract class Decorate extends Drink {
private Drink drink;
public Decorate(Drink drink) {
this.drink = drink;
}
@Override
public String info() {
return drink.info();
}
@Override
public double cost() {
return drink.cost();
}
}
class MilkCoffee extends Decorate {
public MilkCoffee(Drink drink) {
super(drink);
}
@Override
public String info() {
return super.info() + "加入牛奶";
}
@Override
public double cost() {
return super.cost() + 5.0;
}
}
class SugarCoffee extends Decorate {
public SugarCoffee(Drink drink) {
super(drink);
}
@Override
public String info() {
return super.info() + "加入蔗糖";
}
@Override
public double cost() {
return super.cost() + 3.0;
}
}
public class DecorateTest01 {
public static void main(String[] args) {
Drink coffee = new Coffee();
Drink milkCoffee = new MilkCoffee(coffee);
Drink sugarCoffee = new SugarCoffee(coffee);
System.out.println(coffee.info() + "---" + coffee.cost()); // 原味咖啡---10.0
System.out.println(milkCoffee.info() + "---" + milkCoffee.cost()); // 原味咖啡加入牛奶---15.0
System.out.println(sugarCoffee.info() + "---" + sugarCoffee.cost()); // 原味咖啡加入蔗糖---13.0
milkCoffee = new MilkCoffee(sugarCoffee);
System.out.println(milkCoffee.info() + "---" + milkCoffee.cost()); // 原味咖啡加入蔗糖加入牛奶---18.0
}
}
下图就是利用了装饰器设计模式的一些流类!装饰器流/处理流其实是对节点流的装饰。比如 BuffedInputStream 维护了一个缓冲区,搞一定数量的字节后一次性运输,提高性能。
一般地,BuffedInputStream 和 BuffedOutputStream 传入 InputStream 和 OutputStream 使用。BufferedReader 和 BufferedWriter 传入 Reader 和 Writer 使用。
转换流
InputStreamReader 和 OutputStreamWriter,把字节流转为字符流。如果字节流全是文本,就可用使用这两个转换流,再用缓冲字符流包一下,很方便。
package io;
import java.io.*;
public class ConvertTest01 {
public static void main(String[] args) {
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out))){
String s = "";
while (!(s = reader.readLine()).equals("exit") ) {
writer.write(s);
writer.newLine();
writer.flush(); // 强制刷新,不然由于输入的内容少,达不到缓冲区大小,就不会进行写操作
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package io;
import java.io.*;
import java.net.URL;
public class ConvertTest02 {
public static void main(String[] args) {
try (
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new URL("https://www.baidu.com").openStream(), "UTF-8"));
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("baidu.html"), "UTF-8"))){
String s = "";
while ((s = reader.readLine()) != null) {
writer.write(s);
writer.newLine();
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
数据流
DataInputStream 和 DataOutputStream,数据读写字节流,可以按照实际基本数据类型和字符串读写。
package io;
import java.io.*;
public class DataTest {
public static void main(String[] args) throws IOException{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(
new BufferedOutputStream(byteArrayOutputStream));
dataOutputStream.writeBoolean(true);
dataOutputStream.writeInt(123);
dataOutputStream.writeUTF("hello");
dataOutputStream.flush(); // 注意强制刷新下。。
byte[] bytes = byteArrayOutputStream.toByteArray();
DataInputStream dataInputStream = new DataInputStream(
new BufferedInputStream(
new ByteArrayInputStream(bytes)));
// 读取顺序和写入顺序要一致
boolean flag = dataInputStream.readBoolean();
int i = dataInputStream.readInt();
String s = dataInputStream.readUTF();
System.out.println(flag);
System.out.println(i);
System.out.println(s);
}
}
对象流
对象流类似数据流,比数据流更强大些,读写对象!
package io;
import java.io.*;
class Employee implements Serializable{
private String name;
private transient String pwd; // 该字段不序列化
public Employee(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "name=" + name + ", pwd=" + pwd;
}
}
public class ObjectTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 序列化(对象-->字节流)
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
new BufferedOutputStream(byteArrayOutputStream));
objectOutputStream.writeBoolean(true);
objectOutputStream.writeInt(123);
objectOutputStream.writeUTF("hello");
objectOutputStream.writeObject(new Employee("李明", "ming")); // 写入的对象要实现 Serializable
objectOutputStream.flush(); // 注意强制刷新下。。
byte[] bytes = byteArrayOutputStream.toByteArray();
// 反序列化(字节流-->对象)
ObjectInputStream objectInputStream = new ObjectInputStream(
new BufferedInputStream(
new ByteArrayInputStream(bytes)));
// 读取顺序和写入顺序要一致
boolean flag = objectInputStream.readBoolean();
int i = objectInputStream.readInt();
String s = objectInputStream.readUTF();
System.out.println(flag);
System.out.println(i);
System.out.println(s);
Object employee = objectInputStream.readObject();
if(employee instanceof Employee) {
Employee employeeObj = (Employee) employee;
System.out.println(employeeObj);
}
}
}