1、File文件操作类
1.1 File类使用
java.io.File是是一个普通的类,可以通过两个构造方法直接产生实例化对象:
public File(String pathname);
public File(String parent,String child);
—— 设置父路径和子路径
File类提供了以下方法对文件进行基本操作:
- 创建一个新文件:
public boolean createNewFile() throws IOException
- 判断文件是否存在
public boolean exists();
- 删除文件
public boolean delete();
一个简单应用:
public class Test {
public static void main(String[] args) throws IOException {
//定义要操作的文件路径
File file = new File("E:\\比特\\JavaSE\\code\\TestIO.java");
if (file.exists()){
file.delete();
}else {
file.createNewFile();
}
}
}
注意:定义要操作的文件路径时,不同的操作系统使用的斜杠不同,因此我们可以采用File类的一个常量
public static final String separator
来描述。separator由不同操作系统下的JVM来决定到底是哪个杠杠。
上面代码优化为:
public class Test {
public static void main(String[] args) throws IOException {
//定义要操作的文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "TestIO.java");
if (file.exists()) {
file.delete();
} else {
file.createNewFile();
}
}
}
1.2 目录操作
- 取得父路径或父File对象
public String getParent();
public File getParentFile();
- 创建目录(无论有多少级父目录,都会创建)
public boolean mkdirs()
简单应用:
public class Test {
public static void main(String[] args) throws IOException {
//定义要操作的文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "JavaIO.java" + File.separator + "Nanfeng"
+ File.separator + "TestIO.java");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs(); //有多少级父目录就创建多少级
}
if (file.exists()) {
file.delete();
} else {
file.createNewFile();
}
}
}
1.3 文件信息
- 判断路径是否是文件:
public boolean isFile();
- 判断路径是否是目录:
public boolean isDirectory();
- 取得文件大小(字节):
public long length();
- 最后一次修改日期:
public long lastModified();
- 列出一个目录的全部组成:
public File[] listFiles();
2、字节流与字符流
File类不支持文件内容的处理,如果要处理文件内容,必须要通过流的操作模式来完成。流分为输入流和输出流。
在 java.io 包中,流分为两种:字节流和字符流:
1)字节流:InputStream、OutputStream
2)字符流:Reader、Writer
字节流与字符流的本质区别:
字节流是原生的操作,而字符流是经过处理后的操作。
不论是字节流还是字符流,操作流程都是一样的,以文件操作为例:
1)根据文件路径创建File类对象
2)根据字节流或字符流的子类实例化父类对象
3)进行数据的读取或写入操作
4)关闭流(close())
2.1 字节输出流(OutputStream)
由于OutputStream是一个抽象类,所以要想为父类实例化,就必须使用子类。由于方法名称都由父类声明好了,所以我们只需要关心子类的构造方法。如果要进行文件的操作,可以使用FileOutputStream类来处理,这个类的构造方法为:
1)接收File类(覆盖):public FileOutputStream(File file) throws FileNotFoundException;
2)接收File类(追加):public FileOutputStream(File file,boolean append)
简单应用:
public class Test {
public static void main(String[] args) throws IOException {
//定义要操作的文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "hello.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs(); //有多少级父目录就创建多少级
}
OutputStream output = new FileOutputStream(file);
//要求输出到文件的内容
String msg = "南风知我意";
//将内容变为字节数组
output.write(msg.getBytes());
//关闭输出流
output.close();
}
}
上面这段代码如果重复执行,内容会不停的覆盖,如果要实现内容的追加,则需要用另外一种构造方法。
public class Test {
public static void main(String[] args) throws IOException {
//定义要操作的文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "hello.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs(); //有多少级父目录就创建多少级
}
OutputStream output = new FileOutputStream(file, true);
//要求输出到文件的内容
String msg = " 念你已成疾";
//将内容变为字节数组
output.write(msg.getBytes());
//关闭输出流
output.close();
}
}
JDK 1.7之后,追加了一个AutoCloseable接口,主要目的是自动进行关闭处理。
class Message implements AutoCloseable {
public Message() {
System.out.println("创建一条新消息");
}
@Override
public void close() throws Exception {
System.out.println("[AutoCloseable]自动关闭方法");
}
public void print() {
System.out.println("www.nanfeng.java");
}
}
public class Test {
public static void main(String[] args) {
//必须在try中定义对象
try (Message msg = new Message()) {
msg.print();
} catch (Exception e) {
}
}
}
因此上面的例子可以改为:
public class Test {
public static void main(String[] args) throws IOException {
//定义要操作的文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "hello.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs(); //有多少级父目录就创建多少级
}
try (OutputStream output = new FileOutputStream(file, true)) {
//要求输出到文件的内容
String msg = "吹梦到西洲";
//将内容变为字节数组
output.write(msg.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.2 字节输入流(InputStream)
和字节输出流一样,字节输入流也是一个抽象类,如果要对它进行实例化,同样也需要使用子类。如果要对文件进行处理,则使用FileInputStream类。
public class Test {
public static void main(String[] args) throws IOException {
//定义文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "hello.txt");
//保证文件存在
if (file.exists()) {
InputStream input = new FileInputStream(file);
byte[] data = new byte[1024]; //每次可以读取的最大数量
int len = input.read(data); //此时数据读取到数组中
String result = new String(data, 0, len); //将字节数组转为String
System.out.println("读取内容【" + result + "】");
input.close();
}
}
}
2.3 字符输出流:Writer
字符适合处理中文数据,Writer是字符输出流的处理类。
在Writer类里也提供了write()方法,而且该方法接收的类型都是char型。
需要注意的是,Writer类提供了一个直接输出字符串的方法:public void write(String str) throws IOException;
如果要操作文件,使用FileWriter类。
简单应用:
public class Test {
public static void main(String[] args) throws IOException {
//定义文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "hello.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
String msg = "南风知我意,念你已成疾!";
Writer out = new FileWriter(file);
out.write(msg);
out.close();
}
}
2.4 字符输入流:Reader
Reader也是一个抽象类,如果要进行文件的读取,要使用FileReader。
Reader类中没有方法可以直接读取字符串类型,只能通过字符数组进行读取操作。
public class Test {
public static void main(String[] args) throws IOException {
//定义文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "hello.txt");
if (file.exists()) {
Reader in = new FileReader(file);
char[] data = new char[1024];
int len = in.read(data); //将数据读取到字符数组中
String result = new String(data, 0, len);
System.out.println("读取内容【" + result + "】");
in.close();
}
}
}
所有字符流操作,无论是写入还是输出,数据都先保存在缓存中。如果字符流不关闭,数据就有可能保存在缓存中并没有输出到目标源,这时就必须使用
out.flush()
强制刷新才能得到完整数据。
3、转换流
字节流和字符流的相互转换:
- OutputStreamWriter:将字节输出流转成字符输出流
- InputStreamReader:将字节输入流转成字符输入流
简单应用:
public class Test {
public static void main(String[] args) throws IOException {
//定义文件路径
File file = new File("E:" + File.separator + "比特"
+ File.separator + "JavaSE" + File.separator + "code"
+ File.separator + "hello.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream output = new FileOutputStream(file);
Writer out = new OutputStreamWriter(output);
String msg = "南风知我意,吹梦到西洲!";
out.write(msg);
out.close();
}
}
4、字符编码
开发中常用UTF-8编码,解码和编码不一致就会产生乱码。
读取Java运行属性:
System.getProperties().list(System.out);
5、内存操作流
之前所有操作都是针对文件进行IO处理,其实IO操作也可以发生在内存中,这种称为内存操作流。
文件流的操作一定会产生一个文件数据,如果现在需要进行IO处理,但又不希望产生文件,这种情况就可以使用内存作为操作终端。
内存操作流最核心的就是将所有OutputStream输出的程序保存在了程序里面。
内存流也分为两类:
1)字节内存流:ByteArrayInputStream、ByteArrayOutputStream
2)字符内存流:CharArrayReader、CharArrayWriter
简单应用:通过内存流实现大小写转换
public class Test {
public static void main(String[] args) throws IOException {
String msg = "Hello Nanfeng";
//实例化InputStream类对象,实例化的时候需要将你操作的数据保存到内存中,最终读取的就是你设置的内容
InputStream input = new ByteArrayInputStream(msg.getBytes());
OutputStream output = new ByteArrayOutputStream();
int temp = 0;
while ((temp = input.read()) != -1) {
//对每个字节进行处理,处理之后所有数据都在outputStream类中
output.write(Character.toUpperCase(temp));
}
//直接输出output对象
System.out.println(output);
input.close();
output.close();
}
}
使用内存流实现将两个文件合并处理(文件量不大)
public class JavaIO {
public static void main(String[] args) throws IOException {
File[] files = new File[]{
new File("E:" + File.separator + "比特" + File.separator + "JavaSE" + File.separator + "code" + File.separator + "data-a.txt"),
new File("E:" + File.separator + "比特" + File.separator + "JavaSE" + File.separator + "code" + File.separator + "data-b.txt")
};
String[] data = new String[2];
for (int i = 0; i < files.length; i++) {
data[i] = readFile(files[i]);
}
StringBuffer buf = new StringBuffer(); //组合操作
String contentA[] = data[0].split(" ");
String contentB[] = data[1].split(" ");
for (int i = 0; i < contentA.length; i++) {
buf.append(contentA[i]).append("(").append(contentB[i]).append(")").append(" ");
}
System.out.println(buf);
}
public static String readFile(File file) throws IOException {
if (file.exists()) {
InputStream input = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int temp = 0;
byte[] data = new byte[10];
while ((temp = input.read(data)) != -1) {
//将数据保存在bos中
bos.write(data, 0, temp);
}
bos.close();
input.close();
//将读取内容返回
return new String(bos.toByteArray());
}
return null;
}
}
6、打印流
打印流解决的是OutputStream的设计缺陷,如果操作的不是二进制数据,只是想通过程序向终端目标输出信息的话,OutputStream不是很方便,其缺点有两个:
1)所有的数据必须转为字节数组
2)如果要输出的是int,double等类型就不方便了
打印流的设计属于装饰设计模式,核心依然是某个类的功能。
打印流分为字节打印流PrintStream
和字符打印流PrintWriter
,后者使用频率较高。
6.1 使用打印流:
public class DaYinLiu {
public static void main(String[] args) throws FileNotFoundException {
PrintWriter printutil = new PrintWriter(new FileOutputStream(new File("E:\\比特\\JavaSE\\code\\test.txt")));
printutil.print("姓名:");
printutil.println("Nanfeng");
printutil.print("年龄:");
printutil.println(21);
printutil.print("工资:");
printutil.println(10.000000000000000001);
printutil.close();
}
}
6.2 格式化输出
格式化输出:
public PrintStream printf(String format,Object ... args)
public class DaYinLiu {
public static void main(String[] args) throws FileNotFoundException {
String name = "Nanfeng";
int age = 21;
double salary = 1.1063726327;
PrintWriter printutil = new PrintWriter(new FileOutputStream(new File("E:\\比特\\JavaSE\\code\\test.txt")));
printutil.printf("姓名:%s,年龄:%d,工资:%1.2f", name, age, salary);
printutil.close();
}
}
格式化字符串:
public static String format(String fromat,Object ... args);
public class DaYinLiu {
public static void main(String[] args) throws FileNotFoundException {
String name = "Nanfeng";
int age = 21;
double salary = 1.1063726327;
String str = String.format("姓名:%s,年龄:%d,工资:%1.2f", name, age, salary);
System.out.println(str);
}
}
7、System类对IO的支持
在System类中定义了三个操作的常量:
1)标准输出(显示器):public final static PrintStream out;
2)错误输出:public final static PrintStream err;
3)标准输入(键盘):public final static InputStream;
8、两种输入流
8.1 BufferedReader类
BufferedReader类属于一个缓冲的输入流,而且是一个字符流的操作对象。在Java中缓冲流也分为两种:字节缓冲流(BufferedInputStream)、字符缓冲流(BufferedReader)。
BufferedReader类提供了直接读取一行数据的方法(以回车为换行符):
String readLine() throws IOException;
利用BufferedReader类实现键盘输入:
public class ShuRuLiu {
public static void main(String[] args) throws IOException {
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入信息:");
String str = buf.readLine();
System.out.println("输入信息为:" + str);
}
}
使用以上形式实现的键盘输入有两个特点:
1)默认使用回车换行
2)由于接收的数据类型为String,可以使用String类的各种操作进行数据处理并且可以变为各种常见数据类型
8.2 java.util.Scanner类
Scanner是一个专门进行输入流处理的程序类,利用这个类可以方便处理各种数据类型,也可以直接结合正则表达式进行各项处理。Scanner类解决了BufferedReader类的缺陷,替代了BufferedReader类。
Scanner类提供的方法:
- 判断是否有指定数据类型:
public boolean hasNextXxx();
- 取得指定类型的数据:
public 数据类型 nextXxx();
- 定义分隔符:
public Scanner useDelimiter(Pattern pattern);
- 构造方法:
public Scanner(InputStream source);
使用Scanner类实现数据输入:
public class ShuRuLiu {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入信息:");
if (scanner.hasNext()) {
System.out.println("输入信息为:" + scanner.next());
}
scanner.close();
}
}
使用Scanner类操作文件:
public class ShuRuLiu {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(new FileInputStream(new File("E:\\比特\\JavaSE\\code\\test.txt")));
scanner.useDelimiter("\n"); //自定义分隔符
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
scanner.close();
}
}
9、序列化
对象序列化是指:将内存中保存的对象变为二进制数据流的形式进行传输,或者是将其保存在文本中。但是并不是所有类的对象都可以被序列化。
严格来说,需要被序列化的类对象需要传输使用,同时这个类必须实现java.io.Serializable接口,但是这个接口没有任何的方法定义,只是一个标识而已。
实现对象序列化:
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name = " + name + ", age = " + age + "]";
}
}
public class XuLieHua {
public static final File FILE = new File("E:\\比特\\JavaSE\\code\\testSer.txt");
public static void main(String[] args) throws IOException {
ser(new Person("Nanfeng", 21));
}
public static void ser(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE));
oos.writeObject(obj);
oos.close();
}
}
补充:transient关键字
序列化的处理在java.io包里有两类,Serializable是使用最多的序列化接口,它采用自动化模式完成,在默认情况下所有的属性都会被序列化保存下来。如果现在某些属性不希望被保存了,那么就可以使用transient关键字。
还有一个Externalizable接口需要自己动手来处理序列化,很少使用。