黑马程序员_JAVA学习- IO编程

一、File类

File类是在整个java.io包之中唯一一个与文件本身操作有关的类,文件本身操作指的是文件的创建、删除、重命名等,但是如果要进行File类操作那么必须设置好要操作的文件或文件夹的路径,使用如下构造方法:

                   · 构造方法:public File(String pathname),传入完整的路径,WEB开发此方式比较好用;

· 构造方法:public File(File parent, String child),传入父路径和子路径。

范例:基本的文件操作

· 创建新的文件:public boolean createNewFile() throws IOException;

         · 删除文件:public boolean delete()

         · 判断文件是否存在:public boolean exists()


package cn.mldn.demo;

import java.io.File;

public class TestDemo {

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

File file = new File("D:\\demo.txt") ;

if (file.exists()) {    // 文件存在

file.delete() ;

else {

file.createNewFile() ;

}

}

}

此时的程序的确完成了所有的文件基本操作,但是在本程序之中依然会存在有如下的几个问题。

问题一:Java的最大特征是可移植性,但是如果是文件编程此处就需要考虑一个平台问题,在windows之中使用“\”作为路径分割符,而在Linux中使用“/”作为路径分割符,那么在实际的工作之中,往往都会在windows下做开发,而后将项目部署到Linux之中,所以来讲就不能够将路径的分隔符写固定了,为此在File类中提供了一个常量:


public static final String separator

如果按照命名规范来讲,separator应该使用大写字母表示,但是此处是小写字母,原因很简单 —— 历史原因。


File file = new File("D:" + File.separator + "demo.txt");

问题二:在程序执行完成之后文件的并不会立刻删除或者是创建,存在有一定的延迟,因为Java程序是通过JVM间接的调用系统函数实现的文件操作。

问题三:如果在进行文件创建的时候有目录,则需要先创建目录之后才可以创建文件。

· 找到父路径:public File getParentFile();

· 创建目录:public boolean mkdirs();


package cn.mldn.demo;

import java.io.File;

public class TestDemo {

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

File file = new File("D:" + File.separator + "hello" + File.separator

+ "test" + File.separator + "haha" + File.separator

+ "demo.txt");

if (!file.getParentFile().exists()) {   // 父路径不存在

file.getParentFile().mkdirs() ;

}

if (file.exists()) {    // 文件存在

file.delete() ;

else {

file.createNewFile() ;

}

}

}

除了以上的基本的文件和文件夹的操作之外,也提供有一些取得文件信息的操作方法:

· 判断路径是否是文件:public boolean isFile();

· 判断路径是否是文件夹:public boolean isDirectory();

· 最后一次修改日期:public long lastModified();

· 取得文件大小:public long length();

· 修改文件名称:public boolean renameTo(File dest)。


package cn.mldn.demo;

import java.io.File;

import java.math.BigDecimal;

import java.text.SimpleDateFormat;

import java.util.Date;

public class TestDemo {

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

File file = new File("D:" + File.separator + "my.jpg");

if (file.exists()) { // 文件存在

System.out.println(file.isFile() ? "是文件" : "不是文件");

System.out.println(file.isDirectory() ? "是文件夹" : "不是文件夹");

System.out.println("最后一次修改日期:"

new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")

.format(new Date(file.lastModified())));

System.out.println("文件大小:"

new BigDecimal((file.length() / (double) 1024 / 1024))

.divide(new BigDecimal(1), 2,

BigDecimal.ROUND_HALF_UP) + "M");

file.renameTo(new File("D:" + File.separator + "hello.jpg"));

}

}

}

如果说现在给定的路径是一个文件夹,那么如果是文件夹则里面应该会包含有许多的文件或子文件夹,那么现在就可以利用以下的方法列出目录之中的所有内容:

· 列出目录内容:public File[] listFiles()。返回的是一个对象数组

范例:列出目录内容


package cn.mldn.demo;

import java.io.File;

public class TestDemo {

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

File file = new File("D:" + File.separator + "testjava");

if (file.exists() && file.isDirectory()) {

File result [] = file.listFiles() ; // 列出全部内容

for (int x = 0; x < result.length; x++) {

System.out.println(result[x]);

}

}

}

}

但是此时列出来的只是当前目录级别中的数据。

范例:列出一个目录之中的所有文件(包括所有子目录中的文件)

基本原则:首先要判断给定的File类对象是否是目录,如果是目录则继续列出,那么随后循环列出的File类数组,后依次进行判断是否是目录。这样的实现最好使用递归完成。


package cn.mldn.demo;

import java.io.File;

public class TestDemo {

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

File file = new File("D:" + File.separator);

print(file);

}

public static void print(File file) {

if (file.exists() && file.isDirectory()) {

File result[] = file.listFiles(); // 列出全部内容

if (result != null) {

for (int x = 0; x < result.length; x++) {

print(result[x]); // 递归调用

}

}

}

System.out.println(file);

}

}

如果此时的程序之中对于文件的路径不是输出而是执行删除呢?

二、字节流与字符流

使用File类只能够实现文件本身的操作,但是与文件内容的操作无关,如果要想进行文件内容操作的话则可以使用以下两组流完成:

· 字节流:InputStream、OutputStream;

· 字符流:Reader、Writer。

         但是不管使用何种流,基本的操作流程是一样的,以文件操作为例;

                   · 确定操作文件的路径;

                   · 通过字节流或字符流的子类为字节流和字符流类对象实例化;

                   · 进行输入、输出的操作;

                   · 关闭流,流属于资源操作,资源操作完成一定要关闭。

1、字节输出流:OutputStream

java.io.OutputStream是可以进行字节数据(byte)的输出,这个类的定义结构如下:


public abstract class OutputStream

extends Object

implements Closeable, Flushable

首先可以发现在OutputStream类之中实现了两个接口:


CloseableJDK 1.5后提供、


FlushableJDK 1.5后提供


public interface Closeable extends AutoCloseable


public interface Flushable


public void close() throws IOException


public void flush() throws IOException

从实际的开发来讲,对于Closeable和Flushable两个接口是属于后来再次抽象的产物,本身的创建意义是希望将所有存在有关闭资源操作的类进行统一的关闭管理(这类操作有些多余了),而在OutputStream类里面也定义有close()和flush()两个方法,而且这两个方法已经存在很多年了,那么在开发的时候就不会再去关注Closeable、Flushable两个接口了。

         OutputStream类之中存在有三个write()方法:

                   · 输出单个字节:public abstract void write(int b) throws IOException

                   · 输出全部的字节:public void write(byte[] b) throws IOException

                   · 输出部分字节:public void write(byte[] b, int off, int len) throws IOException

但是OutputStream只是一个抽象类,所以如果要想取得本类的实例化对象,那么就必须利用子类进行实例化,本次操作将要进行的是文件操作,所以可以使用FileOutputStream子类。这个类定义了两个常用构造方法:

                   · 构造方法:public FileOutputStream(File file) throws FileNotFoundException,覆盖;

· 构造方法:public FileOutputStream(File file, boolean append) throws FileNotFoundException,追加。

范例:进行文件的输出操作


package cn.mldn.demo;

import java.io.File;

import java.io.FileOutputStream;

import java.io.OutputStream;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs() ;

}

// 通过OutputStream的子类对象为父类对象实例化

OutputStream output = new FileOutputStream(file) ;

String msg = "Hello World ."; // 要输出的数据

byte data [] = msg.getBytes() ; // 将字符串变为字节数组

output.write(data); // 将字节数组数据输出

output.close();// 资源的对象一定要关闭

}

}

但是此时不管执行几次本程序,对于文件中的内容都只是一个覆盖。如果不希望出现覆盖,则可以使用追加的方式创建FileOutputStream类对象。


// 通过OutputStream的子类对象为父类对象实例化,追加内容

OutputStream output = new FileOutputStream(file, true);


String msg = "Hello World .\r\n"; // 要输出的数据

以上是将内容全部进行了输出,那么也可以设置输出的范围。


    output.write(data, 0, 5); // 将字节数组数据输出

如果觉得一组字节数据输出麻烦,那么也可以采用循环的方式进行单个字节的输出。

范例:单个字节


package cn.mldn.demo;

import java.io.File;

import java.io.FileOutputStream;

import java.io.OutputStream;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs() ;

}

// 通过OutputStream的子类对象为父类对象实例化

OutputStream output = new FileOutputStream(file);

String msg = "Hello World ."; // 要输出的数据

for (int x = 0; x < msg.length(); x++) {

output.write(msg.charAt(x));

}

output.close();// 资源的对象一定要关闭

}

}

严格来讲通过charAt()方法返回的数据是字符数据,但是之所以可以直接使用writer()输出,是因为在writer()方法里面接收的是int型数据,而int型数据可以接收char或byte。不过从习惯上来讲,还是更加愿意使用字节数据完成。


byte data [] = msg.getBytes() ;

for (int x = 0; x < data.length; x++) {

output.write(data[x]);

}

在整个OutputStream类之中最为重要的输出方法:输出部分字节数句。

2、字节输入流:InputStream

使用OutputStream可以完成程序向文件的输出,而现在要通过程序读取文件内容,则必须采用InputStream类完成,那么此类的定义如下:


public abstract class InputStream

extends Object

implements Closeable

InputStream类依然是Closeable接口的子类,但是由于InputStream类本身一直存在有close()方法,所以在使用时可以忽略掉Closeable。InputStream类之中定义有三个read()方法:

         · 读取单个字节:public abstract int read() throws IOException

                   |- 每一次使用read()操作将读取一个字节数据,此时返回的是数据,如果数据已经读取完了,则int返回-1

         · 读取内容到字节数组:public int read(byte[] b) throws IOException

                   |- 将内容读取到字节数组之中,返回读取的个数,如果读取完毕,则返回的是-1

         · 读取内容到部分字节数组:public int read(byte[] b, int off, int len) throws IOException

                   |- 将指定长度的内容读取到字节数组之中,返回读取个数,如果读取完毕,返回-1

但是InputStream类属于抽象类,抽象类如果要实例化则使用它的子类。那么可以使用FileInputStream子类完成,此类只关心构造方法:public FileInputStream(File file) throws FileNotFoundException

范例:使用InputStream读取数据


package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (file.exists()) {    // 要操作的文件存在

InputStream input = new FileInputStream(file) ; // 实例化输入流对象

byte data[] = new byte[1024];// 此数组用于接收全部输入的数据

int len = input.read(data) ;    // 将数据保存在数组之中

System.out.println("【" + new String(data, 0, len) + "】");

input.close() ;

}

}

}

此时是将整个数组都填充满了数据,那么下面使用部分读取。


int len = input.read(data,0,10) ;   // 将数据保存在数组之中

如果此时部分的数据读取完毕,那么其它的数据将不再读取。

而在整个InputStream类之中最为重要的就是进行单个字节数据的读取操作,而且代码的难度也是最高的。如果单独使用read()每次读取一个字节,如果读取完毕返回的是-1。

范例:使用循环的方式读取单个字节数据(了解)


package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (file.exists()) {    // 要操作的文件存在

InputStream input = new FileInputStream(file) ; // 实例化输入流对象

byte data[] = new byte[1024];// 此数组用于接收全部输入的数据

int temp = 0 ;  // 接收每次读取进来的数据

int foot = 0 ;  // 定义数组的脚标

do {

temp = input.read() ;   // 读取单个字节

if (temp != -1) {   // 读取的是真实数据

data[foot++] = (byte) temp; // 保存数据

}

while(temp != -1) ;   // 如果没有读取到尾,那么继续读

input.close() ;

System.out.println(new String(data, 0, foot));

}

}

}

以上的程序使用的是do…while操作完成,而在开发之中是不建议使用do…while操作,都使用while代替,而且本操作之中是不确定循环次数,但是知道循环结束条件,一定使用while。

范例:使用while循环读取


package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (file.exists()) {    // 要操作的文件存在

InputStream input = new FileInputStream(file) ; // 实例化输入流对象

byte data[] = new byte[1024];// 此数组用于接收全部输入的数据

int temp = 0 ;  // 接收每次读取进来的数据

int foot = 0 ;  // 定义数组的脚标

        // 组成一:temp = input.read(),表示读取单个的字节;

        // 组成二:(temp = input.read()) != -1,表示判断temp的内容是否是-1,如果不是,则继续读

            while ((temp = input.read()) != -1) {

                data[foot++] = (byte) temp;

            }

input.close() ;

System.out.println(new String(data, 0, foot));

}

}

}

以上的写法一定要习惯,日后进行读取的时候都会采用类似的形式完成。

3、字符输出流:Writer

InputStream和OutputStream两个类是在JDK 1.0的时候引入的,但是在JDK 1.1之后为了方便操作,又提供了一组字符操作流(Writer、Reader),字节输出流和字符输出流最大的区别在于,字节输出流是以byte类型为主的,而字符输出流是以char类型为主的,而且支持String的直接操作。下面首先来观察Writer类的继承结构:


public abstract class Writer

extends Object

implements Appendable, Closeable, Flushable

在Writer类里面提供有一个最为重要的操作方法:

· 输出字符串:public void write(String str) throws IOException。

· 输出字节数组:public void write(char[] cbuf) throws IOException。

但是Writer是一个抽象类,如果要使用它进行文件操作必须使用FileWriter子类。

范例:使用Writer类实现内容的输出


package cn.mldn.demo;

import java.io.File;

import java.io.FileWriter;

import java.io.Writer;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs();

}

Writer out = new FileWriter(file);

String msg = "Hello World .";

out.write(msg); // 直接输出字符串

out.close();

}

}

通过这一操作就可以清楚的发现,使用字符输出流在进行字符串数据输出的时候还是非常方便的。

4、字符输入流:Reader

Reader是负责进行字符数据读取的,此类定义如下:


public abstract class Reader

extends Object

implements Readable, Closeable

同时在这个类之中可以使用read()方法读取数据,但是没有可以直接返回String类型的读取操作,可以利用字符数组:

· 读取数据:public int read(char[] cbuf) throws IOException;

范例:读取数据


package cn.mldn.demo;

import java.io.File;

import java.io.FileReader;

import java.io.Reader;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (file.exists()) { // 文件存在

Reader in = new FileReader(file);

char data[] = new char[1024];

int len = in.read(data);

System.out.println(new String(data, 0, len));

in.close() ;

}

}

}

两种读取的操作本质上讲区别不大,只是字符流操作的都是char/String,而字节流只是byte。

5、字节流和字符流的区别

回顾:BLOB、CLOB数据类型。CLOB保存大文本数据,而BLOB保存大文本、图片、音乐等二进制数据。那么对于字节流和字符流也是一样,如果真的进行选择的话,可以给出如下的参考意见:

· 字符流:当程序处理中文的时候,字符流是最方便的;

· 字节流:当程序处理二进制数据(图片、音乐、电影)或进行网络传输,或者保存到磁盘数据一定都是字节;

除了以上的区别之外,字节流在进行操作的时候是直接与操作终端进行交互,而字符流需要经过缓冲区的处理后才可以进行操作,以OutputStream和Writer两个类输出文件为例,如果OutputStream输出的最后可以不关闭输出流,但是如果是Writer类输出如果没有关闭,那么保存在缓冲之中的数据将无法输出,或者强制性刷新缓冲区。


package cn.mldn.demo;

import java.io.File;

import java.io.FileWriter;

import java.io.Writer;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs();

}

Writer out = new FileWriter(file);

String msg = "Hello World .";

out.write(msg); // 直接输出字符串

out.flush();// 强制刷新缓冲区

}

}

所谓的缓冲实际上是一块内存,当数据读取进来之后会先进入到此内存区域之中进行处理,所以才可以更好的处理中文,所以日后在进行选择的时候,大部分情况都以字节流为主。

三、转换流

那么现在既然存在有字节和字符两种操作流,那么这两种流之间也是可以进行互相转换的,主要使用两个类:InputStreamReader、OutputStreamWriter。这两个类继承结构和构造方法如下:


InputStreamReader


OutputStreamWriter


public class InputStreamReader

extends Reader


public class OutputStreamWriter

extends Writer


public InputStreamReader(InputStream in)


public OutputStreamWriter(OutputStream out)

InputStreamReader是Reader的子类,所以InputStreamReader类对象可以自动转型为Reader类实例。

OutputStreamWriter是Writer的子类,所以OutputStreamWriter类对象可以自动转型为Writer类实例。

范例:观察转换


package cn.mldn.demo;

import java.io.File;

import java.io.FileOutputStream;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

import java.io.Writer;

public class TestDemo {

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

File file = new File("D:" + File.separator + "test" + File.separator

+ "demo.txt"); // 通过File类定义要输出的路径

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs();

}

OutputStream output = new FileOutputStream(file) ;  // 字节输出流

Writer out = new OutputStreamWriter(output) ;   // 字节流变为字符流

out.write("Hello World .");

out.close();

}

}

但是以上的代码存在的意义不大,而之所以介绍转换流主要的目的也在于两组类的继承结构上。

· 观察一:FileInputStream和FileOutputStream类的继承结构


FileInputStream


FileOutputStream


java.lang.Object

java.io.InputStream

java.io.FileInputStream


java.lang.Object

java.io.OutputStream

java.io.FileOutputStream

· 观察二:FileReader和FileWriter类的继承结构。


FileReader


FileWriter


java.lang.Object

java.io.Reader

java.io.InputStreamReader

java.io.FileReader


java.lang.Object

java.io.Writer

java.io.OutputStreamWriter

java.io.FileWriter

通过继承关系可以清楚的发现,所有的字符流数据实际上都经过了转换。

时间: 2024-10-29 19:09:28

黑马程序员_JAVA学习- IO编程的相关文章

黑马程序员_JAVA UDP网络编程学习笔记

一.UDP网络编程概述 采用TCP协议通信时,客户端的Socket必须先与服务器建立连接,连接建立成功后,服务器端也会持有客户端连接的Socket,客户端的Socket与服务器端的Socket是对应的,它们构成了两个端点之间的虚拟通信链路.与TCP通信不同,UDP是面向无连接的.不可靠的基于数据包的传输协议.即应用进程(或程序)在使用UDP协议之前,不必先建立连接.自然,发送数据结束时也没有连接需要释放.因此,减少了开销和发送数据之前的延时.UDP也采用端口来区分进程. 在java中,java.

黑马程序员_JAVA学习- 集合接口

一.Collection集合接口 在之前学习链表的时候可以发现,当使用add()方法向链表增加数据的时候,每次保存的都是一个对象的数据,而Collection操作过程之中每次也只能够保存一个对象.在Collection接口之中一共定义了15个方法,那么常用的方法如下: No. 方法名称 类型 描述 1 public boolean add(E e) 普通 向集合之中保存数据 2 public void clear() 普通 清空集合 3 public boolean contains(Objec

黑马程序员_JAVA 基础加强学习笔记

一.面向对象 (一)继承  1.继承的好处: (1) 提高了代码的复用性. (2) 让类与类之间产生了关系,提供了另一个特征多态的前提. 注意: 子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super();  如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数. 如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数. 2.final特点

黑马程序员_Java网络编程

1,IP地址和InetAddress IP地址是互联网上每台计算机都有的自己的标记,IP地址分为5类,A类保留给政府,B类给中等规模的公司,C类给任何需要的人,D类用于组播,E类用于实验. A类  1.0.0.1-126.255.255.254  B类  128.0.0.1-191.255.255.254   C类  192.0.0.1-223.255.255.254  D类 244.0.0.1-239.255.255.254 E类 240.0.0.1-255.255.255.254 在以后开发

黑马程序员_Java IO(下)

1,字符编码 在Java程序的开发中最常见的是ISO8859-1,GBK/GBK2312,unicode,UTF编码. ISO8859-1:属于单字节编码,最多只能表示0-255的字符范围,主要在英文上应用. GBK/GB2312:中文的国际编码,专门用来表示汉字,是双字节编码,如果在此编码中出现中文,则使用ISO8859-1编码,GBK可以表示简体中文和繁体中文,而GB2312只能表示简体中文,GBK兼容GB2312 unicode:Java中使用此编码方式,是最标准的一种编码,使用十六进制进

黑马程序员_Java高新技术

1  JDK5的新特性 1.1 静态导入       在API中那些不需要new对象的类,可以在类文件的开头,import static java.lang.Math.*;这里把Math中的所有的静态方法都导入了,在类中不需要调用Math类就能直接用Math的方法了 package cn.wjd.staticimport; import static java.lang.Math.*; public class StaticImport { public static void main(Str

黑马程序员_Java基础加强(上)

1.静态导入 静态导入是jdk1.5版本以后出现的新特性,一般是指导入静态方法,如:import static java.lang.System.out 是指导入系统输出的静态方法. 例: import static java.lang.System.out //导入java.lang包下的System类的静态方法out public class StaticImport { public static void main(String[] args) { int x=1; x++; out.p

黑马程序员_Java面向对象思想

面向对象要把握一个重要的经验:谁拥有数据,谁就对外提供操作这些数据的方法 . 1.人在黑板上画圆 对于这个设计,上面有Person, Blackborad , Circle三个对象 动词 : 画 因为画的动作的数据:坐标(x,y),半径(randius) 是Circle拥有的数据,所以 draw()方法应该属于Circle对象,而不是动作的发出者Person. 注意:[动作发出者往往会委托动作承受者 ] 2.司机紧急刹车 对于这个设计,上面有Driver, Car两个对象 动词 : 刹车 因为刹

黑马程序员-OC学习笔记之Foundation框架NSNumber、NSValue和NSDate

---------------------- IOS开发.Android培训.期待与您交流! ---------------------- 一.NSNumber 前几篇笔记中,小桥复习了Foundatio框架中的几个类,这三个是OC学习的尾声了. 小桥已经复习过OC数组类NSArray,也说过它只能存放OC的对象,对于基本的数据类型确无能为力,但是实际编程中经常要把基本的数据如int.float,结构体存放的OC数组中,怎么办?这里的NSNumber就有用了,它能够把基本数据类型包装成OC对象.