下面首先介绍上述两个父类提供的常用方法,然后介绍如何使用它们的子类输入和输出字节流,包括 ByteArrayInputStream 类、ByteArrayOutputStream 类、FileInputStream 类和 FileOutputStream 类。
InputStream 类及其子类的对象表示字节输入流,InputStream 类的常用子类如下。
使用 InputStream 类的方法可以从流中读取一个或一批字节。表 1 列出了 InputStream 类的常用方法。
注意:在使用 mark() 方法和 reset() 方法之前,需要判断该文件系统是否支持这两个方法,以避免对程序造成影响。
OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。
利用 OutputStream 类的方法可以从流中写入一个或一批字节。表 2 列出了 OutputStream 类的常用方法。
ByteArrayInputStream 类可以从内存的字节数组中读取数据,该类有如下两种构造方法重载形式。
使用 ByteArrayInputStream 类编写一个案例,实现从一个字节数组中读取数据,再转换为 int 型进行输出。代码如下:
public class test08 {public static void main(String[] args) {byte[] b = new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组ByteArrayInputStream bais = new ByteArrayInputStream(b, 0, 6); // 创建字节数组输入流int i = bais.read(); // 从输入流中读取下一个字节,并转换成int型数据while (i != -1) { // 如果不返回-1,则表示没有到输入流的末尾System.out.println("原值=" + (byte) i + "\t\t\t转换为int类型=" + i);i = bais.read(); // 读取下一个}}
}
在该示例中,字节输入流 bais 从字节数组 b 的第一个元素开始读取 4 字节元素,并将这 4 字节转换为 int 类型数据,最后返回。
提示:上述示例中除了打印 i 的值外,还打印出了 (byte)i 的值,由于 i 的值是从 byte 类型的数据转换过来的,所以使用 (byte)i 可以获取原来的 byte 数据。
该程序的运行结果如下:
原值=1 转换为int类型=1
原值=-1 转换为int类型=255
原值=25 转换为int类型=25
原值=-22 转换为int类型=234
原值=-5 转换为int类型=251
原值=23 转换为int类型=23
从上述的运行结果可以看出,字节类型的数据 -1 和 -22 转换成 int 类型的数据后变成了 255 和 234,对这种结果的解释如下:
可见,从字节类型的数转换成 int 类型的数时,如果是正数,则数值不变;如果是负数,则由于转换后,二进制形式前面直接补了 24 个 0,这样就改变了原来表示负数的二进制补码形式,所以数值发生了变化,即变成了正数。
提示:负数的二进制形式以补码形式存在,例如 -1,其二进制形式是这样得来的:首先获取 1 的原码 00000001,然后进行反码操作,1 变成 0,0 变成 1,这样就得到 11111110,最后进行补码操作,就是在反码的末尾位加 1,这样就变成了 11111111。
ByteArrayOutputStream 类可以向内存的字节数组中写入数据,该类的构造方法有如下两种重载形式。
ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外,还有如下两个方法。
使用 ByteArrayOutputStream 类编写一个案例,实现将字节数组中的数据输出,代码如下所示。
public class Test09 {public static void main(String[] args) {ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] b = new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组baos.write(b, 0, 6); // 将字节数组b中的前4个字节元素写到输出流中System.out.println("数组中一共包含:" + baos.size() + "字节"); // 输出缓冲区中的字节数byte[] newByteArray = baos.toByteArray(); // 将输出流中的当前内容转换成字节数组System.out.println(Arrays.toString(newByteArray)); // 输出数组中的内容}
}
该程序的输出结果如下:
数组中一共包含:6字节
[1, -1, 25, -22, -5, 23]
FileInputStream 是 Java 流中比较常用的一种,它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。
在创建 FileInputStream 类的对象时,如果找不到指定的文件将拋出 FileNotFoundException 异常,该异常必须捕获或声明拋出。
FileInputStream 常用的构造方法主要有如下两种重载形式。
下面的示例演示了 FileInputStream() 两个构造方法的使用。+
try {// 以File对象作为参数创建FileInputStream对象FileInputStream fis1 = new FileInputStream(new File("F:/mxl.txt"));// 以字符串值作为参数创建FilelnputStream对象FileInputStream fis2 = new FileInputStream("F:/mxl.txt");
} catch(FileNotFoundException e) {System.out.println("指定的文件找不到!");
}
假设有一个 D:\myJava\HelloJava.java 文件,下面使用 FileInputStream 类读取并输出该文件的内容。具体代码如下:
public class Test10 {public static void main(String[] args) {File f = new File("D:/myJava/HelloJava.java");FileInputStream fis = null;try {// 因为File没有读写的能力,所以需要有个InputStreamfis = new FileInputStream(f);// 定义一个字节数组byte[] bytes = new byte[1024];int n = 0; // 得到实际读取到的字节数System.out.println("D:\\myJava\\HelloJava.java文件内容如下:");// 循环读取while ((n = fis.read(bytes)) != -1) {String s = new String(bytes, 0, n); // 将数组中从下标0到n的内容给sSystem.out.println(s);}} catch (Exception e) {e.printStackTrace();} finally {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}
}
如上述代码,在 FileInputDemo 类的 main() 方法中首先创建了一个 File 对象 f,该对象指向 D:\myJava\HelloJava.java 文件。接着使用 FileInputStream 类的构造方法创建了一个 FileInputStream 对象 fis,并声明一个长度为 1024 的 byte 类型的数组,然后使用 FileInputStream 类中的 read() 方法将 HelloJava.java 文件中的数据读取到字节数组 bytes 中,并输出该数据。最后在 finally 语句中关闭 FileInputStream 输入流。
图 1 所示为 HelloJava.java 文件的原始内容,如下所示的是运行程序后的输出内容。
D:\myJava\HelloJava.java文件内容如下:
/*
*第一个java程序
*/
public class HelloJava {
// 这里是程序入口
public static void main(String[] args) {
// 输出字符串
System.out.println(“你好 Java”);
}
}
注意:FileInputStream 类重写了父类 InputStream 中的 read() 方法、skip() 方法、available() 方法和 close() 方法,不支持 mark() 方法和 reset() 方法。
FileOutputStream 类继承自 OutputStream 类,重写和实现了父类中的所有方法。FileOutputStream 类的对象表示一个文件字节输出流,可以向流中写入一个字节或一批字节。
在创建 FileOutputStream 类的对象时,如果指定的文件不存在,则创建一个新文件;如果文件已存在,则清除原文件的内容重新写入。
FileOutputStream 类的构造方法主要有如下 4 种重载形式。
注意:使用构造方法 FileOutputStream(String name,boolean append) 创建一个文件输出流对象,它将数据附加在现有文件的末尾。该字符串 name 指明了原文件,如果只是为了附加数据而不是重写任何已有的数据,布尔类型参数 append 的值应为 true。
对文件输出流有如下四点说明:
同样是读取 D:\myJava\HelloJava.java 文件的内容,在这里使用 FileInputStream 类实现,然后再将内容写入新的文件 D:\myJava\HelloJava.txt 中。具体的代码如下:
public class Test11 {public static void main(String[] args) {FileInputStream fis = null; // 声明FileInputStream对象fisFileOutputStream fos = null; // 声明FileOutputStream对象fostry {File srcFile = new File("D:/myJava/HelloJava.java");fis = new FileInputStream(srcFile); // 实例化FileInputStream对象File targetFile = new File("D:/myJava/HelloJava.txt"); // 创建目标文件对象,该文件不存在fos = new FileOutputStream(targetFile); // 实例化FileOutputStream对象byte[] bytes = new byte[1024]; // 每次读取1024字节int i = fis.read(bytes);while (i != -1) {fos.write(bytes, 0, i); // 向D:\HelloJava.txt文件中写入内容i = fis.read(bytes);}System.out.println("写入结束!");} catch (Exception e) {e.printStackTrace();} finally {try {fis.close(); // 关闭FileInputStream对象fos.close(); // 关闭FileOutputStream对象} catch (IOException e) {e.printStackTrace();}}}
}
如上述代码,将 D:\myJava\HelloJava.java 文件中的内容通过文件输入/输出流写入到了 D:\myJava\HelloJava.txt 文件中。由于 HelloJava.txt 文件并不存在,所以在执行程序时将新建此文件,并写入相应内容。
运行程序,成功后会在控制台输出“写入结束!”。此时,打开 D:\myJava\HelloJava.txt 文件会发现,其内容与 HelloJava.java 文件的内容相同,如图 2 所示。
技巧:在创建 FileOutputStream 对象时,如果将 append 参数设置为 true,则可以在目标文件的内容末尾添加数据,此时目标文件仍然可以暂不存在。
下一篇:JVM—类加载与字节码技术