admin 管理员组

文章数量: 1184232

18 文件属性查看器( GUI+ 文件操作)

本章通过 Swing 组件实现文件属性查看器界面,通过文件的操作来获取文件的相关属性并显示在界面中。“文件属性查看器”项目的实现,综合了图形用户界面的相关知识点和文件的操作。

本章的学习目标如下:

q 掌握组件和面板的使用方法;

q 了解文件的操作;

q 熟悉文件操作和访问的类。

文件属性查看器原理

“文件属性查看器”项目通过单击“查看”按钮,打开显示文件或目录的属性表格,这些文件或目录的具体位置为文本框中表示地址的字符串。

18.1.1 项目结构框架分析

文件属性查看器项目可利用 Swing 组件实现图形用户界面。文件属性查看器项目目录如图 18.1 所示,各个包的功能如下。

q FileAttrView :自定义窗口组件类。

q FileAttrFrame :利用 Swing 组件实现界面。

18.1 项目目录

18.1.2 项目功能业务分析

本节将向读者介绍整个项目要实现的功能。这些功能包括文件属性查看器的初始化界面、查看已存在文件属性、查看已存在的目录属性,以及查看不存在的文件、目录属性和退出功能。

1 .初始化界面

当运行文件属性查看器项目中的 FileViewer 类后,会出现如图 18.2 所示的初始界 面——文件属性查看器界面。

18.2 初始化界面

2 .查看已存在的文件属性

当出现初始化界面后,在“文件的地址”文本框中输入 D:\\cjgong.txt 字符串(已存在的文件地址),然后单击“查看”按钮,主界面的中间会显示出该文件的所有属性信息,具体过程如图 18.3 所示。

18.3 查看已存在的文件

3 .查看已存在的目录属性

当出现初始化界面后,在“文件的地址”文本框中输入 D:\\cjgong 字符串(已存在的目录地址),然后单击“查看”按钮,主界面的中间会显示出该目录的所有属性信息,具体过程如图 18.4 所示。

18.4 查看已存在的目录

4 .查看不存在的文件和目录属性

当出现初始化界面后,在“文件的地址”文本框中输入 D:\\test 字符串(不存在的目录地址),然后单击“查看”按钮,主界面的中间会显示出该目录的所有属性信息,具体过程如图 18.5 所示。

18.5 查看不存在的目录过程

5 .退出功能

当出现初始界面后,如果想实现退出功能,可以单击右上角的 按钮,如图 18.6 所示。

18.6 退出功能

文件属性查看器项目

文件属性查看器项目具体程序架构如图 18.7 所示,它包含一个“文件属性查看器输入界面”的自定义窗口类 FileAttrFrame.java ,以及自定义窗口显示位置的类 FileAttrView.java

18.7 程序关系图

18.2.1 实现显示文件信息的自定义窗口

FileViewer 为“文件属性查看器”项目中的自定义窗口类,该类不仅继承了 JFrame 类,而且还实现了各个组件的相应功能,具体内容如代码 18.1 所示。

代码 18.1 自定义窗口类: FileAttrFrame.java

public class FileAttrFrame extends JFrame {

//创建成员变量

private JPanel contentPane;

private BorderLayout borderLayout1 = new BorderLayout();                                                     //创建布局管理器对象

private JTextField jTextField1 = new JTextField();                                                           //创建文本域对象jTextField1

private JScrollPane jScrollPane1 = new JScrollPane();                                                        //创建滚动面板对象jScrollPane1

private JTable jTable1;                             //创建表格对象

private JButton jButton1 = new JButton();           //创建按钮对象

File file;                                               //创建文件对象

public FileAttrFrame() {                                 //构造函数

//注册window事件

enableEvents(AWTEvent.WINDOW_EVENT_MASK);

try {

jbInit();                                     //调用jbInit()方法

} catch (Exception e) {

e.printStackTrace();

}

}

public void jbInit() throws Exception {             //创建初始化方法

contentPane = (JPanel) this.getContentPane();  //为contentPane赋值

jTextField1.setText("文件的地址");                  //为文本域对象设置文本

contentPane.setLayout(borderLayout1);            //设置布局管理器

this.setSize(new Dimension(394, 164));           //设置大小

this.setTitle("文件属性查看器");                     //设置标题

jScrollPane1.setAutoscrolls(true);               //设置滚动面板对象

jButton1.setText("查看");                            //设置按钮文本

//按钮的处理事件

jButton1.addActionListener(new java.awt.event.ActionListener() {

@Override

public void actionPerformed(ActionEvent e) {

jButton1_actionPerformed(e);              //调用相应的方法

}

});

//添加各个组件到主窗口中

contentPane.add(jButton1, BorderLayout.SOUTH);

contentPane.add(jTextField1, BorderLayout.NORTH);

contentPane.add(jScrollPane1, BorderLayout.CENTER);

}

//处理窗口的各种事件

protected void processWindowEvent(WindowEvent e) {

super.processWindowEvent(e);                       //处理窗口关闭方法

if (e.getID() == WindowEvent.WINDOW_CLOSING) {

System.exit(0);                                   //退出窗口

}

}

Object[] getfileInfoType() {                   //创建文件属性的标题名字数组

return new Object[] { "Name", "isFile", "length", "canRead",

"canWrite", "lastMotified", "isHidden" };

}

Object[][] getFileInfo(File file) {          //获取文件属性信息数组

File theFile = file;                        //创建theFile对象

Object[][] data = new Object[1][7];    //文件信息数组变量data

if (theFile.exists())

return null;

//用File类的各种方法获取文件属性为数组data赋值

data[0][0] = theFile.getName();         //文件的名字

data[0][1] = String.valueOf(theFile.isFile());     //是否为文件

data[0][2] = String.valueOf(theFile.length());     //文件的大小

data[0][3] = String.valueOf(theFile.canRead());   //是否可读

data[0][4] = String.valueOf(theFile.canWrite());  //是否可写

data[0][5] = getDateString(theFile.lastModified());   //最后修改时间

data[0][6] = String.valueOf(theFile.isHidden());  //是否隐藏属性

return data;                                                //返回数组对象

}

public static String getDateString(long mill) {        //日期格式化方法

if (mill < 0)

return "";

Date date = new Date(mill);

Calendar rightNow = Calendar.getInstance();

rightNow.setTime(date);

int year = rightNow.get(Calendar.YEAR);

int month = rightNow.get(Calendar.MONTH);

int day = rightNow.get(Calendar.DAY_OF_MONTH);

int hour = rightNow.get(Calendar.HOUR_OF_DAY);

int min = rightNow.get(Calendar.MINUTE);

return year + "-" + (month < 10 ? "0" + month : "" + month) + "-"

+ (day < 10 ? "0" + day : "" + day)

+ (hour < 10 ? "0" + hour : "" + hour) + ":"

+ (min < 10 ? "0" + min : "" + min);

}

void jButton1_actionPerformed(ActionEvent e) {     //按钮的事件监听器

file = new File(this.jTextField1.getText());  //为file对象赋值

//显示相应信息的表格

this.jTable1 = new JTable(this.getFileInfo(file), this

.getfileInfoType());

jScrollPane1.getViewport().add(jTable1, null); //添加到滚动面板上

}

}

【代码解析】

q 上述代码实现了文件属性查看器中的自定义窗口类,该用户界面涉及的具体容器、对象和布局如图 18.8 所示。

18.8 布局

q 在上述代码中存在一个处理按钮的事件监听器方法 jButton1_actionPerformed() ,在该方法中首先通过获取到的地址创建一个文件对象 file ,然后把该文件的所有信息通过 getFileInfo() 方法存储到数组 data 中并同时显示在表格组件 jTable1 中,最后把该表格组件显示在滚动面板 jScrollPane1 上。

18.2.2 自定义窗口的显示

18.2.1 节创建了自定义窗口类,该窗口实现了“文件属性查看器”项目的全部功能。本节将讲解如何将自定义窗口在计算机屏幕上居中显示出来,具体内容如代码 18.2 所示。

代码 18.2 实现窗口的显示: FileAttrView.java

public class FileAttrView {

private boolean packFrame = false;              //创建布尔变量

public FileAttrView() {                           //构造函数

FileAttrFrame frame = new FileAttrFrame();//创建FileAttrFrame对象

if (packFrame) {                               //根据值来调用相应方法

frame.pack();

} else {

frame.validate();

}

//用来实现居中显示

Dimension screenSize = Toolkit.getDefaultToolkit().getScreen-

Size();                                    //获取屏幕大小

Dimension frameSize = frame.getSize();     //获取窗口大小

if (frameSize.height > screenSize.height) {

frameSize.height = screenSize.height;

}

if (frameSize.width > screenSize.width) {

frameSize.width = screenSize.width;

}

//设置窗口的位置

frame.setLocation((screenSize.width - frameSize.width) / 2,

(screenSize.height - frameSize.height) / 2);

frame.setVisible(true);                         //显示窗口

}

public static void main(String[] args) {

new FileAttrView();                              //创建FileAttrView对象

}

}

【代码解析】

在上述代码中,为了使自定义窗口居中显示,首先获取计算机屏幕大小 screenSize 及自定义窗口的大小 frameSize ,然后通过表达式( screenSize.width-framesize.width /2 和( screenSize.height-frameSize.height /2 来获取中心位置的坐标。

知识点扩展 —— 文件的操作和访问

Java 语言中存在 I/O Input/Output )机制,即输入和输出机制。通过 I/O 处理技术可以将数据保持到文本文件、二进制文件甚至 ZIP 压缩文件,以达到数据永远保存的要求。

18.3.1 通过 FileOp 类实现文件创建和删除功能

Java 语言中,目录是被当作一种特殊的文件来使用的。查看 API 帮助文档可以发现, File 类是唯一代表磁盘文件对象的类。这里的文件本身不包含文件的内容部分。既然 File 类是文件的实体类,那么该类拥有了许多文件方面的属性和方法。

对于 File 类中提供的属性和方法,有一些是针对文件处理的,有一些是针对目录处理的,还有一些属于共用。由于该类的属性和方法太多,所以不能一一列举,下面通过 FileOp 类的实例,来讲解 File 类的一些主要属性和方法,具体内容如代码 18.3 所示。

代码 18.3 文件的基本操作: FileOp.java

public class FileOp {

public static void main(String[] args) {

File f = new File("c:\\1.txt");              //创建文件对象

if (f.exists())                                   //判断文件是否存在

f.delete();                              //如存在则删除

else                                            //如不存在则创建

try {

f.createNewFile();

} catch (Exception e) {

System.out.println(e.getMessage());

}

System.out.println("File name:" + f.getName());    //输出文件的名字

System.out.println("File path:" + f.getPath());    //输出文件的路径

System.out.println("Abs path:" + f.getAbsolutePath());                                                                //输出文件的绝对路径

System.out.println("Parent:" + f.getParent()); //输出文件的父路径

System.out.println(f.exists() ? "exists" : "does not exist");                                                    //文件是否存在于磁盘中

System.out.println(f.canWrite() ? "is writeable" : "is not

writeable");                               //是否可写

System.out.println(f.canRead() ? "is readable" : "is not readable");                                                 //是否可读

System.out.println(f.isDirectory() ? "is " : "is not" + " a

directory");                               //是否是目录

System.out.println(f.isFile() ? "is normal file"

: "might be a named pipe");        //是否是文件

//是否绝对路径

System.out.println(f.isAbsolute() ? "is absolute" : "is not

absolute");

System.out.println("File last modified:" + f.lastModified());                                                    //输出文件的最后修改时间

System.out.println("File size:" + f.length() + " Bytes");                                                        //输出文件的大小

}

}

运行 FileTest.java 类,当文件 c:\1.txt 存在时,控制台窗口如图 18.9 所示;当文件不存在时,控制台窗口如图 18.10 所示。

【代码解析】

在上述代码中,首先创建了与文件 1.text 相关联的 File 类对象 f ,然后通过该对象的 exists() 方法判断 1.text 文件是否存在。如果存在就通过该对象的 delete() 方法删除;否则通过该对象的 createNewFile() 方法实现该文件的创建。最后,通过对象的各种属性和方法输出所创建文件 1.text 的各种属性。

18.9 文件存在时控制台窗口显示 18.10 文件不存在时控制台窗口显示

18.3.2 通过 FileDir 类实现列举文件和目录的功能

Java 使用 FileDir 类来列举文件和目录,下面举例说明,如代码 18.4 所示。

代码 18.4 文件列举的基本操作: FileDir.java

public class FileDir {

public static void main(String argv[]) {

File f = new File(System.getProperty("user.dir")); //创建File对象

File files[] = f.listFiles();       //获取File对象下的所有文件数组

for (int i = 0; i < files.length; i++) {   //遍历文件数组

if (files[i].isDirectory())              //判断是否为目录

System.out.print("<Dir>\t");

else

System.out.print("\t" + files[i].length());//输出文件的大小

System.out.println("\t" + files[i].getName()); //输出文件的名字

}

}

}

运行 FileDir.java 类,控制台窗口如图 18.11 所示,而用来列举文件的文件夹目录如图 18.12 所示。

18.11 运行结果 18.12 文件夹目录

【代码解析】

在上述代码中,首先创建了与系统属性变量 user 相对应的文件夹相连的 File 对象 f ,然后通过该对象的 listFiles() 方法获取文件数组,最后遍历该数组,如果为文件类型,则输出文件的长度和名字;如果为目录,则输出目录名字。

18.3.3  File 类提供的属性和方法

通过 API 帮助文档可以查看 File 类提供的属性和方法,如下所示。

1 .构造函数

对于文件操作方法,首先从 File 类的构造方法说起,该类具有 3 种构造函数,它们的语法格式分别如下:

1 File (String pathname)

参数 pathname 表示文件的路径名(包含文件名),该函数用于创建一个指定路径的 文件。

% 注意: 对于参数pathname,一般格式为“路径\文件名”格式,如果没有路径名称,则在程序运行的目录下创建文件。

2 File (String path, String filename)

参数 path 表示文件的父路径名(不包含文件名),参数 filename 为文件的名字,该函数用于创建一个指定路径和名字的文件。

3 File (File parent, String filename)

参数 parent 表示文件的父路径,参数 filename 为文件的名字(包含子路径),该函数用于创建一个指定路径和名字的文件。

Windows 系统中目录的分隔符号为“ \ ”( separator ),而在其他系统中却不是这样,例如 Linux 系统中是“ / ”。为了写出通用的程序,在具体设置目录时,首先需要通过 System..getProperty("os.name") 获取操作系统,然后通过 File 类中分隔符的常量具体设置。目录和路径要区分开,目录里的是目录分隔符,而路径里的是路径分隔符,分别对应于 File 类中的 4 个常量,即 separator separatorChar pathseparator pathseparatorChar

% 注意: separator是Sting类型,而separatorChar是Char类型。同理,pathseparator是Sting类型,而pathseparatorChar是Char类型。

2 .路径的方法

在构造函数中存在一个重要的参数,即路径的参数。当获取 File 类对象后,如何获取路径的属性呢?查看 API 帮助文档,可以发现如下方法。

q getName() 方法:获取与 File 对象相连接的文件或目录的名称(不包含路径名称)。

q getPath() 方法:获取与 File 对象相连接的文件或目录的名称(包含路径名称)。

q getAbsolutePath() 方法:获取与 File 对象相连接的文件或目录的绝对路径名称。

q getParent() 方法:获取与 File 对象相连接的文件或目录的父路径名称。

q isAbsolute () 方法:判断与 File 对象相连接的文件或目录的父路径是否绝对路径。

例如创建一个 File 类对象,该程序是在 D:\Java 目录里运行,具体代码如下:

File test=new File("test","testfile.text");

对于对象 test ,调用 getAbsolutePath() 方法将返回 D:\Java\test\testfile.text 绝对路径;调用 getPath() 方法则返回 test\testfile.text 绝对路径;而调用 getName() 方法和 getParent() 方法将分别返回 testfile.text testfile.text 路径。有时还需要判断 File 类对象是否使用了绝对路径,这时就需要通过 isAbsolute () 方法来判断,对于 test 对象将返回 false ,即不是绝对路径。如果修改 test 对象如下,则返回 true (使用了绝对路径)。

File test=new File("D:\\Java\\test\\testfile.text");

3 .操作文件和目录——创建文件方法

如果想创建一个文件,首先需要判断该文件是否存在,然后才能创建。查看 API 帮助文档可以发现如下方法。

q exists() 方法:检查与 File 对象相连接的文件和目录是否存在于磁盘中。

q createNewFile() 方法:如果与 File 对象相连接的文件不存在,则创建一个空文件。

q createTempFile() 方法:创建一个 File 对象并同时在磁盘上创建指定的文件。

% 注意: createNewFile()方法通常与exists()方法相配合使用,而createTempFile()方法是一个类方法,其参数也具有特殊的规定。

4 .操作文件和目录——创建目录方法

如果想创建一个文件,首先需要判断该文件是否存在,然后才能创建。查看 API 帮助文档可以发现如下方法。

q mkdir() 方法:创建与 File 对象相连接的目录名称。

q mkdirs() 方法:创建与 File 对象相连接的目录名称,如果父目录不存在,系统会自动生成。

% 注意: 上述两个方法的区别在于,如果要创建D:\Java\test这个目录,但是D:\Java不存在,这时如果用mkdir()方法创建,则不会成功;用mkdirs()方法创建会成功。

5 .操作文件和目录——删除方法

如果想删除一个文件和目录,首先需要判断该对象是目录还是文件,然后才能删除。查看 API 帮助文档可以发现如下方法。

q isDirectory() 方法:检查与 File 对象相连接的对象是否为目录。

q isFile() 方法:检查与 File 对象相连接的对象是否为文件。

q delete() 方法:删除与 File 对象相连接的文件和目录。

q deleteOnExit() 方法:删除与 File 对象相连接的文件和目录,其不会立即运行,而是在整个程序结束时才会被执行。

% 注意: 在删除之前之所以要判断是目录还是文件,因为如果是目录,则需要判断目录下是否有文件和子目录,只有没有文件或子目录的情况下才可以正常删除。

6 .操作文件和目录——列举方法

有时需要实现列举一个目录下的所有子目录和文件的功能,查看 API 帮助文档可以发现如下方法。

q list 方法:返回与 File 对象相连接的目录下的所有子目录和文件。

q listFile() 方法:返回与 File 对象相连接的目录下的所有文件。

q listRoots() 方法:返回与 File 对象相连接的对象所属的根目录,即磁盘符号。

7 .文件的属性

如果想删除一个文件和目录,首先需要判断该对象是目录还是文件,然后才能删除。查看 API 帮助文档可以发现如下方法。

q canRead() 方法:判断与 File 对象相连接的文件是否可以读取里面的数据。

q canWrite() 方法:判断与 File 对象相连接的文件是否可以写入数据。

q isHidden() 方法:判断与 File 对象相连接的文件和目录是否隐藏。

q length() 方法:返回与 File 对象相连接的文件的大小。

q lastModified() 方法:返回与 File 对象相连接的文件的最后修改时间。

q setLastModified() 方法:设置与 File 对象相连接的文件的最后修改时间。

18.3.4 文件访问的基本概念

如果想彻底理解文件的访问功能,必须先理解 Stream (流)这个概念。当用水管浇花、洗澡等时,水是从水龙头经水管的一端流向另一端,而且只要水龙头不断的有水送出来,那么水就会接连不断地经水管流向另一端。这就是所谓的流概念。

文件的访问就是以上述概念来处数据的输入和输出的, InputStream OutputStream 是所有以字节为单位的输入和输出类的父类,而 Reader Writer 是所有以字符为单位读取和输出类的父类。下面详细讲解这 4 个类的基础知识。

1 InputStream

InputStream 是用来实现输入字节的类,即读取数据的意思。如果要实现读取功能,就需要用到 read() 方法,该类提供了 3 种重载的方法,分别如下。

abstract int read()

该函数无任何参数,用来实现一次读取一个字节的数据,并以 int 类型返回读取的数据。如果没有数据,将返回 –1

int read(byte[] b)

参数 b 为缓冲区数组,该函数用来实现一次读取一个字节的数据,读取的数据会存储到缓冲区数组里,其返回的是真正读取的字节数目。

int read(byte[] b, int off, int len)

参数 off 为缓冲区数组的起始位置,参数 len 为一次要读取多少个字节数组。该函数用法与第 2 种重载方法一样。

由于有些数据源一次只能允许一个流来传送数据,所以如果用完了 InputStream 对象,则通过 close() 方法来关闭对象。

如果想知道流中还有多少个字节的数据,可以通过 available() 方法来实现,如下所示。

int available()

该方法会返回一个 int 类型数据,表示还有多少个字节的数据可以读取。

% 注意: 如果用InputStream对象调用该方法,只会返回0。该方法必须由继承InputStream的子类对象调用才能返回正确的数据。

如果想跳过流中的某些数据,可以通过 skip() 方法来实现,如下所示。

long skip(long n)

参数 n 表示要跳过几个字节的数据,该函数用来实现跳过指定字节的数据,返回真正跳过的字节数据。

对于流中的数据,如果想返回重新读取,可以先通过 mark() 方法设置回头的位置,然后通过 reset() 方法使流返回到设置的位置处。

void mark(int readlimit)

参数 readlimit 为返回的位置,在此输入流中标记指定的位置。

void reset()

该函数用来实现将流重新定位到对输入流最后调用 mark() 方法时的位置。

% 注意: 对于上述两个方法,不是每个InputStream子类都可以使用的,所以需要通过markSupported()方法来判断类是否支持这两个类。

2 OutputStream

OutputStream 是用来实现输出字节的类,即写入数据的意思。如果要实现写入功能,就需要用到 write() 方法,该类提供了 3 种重载的方法,分别如下。

abstract void write(int b)

参数 b 为字节的数目,该函数用来实现把 b.length 个字节写入输出流。

void write (byte[] b)

参数 b 为指定的字节数组,该函数用来实现把指定的字节数组写入输出流。

void write (byte[] b, int off, int len)

参数 off 为缓冲区数组的起始位置,参数 len 为一次要读取多少个字节数组。该函数用法与第 2 种重载方法一样。

由于有些数据源一次只能允许一个流来传送数据,所以如果用完 OutputStream 对象后,同样需要通过 close() 方法来关闭对象。

当用 write() 方法输出数据时,这些数据并不会马上输出到指定的目的文件里,而是先存储到内存的缓冲中。如果想把数据立即输出到目的文件中,可以调用 flush() 方法,该方法的语法如下:

void flush()

该函数用来实现刷新输出流并强制输出缓冲中的所有字节。

% 注意: 对于输出流,在调用close()方法前,一般会先调用flush()方法确保把所有的数据都输出到目的文件里。

3 Reader

Reader 是用来实现输入字符的类,即读取字符数据的意思。与 InputStream 类相比, Reader 类操作的对象为 char 类型,而 InputStream 类为 byte 类型。所以只有把 InputStream 类提供的方法中,在用到 byte 类型的地方改为 char 类型就可以被使用。

% 注意: 在Reader类中用ready()方法代替InputStream类中的available()方法,表示已经准备好输入数据。

4 Writer

Writer 是用来实现输出字符的类,即写入字符数据的意思。与 OutputStream 类相比, Writer 类操作的对象为 char 类型,而 OutputStream 类为 byte 类型。所以只有把 OutputStream 类提供的方法中,在用到 byte 类型的地方改为 char 类型就可以被使用。对于 write() 方法, Writer 类比 OutputStream 类多了两个重载的方法,分别如下:

void write(String str)

参数 str 为写入的字符串,该函数用来实现把指定的字符串写入输出流。

void write(String str, int off, int len)

参数 off 为写入字符串的起始位置,参数 len 为一次要读取多少个字符。该函数用法与第 2 种重载方法一样。

18.3.5 文件的基本访问方式——字节方式

18.3.4 节讲解了文件的操作方法和文件访问的父类,本节将详细讲解如何实现对文件的访问。查看 API 帮助文档,可以发现有 3 种方式来访问文件,本节将详细讲解以字节方式(类 FileInputStream FileOutputStream )访问文件的内容。

下面通过实现文件访问功能的 ByteAccess 类来讲解 FileInputStream FileOutputStream 类的基本用法,具体内容如代码 18.5 所示。

代码 18.5 文件的访问: ByteAccess.java

public class ByteAccess {

//创建成员变量

public File file = new File("C:\\", "Byte.txt");

public byte bytes[] = new byte[512];

public static void main(String args[]) {        //主函数

ByteAccess test = new ByteAccess();         //创建ByteAccess方式

test.writemethod();                         //调用写入方法

test.readmethod();                         //调用读取方法

}

public void writemethod() {                   //写入方法

int b;                                          //定义一个变量

System.out.println("请输入存入文本的内容:");  //输出相应提示信息

try {

if (!file.exists())                       //判断文件是否存在

file.createNewFile();                //创建新文件

b = System.in.read(bytes);      //把从键盘输入的字符存入bytes里

//创建文件输出流

FileOutputStream fos = new FileOutputStream(file, true);

fos.write(bytes, 0, b);          //把bytes写入到指定文件中

fos.close();                                //关闭输出流

} catch (IOException e) {

e.printStackTrace();

}

}

public void readmethod() {                     //读取方法

try {

FileInputStream fis = new FileInputStream(file);                                                              //创建文件字节输入流

int rs = 0;                                   //创建变量rs

System.out.println("开始读取文件内容:"); //输出相应提示

while ((rs = fis.read(bytes, 0, 512)) > 0) {   //读取文件内容

//在循环中读取输入流的数据

String s = new String(bytes, 0, rs);

System.out.println(s);

}

fis.close();                                          //关闭输入流

} catch (IOException e) {

e.printStackTrace();

}

}

}

运行 ByteAccess.java 类之前, C 盘里 Byte.txt 文件的内容如图 18.13 所示,按照如图 18.14 所示的运行过程运行 ByteAccess 类后, C 盘里 Byte.txt 文件的内容如图 18.15 所示。

18.13 程序运行前的文件

18.14 运行过程

【代码解析】

q 在写入数据的方法 writemethod() 中,首先判断指定的文件是否存在,如果不存在,则调用 createNewFile() 方法进行创建,然后获取从键盘中输入字符,最后通过创建文件输出流的 write() 方法,把字符字节数组输出到文件里。

q 在读取数据的 readmethod() 方法中,首先创建与文件相关联的读取流,然后在循环中通过文件流的 read() 方法把文件中的内容读取到字节数组中,最后再输出字节数组中的内容。

18.3.6 文件的基本访问方式——字符方式

18.3.5 节讲解了如何通过字节方式来访问文件,本节将详细讲解如何通过字符方式来访问文件,即通过 FileInputStream FileOutputStream 类实现对文件的访问。

下面通过实现文件访问功能的 CharAccess 类,来讲解 Filereader FileWriter 类的基本使用方法,具体内容如代码 18.6 所示。

代码 18.6 文件的访问: CharAccess.java

public class CharAccess {

//创建成员变量

File filein = new File("C:\\char.txt");    //创建原文件对象filein

File fileout = new File("C:\\char-1.txt"); //创建复制后的文件对象fileout

public char chars[] = new char[512];       //创建字符数组chars

public static void main(String args[]) {  //主函数

CharAccess test = new CharAccess ();  //创建CharAccess对象

test.readmethod();                          //调用读取方法

test.writermethod();                        //调用写入方法

}

public void writermethod() {             //复制一个文件的内容到另一个文件中

try {

//创建新文件

if (!fileout.exists())                //如果文件不存在

fileout.createNewFile();

//创建源文件的读取流和目的文件的写入流

FileReader fin = new FileReader(filein);

FileWriter fos = new FileWriter(fileout);

int is;                                   //变量

while ((is = fin.read()) != -1) { //遍历读取流

fos.write(is);

}

fin.close();                             //关闭读取流

fos.close();                             //关闭写入流

} catch (IOException e) {

e.printStackTrace();

}

}

public void readmethod() {                     //读取方法

try {

if (!filein.exists())                 //如果文件不存在

filein.createNewFile();          //创建新文件

FileReader fin = new FileReader(filein);   //创建源文件的读取流

int is;                                            //创建变量

while ((is = fin.read(chars)) != -1) {     //遍历读取

String str = new String(chars, 0, is); //创建字符串

//输出字符串

System.out.println("char文件的内容为:"+str);

}

fin.close();                                      //关闭读取流

} catch (IOException e) {

e.printStackTrace();

}

}

}

打开 C 盘可以发现 char.text 文件,该文件的内容如图 18.16 所示。运行 CharAccess.java 类,控制台窗口如图 18.17 所示。这时如果再次打开 C 盘,可以发现多出了一个名为 char-1.text 的文件,该文件的内容如图 18.18 所示。

18.16  char.text 文件的内容 18.17 控制台窗口

18.18  char-1.text 文件的内容

【代码解析】

q 在读取数据的 readmethod() 方法中,首先创建与文件相关联的读取流,然后在循环中通过文件流的 read() 方法把文件中的内容读取到字符数组中,最后再输出字符数组组成的字符串内容。

q 在写入数据的 writemethod() 方法中,首先判断源文件和目的文件是否存在,如果不存在,则调用 createNewFile() 方法进行创建,然后获取该文件的输入流和输出流,最后在循环中把读取流中的字符通过输入流中的 write() 方法写入目的文件中,从而达到从源文件复制目标文件的效果。

18.3.7 文件的高级访问方式

18.3.6 节讲解了文件访问的两种基本方式,本节将详细讲解文件访问的最后一种方 式—— RandomAccessFile 类的方式。查看 API 帮助文档,可以发现 RandomAccessFile 类可以实现对文件更高级的访问。下面将通过一个具体实例来讲解,具体步骤如下。

1 )下面通过实现文件访问功能的 RandomAccess 类来讲解 RandomAccessFile 类的基本使用方法,具体内容如代码 18.7 所示。

代码 18.7 文件的访问: RandomAccess.java

public class RandomAccess {

public static void main(String[] args) throws Exception {

//创建3个雇员变量

Employee e1 = new Employee("zhangsan", 23);

Employee e2 = new Employee("cjgong", 24);

Employee e3 = new Employee("Wangwu", 25);

//创建一个文件的RandomAccessFile对象

RandomAccessFile ra = new RandomAccessFile("c:\\Random.txt", "rw");

//把3个雇员的信息写入文件里

ra.write(e1.name.getBytes());

ra.writeInt(e1.age);

ra.write(e2.name.getBytes());

ra.writeInt(e2.age);

ra.write(e3.name.getBytes());

ra.writeInt(e3.age);

ra.close();                                       //关闭ra流对象

//创建一个文件的RandomAccessFile对象

RandomAccessFile raf = new RandomAccessFile("c:\\Random.txt", "r");

int len = 8;                                          //名字长度变量

//跳过第一个员工的信息,其中姓名8字节,年龄4字节

raf.skipBytes(12);                              //跳过12字节

System.out.println("第二个员工信息:");

String str = "";                                     //雇员信息变量

//通过循环为str赋值

for (int i = 0; i < len; i++)

str = str + (char) raf.readByte();

System.out.println("name:" + str.trim());  //输出雇员名字变量的值str

System.out.println("age:" + raf.readInt());//输出雇员的年龄

//输出第一个员工的信息

raf.seek(0);                             //将文件指针移动到文件开始位置

System.out.println("第一个员工的信息:");

str = "";

for (int i = 0; i < len; i++)

str = str + (char) raf.readByte();

System.out.println("name:" + str.trim());

System.out.println("age:" + raf.readInt());

//输出第三个员工的信息

raf.skipBytes(12);                          //跳过第二个员工的信息

str="";

System.out.println("第三个员工的信息:");

for (int i = 0; i < len; i++)

str = str + (char) raf.readByte();

System.out.println("name:" + str.trim());

System.out.println("age:" + raf.readInt());

raf.close();                             //关闭流对象raf

}

}

打开 C 盘可以发现 Random.text 文件,该文件的内容如图 18.19 所示。运行 RandomAccess.java 类,控制台窗口如图 18.20 所示。这时如果再次打开 C 盘的 char.text 文件,该文件的内容如图 18.21 所示。

18.19 程序运行前的文件 18.20 控制台窗口 18.21 程序运行后的文件

【代码解析】

q 对于文件 Random.txt ,首先通过与其关联的 RandomAccessFile 类型对象 ra 把雇员信息按照“第一雇员、第二雇员和第三雇员”的顺序写入文件。然后通过与其关联的 RandomAccessFile 类型对象 raf 把雇员信息按照“第二雇员、第一雇员和第三雇员”的顺序读取并输出到控制台窗口。

q 在具体写信息到文件的过程中,首先创建一个“读写”方式的 RandomAccessFile 类型对象 ra ,然后通过 write() 方法把所有“雇员”的姓名( name )信息写入文件中, writeInt() 方法把所有“雇员”的年龄( age )信息写入文件中。

q 在具体从文件读取信息的过程中,需要创建一个“只读”方式的 RandomAccessFile 类型对象 raf 。对于第二个雇员信息的读取,首先需要通过 skipBytes() 方法跳过第一个雇员的信息,然后在遍历过程中通过 readByte() 方法把姓名( name )信息读出,最后通过 readInt() 方法把年龄( age )信息读出。对于第一个雇员信息的读取,首先需要通过 seek() 方法移动到文件的开始位置,然后在遍历过程中通过 readByte() 方法把姓名( name )信息读出,最后通过 readInt() 方法把年龄( age )信息读出。对于第三个雇员信息,同读取第二个雇员的过程基本一样,只是通过 skipBytes() 方法跳过第一个和第二个雇员的信息。

2 )创建一个封装“雇员”信息名为 Employee 的类,该类的具体内容如代码 18.8 所示。

代码 18.8 雇员的类: Employee.java

class Employee {

String name;                                 //雇员的名字

int age;                                      //雇员的年龄

final static int LEN = 8;                     //雇员名字长度的常量

public Employee(String name, int age) {   //构造函数

if (name.length() > LEN) {                //当名字长度大于8个字符时

name = name.substring(0, 8);         //截取前8个字符

} else {                                       //当名字长度小于8个字符时

while (name.length() < LEN)          //用空格填补成8个字符

name = name + "\u0000";

}

//为成员变量赋值

this.name = name;

this.age = age;

}

}

【代码解析】

q 由于一个“雇员”信息就是文件中的一条记录,而对于记录来说需要保证其大小相同,因此每个“雇员”的姓名( name )和年龄( age )属性值在文件中的长度是一样的。

q 对于年龄( age )属性,由于其是 int 类型,所以每个成员的年龄( age )属性值长度相同。对于姓名( name )属性,由于其是 String 类型,即长度不确定,所以需要在构造函数中通过判断语句对其值进行操作。当长度大于 8 个字符时,则截取前 8 个字符;当长度小于 8 个字符时,则补空格( \u0000 )。

3 )通过 API 帮助文档来查看,以高级方式访问文件涉及的 RandomAccessFile 类的一些基础知识,分别如下:

RandomAccessFile 存在两种构造函数,分别如下所示。

RandomAccessFile(File file, String mode)

参数 file 表示文件对象,参数 mode 为操作文件的方式,该函数用于创建一个与指定文件对象相关联的写入或读取数据的随机存取文件流。

RandomAccessFile(String name, String mode)

参数 name 表示文件的路径(包含文件名),该函数用于创建一个与指定文件对象相关联的写入或读取数据的随机存取文件流。

上述构造函数中的参数 mode 表示访问文件的权限,其取值如表 18.1 所示。

18.1  mode 的值

小    结

本章主要通过对文件的操作来获取文件的属性,并把相应的文件属性内容显示在相应的 Swing 组件里。虽然设计图形用户界面很重要,但是实现图形用户界面中组件的互访更重要。

在本章的最后还详细介绍了文件操作和访问的基础知识,对于文件的访问介绍了 3 种方式,分别为通过字节方式访问、通过字符方式访问和通过高级方式访问。

本文标签: 系统 参数 编程