Java I/O
1、File类
File类能新建、删除、重命名文件或目录,如果需要访问文件内容则使用I/O流:
import java.io.File;
import java.nio.file.Files;
public class Test
{
public static void main(String[] args) throws Exception
{
File file = new File(".");//.表示当前路径,它是运行Java虚拟机时的路径,即使用"java"命令执行程序时所在的目录。
String str = file.getName(); // .
str = file.getPath(); // .
str = file.getAbsolutePath(); // D:\java_project\Test\sources\.
str = file.getCanonicalPath(); // D:\java_project\Test\sources
str = file.getParent(); // null
File fileAbs = file.getAbsoluteFile();
str = fileAbs.getName(); // .
str = fileAbs.getPath(); // D:\java_project\Test\sources\.
str = fileAbs.getAbsolutePath(); // D:\java_project\Test\sources\.
str = fileAbs.getCanonicalPath(); // D:\java_project\Test\sources
str = fileAbs.getParent(); // D:\java_project\Test\sources
file = new File("test");
str = file.getName(); // test
str = file.getPath(); // test
str = file.getAbsolutePath(); // D:\java_project\Test\sources\test
str = file.getCanonicalPath(); // D:\java_project\Test\sources\test
str = file.getParent(); // null
fileAbs = file.getAbsoluteFile();
str = fileAbs.getName(); // test
str = fileAbs.getPath(); // D:\java_project\Test\sources\test
str = fileAbs.getAbsolutePath(); // D:\java_project\Test\sources\test
str = fileAbs.getCanonicalPath(); // D:\java_project\Test\sources\test
str = fileAbs.getParent(); // D:\java_project\Test\sources
//重命名文件
File file = new File("E:/test1.txt");
File fileNew = new File("E:/test2.txt");
file.renameTo(fileNew);
//移动文件并重命名文件
File file = new File("E:/test2.txt");
File fileTo = new File("F:/source/test3.txt");
file.renameTo(fileTo);
//复制并重命名文件
File file = new File("E:/test2.txt");
File fileDest = new File("F:/source/test3.txt");
Files.copy(file.toPath(), fileDest.toPath());
//注册删除钩子:当java虚拟机退出时删除指定文件
File file = new File("E:/test.txt");
file.deleteOnExit();
}
}
File类中其它常用方法:
exists():判断文件或目录是否存在。
length():获取文件大小。
delete():删除文件或目录。
createNewFile():创建文件,如果该文件不存在的话。
mkdir():创建目录。
list()/listFiles():获得所有子文件和目录。
其中list()的一个版本支持接收一个FilenameFilter接口,FilenameFilter接口里包含一个accept()方法,该方法会对File的子目录或文件进行迭代,返回true的话list()才会列出该子目录或文件。FilenameFilter接口里只有一个抽象方法,因此该接口是一个函数式接口,可以使用lambda表达式创建实现该接口的对象:
public class Test
{
public static void main(String[] args)
{
File file = new File(".");
// 使用Lambda表达式实现文件过滤器,如果文件名以.java结尾,或者文件对应一个路径,返回true
String[] nameList = file.list((dir, name) -> name.endsWith(".java") || new File(name).isDirectory());
for(String name : nameList)
{
System.out.println(name);
}
}
}
2、I/O流
java的I/O流体系如下图所示:
上图中粗字体表示节点流,必须直接与指定物理节点(文件、数组、管道、字符串)关联,处理流则可以包装一个节点流来使用。
以File开头的为读写文件流、ByteArray或CharArray开头的读写二进制数组或字符数组流、Piped为读写管道,String开头的为读写字符串流。
InputStream、Reader是输入流抽象基类,OutputStream、Writer是输出流抽象基类,这里的输入、输出指的是从内存考虑,即数据从文件输入到内存buffer、数据从内存buffer输出到文件。
字节流以InputStream、OutputStream为基类,数据单元为一个字节,字符流以Reader、Writer为基类,数据单元为两个字节。
因为I/O流类实现了AutoClosebale接口,因此也可以像JDBC那样通过自动关闭资源的try块来自动关闭I/O流。
以下为使用FileInputStream来读取文件的示例:
public class FileStreamTest
{
public void readData() throws IOException
{
FileInputStream fis = new FileInputStream("test.java");
byte[] bbuf = new byte[1024];
int hasRead = 0;
while ((hasRead = fis.read(bbuf)) > 0 ) //读取1024个单位的数据到buffer
{
//ToDo:
}
fis.close();
}
public void writeString() throws IOException
{
try(
FileWriter fw = new FileWriter("test.txt"))
{
fw.write("测试数据\r\n");
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
以下为使用StringReader和StringWriter来读写String的示例:
import java.io.*;
public class StringNodeTest
{
public static void main(String[] args)
{
// 采用循环读取的访问读取字符串
String src = "从明天起,做一个幸福的人\n"
+ "喂马,劈柴,周游世界\n"
+ "从明天起,关心粮食和蔬菜\n"
+ "我有一所房子,面朝大海,春暖花开\n"
+ "从明天起,和每一个亲人通信\n"
+ "告诉他们我的幸福\n";
char[] buffer = new char[32];
int hasRead = 0;
try(
StringReader sr = new StringReader(src))
{
while((hasRead = sr.read(buffer)) > 0)
{
System.out.print(new String(buffer ,0 , hasRead));
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
// 调用StringWriter的方法执行输出
try(
// 创建StringWriter时,因为String是不可变的字符串对象,所以实际上以一个StringBuffer
//作为输出节点,指定的20就是StringBuffer的初始长度
StringWriter sw = new StringWriter(20))
{
sw.write("有一个美丽的新世界,\n");
sw.write("她在远方等我,\n");
sw.write("哪里有天真的孩子,\n");
sw.write("还有姑娘的酒窝\n");
System.out.println("----下面是sw的字符串节点里的内容----");
System.out.println(sw.toString());// 使用toString()方法返回StringWriter的字符串节点的内容
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
InputStreamReader、OutputStreamWrite可以将包装的字节流转换成字符输入流、字符输出流。BufferedReader、BufferedWriter流具有缓冲功能,它可以一次读取/写入一行文本。下面代码将标准输入System.in(System.in是字节流类的实例)通过InputStreamReader转换成字符输入流,再将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容:
import java.io.*;
public class KeyinTest
{
public static void main(String[] args)
{
try(
InputStreamReader reader = new InputStreamReader(System.in);// 将Sytem.in对象转换成字符输入对象Reader
BufferedReader br = new BufferedReader(reader))// 将普通Reader包装成BufferedReader
{
String line = null;
// 采用循环方式来一行一行的读取
while ((line = br.readLine()) != null)
{
// 如果读取的字符串为"exit",程序退出
if (line.equals("exit"))
{
System.exit(1);
}
System.out.println("输入内容为:" + line);// 打印读取的内容
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
PushbackInputStream和PushbackReader是推回输入流,可以调用其unread()方法将字节或字符数组中数据推回到“推回缓冲区”中,然后调用read()方法读取数据的时候会先从“推回缓冲区”中读取数据。
3、处理流
按照流的角色可以分为节点流(低级流)和处理流(高级流、包装流),如下所示,节点流必须与指定的物理节点(文件、数组、管道 、字符串)关联,处理流可以包装一个节点流来使用。
使用处理流进行I/O操作更加方便快捷:无需理会I/O节点是磁盘、网络还是其他I/O设备,只要将这些节点包装成处理流,就可以使用相同的I/O代码来读写不同的I/O设备。也就是说程序通过处理流来执行I/O功能,让节点流与底层I/O设备交互,程序员只需关心处理流的操作。比如下面使用PrintStream处理流来包装FileOutputStream,PrintStream的输出功能非常强大,它包含的成员方法可以很方便的进行输出操作,System.out的类型就是PrintStream:
package xu;
import java.io.*;
public class Test
{
public static void main(String[] args) throws Exception
{
try(
FileOutputStream fos = new FileOutputStream("test.txt");
PrintStream ps = new PrintStream(fos))
{
ps.println("普通字符串"); //输出字符串
ps.println(new Test()); //输出对象
ps.println(99); //输出int
ps.format("test%d", 100); //格式化输出
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
4、重定向标准输入/输出
Java的标准输入System.in是从键盘读取输入,标准输出System.out是输出到显示器,可以通过System的静态方法setErr()、setIn()、setOut()来重定向标准错误输出流、标准输入流、标准输出流。
下面程序重定向了标准输出流System.out到文件输出,而不是向显示器输出:
import java.io.*;
public class RedirectOut
{
public static void main(String[] args)
{
try(
//创建PrintStream输出流
PrintStream ps = new PrintStream(new FileOutputStream("out.txt")))
{
System.setOut(ps);// 将标准输出重定向到ps输出流
System.out.println("普通字符串");// 向标准输出输出一个字符串
System.out.println(new RedirectOut());// 向标准输出输出一个对象
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
下面程序重定向祖标准出入流System.in到文件输入,而不是从键盘输入:
import java.util.*;
import java.io.*;
public class RedirectIn
{
public static void main(String[] args)
{
try(
//创建FileInputStream输入流
FileInputStream fis = new FileInputStream("RedirectIn.java"))
{
System.setIn(fis);// 将标准输入重定向到fis输入流
Scanner sc = new Scanner(System.in);// 使用System.in创建Scanner对象,用于获取标准输入
sc.useDelimiter("\n");// 只把回车作为分隔符
// 判断是否还有下一个输入项
while(sc.hasNext())
{
// 输出输入项
System.out.println("键盘输入的内容是:" + sc.next());
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
5、读写其它进程的数据
使用Runtime对象的exec()方法可以运行其他进程,该方法产生一个Process对象,该对象代表启动的子进程。调用Process的以下三个方法可以与启动的子进程进行通信:
getErrorStream():获取子进程的标准错误流
getInputStream():获取子进程的标准输入流
getOutputStream():获取子进程的标准输出流
下面的代码运行了子进程javac命令程序,获取到其标准错误流,该标准错误流对与子进程来说是输出流,而对于本程序则是输入流,将其转换成字符输入流InputStreamReader后包装成BufferedReader就可以来读取其数据并显示出来:
import java.io.*;
public class ReadFromProcess
{
public static void main(String[] args)
throws IOException
{
// 运行javac命令,返回运行该命令的子进程
Process p = Runtime.getRuntime().exec("javac");
try(
// 以p进程的错误流创建BufferedReader对象
BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream())))
{
String buff = null;
// 采取循环方式来读取p进程的错误输出
while((buff = br.readLine()) != null)
{
System.out.println(buff);
}
}
}
}
下面的代码通过Process的getOutputStream获取到子进程“ReadStandard”的标准输入流,对于本程序来说就是获取到了向子进程输出数据的流,本程序通过该输出流向子进程输入数据,子进程中将标准输入流的内容写入到out.txt文件中:
import java.io.*;
import java.util.*;
public class WriteToProcess
{
public static void main(String[] args)
throws IOException
{
// 运行java ReadStandard命令,返回运行该命令的子进程
Process p = Runtime.getRuntime().exec("java ReadStandard");
try(
// 以p进程的输出流创建PrintStream对象
// 这个输出流对本程序是输出流,对p进程则是输入流
PrintStream ps = new PrintStream(p.getOutputStream()))
{
// 向ReadStandard程序写入内容,这些内容将被ReadStandard读取
ps.println("普通字符串");
ps.println(new WriteToProcess());
}
}
}
// ReadStandard类,该类可以接受标准输入,并将标准输入写入到out.txt文件。
class ReadStandard
{
public static void main(String[] args)
{
try(
// 使用System.in创建Scanner对象,用于获取标准输入
Scanner sc = new Scanner(System.in);
PrintStream ps = new PrintStream(new FileOutputStream("out.txt")))
{
sc.useDelimiter("\n");// 只把回车作为分隔符
// 判断是否还有下一个输入项
while(sc.hasNext())
{
ps.println("键盘输入的内容是:" + sc.next());// 输出输入项
}
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}
6、RandomAccessFile
RandomAccessFile可以读、写文件,并提供移动文件指针的功能,getFilePointer()成员可以获得当前文件指针,seek()用来移动文件指针。创建RandomAccessFile时需要指定一个mode参数,其取值可以为:
"r":只读,写的话会抛出IOException异常。
"rw":读写,如果文件不存在则创建该文件。
"rwd":读写,且对文件内容的每个更新都同步写入到底层存储设置。
"rws":读写,且对文件内容和元数据的每个更新都同步写入到底层存储设置。
import java.io.*;
public class AppendContent
{
public static void main(String[] args)
{
try(
//以读、写方式打开一个RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile("out.txt" , "rw"))
{
//将记录指针移动到out.txt文件的最后并写入数据
raf.seek(raf.length());
raf.write("追加的内容!\r\n".getBytes());
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
推荐阅读
-
ant编译java报“非法字符: \65279 ”错误的解决方法 z
-
Java的参数传递是「按值传递」还是「按引用传递」?
-
安装ssl证书后报错Caused by: java.io.IOException: DerInputStream.getLength(): lengthTag=109, too big.
-
java获取文件的mime,java获取文件是不是文本,java获取文件类型(非后缀方式)
-
【JAVA设计模式】- 建造者模式
-
Java中静态代码块、构造代码块、构造函数、普通代码块
-
01 java 基础:jdk jre path classpath 相关问题
-
Java的内存溢出
-
Eclipse中Java自定义注解模板详解
-
java问题排查工具