欢迎光临我的博客[http://poetize.cn],前端使用Vue2,聊天室使用Vue3,后台使用Spring Boot
I/O操作
流的作用:为数据源和目的地建立一个输送通道。
磁盘操作:File
File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。
字节操作:InputStream 和 OutputStream(二进制格式操作)
InputStream:抽象类。基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
OutputStream:抽象类。基于字节的输出操作,是所有输出流的父类。定义了所有输出流都具有的共同特征。
字符操作:Reader 和 Writer(文件格式操作)
Reader:抽象类,基于字符的输入操作。
Writer:抽象类,基于字符的输出操作。
RandomAccessFile(随机文件操作):
一个独立的类,直接继承至Object.它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。
对象操作:Serializable
网络操作:Socket
新的输入/输出:NIO
I/O流分类
根据处理数据类型的不同分为:字符流和字节流
据数据流向不同分为:输入流和输出流
按数据来源(去向)分类:
1、File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter
2、byte[]: ByteArrayInputStream, ByteArrayOutputStream
3、Char[]: CharArrayReader,CharArrayWriter
4、管道操作: PipedInputStream、PipedOutputStream、PipedReader、PipedWriter
5、网络数据流: InputStream,OutputStream, Reader, Writer
6、打印:PrintStream、PrintWriter
7、对象序列化反序列化:ObjectInputStream、ObjectOutputStream
8、转换:InputStreamReader、OutputStreWriter
9、缓冲操作:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
字符流与字节流
字节流读取单个字节,字符流读取单个字符。
字符转字节:
byte[] bytes = string.getBytes("UTF-8");
字节转字符:
String s = new String(byte bytes[], int offset, int length, String charsetName);
字符流使用了缓冲区(关闭字符流时会强制性地将缓冲区中的内容进行输出),而字节流没有使用缓冲区(不关闭字节流也可以输出):
在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区暂存数据。
如果想在不关闭时也可以将字符流的内容全部输出,则可以使用Writer类中的flush()方法完成。
InputStreamReader(InputStream in):将字节流以字符流输入。
OutputStreamWriter(OutputStream out):将字节流以字符流输出。
将UTF-8转换成GBK:
FileInputStream fis = new FileInputStream("a.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
FileOutputStream fos = new FileOutputStream("b.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。
编码与解码:
编码就是把字符转换为字节,而解码是把字节重新组合成字符。
char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。
Reader 与 Writer:
不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。
但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。
逐行读取文件内容
public static void readFileContent(String filePath) throws IOException {
FileReader fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
// 装饰者模式使得 BufferedReader 组合了一个 Reader 对象
// 在调用 BufferedReader 的 close() 方法时会去调用 Reader 的 close() 方法
// 因此只要一个 close() 调用即可
bufferedReader.close();
}
装饰(Decorator)设计模式
在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。
适用性:
需要扩展一个类的功能,或给一个类添加附加职责。
当不能采用生成子类来实现,比如final类。
装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类:
FileInputStream fileInputStream = new FileInputStream(filePath);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
抽象构件(Component)
public abstract class InputStream implements Closeable {
public abstract int read() throws IOException;
}
具体构件(Concrete Component)
public class FileInputStream extends InputStream {
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;
}
抽象装饰(Decorator):内部一定要有一个指向具体构件对象的引用
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
}
具体装饰(ConcreteDecorator):添加附加的责任
public class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
protected volatile byte[] buf;
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
文件复制
public class CopyTest {
public static void main(String[] args) throws IOException {
File file1 = new File("E:\\PE系统.rar");
File file2 = new File("E:\\PE系统_copy.rar");
copyTest(file1, file2);
}
private static void copyTest(File file1, File file2) throws IOException {
FileInputStream fis = null; //输入流
FileOutputStream fos = null; //输出流
try {
fis = new FileInputStream(file1);
fos = new FileOutputStream(file2);
//创建缓冲流,提高字节流的操作效率
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int len = 0;
byte[] bytes = new byte[4096];
//返回 -1 的时候表示读到 eof,即文件尾
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
bos.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fis != null)
fis.close();
if (fos != null)
fos.close();
}
}
}
文件分割
class SplitFile {
public static void main(String[] args) throws Exception {
/**
* 文件切割器
*/
File file = new File("E:\\1.mp4");
File desFile = new File("E:\\split");
if (!desFile.exists()) {
desFile.mkdirs();
}
splitFile(file, desFile);
}
private static void splitFile(File file, File desFile) throws IOException {
// 读取源文件
FileInputStream fis = new FileInputStream(file);
// 创建目的(输出流每生成一个文件都需要重新构建一下)
FileOutputStream fos = null;
// 创建一个缓冲区
byte[] by = new byte[1024 * 1024 * 10];
int len = 0;
int count = 0;
while ((len = fis.read(by)) != -1) {
// 创建输出流,明确写入文件的对象
File partFile = new File(desFile, (++count) + ".part");
fos = new FileOutputStream(partFile);
fos.write(by, 0, len);
fos.close();
}
// 创建配置文件
File configFile = new File(desFile, "config.properties");
if (!configFile.exists()) configFile.createNewFile();
// 载入配置文件
Properties pro = new Properties();
pro.setProperty("partcount", Integer.toString(count));
pro.setProperty("filename", file.getAbsolutePath());
//输出配置文件
fos = new FileOutputStream(configFile);
pro.store(fos, "切割文件配置信息");
fos.close();
fis.close();
}
}
压缩文件
class ZipTest {
public static void main(String[] args) throws IOException {
File file1 = new File("E:\\表白神器1"); //源文件
String path = file1.getName(); //源文件名
File file2 = new File("E:\\表白神器.zip"); //目的文件
//打开输出流
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(file2));
//需要用到递归
zip(zos, file1, path);
zos.close();
}
private static void zip(ZipOutputStream zos, File file1, String path) throws IOException {
if (file1.isDirectory()) {
File[] filer = file1.listFiles(); //当前目录下的所有文件or目录
if (filer.length == 0) { //当前目录是空目录
zos.putNextEntry(new ZipEntry(path + "/")); //如果文件夹为空,则只需写入一个目录
} else { //如果文件夹不为空,则递归调用,文件夹中的每一个文件(或文件夹)进行压缩
for (File f : filer) {
zip(zos, f, path + "/" + f.getName());
System.out.println(path + "/" + f.getName());
}
}
} else {
zos.putNextEntry(new ZipEntry(path)); //写入文件(表白神器/新建文件夹/表白神器.TXT)
//打开输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file1));
//输出到Zip文件
byte[] by = new byte[1024];
int len;
while ((len = bis.read(by)) != -1) {
zos.write(by, 0, len);
zos.flush();
}
bis.close();
}
}
}
对象操作
序列化:
序列化就是将一个对象转换成字节序列,方便存储和传输。
序列化:ObjectOutputStream.writeObject()
反序列化:ObjectInputStream.readObject()
不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。
Serializable:
序列化的类需要实现 Serializable 接口。
transient 关键字可以使一些属性不会被序列化。
ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,
因此就不需要所有的内容都被序列化。
通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
private transient Object[] elementData;
网络操作
InetAddress:用于表示网络上的硬件资源,即 IP 地址
没有公有的构造函数,只能通过静态方法来创建实例:
InetAddress.getByName(String host);
InetAddress.getByAddress(byte[] address);
URL:统一资源定位符
Sockets:使用 TCP 协议实现网络通信
Datagram:使用 UDP 协议实现网络通信
URL:可以直接从 URL 中读取字节流数据。
public static void main(String[] args) throws IOException {
URL url = new URL("http://www.baidu.com");
/* 字节流 */
InputStream is = url.openStream();
/* 提供缓存功能 */
BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}