什么是输入/输出流
输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)
数据流是 Java 进行 I/O 操作的对象,它按照不同的标准可以分为不同的类别。
按照流的方向主要分为输入流和输出流两大类。
数据流按照数据单位的不同分为字节流和字符流。
按照功能可以划分为节点流和处理流。
输入流
Java 流相关的类都封装在 java.io 包中,而且每个数据流都是一个对象。所有输入流类都是 InputStream 抽象类(字节输入流)和 Reader 抽象类(字符输入流)的子类。其中 InputStream 类是字节输入流的抽象类,是所有字节输入流的父类,其层次结构如图 3 所示。
输出流
在 Java 中所有输出流类都是 OutputStream 抽象类(字节输出流)和 Writer 抽象类(字符输出流)的子类。其中 OutputStream 类是字节输出流的抽象类,是所有字节输出流的父类,其层次结构如图 4 所示。
==============================================================
===========================================================
Java系统流
每个 Java 程序运行时都带有一个系统流,系统流对应的类为 java.lang.System。Sytem 类封装了 Java 程序运行时的 3 个系统流,分别通过 in、out 和 err 变量来引用。这 3 个系统流如下所示:
System.in:标准输入流,默认设备是键盘。
System.out:标准输出流,默认设备是控制台。
System.err:标准错误流,默认设备是控制台。
System.in 是 InputStream 类的一个对象,因此上述代码的 System.in.read() 方法实际是访问 InputStream 类定义的 read() 方法。
System.out 和 System.error 是 PrintStream 类的对象。因为 PrintStream 是一个从 OutputStream 派生的输出流,所以它还执行低级别的 write() 方法。因此,除了 print() 和 println() 方法可以完成控制台输出以外,System.out 还可以调用 write() 方法实现控制台输出。
注意: 在实际操作中,print() 方法和 println() 方法比 write() 方法更常用。
代码示例:
public class SystemIo {
public static void main(String【】 args) {
byte【】 byteDate = new byte【100】; //声明一个字节数组
System.out.println("请输入英文:");
try{
System.in.read(byteDate);
}catch(IOException e){
e.printStackTrace();
}
System.out.println("你输入的内容如下:");
for(int i=0;i<=byteDate.length;i++){
System.out.write(byteDate【i】);
}
System.err.print("you are right");
}
}
=================================================================
=================================================================
字符编码
计算机中,任何的文字都是以指定的编码方式存在的,在 Java 程序的开发中最常见的是 ISO8859-1、GBK/GB2312、Unicode、 UTF 编码。
Java 中常见编码说明如下//代码效果参考:http://hnjlyzjd.com/hw/wz_25198.html
:ISO8859-1:属于单字节编码,最多只能表示 0~255 的字符范围。
GBK/GB2312:中文的国标编码,用来表示汉字,属于双字节编码。GBK 可以表示简体中文和繁体中文,而 GB2312 只能表示简体中文。GBK 兼容 GB2312。
Unicode:是一种编码规范,是为解决全球字符通用编码而设计的。UTF-8 和 UTF-16 是这种规范的一种实现,此编码不兼容 ISO8859-1 编码。Java 内部采用此编码。
UTF:UTF 编码兼容了 ISO8859-1 编码,同时也可以用来表示所有的语言字符,不过 UTF 编码是不定长编码,每一个字符的长度为 1~6 个字节不等。一般在中文网页中使用此编码,可以节省空间。
public class CharEncoding {
//自己:异常》=3个时就合并成一个父类。
public static void main(String【】 args) throws IOException {
//获取系统编码
System.out.println("系统默认编码"+System.getProperty("file.encoding"));
//由于java需要转义,所以\代表\
File f = new File("C:\Users\crystal\Desktop\CC.txt");
OutputStream //代码效果参考:http://hnjlyzjd.com/xl/wz_25196.html
os = new FileOutputStream(f);// 指定编码
byte b【】 = "c你好啊".getBytes("ISO8859-1");
os.write(b);
os.close();
}
}
=================================================================
=================================================================
Java File类(文件操作类)详解
在 Java 中,File 类是 java.io 包中唯一代表磁盘文件本身的对象,也就是说,如果希望在程序中操作文件和目录,则都可以通过 File 类来完成。File 类定义了一些方法来操作文件,如新建、删除、重命名文件和目录等。
File 类提供了如下三种形式构造方法。
File(String path):如果 path 是实际存在的路径,则该 File 对象表示的是目录;如果 path 是文件名,则该 File 对象表示的是文件。
File(String path, String name):path 是路径名,name 是文件名。
File(File dir, String name):dir 是路径对象,name 是文件名。
File 类中有以下两个常用常量:
public static final String pathSeparator:指的是分隔连续多个路径字符串的分隔符,Windows 下指"分号"。例如 java -cp test.jar;abc.jar HelloWorld。
public static final String separator:用来分隔同一个路径字符串中的目录的,Windows 下指“/”。例如 C:/Program Files/Common Files。
注意:Windows 的路径分隔符使用反斜线“\”,而 Java 程序中的反斜线表示转义字符,所以如果需要在 Windows 的路径下包括反斜线,则应该使用两条反斜线或直接使用斜线“/”也可以。Java 程序支持将斜线当成平台无关的路径分隔符。
例如:D:\javaspace\hello.java要写成D:/javaspace/hello.java 或者 D:\javaspace\hello.java。
获取文件属性
第一步是先创建一个 File 类对象并指向一个已存在的文件
public class FileIo {
public static void main(String【】 args) {
String path ="C:\Users\crystal\Desktop";//指定文件目录
File f = new File(path,"CC.txt");//建立File变量
System.out.println("C:\Users\crystal\Desktop/CC.txt文档信息如下:");
System.out.println("================================================");
System.out.println("文件长度:"+f.length()+"字节");
System.out.println("文件或者非文件:"+(f.isFile() ? "是文件" : "不是文件"));
System.out.println("目录或者非目录:"+(f.isDirectory() ? "是目录" :"不是目录"));
f.canRead();//可读
f.canWrite();//可写
f.isHidden();//是否隐藏
new Date(f.lastModified());//最后修改时间
f.getName();//文件名
f.getPath();//文件路径
f.getAbsolutePath();//绝对路径
}
}
创建和删除文件
File f = new File("C:\test.txt"); // 创建指向文件的File对象
if (f.exists()) // 判断文件是否存在
{
f.delete(); // 存在则先删除
}
f.createNewFile(); // 再创建
注意:在不同的操作系统中,路径的分隔符是不一样的,例如:
Windows 中使用反斜杠\表示目录的分隔符。
Linux 中使用正斜杠/表示目录的分隔符。
String path = "C:" + File.separator + "test.txt"; // 拼凑出可以适应操作系统的路径
File f = new File(path);
if (f.exists()) // 判断文件是否存在
{
f.delete(); // 存在则先删除
}
f.createNewFile(); // 再创建
创建和删除目录
String path = "C:/config/"; // 指定目录位置
File f = new File(path); // 创建File对象
if (f.exists()) {
f.delete();
}
f.mkdir(); // 创建目录
遍历目录
File 类的 list() 方法提供了遍历目录功能,该方法有如下两种重载形式。
1. String【】 list():如果调用的 File 对象不是目录,则返回 null。
2. String【】 list(FilenameFilter filter):如果 filter 为 null,则接受所有名称。
File f = new File("C:/"); // 建立File变量,并设定由f变量变数引用
System.out.println("文件名称\t\t文件类型\t\t文件大小");
System.out.println("===================================================");
String fileList【】 = f.list(); // 调用不带参数的list()方法
for (int i = 0; i < fileList.length; i++) { // 遍历返回的字符数组
System.out.print(fileList【i】 + "\t\t");
System.out.print((new File("C:/", fileList【i】)).isFile() ? "文件" + "\t\t" : "文件夹" + "\t\t");
System.out.println((new File("C:/", fileList【i】)).length() + "字节");
}
假设希望只列出目录下的某些文件,这就需要调用带过滤器参数的 list() 方法。首先需要创建文件过滤器,该过滤器必须实现 java.io.FilenameFilter 接口,并在 accept() 方法中指定允许的文件类型。
String fileList【】 = f.list(new ImageFilter());
public class ImageFilter implements FilenameFilter {
// 实现 FilenameFilter 接口
@Override
public boolean accept(File dir, String name) {
// 指定允许的文件类型
return name.endsWith(".sys") || name.endsWith(".txt") || name.endsWith(".bak");
}
}
Java动态读取文件内容---扩展
Java RandomAccessFile类:动态读取文件内容
例 1
编写一个程序,使用 RandomAccessFileDemo 类创建一个 words.txt 文件,然后写入一个长中文字符串,再从第 6 个字节开始读取并输出。
1)创建一个 RandomAccessFileDemo 类对象。在 main() 方法中创建到 D:\JavaCodes\words.txt 的 File 对象,如果该文件已经存在则先删除再创建,代码如下所示。
public class RandomAccessFileDemo {
public static void main(String【】 args) {
try {
File file = new File("D:\myJava\words.txt"); // 指定文件路径
if (file.exists()) { // 判断文件是否存在
file.delete();
file.createNewFile();
}
} catch (IOException e) {
System.out.print(e);
}
}
}
RandomAccessFile raf = new RandomAccessFile(file,"rw");
String str1 = "晴天,阴天,多云,小雨,大风,中雨,小雪,雷阵雨"; // 要写入的字符串
String str2 = new String(str1.getBytes("GBK"),"ISO-8859-1"); // 编码转换
raf.writeBytes(str2); //写入文件
System.out.println("当前文件指针的位置:" + raf.getFilePointer());
raf.seek(6); // 移动文件指针
System.out.println("从文件头跳过6个字节,现在文件内容如下:");
byte【】 buffer = new byte【2】;
int len = 0;
while ((len = raf.read(buffer, 0, 2)) != -1) {
System.out.print(new String(buffer, 0, len)); // 输出文件内容
}
=============================================================================
================================================================================
Java字节流的使用:字节输入/输出流、文件输入/输出流、字节数组输入/输出流
InputStream 是 Java 所有字节输入流类的父类,OutputStream 是 Java 所有字节输出流类的父类,它们都是一个抽象类,因此继承它们的子类要重新定义父类中的抽象方法。
介绍如何使用它们的子类输入和输出字节流,包括 ByteArrayInputStream 类、ByteArrayOutputStream 类、FileInputStream 类和 FileOutputStream 类。
字节输入流
InputStream 类及其子类的对象表示字节输入流,InputStream 类的常用子类如下。
ByteArrayInputStream 类:将字节数组转换为字节输入流,从中读取字节。
FileInputStream 类:从文件中读取数据。
PipedInputStream 类:连接到一个 PipedOutputStream(管道输出流)。
SequenceInputStream 类:将多个字节输入流串联成一个字节输入流。
ObjectInputStream 类:将对象反序列化。
使用 InputStream 类的方法可以从流中读取一个或一批字节。
注意:在使用 mark() 方法和 reset() 方法之前,需要判断该文件系统是否支持这两个方法,以避免对程序造成影响。
字节输出流
OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。
ByteArrayOutputStream 类:向内存缓冲区的字节数组中写数据。
FileOutputStream 类:向文件中写数据。
PipedOutputStream 类:连接到一个 PipedlntputStream(管道输入流)。
ObjectOutputStream 类:将对象序列化。
字节数组输入流
ByteArrayInputStream 类
ByteArrayInputStream(byte【】 buf):创建一个字节数组输入流,字节数组类型的数据源由参数 buf 指定。
ByteArrayInputStream(byte【】 buf,int offse,int length):创建一个字节数组输入流,其中,参数 buf 指定字节数
例 1
使用 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(); // 读取下一个
}
}
}
原值=1 转换为int类型=1
原值=-1 转换为int类型=255
原值=25 转换为int类型=25
原值=-22 转换为int类型=234
原值=-5 转换为int类型=251
原值=23 转换为int类型=23
从上述的运行结果可以看出,字节类型的数据 -1 和 -22 转换成 int 类型的数据后变成了 255 和 234,对这种结果的解释如下:
字节类型的 1,二进制形式为 00000001,转换为 int 类型后的二进制形式为 00000000 00000000 0000000000000001,对应的十进制数为 1。
字节类型的 -1,二进制形式为 11111111,转换为 int 类型后的二进制形式为 00000000 00000000 0000000011111111,对应的十进制数为 255。
可见,从字节类型的数转换成 int 类型的数时,如果是正数,则数值不变;如果是负数,则由于转换后,二进制形式前面直接补了 24 个 0,这样就改变了原来表示负数的二进制补码形式,所以数值发生了变化,即变成了正数。
提示:负数的二进制形式以补码形式存在,例如 -1,其二进制形式是这样得来的:首先获取 1 的原码 00000001,然后进行反码操作,1 变成 0,0 变成 1,这样就得到 11111110,最后进行补码操作,就是在反码的末尾位加 1,这样就变成了 11111111。
字节数组输出流
ByteArrayOutputStream 类可以向内存的字节数组中写入数据,该类的构造方法有如下两种重载形式。
ByteArrayOutputStream():创建一个字节数组输出流,输出流缓冲区的初始容量大小为 32 字节。
ByteArrayOutputStream(int size):创建一个字节数组输出流,输出流缓冲区的初始容量大小由参数 size 指定。
ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外,还有如下两个方法。
intsize():返回缓冲区中的当前字节数。
byte【】 toByteArray():以字节数组的形式返回输出流中的当前内容。
例 2
使用 ByteArrayOutputStream 类编写一个案例,实现将字节数组中的数据输出,代码如下所示。
public class Test09 {
public static void main(String【】 args) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte【】 b = new byte【】 { 1, -1, 25, -22, -5, 23 }; // 创建数组<