I/O(Input/Output,输入/输出)可以实现数据的读取与写入操作,Java针对I/O操作的实现提供了java.io工具包,此包的核心组成由File类、InputStream类OutputStream类、Reader类、Writer类、Serializable接口。
File文件操作
java.io.File类是一个与文件本身操作有关的类,此类可以实现文件的创建、删除、重命名、取得文件大侠、修改日期等常见的系统文件操作。
File类文件基本操作方法
方法 | 类型 | 描述 |
---|---|---|
public File(String pathname) | 构造 | 给定一个要操作文件的完整路径 |
public File(File parent,String child) | 构造 | 给定要操作文件的父路径和子文件名称 |
public boolean createNewFile() throws IOException | 普通 | 创建文件 |
public boolean delete() | 普通 | 删除文件 |
public boolean exists() | 普通 | 判断给定路径是否存在 |
package cn.uihtml.demo;
import java.io.File;
public class JavaDemo {
public static void main(String[] args) throws Exception {
File file = new File('D:\uihtml.txt');
if(file.exists()) {
file.delete();
} else {
file.createNewFile();
}
}
}
在使用File类进行文件创建时需要设置完整路径,但是对于不同操作系统,文件的路径分隔符也有所不同。为了解决这个问题,在java.io.File类中提供了一个路径分隔符的常量【public static final String separator】。
package cn.uihtml.demo;
import java.io.File;
public class JavaDemo {
public static void main(String[] args) throws Exception {
File file = new File('D:' + File.separator + 'uihtml.txt');
if (file.exists()) {
file.delete();
} else {
file.creatNewFile();
}
}
}
使用File类创建文件时必须保证父路径存在,之前的案例程序时直接在根路径下进行文件创建,所以用户可以直接使用createNewFile()方法创建文件。如果此时文件需要保存在特定的目录中,则必须先创建父目录后才可以进行文件创建。
方法 | 类型 | 描述 |
---|---|---|
public File getParentFile() | 普通 | 找到一个指定路径的父路径 |
public boolean mkdirs() | 普通 | 创建指定目录 |
package cn.uihtml.demo;
import java.io.File;
public class JavaDemo {
public static void main(String[] args) throws Exception {
File file = new File('D:' + File.separator + 'hello' + File.sepatator + 'demo' + 'uihtml.txt');
if (file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
if (file.exists()) {
file.delete();
} else {
file.createNewFile();
}
}
}
获取文件信息的方法
方法 | 类型 | 描述 |
---|---|---|
public boolean canRead() | 普通 | 文件是否能读 |
public boolean canWrite() | 普通 | 文件是否能写 |
public boolean canExecute() | 普通 | 文件是否能执行 |
public long length() | 普通 | 获取文件大小,返回字节长度 |
public long lastModiffied() | 普通 | 获得最后一次修改日期 |
public boolean isDiretory() | 普通 | 是否是目录 |
public boolean isFile() | 普通 | 是否是文件 |
public boolean isHidden() | 普通 | 是否隐藏 |
public File[] listFiles() | 普通 | 列出目录中全部文件信息 |
字节流与字符流
在程序中所有的数据都是以流的方式进行传输或保存的,在流操作中存在有输入流和输出流的概念。
当程序需要通过数据文件读取数据时就可以利用输入流来完成,而当程序需要将数据保存到文件时,就可以使用输出流完成。
数据流是一种重要的资源操作,而执行资源操作时一般按照以下几个步骤进行。
- 如果要操作的是文件,那么首先要通过File类对象找到一个要操作的文件路径(路径可能存在,也可能不存在,如果不存在,则要创建路径)。
- 通过字节流或字符流的子类为字节流或字符流的对象实例化(向上转型)。
- 执行读/写操作。
- 一定要关闭操作的资源(close()),不管随后代码如果操作,资源永远要关闭。
字节流操作:InputStream、OutputStream
字符流操作:Writer、Reader
OutputStream字节输出流
常用方法
方法 | 类型 | 描述 |
---|---|---|
public abstract void write(int b) throws IOException | 普通 | 输出单个字节数据 |
public void write(byte[] b) throws IOException | 普通 | 输出一组字节数据 |
public void write(byte[] b,int off,int len) throws IOException | 普通 | 输出部分字节数据 |
public void close() throws IOException | 普通 | 关闭输出流 |
public void flush() throws IOException | 普通 | 刷新缓冲区 |
OutputStream定义了公共的字节输出操作,由于其定义为一个抽象类,所以需要依靠子类进行对象实例化,如果需要通过程序向文件进行内容输出,可以使用FileOutputStream子类。
FileOutputStream类常用方法
方法 | 类型 | 描述 |
---|---|---|
public FileOutputStream(File file) throws FileNotFoundException | 构造 | 采用覆盖的形式创建文件输出流 |
public FileOutStream(File file,boolean append) throws FileNotFoundException | 构造 | 采用覆盖或追加的形式创建文件输出流 |
package cn.uihtml.demo;
import java.io,File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class JavaDemo {
public static void main(String[] args) throws Exception{
File file = new File('D:' + File.separator + 'hello' + File.separator + 'uihtml.txt');
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs()
}
OutputStream output = new FileOutputStream(file);
String str = '中华人民共和国万岁,中华人民大团结万岁';
output.write(str.getBytes());
output.close();
}
}
使用AutoCloseable自动关闭接口
package cn.uihtml.demo;
import java.io,File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class JavaDemo {
public static void main(String[] args) throws Exception{
File file = new File('D:' + File.separator + 'hello' + File.separator + 'uihtml.txt');
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs()
}
try (OutputStream output = new FileOutputStream(file,true)) {
String str = '中华人民共和国万岁,中华人民大团结万岁';
output.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
InputStream字节输入流
当程序需要通过数据流进行字节数据读取时就可以利用java.io.InputStream类来实现。
InputStream类常用方法
方法 | 类型 | 描述 |
---|---|---|
public abstract int read() throws IOException | 普通 | 读取单个字节数据,如果现在已经读取到底了,返回-1 |
public int read(byte[] b) throws IOException | 普通 | 读取一组字节数据,返回的是读取的个数;如果没有数据,且已经读到底则返回-1 |
public int read(byte b,int off,int len) throws IOException | 普通 | 读取一组字节数据(只占数组的部分) |
public void close() throws IOException | 普通 | 关闭输出流 |
public byte[] readAllBytes() throws IOException | 普通 | 读取输入流全部字节数据 |
public long transferTo(OutputStream out) throws IOException | 普通 | 输入流转存到输出流 |
package cn.uihtml.demo;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class JavaDemo {
public static void main(String[] args) throws Exception {
File file = new File('D:' + File.separator + 'hello' + File.separator + 'uihtml.txt');
if (file.exists()) {
InputStream input = new FileInputStream(file);
byte data[] = new byte[1024];
int len = input.read(data);
System.out.println(new String(data,0,len));
input.close();
}
}
}
Writer字符输出流
在底层通信处理中都是依靠字节实现的数据交互,在程序中为了方便进行中文的数据处理,往往都会采用字符数据类型。
字符流最大的特点是可以直接进行字符串数据输出。
Writer类常用方法
方法 | 类型 | 描述 |
---|---|---|
public Writer append(CharSequence csq) throws IOException | 普通 | 追加输出内容 |
public void write(char[] cbuf) throws IOException | 普通 | 输出字符数组 |
public void write(int c) throws IOException | 普通 | 输出单个字符 |
public void write(String str) throws IOException | 普通 | 输出字符串 |
public abstract void flush() throws IOException | 普通 | 刷新缓冲区 |
public abstract void close() throws IOException | 普通 | 关闭输入流 |
Writer类进行文件操作时可以利用FileWriter子类进行对象实例化。
FileWriter类常用方法
方法 | 类型 | 描述 |
---|---|---|
public FileWriter(File file) throws IOException | 构造 | 采用覆盖的形式创建文件输出流 |
public FileWriter(File file,boolean append) throws IOException | 构造 | 采用覆盖或追加的形式创建文件输出流 |
package cn.uihtml.demo;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class JavaDemo {
public static void main(String[] args) throws Exception {
File file = new File('D:' + File.separator + 'hello' + File.separator + 'uihtml.txt');
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
Writer out = new FileWriter(file);
out.write('蜀国国主-->刘备');
out.append('蜀国宰相-->诸葛亮');
out.close();
}
}
与OutputStream类相比,Writer可以直接输出字符串内容,同时也可以在输出后继续利用append()方法追加输出内容。
Reader字符输入流
Reader类常用方法
方法 | 类别 | 描述 |
---|---|---|
public int read() throws IOException | 普通 | 读取单个字符,无数据读取时返回-1 |
public int read(char[] cbuf) throws IOException | 普通 | 读取多个字符,并返回读取个数 |
public long skip(long n) throws IOException | 普通 | 跳过指定的字符个数后读取 |
public boolean ready() throws IOException | 普通 | 是否可以开始读取数据 |
public abstract void close() throws IOException | 普通 | 关闭输入流 |
package cn.uihtml.demo;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class JavaDemo {
publi static void main(String[] args) throws Exception {
File file = new File('D:' + File.separator + 'hello' + File.separator + 'uihtml.txt');
if (file.exists()) {
Reader in = new FileReader(file);
char data[] = new char[1024];
in.skip(9);
int len = in.read(data);
System.out.println(new String(data,0,len));
in.close();
}
}
}
字节流与字符流的区别
虽然java.io包中提供字节流和字符流两类处理支持类,但是在数据传输(或者将数据保存在磁盘)时所操作的数据依然为字节数据,字符数据都是通过缓冲区进行处理后得到的内容。两类操作流最大的区别就在于字符流使用到了缓冲区(这样更适合进行中文数据的操作),而字节流是直接进行数据处理操作。所以当使用字符输出流进行输出时就必须使用Flushable接口中提供的flush()方法强制性刷新缓冲区中的内容,否则数据将不会输出。
package cn.uihtml.demo;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class JavaDemo {
public static void main(String[] args) throws Exception {
File file = new File('D:' + File.separator + 'hello' + File.separator + 'uihtml.txt');
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
Writer out = new FileWriter(file);
out.write('蜀国国主-->刘备');
out.append('蜀国宰相-->诸葛亮');
out.flush();
out.close();
}
}
转换流
转换流的设计目的是解决字节流与字符流之间操作类型的转换。java.io包中提供有两个转换流。
- OutputStreamWriter:字节输出流转换为字符输出流
- InputStreamReader:字节输入流转换为字符输入流
package cn.uihtml.demo;
import java.io.*;
public class JavaDemo {
public static void main(String[] args) throws Exception {
File file = new File("D:" + File.separator + "hello" + File.separator + "uihtml.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream output = new FileOutputStream(file);
Writer out = new OutputStreamWriter(output);
out.write("蜀国国主-->刘备");
out.append("蜀国宰相-->诸葛亮");
out.flush();
out.close();
output.close();
}
}
字符编码
在计算机的世界中,所有显示文字都是按照其指定的数字编码进行保存。
ISO8859-1:是一种国际通用单字节编码,最多只能表示0~255的字符范围,主要在英文传输中使用。
GBK/GB2312:中文的国际编码,专门用来表示文字,是双字节编码。GBK可以表示简体中文和繁体中文,而GB2312只能表示简体中文。
UNICODE:十六进制编码,可以准确地表示出任何语言文字,此编码不兼容ISO8859-1编码。
UTF-8:兼容ISO8859-1编码,可表示所有的语言字符。
内存操作流
内存操作流是以内存作为操作终端实现的I/O数据处理,与文件操作不同的地方在于,内存操作流不会进行磁盘数据操作。Java中提供两种内存操作流。
- 字节内存操作流:ByteArrayOutputStream、ByteArrayInputStream。
- 字符内存操作流:CharArrayWriter、CharArrayReader。
利用内存流实现大小写字母转换的操作
package cn.uihtml.demo;
import java.io.*;
public class JavaDemo {
public static void main(String[] args) throws Exception{
String str = 'www.uihtml.cn';
Input input = new ByteArrayInputStream(str.getBytes());
Output output = new ByteArrayOutputStream();
int data = 0;
while ((data = input.read()) != -1) {
output.write(Character.toUpperCase(data));
}
System.out.println(output);
input.close();
output.close();
}
}
管道流
管道流的主要作用是可以进行两个线程间的通信,分为管道输出流【PipedOutputStream、PipedWriter】、管道输入流【PipedInputStream、PipedReader】。如果要想进行管扫输出,则必须把输出流连接在输入流之上。
使用字节管道流实现线程通信
package cn.uihtml.demo;
import java.io.*;
class SendThread implements Runnable {
private PipeOutputStream output;
public SendThread () {
this.output = new PipedOutputStream();
}
@Override
public void run() {
try {
this.output.write("蜀国国主-->刘备".getBytes());
this.output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public PipedOutputStream getOutput() {
return output;
}
}
package cn.uihtml.demo;
import java.io.*;
class ReceiveThread implements Runnable {
private PipedInputStream input;
public ReceiveThread() {
this.input = new PipedInputStream();
}
@Override
public void run() {
byte data[] = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
while((len = this.input.read(data)) != -1) {
bos.write(data,0,len);
}
System.out.println(new String(bos.toByteArray()));
bos.close();
} catch(IOException e) {
e.printStackTrace();
}
try {
this.input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public PipedInputStream getInput() {
return input;
}
}
package cn.uihtml.demo;
class JavaDemo {
public static void main(String[] args) throws Exception {
SendThread send = new SendThread();
ReceiveThread receive = new ReceiveThread();
send.getOutput().connect(receive.getInput());
new Thread(send,"消息发送线程:").start();
new Thread(receive,"消息接收线程:").start();
}
}
BufferedReader缓冲输入流
BufferedReader提供了一种字符流的缓冲区数据读取,利用此类进行数据读取时会将读取到的数据暂时保存在缓冲区,而后利用其内部提供的方法将读取到的内容一次性取出。
常用方法
方法 | 类型 | 描述 |
---|---|---|
public BufferedReader(Read in) | 构造 | 接收一个Reader类的实例 |
public String readLine() throws IOException | 普通 | 一次性从缓冲区中将内容全部读取出来 |
对象序列化
对象序列化,就是把一个对象变为二进制的数据流的一种方法。通过对象序列化可以方便地实现对象的传输或存储。
一个类的对象想被序列化,则对象所在的类必须实现java.io.Serializable接口。此接口并没有提供任何抽象方法,只是一个标识接口,只表示一种对象可以被序列化的能力。
package cn.uihtml.demo;
class Member implements Serializable {
private String name;
private int age;
public Member(String name,int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
@Override
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
本程序定义的Member类实现了Serializable接口,所以此类的实例化对象都允许进行二进制传输。
序列化与反序列化处理
Serializable接口只是定义了某一个类的对象是否允许序列化的支持,然后对于对象序列化的反序列化的具体实现,则需要依靠ObjectOutputStream与ObjectInputStream两类完成。
ObjectOutputStream类可以将对象转为特定格式的二进制数据输出,ObjectInputStream可以读取ObjectOutputStream类输出的二进制对象数据,并将其转为具体类型的对象返回。
ObjectOutputStream类常用方法
方法 | 类型 | 描述 |
---|---|---|
public ObjectOutputStream(OutputStream out) throws IOException | 构造 | 传入输出的对象 |
public final void writeObject(Object obj) throws IOException | 普通 | 输出对象 |
ObjectInputStream类常用方法
方法 | 类型 | 描述 |
---|---|---|
public ObjectInputStream(InputStream in) throws IOException | 构造 | 构造输入对象 |
public final Object readObject() throws IOException,ClassNotFoundException | 普通 | 从指定位置读取对象 |
package cn.uihtml.demo;
class JavaDemo {
public static final File SAVE_FILE = new File("D:" + File.separator + "uihtml.txt");
public static void main(String[] args) throws Exception {
saveObject(new Member("蜀国国主-->刘备"));
System.out.println(loadObject());
}
public static void saveObjec(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SAVE_FILE));
oos.wirteObject(obj);
oos.close();
}
public static Object loadObject() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SAVE_FILE));
Object obj = ois.readObject();
ois.close();
return obj;
}
}
transient 关键字
默认情况下,当执行对象序列化的时候,会将类中的全部属性的内容进行序列化操作,但有些属性可能并不需要进行序列化的处理,这个时候可以在属性定义上使用transient关键字来完成了。
原创文章,作者:ZERO,如若转载,请注明出处:https://www.edu24.cn/course/java/java-i-o-action.html