java.io包提供了大量的流类,其中所有输入流都是InputStream抽象类(字节输入流)和Reader抽象类(字符输入流)的子类,而所有的输出流都是OutputStream抽象类(字节输出流)和Writer抽象类(字符输出流)的子类。
File类:(处理文件和目录)
File对象主要获取文件本身的属性,如文件目录、文件长度、和文件读写权限等,不涉及对文件内容的读写操作。
创建File对象的构造方法有如下三个:
File(String filename)
File(String directoryPath, String filename)
File(File f, String filename) //f是制定一个目录的文件
文件的属性:
经常使用File类下列方法获取文件本身一些信息:
public String getName() //获取文件名
public boolean canRead() //判断文件是否可读
public boolean canWrite() //判断文件是否可写
public boolean exists() //判断文件是否存在
public long length() //获取文件长度(字节单位)
public String getAbsolutePath() //获取文件绝对路径
public String getParent() //获取文件父目录
public boolean isFile() //判断文件是否是一个正常文件,而不是目录
public boolean isDirectory() //判断文件是否是一个(已存在)目录
public boolean isHidden() //判断文件是否是隐藏文件
public long lastModified() //获取文件最后修改时间(时间是从1970年午夜至文件最后修改时刻的毫秒数)
目录:
1、创建目录:
使用File对象调用方法public boolean mkdir()可创建一个目录,成功创建返回true,否则返回false(如果该目录已经存在则返回false)。
2、列出目录中文件:
如果File对象是一个目录,该对象可以调用下列方法列出该目录下的文件和子目录。
public String[] list() //用字符串形式返回目录下的全部文件
public File[] listFiles() //用File对象形式返回目录下的全部文件
文件过滤器:
有时需要列出目录下指定类型文件,可使用File类的两个方法,如下:、
public String[] list(FilenameFilter obj)
public File[] listFiles(FilenameFilter obj)
FilenameFilter是一个接口,该接口有一个方法:
public boolean accept(File dir, String name)
使 用list方法时,需要想该方法传递一个实现FilenameFilter接口的对象。list方法执行时,参数不断回调接口方法accept(File dir, String name),参数name是实例化目录的一个文件名,参数dir为调用list的当前目录,当接口方法返回true时,list方法就将目录dir中的文 件存放到返回的数组中。
例子:(列出当前目录下全部java文件名称)
import java.io.*;
public class test1 {
public test1() {
// TODO 自动生成构造函数存根
}
public static void main(String[] args) {
File dir = new File("."); //系统中"."表示当前目录,".."表示当前上一级目录,("\"为根目录,但为了转义表示为"\\")
FileAccept acceptCondition = new FileAccept("java");
String[] fileName = dir.list(acceptCondition);
for(int i=0; i<fileName.length; i++){
System.out.println(fileName[i]);
}
}
}
public class FileAccept implements FilenameFilter {
String str = null;
public FileAccept(String s) {
// TODO 自动生成构造函数存根
str = "."+s;
}
public boolean accept(File dir, String name) {
// TODO 自动生成方法存根
return name.endsWith(str);
}
}
文件的创建与删除:(如下例)
File f = new File("c:\\","letter.dat"); //先用File类创建一个文件对象。
if(!f.exists()){ //如果该文件夹中没有该文件,就新建一个
try{
f.createNewFile();
}
catch(IOException e){
}
}
if(f.exists()){ //如果存在该文件,就删除。也可以之间用f.deleteOnExit()方法。
f.delete();
}
运行可执行文件:
要运行一个可执行文件,用java.lang.Runtime类。
先用Runtime类声明一个对象,如:
Runtime run;
然后用该类的类方法getRuntime()创建这个对象,如:
run = Runtime.getRuntime();
其中run可以调用exec(String command)方法打开本地机子上的可执行文件或执行一个操作。
示例如下:
try{
Runtime run = Runtime.getRuntime();
File file = new File("c:\\windows","Notepad.exe");
run.exec(file.getAbsolutePath());
}
catch(Exception e){
System.out.println(e);
}
字节输入输出流类:(FileInputStream类和FileOutputStream类)
当文件读取需求比较简单时,可使用FileInputStream类,该类派生于InputStream类。
FileInputStream类常用构造方法两个:
FileInputStream(String name) //使用给定文件名name创建FileInputStream对象
FileInputStream(File file) //使用File对象创建FileInputStream对象
使用FileInputStream文件输入流对象读取文件,示例如下:
FileInputStream istream = new FileInputStream("hello.txt");
或
File file = new File("hello.txt");
FileInputStream istream = new FileInputStream(file);
处理I/O异常:
使用文件输入流构造方法创建对象可能会出现错误,如要打开的文件不存在等,使用IOException对象表示这个出错信号。程序必须捕获处理这个异常。类似如下:
try {
FileInputStream istream = new FileInputStream("hello.txt");
}
catch(IOException e) {
System.out.println("File read error:"+e);
}
(I/O操作对于错误特别敏感,所有许多流类的构造方法和读写方法都会引起I/O异常,程序必须捕获处理这些异常。)
从输入流读取字节:
输入流调用read()方法从输入流中读出数据。方法如下:
int read(); //顺序读取源中单个字节数据,返回字节值(0-255整数),如果到达源末尾,返回-1。
int read(byte b[]); //读取b.length个字节,存放在数组b中,返回实际读取的字节数。
int read(byte b[], int off, int len) //读取len个字节,存放在数组b中,off是首字节在数组中的存放位置,返回实际读取字节数。
(read()方法顺序读取文件,只要流不关闭,每次调用read()方法就顺序读取源中其余内容,直到源的末尾或流关闭!)
关闭流:
使用完流关闭它是个好习惯,尽管java在程序结束时会自动关闭所有打开的流。因为一个被打开的流可能会用尽系统资源,这取决 与平台和实现。如果不关闭被打开的流,就可能不允许另一个程序操作利用这些流所用的资源。关闭输出流的另一个原因是保证操作系统把流缓冲区的数据写到磁盘 上(目的地)。
可以用文件输入/输出流对象调用close()方法关闭流。
FileOutputStream类与FileInputStream类对应。构造方法及其使用与FileOInputStream一致。
可以用输出流对象调用write方法把字节写入到输出流,到达目的地。如下:
public void write(int b) //将指定字节写到输出流
public void write(byte b[]) //写b.length个字节到输出流
public void write(byte b[], int off, int len) //从字节数组b中起始于偏移量off处写入len个字节到输出流
(FileOutputStream流顺序地写文件,只要不关闭流,每次调用write()方法就顺序地向输出流写入数据,直到流关闭!)
字符输入输出流类:(FileReader类和FileWriter类)
字节输入输出流类以字节单位读写数据,不能很好的操作Unicode字符。如果使用字符流就不容易出现乱码问题。
FileReader和FileWriter分别是Reader和Writer的子类,构造方法如下:
FileReader(String name); FileReader(File file);
FileWriter(String name); FileWriter(File file);
字符输入/输出流的read()和Write()方法使用字符数组读写数据,以字符(char)为基本单位。常用方法如下:
int read() //从源中读取一个字符,返回一个Unicode字符值(0-65535整数),未读出字符返回-1。
int read(char b[])
int read(char b[], int off, int len)
void write(int n) //向目的地写入一个字符(Unicode字符值)
void write(char b[])
void write(char b[], int off, int len)
BufferedReader类和BufferedWriter类:
当需要每次读写一行,无法知道一行有多少字符,即字符数组大小无法确定,这时FileReader,FileWriter类就很难完成这样的任务。
java 中用BufferedReader和BufferedWriter流可以完成这样的工作。二者的源和目的地必须是字符输入流和字符输出流。因此,可以把 Reader(字符输入流)作为BufferedReader流的源,把Writer(字符输出流)流作为BufferedWriter流的目的地。
构造方法如下:
BufferedReader(Reader in);
BufferedWriter(Writer out);
用BufferedReader类对象调用readLine()方法读取文本行:
FileReader filereader = new FileReader("Student.txt");
BufferedReader in = new BufferedReader(filereader);
String strLine = in.readLine();
类似的,可以将BufferedWriter流和FileWriter流连接在一起,然后使用BufferedWriter流将数据写到目的地,如:
FileWriter filewriter = new FileWriter("Student.txt");
BufferedWriter out = new BufferedWriter(filewriter);
然后输出使用BufferedWriter类的方法,如:
write(String s, int off, int len) //把字符串s写到Student.txt中,off是s开始处的偏移量,len是写入的字符数量。
可 以把BufferedReader和BufferedWriter称作上层流,把它们指向的字符流称作底层流。java采用缓存技术将上层流和底层流连 接。底层字符输入流先把数据读入缓存,BufferedReader流再从缓存读取数据;BufferedWriter流将数据写入缓存,底层字符输出流 会不断地将缓存中的数据写到目的地。
当BufferedWriter流调用flush()方法刷新缓存或调用close()方法关闭时,即使缓存还没有溢满,底层流也会立刻将缓存的数据写到目的地。
RandomAccessFile类:
Java提供了更完善的用来处理文件输入输出操作功能的RandomAccessFile流,该流的指向既可以作为源也可以作为目的地,也就是说对一个文件进行读写操作时,创建一个指向文件的RandomAccessFile流即可。
RandomAccessFile类两个构造方法:
RandomAccessFile(String name, String mode) //mode取r(只读)或rw(可读写)
RandomAccessFile(File file, String mode) //mode取r(只读)或rw(可读写)
RandomAccessFile类中有个方法seek(long a),用来定位RandomAccessFile流的读写位置,参数a确定读写位置距离文件开头的字节数。还可以调用getFilePointer()方 法获取流当前读写位置。RandomAccessFile流对文件的读写比顺序读写更为灵活!
(该类还有许多方法,详细查看JDK)
数据流:(DataInputStream和DataOutputStream类)
数据输入流和数据输出流,允许程序按照与机器无关的风格读取Java原始数据。也就是说,当读取一个数值时,不必关心这个数值应该是多少字节。
构造方法:
DataInputStream(InputStream in) //将创建的数据输入流指向一个由in指定的底层输入流
DataInputStream(OutputStream out) //将创建的数据输出流指向一个由out指定的底层输出流
(这个流类很神奇!Java确实提供了很多C++程序员很渴望的高级工具,详细查看JDK吧)
带进度条的输入流:
使用javax.swing.ProgressMonitorInputStream类创建的输入流读取文件时可以看见文件的读取进度。
构造方法如下:
ProgressMonitorInputStream(Component c, Object message, InputStream in) //进度条在c指定组件正前方显示,若取null,则在屏幕正前方显示,message指定在进度条上的显示信息(String)
示例如下:
import javax.swing.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class test1 {
public static void main(String[] args) {
byte b[] = new byte[2];
JTextArea text = new JTextArea(20,20);
JFrame jframe = new JFrame();
jframe.setSize(280,300);
jframe.setVisible(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.add(new JScrollPane(text),BorderLayout.CENTER);
jframe.validate();
try{
FileInputStream input = new FileInputStream("test1.java");
ProgressMonitorInputStream input_progress = new ProgressMonitorInputStream(jframe,"读取java文件",input);
ProgressMonitor p = input_progress.getProgressMonitor();
while(input_progress.read(b) != 1){
String s = new String(b);
text.append(s);
Thread.sleep(10);
}
}
catch(Exception e){}
}
}
对象流:(ObjectInputStream和ObjectOutputSteam类)
ObjectInputStream(对象输入流)和ObjectOutputSteam(对象输出流)类分别是InputStream和OutputStream类的子类。
ObjectInputStream对象调用readObject()方法读取一个对象到程序中,ObjectOutputStream对象调用writerObject(Object obj)方法将一个对象obj写入到一个文件。
两个类构造方法如下:
ObjectInputStream(InputStream in) //in为InputStream的子类创建的输入流对象
ObjectOutputSteam(OutputStream out) //out为OutputStream的子类创建的输出流对象
使用对象流时,要保证对象是序列化的。这是为了保证能把对象写入文件并能正确读回到程序的缘故。
一个类实现了Serializable 接口,它创建的对象就是序列化的。(Serializable接口中的方法对程序不可见,由JVM自动实现,因此实现该接口不需要实现额外的方法。但要注 意,使用对象流把一个对象写入到文件时,除了要保证对象是序列化的,还要保证对象的成员对象也必须是序列化的。)
示例:
import java.io.*;
public class Student implements Serializable {
String name = null;
double height;
public Student(String name, double height) {
// TODO 自动生成构造函数存根
this.name = name;
this.height = height;
}
public void setHeight(double c){
this.height = c;
}
}
public class test1 {
public static void main(String[] args) {
Student zhang = new Student("张三", 1.65);
try{
FileOutputStream file_out = new FileOutputStream("s.txt");
ObjectOutputStream object_out = new ObjectOutputStream(file_out);
object_out.writeObject(zhang);
FileInputStream file_in = new FileInputStream("s.txt");
ObjectInputStream object_in = new ObjectInputStream(file_in);
Student li = (Student)object_in.readObject();
li.setHeight(1.78);
li.name = "李四";
System.out.println(zhang.name+"身高是:"+zhang.height);
System.out.println(li.name+"身高是:"+li.height);
}
catch(ClassNotFoundException event){
System.out.println("不能读出对象");
}
catch(IOException event){
System.out.println("can not read file "+event);
}
}
}
序列化与对象克隆:
一个类的两个对象如果具有相同引用,那么他们具有相同的实体和功能。如:
A one = new A();
A two = one;
这样对one成员变量的操作将会改变two中的成员变量(两个实体是同一个)。
如果想得到对象的克隆对象(克隆对象的变化不会引起原对象的变化),调用clone()方法即可。(注意:如果源对象有引用型成员变量,就牵涉到深度克隆问题,这时要重写clone()方法,类似于C++中的深浅拷贝。)
使用对象流很容易获取一个序列化对象的克隆。(因为将对象写入对象输出流指向的目的地再读回的对象一定是原对象的一个克隆)
专门写一个利用对象输入输出流克隆对象的类:
import java.io.*;
public class GetCloneObject {
public Object getClone(Object obj){
Object object = null;
try{
FileOutputStream file_out = new FileOutputStream("对象的序列化信息.txt");
ObjectOutputStream object_out = new ObjectOutputStream(file_out);
object_out.writeObject(obj);
FileInputStream file_in = new FileInputStream("对象的序列化信息.txt");
ObjectInputStream object_in = new ObjectInputStream(file_in);
object = object_in.readObject();
}
catch(Exception event){
System.out.println(event);
}
return object;
}
}
Java提供的绝大多数对象都是序列化的。如StringBuffer,Component类等,它们都实现了Serializable接口。可以把组件写入输出流,然后再用输入流读取该组件的克隆。
如下示例:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MyWin extends JFrame implements ActionListener {
JTextArea text = null;
JButton button;
GetCloneObject cloneObject;
public MyWin(){
// TODO 自动生成构造函数存根
setLayout(new FlowLayout());
cloneObject = new GetCloneObject();
text = new JTextArea(6,10);
button = new JButton("得到文本区的克隆");
button.addActionListener(this);
setVisible(true);
add(new JScrollPane(text));
add(button);
setSize(580,300);
validate();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
// TODO 自动生成方法存根
JTextArea textClone = (JTextArea)cloneObject.getClone(text);
textClone.setBackground(Color.pink);
this.add(new JScrollPane(textClone));
this.validate();
}
}
public class test1 {
public static void main(String[] args) {
MyWin win = new MyWin();
}
}
文件锁:(FileLock和FileChannel类)
java.nio.FileLock和java.nio.channels.FileChannel类。输入,输出流读写文件时可以用文件锁,用RandomAccessFile类创建的流在读写文件时可以使用文件锁,只要不解除该锁,其他线程无法操作被锁定的文件。
(1)、先用RandomAccessFile流建立指向文件的流对象(读写属性必须是rw):
RandomAccessFile input = new RandomAccessFile("hello.java","rw");
(2)、流对象input调用方法getChannel()获得一个连接到底层文件的FileChannel对象(信道):
FileChannel channel = input.getChannel();
(3)、信道调用tryLock()或lock()方法获得一个FileLock(文件锁)对象,这个过程叫做对文件加锁:
FileLock lock = channel.tryLock();
文件锁对象产生后,禁止任何程序对文件进行操作或再进行加锁。如果想进行操作必须先让FileLock对象调用release()方法释放文件锁:
lock.release();