File文件操作类
在java.io包中,File类是唯一一个与文件本身操作(创建、删除、取得信息..)有关的程序类。
File类的基本使用
在windows下,路径记得加盘符如”C:”
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test"+File.separator+"Test.txt");
File file2 = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"HashMap与HashTable区别.png");
if (!file.getParentFile().exists()) {
//file.mkdirs();//全部当成文件夹
file.getParentFile().mkdirs();
}
if(!file.exists()) {//不存在,创建
file.createNewFile();
}
if(file.exists()) {
file.delete();//先删除文件
file.getParentFile().delete();//再删除路径
}
System.out.println(file2.exists());//存在否
System.out.println(file2.length());//大小
System.out.println(new Date(file2.lastModified()));//最后一次时间,是时间戳,转换一下
}
}
//+++++++++++++++++++++++++++++++++++++++++++
true
352885
Mon May 21 17:56:00 GMT+08:00 2018
显示当前文件夹下文件
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop");//+File.separator+"Test.txt");
File [] result = file.listFiles();//打印文件路径下文件
for (File file2 : result) {
System.out.println(file2);
}
}
}
//+++++++++++++++++++++++++++++++++++++++++++++
C:\Users\wrinkle\Desktop\360安全卫士.lnk
C:\Users\wrinkle\Desktop\360软件管家.lnk
C:\Users\wrinkle\Desktop\Advanced Archive Password Recovery.lnk
C:\Users\wrinkle\Desktop\Control Panel.lnk
C:\Users\wrinkle\Desktop\desktop.ini
C:\Users\wrinkle\Desktop\eclipse.exe - 快捷方式.lnk
C:\Users\wrinkle\Desktop\eclipse.exe.lnk
...
递归打印当前文件夹所有文件:
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop");//+File.separator+"Test.txt");
listFiles(file);//递归函数
}
public static void listFiles(File file) {
if (file.isDirectory()) {
File[] result = file.listFiles();
if(null != result) {
for (File file2 : result) {//取出所有文件路径
listFiles(file2);//开始递归
}
}
}
else {
System.out.println(file);
}
}
}
//+++++++++++++++++++++++++++++++++
...
字节流与字符流
流操作简介:
File文件不支持文件内容处理,如果处理文件内容,必须要通过流操作模式来完成。流分为输入流和输出流。
在java.io包中,流又分为字节流和字符流:
字符流和字节流的区别:
使用流程:
- 根据文件路径创建File类对象
- 根据字节流或字符流的子类实例化父类对象
- 进行数据读写操作
- 关闭流(close())
字节输出流 OutputStream
使用字节输出流需要使用Java.io.OutputStream
OutputStream实现了Closeable、Flushable接口,这两个接口中的方法:close(),flush()
此外,还有其他方法:
由于OutputStream是一个抽象类,想要为父类实例化,就必须使用子类,父类已经声明好,只需要使用构造方法即可FileOutputStream(File)、FileOutputStream(File,boolean)。
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test"+File.separator+"Test.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
//OutputStream outputStream = new FileOutputStream(file); //重复写
OutputStream outputStream = new FileOutputStream(file,true); //追加
String string = "Hello Jan\r\n";
outputStream.write(string.getBytes());//全部输出
//outputStream.write(string.getBytes(),0,5);//部分输出
// file.delete();//在这里是不能删除的,文件还在缓存区,还没有保存,所以无法删除,应当在close之后执行
// file.getParentFile().delete();//还在操作,所以不能删除
outputStream.close();
// file.delete();//删文件
// file.getParentFile().delete();//删路径
}
}
//+++++++++++++++++++++++++++++
Hello Jan
Hello Jan
在进行文件输出时,文件会自动创建,所以不需要再手动createNewFile()
FileOutputStream(File,false)就相当于FileOutputStream(File)
AutoCloseable自动关闭
JDK1.7以后追加了自动关闭AutoCloseable接口,但是使用起来复杂,比较奇怪,要结合try catch,而且对象实例化的操作时放在try()上的,而不是在try内部。
字节输入流 InputStream
利用InputStream可以实现文件内容的读取
InputStrea只实现了Closeable接口
同样,操作时也需要使用FileInputStream类。
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test"+File.separator+"Test.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
if (file.exists()) {
InputStream input = new FileInputStream(file);
byte [] data = new byte [1024];//每次读取最大数量
int len = input.read(data);//数据放到data中
String str = new String(data, 0, len);//转为String
System.out.println(str);
input.close();
}
}
}
//+++++++++++++++++++++++++++
Hello Jan
Hello
流操作一定记得Close()。
字符输出流 Writer
字符的特点是适合处理中文。
它比OutputStream多了一个Appendable接口。
在Writer类里也提供了Write()方法,但是接收类型是char,Writer类提供了一个直接输出字符串的write()方法。
操作文件需要使用FileWriter子类。
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test"+File.separator+"Test.txt");
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
String str = "Hello Jan \r\n";
Writer writer = new FileWriter(file);//创建对象
writer.write(str);//输出
writer.close();//关闭
}
}
字符输入流 Reader
使用FileReader,在Reader类中没有直接读取字符串的方法,需要通过字符数组进行读取操作。
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test"+File.separator+"Test.txt");
if (file.exists()) {
Reader reader = new FileReader(file);//创建对象
char [] data = new char[1024];//用data装数据
//int len = reader.read(data);
//String str = new String(data, 0, len);
//System.out.println(str);
reader.read(data);//输入
for (char c : data) {//foreach打印
System.out.print(c);
}
reader.close();//关闭
}
}
}
//+++++++++++++++++++++++++++++++++++++
Hello Jan
字符留适合处理中文,字节流适合处理一切数据(但对中文不友好)
字节流与字符流的区别
字节流与字符流区别不大,一般优先考虑字节流,有中文是才考虑字符流,字符需要通过内存缓冲进行处理。如果字符流不关闭,数据就有可能存于缓冲而位输出,需要强制刷新才能得到完整数据。
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test"+File.separator+"Test.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
String mse = "HaHa Jan \r\n";//输出内容
Writer out =new FileWriter(file);//创建对象
out.write(mse);//输出
out.flush();//刷新,无close()
}
}
在没有中文处理时,优先选择字节流。
转换流
转换流的基本使用
实际上字节流与字符流是可以相互转换的OutputStreamWriter、InputStreamReader(),在实际开发中并没有什么用处。
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test"+File.separator+"Test.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
OutputStream out = new FileOutputStream(file,true);//创建
String mse = "Hello Jan \r\n";//输出内容
Writer out2 =new OutputStreamWriter(out);//创建对象,转换
out2.write(mse);//输出
out2.flush();//刷新,无close()
out2.close();
}
}
实例:文件拷贝
实现把给定文件路径的文件拷贝另一个路径,如果路径不存在则创建
class CopyFileUtil{//工具类
private CopyFileUtil() {};//构造方法私有化
//路径检测
public static boolean fileIsExists(String path) {
return new File(path).exists();
}
//路径创建
public static void createParentDir(String path) {
File file = new File(path);
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
}
//拷贝文件
public static boolean copyFile(String srcPath,String desPath) {
File inputFile = new File(srcPath);//文件取得
File outputFile = new File(desPath);
FileInputStream fileInputStream= null;//输入流
FileOutputStream fileOutputStream= null;//输出流
try {
fileInputStream = new FileInputStream(inputFile);
fileOutputStream = new FileOutputStream(outputFile);
copyFileHandle(fileInputStream,fileOutputStream);//真 拷贝操作
} catch (IOException e) {
e.printStackTrace();
return false;
}finally {
try {
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
return true;
}
//拷贝核心操作
private static void copyFileHandle(InputStream inputStream,OutputStream outputStream) throws IOException {
// byte[] data = new byte[1024];//缓冲区
byte[] data = new byte[10240000];//缓冲区
int len = 0;
//read()读完返回-1
long start = System.currentTimeMillis();
while ((len = inputStream.read(data)) != -1) {
outputStream.write(data, 0, len);//0~len是因为万一不满时。
}
long end = System.currentTimeMillis();
System.out.println("Time: "+(end-start));
}
}
public class TestDemo{
public static void main(String[] args) throws IOException {
String srcPath = "G:"+File.separator+"BaiduYunDownload"+File.separator+"生存家族.Survival.Family.2017.BD720P.日语中字.mp4";
String desPath = "G:"+File.separator+"生存家族.Survival.Family.2017.BD720P.日语中字.mp4";
if(CopyFileUtil.fileIsExists(srcPath)) {
CopyFileUtil.createParentDir(desPath);
System.out.println(CopyFileUtil.copyFile(srcPath, desPath));
}else {
System.out.println("源文件不存在");
}
}
}
这个拷贝的实质其实就是读取数据,然后将文件输出。
字符编码
常用字符编码
常用的有:
乱码的产生
乱码产生的原因一般是因为编码与解码不统一。
public class TestDemo{
public static void main(String[] args) throws IOException {
File file = new File("C:"+File.separator+"Users"+File.separator+"wrinkle"+File.separator+"Desktop"+File.separator+"Test.txt");
OutputStream out = new FileOutputStream(file);
out.write("今晚没听懂".getBytes("ISO8859-1"));
out.close();
}
}
//+++++++++++++++++++++++++++
???????
内存操作流
概念
IO操作发生在内存中,而不管之后这个数据是否被保留。
即:需要IO操作,但不希望产生文件,这时就可以使用内存作为操作终端
分类:
通过内存流实现大小写转换
public class TestDemo{
public static void main(String[] args) throws IOException {
String str = "Hello Jan";
//实例化的内容保存在内存中,而不是FileInputStream
InputStream input = new ByteArrayInputStream(str.getBytes());
OutputStream output = new ByteArrayOutputStream();
int tmp = 0;
while ((tmp = input.read())!= -1) {
output.write(Character.toUpperCase(tmp));
}
System.out.println(str);
System.out.println(output);
input.close();
output.close();
}
}
//++++++++++++++++++++++++++++++++
Hello Jan
HELLO JAN
内存流操作
将两个小文件合并
public class TestDemo{
public static void main(String[] args) throws IOException {
File [] files = new File [] {new File("C:/Users/wrinkle/Desktop/one.txt"),new File("C:/Users/wrinkle/Desktop/two.txt")};
String [] data = new String [2];
File [] files = new File [] {new File("C:/Users/wrinkle/Desktop/one.txt"),new File("C:/Users/wrinkle/Desktop/two.txt")};
String [] data = new String [2];//用来放读取的数据
StringBuffer buffer = new StringBuffer();//用来合并后的数据
for (int i = 0; i < files.length; i++) {
data [i] = readFile(files[i]);//读文件
buffer.append(data[i]);//合并
buffer.append(" ");//用空格分隔
}
System.out.println(buffer);
}
//读文件操作
public static String readFile(File file) throws IOException{
if (file.exists()) {
InputStream input = new FileInputStream(file);//输入流
ByteArrayOutputStream byt = new ByteArrayOutputStream();//内存输出流,保存读取的数据
int tmp = 0;
byte [] data = new byte [10];
while ((tmp =input.read(data)) != -1) {//读取
byt.write(data,0,tmp);//输出到内存
}
byt.close();
input.close();
return new String(byt.toByteArray());
}
return null;
}
}
buffer.append(data[i])
里append()的作用是追加String内容到buffer,因为buffer是StringBuffer类型,所以是可以追加的。之所以只能合并小文件就是String的大小是有限的,不能放太大的文件。
打印流
打印流是为了解决OutputStream的设计缺陷,属于的OutputStream的加强,如果操作的不是二进制数据,只是想向目标终端输出信息的话,OutputStream就不是很方便,其缺点:
打印流概念
为了解决OutputStream的设计缺陷
自定义打印流
class PrintUtil{
private OutputStream out;
public PrintUtil(OutputStream out) {
this.out = out;
}
//核心操作,输出流,后面都是调用此方法或者重载
public void print(String string) {
try {
this.out.write(string.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
public void println(String string) {
this.print(string+"\r\n");
}
public void print(int data) {
this.print(String.valueOf(data));
}
public void println(int data) {
this.println(String.valueOf(data));
}
public void print(double data) {
print(String.valueOf(data));
}
public void println(double data) {
this.print(String.valueOf(data));
}
}
public class TestDemo{
public static void main(String[] args) throws IOException {
PrintUtil printUtil = new PrintUtil(new FileOutputStream(new File("C:/Users/wrinkle/Desktop/Print.txt")));
printUtil.print(1);
printUtil.println(2);
printUtil.print("Hello");
printUtil.println("Jan");
printUtil.print(6.0);
}
}
//
12
HelloJan
6.0
打印流的本质就是对OutputStream进行封装
使用系统打印流
打印流分为
PrintStream使用几率比较高
打印流的设计模式属于装饰者设计模式:核心功能是某个类的功能,但是为了得到更好的操作效果,让支持的功能更多一些。
格式化输出
类似于C语言中的printf,不太常用。
public static String format(String format, Object... args)
public class TestDemo{
public static void main(String[] args) {
String name = "Jan";
int age = 20;
double money = 0.005892525;
//格式化
String str = String.format("name: %s age: %d money: %1.5f",name,age,money);
System.out.println(str);
}
}
//++++++++++++++++++++++++++++++++
name: Jan age: 20 money: 0.00589
System类对IO的支持
实际上我们使用的系统输出就是利用了IO流的模式完成的,在System类中定义了三个操作常量。
- 标准输出(显示器) : public final static PrintStream out
- 错误输出 : public final static PrintStream err
- 标准输入(键盘):public final static InputStream in
系统输出
系统输出一共有两个常量:out、err,都是PrintStream类的对象。
out输出是希望用户看到的内容
err输出是不希望用户看到的内容
在实际开发中其实都用日志代替了log
System.err只是作为一个保留属性,平时几乎不会用到,更多的则是System.out,如System.out.println()
由于System.out是PrintStream的实例化对象,而PrintStream又是OutputStream的子类,所以可以直接使用System.out直接为OutputStream实例化,这个时候的OutputStream输出的位置将变为屏幕。
修改前面的自定义打印
class PrintUtil{
private OutputStream out;
public PrintUtil(OutputStream out) {
this.out = out;
}
public void print(String string) {
try {
this.out.write(string.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
public void println(String string) {
this.print(string+"\r\n");
}
public void print(int data) {
this.print(String.valueOf(data));
}
public void println(int data) {
this.println(String.valueOf(data));
}
public void print(double data) {
print(String.valueOf(data));
}
public void println(double data) {
this.print(String.valueOf(data));
}
}
public class TestDemo{
public static void main(String[] args) throws IOException {
PrintUtil printUtil = new PrintUtil(System.out);
printUtil.print(1);
printUtil.println(2);
printUtil.print("Hello");
printUtil.println("Jan");
printUtil.print(6.0);
}
}
//++++++++++++++++++++++++++
12
HelloJan
6.0
输出内容没有任何变化,只是输出流从写入文件变成了输出到屏幕。
系统输入:in
System.in对应的是InputStream,而这种输入流指的是用户通过键盘进行输入,java本身并没有直接的用户输入处理,如果想要实现这种操作,必须使用java.io的模式完成。
public class TestDemo{
public static void main(String[] args) throws IOException {
InputStream in = System.in;//将键盘值作为输入流
byte[] data = new byte[4];//缓存区
System.out.println("input:>");//提示
int tmp = in.read(data);//输入操作
in.close();
System.out.println(new String(data,0,tmp));//打印
}
}
//++++++++++++++++++++++++++++++++++++++++
input:>
你好啊
你好
当输入时,程序进入阻塞状态,直到用户输入回车才继续运行,另外,就如上面打印结果所示,缓存区是固定值,太小的话会造成数据确实。
所以引用内存流。
public class TestDemo{
public static void main(String[] args) throws IOException {
InputStream in = System.in;//屏幕接收输入流
ByteArrayOutputStream bb = new ByteArrayOutputStream();//内存输出流
byte[] data = new byte[4];//缓存
System.out.println("input:>");//提示
int tmp = 0;
while ((tmp = in.read(data)) != -1) {//读到内存流
bb.write(data,0,tmp);//写到内存流
if (tmp < data.length) {//手动检测读取结束
break;
}
}
in.close();//记得关闭流
bb.close();
System.out.println(new String(bb.toByteArray()));//
}
}
两种输入流
BufferedReader类
BufferedReader类属于一个缓冲输入流,而且是一个字符流的操作对象,在java中对于缓冲流也分为两类
BufferedReader类的一个方法:
String readLine() throws IOException
这个方法可以直接读取一行数据,(回车)
public class TestDemo{
public static void main(String[] args) throws IOException {
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));//InputReader();
System.out.println("input:>");
String str = buf.readLine();//读行
System.out.println(str);
}
}
这是多年以前的标准输入格式,早已被Scanner取代。
java.util.Scanner类
打印流解决的是OutputStream类的缺陷,BufferedReader解决的是InputStream类的缺陷。而Scanner解决的是BufferedReader类的缺陷(替换了BufferedReader类)
Scanner是一个专门进行输入处理的程序类,可以结合正则表达式进行各项处理,常用方法:
实例
public class TestDemo{
public static void main(String[] args) throws IOException {
Scanner scn = new Scanner(System.in);
System.out.println("Input:>");
if(scn.hasNext()) {//是否有输入内容
System.out.println(scn.next());
}
scn.close();
}
}
接收其他数据类型
public class TestDemo{
public static void main(String[] args) throws IOException {
Scanner scn = new Scanner(System.in);
System.out.println("Input:>");
if(scn.hasNext()) {
int age = scn.nextInt();//输入int,如果是非int程序会抛出异常
System.out.println(age);
}else {
System.out.println("input error");
}
scn.close();
}
}
文件操作
public class TestDemo{
public static void main(String[] args) throws IOException {
Scanner scn = new Scanner(new FileInputStream(new File("C:/Users/wrinkle/Desktop/Localhost名字问题.txt")));
// scn.useDelimiter("\n");//自定义当前文件
while (scn.hasNext()) {
System.out.println(scn.next());
}
scn.close();
}
}
总结:以后出来二进制文件拷贝处理之外,那么只要是针对程序的信息输出都用打印流(PrintStream、PrintWriter),信息输出用Scanner。
序列化
所有的开发都涉及到序列化
概念
将内存中的对象转为二进制数据流的形式进行传输,或是将其保存在文本中。不过,并非所有的对象都可以被实例化,一般要进行传输的才进行实例化,需要实例化java.io.Serializable接口,这个接口并没有任何方法定义,只是一个标识。
序列化与反序列化操作
需要用到java.io中的两个类
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 TestDemo{
public static final File FILE = new File("C:/Users/wrinkle/Desktop/test.txt");
public static void main(String[] args) throws IOException, ClassNotFoundException {
ser(new Person("Jan", 23));
}
public static void ser(Object obj)throws FileNotFoundException,IOException, ClassNotFoundException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(FILE));
// ObjectOutputStream out = new ObjectOutputStream(System.out);
out.writeObject(obj);
out.close();
//反序列化操作
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(FILE));
System.out.println(oin.readObject());
oin.close();
}
}
transient关键字
使某些属性不被序列化,不会保存
class Person implements Serializable{
private transient 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 TestDemo{
public static final File FILE = new File("C:/Users/wrinkle/Desktop/test.txt");
public static void main(String[] args) throws IOException, ClassNotFoundException {
ser(new Person("Jan", 23));
}
public static void ser(Object obj)throws FileNotFoundException,IOException, ClassNotFoundException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(FILE));
// ObjectOutputStream out = new ObjectOutputStream(System.out);
out.writeObject(obj);
out.close();
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(FILE));
System.out.println(oin.readObject());
oin.close();
}
}
//++++++++++++++++++++++++++++++++++++
Person [name=null, age=23]
在name
的属性加了transient
关键字,最后结果为null,因为没有被序列化保存。