所有的IO操作都在java.io中进行定义,整个java.io包实际上就是五个类和一个接口
- 五个类:File,InputStream,OutputStream、Reader、Writer;
- 一个接口:Serializable
文章目录
文件操作类:File
- 用来删除,创建文件等
package com.oceanstar.test3;
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:\\workspace\\test\\test.txt");
if (file.exists()){
file.delete();
}else{
System.out.println(file.createNewFile()); // 创建新文件
}
}
}
createNewFile:只有父路径存在,才能创建新文件,否则:java.io.IOException异常
package com.oceanstar.test3;
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt");
if (!file.getParentFile().exists()){ // 父路径不存在
file.getParentFile().mkdirs(); // 创建父路径们
}else{
System.out.println(file.createNewFile()); // 创建新文件
}
}
}
- 获取文件、目录信息
package com.oceanstar.test3;
import java.io.File;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt"); // 文件路径
if(file.exists()){ // 文件是否存在
System.out.println("是否是文件:" + file.isFile());
System.out.println("是否是目录:" + file.isDirectory());
System.out.println(("文件大小:" +
new BigDecimal((double)(file.length()/1024/1024))
.divide(new BigDecimal(1), 2,
BigDecimal.ROUND_HALF_UP)) + "M");
System.out.println("上次修改的时间:" +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date(file.lastModified())));
}
// 是否是文件:true
// 是否是目录:false
// 文件大小:0.00M
// 上次修改的时间:2019-08-23 17:09:05
}
}
- 获取目录下的文件信息
package com.oceanstar.test3;
import java.io.File;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace"); // 文件路径
if(file.isDirectory()){ // 文件是否存在
File []res = file.listFiles();
for (int i = 0; i < res.length; i++){
System.out.println(res[i]);
}
}
// E:\workspace\111.jpg
// E:\workspace\go
// E:\workspace\protobuf-3.7.1
// E:\workspace\protobuf-all-3.7.1.tar.gz
}
}
- 列出目录下的目录以及子目录信息
package com.oceanstar.test3;
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace"); // 文件路径
print(file);
}
public static void print(File file){
if(file.isDirectory()){ // 文件是否存在
File []res = file.listFiles();
if (res != null){
for (int i = 0; i < res.length; i++){
print(res[i]);;
}
}
}
System.out.println(file);
}
}
字节流与字符流
file类用来操作文件,操作文件内容要靠流。主要有两种流:
- 字节流:InputStream(输入字节流);OutputStream(输出字节流)
- 字符流:Reader(输入字符流)、Writer(输出字符流)
这四个流全部属于抽象类,所以在使用这些类的时候,一定要通过子类对象的向上转型来进行抽象类对象的实例化操作。这四种类有不同的子类,每个子类代表着不同的输入流、输出流。
字节输出流:OutStream
定义如下
public abstract class OutputStream implements Closeable, Flushable
可以看出这是一个抽象类,必须使用子类对象的向上转型来进行实例化操作。如果要进行文件操作,则可以使用FileOutputStream子类完成操作
package com.oceanstar.test3;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt"); // 文件路径
if (!file.getParentFile().exists()){ // 父路径不存在
file.getParentFile().mkdirs(); // 创建父路径们
}
OutputStream output = new FileOutputStream(file);
String str = "wwwwww***wwww.abdiis";
byte data[] = str.getBytes(); // 文件覆盖写
output.write(data);
output.close();
}
}
- 文件追加写
package com.oceanstar.test3;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt"); // 文件路径
if (!file.getParentFile().exists()){ // 父路径不存在
file.getParentFile().mkdirs(); // 创建父路径们
}
OutputStream output = new FileOutputStream(file, true); // 文件追加
String str = "wwwwww***wwww.abdiis";
byte data[] = str.getBytes(); // 文件
output.write(data);
// output.write(data[0]); // 单个字节输出
// output.write(data, 5, 5); // 输出部分字节数组
output.close();
}
}
字节输入流:InputStream
public abstract class InputStream implements Closeable
这是一个抽象类,必须使用子类对象的向上转型来进行实例化操作。如果要进行文件操作,则可以使用FileInputStream类完成操作
- 从文件中读取数据
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.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) + "]" );
// 一个个读取
InputStream input2 = new FileInputStream(file);
int temp;
int foo = 0;
while ((temp = input2.read()) != -1){
data[foo++] = (byte) temp;
}
input.close();
System.out.println("[" + new String(data, 0, foo) + "]" );
}
}
}
字符输出流:Writer
public abstract class Writer implements Appendable, Closeable, Flushable
这是一个抽象类,必须使用子类对象的向上转型来进行实例化操作。如果要进行文件操作,则可以使用FileWriter类完成操作
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt"); // 文件路径
if (!file.getParentFile().exists()){ // 父路径不存在
file.getParentFile().mkdirs(); // 创建父路径们
}
Writer output = new FileWriter(file); // 文件覆盖写
// Writer output = new FileWriter(file, true); // 文件追加写
String str = "wwwwww***wwww.ccccabdiis";
output.write(str);
output.close();
}
}
字符输入流:Reader
public abstract class Reader implements Readable, Closeable
- 抽象类,利用子类自动向上转型实例化
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt"); // 文件路径
if (file.exists()){
Reader input = new FileReader(file); // 可以看成是一个移动的指针
char data[] = new char[1024]; //返回读取了多少
int len = input.read(data);
input.close();
System.out.println(data);
}
}
}
字符流与字节流的区别
- 其最大的区别是:字节流直接与终端文件进行数据交互,字符流要将数据缓存区处理才能与终端文件进行交互。
在OutputStream输出数据时即使最后没有关闭输出流,内容也可以正常输出,但是反过来如果使用的时字符输出流Writer,在执行到最后如果不关闭输出流,就表示在缓冲区中处理的内容不会被强制性清空,所以就不会输出数据。如果有特殊星空情况关闭字符输出流,可以使用filush()方法强制清空缓存区。
提示:字符流最大的好处时可以处理中文,在开发中,如果需要处理中文就优先考虑字符流,否则使用字节流
转换流
虽然字节流和字符流表示两种不同的数据流操作,但是这两种流彼此间可以相互转换,而要实现这样的转换请使用InputStreamReader、OutStreamReader两个类。
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception{
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt"); // 文件路径
if (!file.getParentFile().exists()){ // 父路径不存在
file.getParentFile().mkdirs(); // 创建父路径们
}
OutputStream output = new FileOutputStream(file); // 文件覆盖写
Writer out = new OutputStreamWriter(output);
out.write("wawccdsax");
out.close();
}
}
不常用。
字符编码
- 设置字符编码以及获取字符编码
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
//System.getProperties().list(System.out); // sun.jnu.encoding=GBK 获取字符编码:GBK
File file = new File("E:\\workspace\\test\\test.txt");
OutputStream output = new FileOutputStream(file);
output.write("字符编号原来为GBK".getBytes("UNICODE"));
output.close();
}
}
内存流
如果一组应用需要进行IO操作,但是又不希望在磁盘上产生一些文件时,就可以将内存当作一个临时文件进行操作:
- 字节内存流:ByteArrayInputStream(内存字节输入流)、ByteArrayOutputStream(内存字节输出流)
- 字符内存流:CharArrayReader(内存字符输入流)、CharArraayWriter(内存字符输出流)
内存字符流与内存字节流的唯一区别在于操作的数据不同。一个操作byte,一个操作char。主要使用内存字节流。
- 将字符转换为大写输出
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
String str = "fdesqwazxcf1345";
//将要读取的数据保存到内存流中
InputStream in = new ByteArrayInputStream(str.getBytes());
OutputStream out = new ByteArrayOutputStream();
int temp;
while ((temp = in.read()) != -1){ // 将数据一个字节一个字节的读取,并临时保存到temp中
out.write(Character.toUpperCase(temp)); // 转换为大写
}
System.out.println(out); // 输出
out.close();
in.close();
}
}
- 将保存在内存中的数据一次读取出来,可以用来进行文件的合并读取
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
String str = "fdesqwazxcf1345";
String str2 = "123333333333333";
//将要读取的数据保存到内存流中
InputStream in = new ByteArrayInputStream(str.getBytes());
InputStream in2 = new ByteArrayInputStream(str2.getBytes());
ByteArrayOutputStream out = new ByteArrayOutputStream();
int temp;
while ((temp = in.read()) != -1){ // 将数据一个字节一个字节的读取,并临时保存到temp中
out.write(temp); // 转换为大写
}
while ((temp = in2.read()) != -1){ // 将数据一个字节一个字节的读取,并临时保存到temp中
out.write(temp); // 转换为大写
}
byte data[] = out.toByteArray(); // 将保存到内存中的数据一次全部读取出来
System.out.println(new String(data)); // 输出
out.close();
in.close();
}
}
打印流
在Java包中OutputStream是进行输出操作的最核心控制类,但是利用OutputStream会存在一个问题:所有的输出数据必须以字节类型的数据为主,也就是如果要输出int,double等类型也必须先转换为int才能输出。为了解决这个问题,Java提供了打印流来输出。
设计思想
- 装饰设计模式:打印流的核心思想是首先包装一个OutputStream类的实例化对象,然后再打印流的内部自动帮助用户处理好各种数据类型以及字节数组的转换操作。
- 装饰设计模式思想:将原本功能不足的类使用另外一个类进行包装,并且提供更多更方便的操作方法。
打印字节流:PrintStream
打印字节流PrintStream与打印字符流PrintWriter功能一样,方法一样
package com.oceanstar.test3;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
public class Test {
public static void main(String[] args) throws Exception {
// 利用FileOutputStream类实例化PrintStream类对象
PrintStream pu = new PrintStream(new FileOutputStream("E:\\workspace\\test\\test.txt"));
pu.print("中文");
pu.print(11);
pu.print('a');
pu.print(1+2);
pu.printf("姓名:%s, 年龄: %d", "oceanstar", 18);
pu.close();
}
}
System类对IO的支持
Java中定义了与IO有关的三个常量
错误输出:System.err
定义:
public static final PrintStream err = null;
分析:err是PrintStream类对象,专门用来进行错误输出
package com.oceanstar.test3;
public class Test {
public static void main(String[] args) throws Exception {
try {
Integer.parseInt("anbc");
}catch (Exception e){
System.err.println(e);
}
}
}
问:System.err与System.out有什么区别?
答:System.err是不希望用户看到的异常,而System.out是输出希望用户看到的信息
错误输出:System.out
定义:
public static final PrintStream out = null;
分析:System.out是PrintStream类对象【并且有JVM负责该对象的实例化】,PrintStream继承自OutputStream,因此可以使用System.out自动向上转型实例化OutputStream
package com.oceanstar.test3;
import java.io.OutputStream;
public class Test {
public static void main(String[] args) throws Exception {
OutputStream on = System.out; // 可以看出如果使用FileOutputStream实例化OutputStream抽象类,会输出到文件,这里我们输出到屏幕
on.write("aaaaa".getBytes());
}
}
- 消费型函数式接口与方法引用
再Java1.8提供的函数式接口里面,有一个消费型的函数式接口,此接口中的方法不返回结果,但是会接收参数。此时就可以利用方法引用的概念,利用 System.out.println实现Consumer接口的实例化。
package com.oceanstar.test3;
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) throws Exception {
Consumer<String> con = System.out::println;
con.accept("函数式接口");
con.accept("卡是策划");
}
}
系统输入:System.in
- 实现键盘的数据输入
package com.oceanstar.test3;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
InputStream in = System.in;
byte data[] = new byte[10];
System.out.println("请输入数据:");
int len = in.read(data);
System.out.println("输入数据为:" + new String(data, 0, len));
}
}
但是上面的程序有一个缺点,那就是当读取的数据操作data的长度之后,就会自动截断,超出数组长度的部分不会被接收。
改进方法:利用内存流先将读取的数据暂时保存到内存中,然后toByteArray一次输出。
package com.oceanstar.test3;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
InputStream in = System.in;
System.out.println("请输入数据:");
ByteArrayOutputStream out = new ByteArrayOutputStream();
int temp ;
while ((temp = in.read()) != -1){
if (temp == '\n'){
break;
}
out.write(temp);
}
System.out.println("输入数据为:" + new String(out.toByteArray()));
out.close();
}
}
思考:stringbuffer可以缓存数据,能否利用此来进行改进呢?
package com.oceanstar.test3;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
InputStream in = System.in;
StringBuffer buf = new StringBuffer();
System.out.println("请输入数据:");
int temp ;
while ((temp = in.read()) != -1){
if (temp == '\n'){
break;
}
buf.append((char)temp); // 记得转换,否则会以byte类型存储
}
System.out.println("输入数据为:" + buf);
// 请输入数据:
// 乱码 aaaaa
// 输入数据为:ä¹±ç aaaaa
}
}
分析:上面的程序不受数据长度限制,但是输入中文的时候会乱码。乱码原因是中文占两个字节,但是in.read()每次只读取一个字节,因此编码会出错。如果想要解决这个问题,可以利用字符缓冲输入流完成。
字符缓冲流:BufferedReader
如果要使用缓存区进行数据操作,java.io包提供了以下两种操作流:
- 字符缓存区流:BufferedReader,BufferedWriter;
- 字节缓冲区流:BufferedInputStream,BufferedOutputStream;
package com.oceanstar.test3;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Test {
public static void main(String[] args) throws Exception {
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入数据:");
String str = buf.readLine(); // 读取一行 \n结束
System.out.println(str);
}
}
问:为什么不适用字节流:BufferedInputStream
答:字符流方便处理中文,并且支持String返回,这样可以进行更多操作。如正则验证等。
备注:接收数据返回字符串是最方便的。
字符缓冲流除了可以接受输入信息之外,还可以读取文件
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
File file = new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt"); // 文件路径
BufferedReader buf = new BufferedReader(new FileReader(file));
String str = null; // 读取一行 \n结束
while ((str = buf.readLine()) != null){
System.out.println(str);
}
buf.close();
}
}
扫描流:Scanner
在JDK1.5之前如果想要进行数据的输入操作,使用BufferReader类是最方便的,但是BufferReader类会存在以下两个问题:
- 它读取数据时只能按照字符串返回
- 所有分隔符都是固定的。
为此,从JDK1.5开始增加了新的Scanner类。
package com.oceanstar.test3;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("请输入内容:");
while (scan.hasNext()){ // 有没有下一个 // 以tab为截至字符
System.out.println("输出内容:" + scan.next());
}
scan.close();
}
}
- 输入数字—double
package com.oceanstar.test3;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("请输入数字:");
if (scan.hasNextDouble()){ // 有没有下一个 // 以tab为截至字符
System.out.println("输出内容:" + scan.nextDouble());
}else{
System.out.println("输入错误");
}
scan.close();
}
}
- 正则匹配验证
package com.oceanstar.test3;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("请输入出生日期:");
if (scan.hasNext("\\d{4}-\\d{2}-\\d{2}")){ // 有没有下一个 // 以tab为截至字符
System.out.println("输出内容:" + scan.next("\\d{4}-\\d{2}-\\d{2}"));
}else{
System.out.println("输入错误");
}
scan.close();
}
}
- 读取文件
package com.oceanstar.test3;
import java.io.File;
import java.io.FileInputStream;
import java.util.Scanner;
public class Test {
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(new FileInputStream(new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt")));
scan.useDelimiter("\n"); // 设置分隔符
while (scan.hasNext()){
System.out.println(scan.next());
}
scan.close();
}
}
对象序列化
Java允许用户在程序运行中进行对象的创建。但是这些创建的对象都只保存在内存中,所以这些对象的生命周期都不会超过JVM进行。但是很多时候可能需要在JVM进行结束知乎对象依然可以保存下来,或者在不同的JVM进程中要进行对象传输,那么这种情况就可以采用对象序列化的方式进行处理。
序列化接口:Serializable
- 对象序列化的本质实际上时将内存中保存的对象数据转换为二进制数据流进行传输的操作。
- 并不是所有的类对象都可以直接进行序列化操作,只有实现了Serializable 的接口才能序列化
定义:
public interface Serializable {
}
可以看出这个接口中没有任何方法,说明它是一种标识接口,表示一种能力。
- 定义一个可以被序列化对象的类
package com.oceanstar.test3;
import java.io.Serializable;
@SuppressWarnings("serial") // 压制序列化版本警告信息
class Book_s implements Serializable{ //Serializable表示这个类可以进行二进制传输以及文件保存
public Book_s(){
}
}
实现序列化以及反序列化
实现了Serializable接口之后并不意味着对象可以实现序列化操作。实际上在对象序列化与反序列化的操作中,还需要以下两个类的支持:
- java.io.ObjectOutputStream:将对象序列化为指定格式的二进制数据
- java.io.ObjectInputStream:将序列化的二进制对象转换为二进制对象内容
package com.oceanstar.test3;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt")));
oos.writeObject("加减乘除的上次开会督促"); // 序列化对象
oos.close(); // 被保存为: t !鍔犲噺涔橀櫎鐨勪笂娆″紑浼氱潱淇?
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(new File("E:" +
File.separator + "workspace" +
File.separator + "test" +
File.separator + "test" +
File.separator +"test.txt")));
System.out.println(oin.readObject()); // 反序列化对象
oin.close();
}
}
transient关键字
java中对象最有意义的就是对象的属性信息,所以在默认情况下,如果要进行对象的序列化操作,所序列化的一定是对象的属性信息,并且该对象的所有属性信息将会被序列化。如果某些属性的内容不需要被保存,定义时用transient关键字修饰就可以了