1、Java中I/O操作主要是指使用Java进行输入(input),输出(output)操作。 Java所有的I/O机制都是基于数据流(stream)进行输入输出,最常见的是对文件(File)进行输入输出流操作,这些数据流表示了字符或者字节数据的流动序列。
2、Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些数据流类代表了输入源和输出目标。。使用到了 Decorator(装饰器)模式(软件工程设计模式),按功能划分Stream,您可以动态装配这些 Stream,以便获得您需要的功能。
3、数据流Stream是一串连续不断的数据的集合,在读取数据流的过程中,看不到数据流在写入时的分段情况,每次可以读取其中的任意长度的数据,但只能先读取前面的数据后,再读取后面的数据。
4、“流是磁盘或其它外围设备中存储的数据的源点或终点。”不管写入时是将数据分多次写入,还是作为一个整体一次写入,读取时的效果都是完全一样的。采用数据流的目的就是使得输出输入独立于设备。
1、在电脑上的数据有三种存储方式,一种是外存,一种是内存,一种是缓存。比如电脑上的硬盘,磁盘,U盘等都是外存,在电脑上有内存条,缓存是在CPU里面的。
2、存储量(依次递减): 外存-->内存-->缓存
存取速度(依次递减): 缓存-->内存-->外存
3、在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:
(1)标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流等等,java中将输入输出抽象称为流,就好像水管,将两个容器连接起来。将数据冲外存中读取到内存中的称为输入流,将数据从内存写入外存中的称为输出流。
(2)流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。
4、输入流: 表示从一个源读取数据。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道。
输出流: 表示向一个目标写数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。
在整个Java.io包中最重要的是5个类和一个接口。
5个类指的是File、OutputStream、InputStream、Writer、Reader;
一个接口指的是Serializable。
import java.io.*;
/*创建字符流 BufferedReader 的基本语法:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); */
//1、从控制台读取 单字符
//使用 BufferedReader 在控制台读取字符
public class BRRead {
public static void main(String[] args) throws IOException {
char c;
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入字符, 按下 'q' 键退出。");
// 每次调用 read() 方法,它从输入流读取一个字符并把该字符作为整数值返回
do {
c = (char) br.read();
System.out.println(c);
} while (c != 'q');
}
}//当流结束的时候返回 -1。该方法抛出 IOException。
//2、从控制台读取 字符串
//读取一个字符串需要使用readLine() 方法
public class BRReadLines {
public static void main(String[] args) throws IOException {
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str;
System.out.println("Enter lines of text.");
System.out.println("Enter 'end' to quit.");
do {
str = br.readLine();
System.out.println(str);
} while (!str.equals("end"));
}
}
控制台的输出由 print( ) 和 println() 完成。这些方法都由类 PrintStream 定义,System.out 是该类对象的一个引用。PrintStream 继承了 OutputStream类,并且实现了方法 write()。这样,write() 也可以用来往控制台写操作。
即:System.out.write(b); == System.out.println(b);
//字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中
/*
1、接收字节数组作为参数创建:
ByteArrayInputStream bArray = new ByteArrayInputStream(byte [] a);
2、接收一个字节数组,和两个整形变量 off、len,off表示第一个读取的字节,len表示读取字节的长度:
ByteArrayInputStream bArray = new ByteArrayInputStream(byte []a, int off, int len)
*/
//字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中
/*
1、下面的构造方法创建一个32字节(默认大小)的缓冲区:
OutputStream bOut = new ByteArrayOutputStream();
2、创建一个大小为 a 字节的缓冲区:
OutputStream bOut = new ByteArrayOutputStream(int a)
*/
ByteArrayInputStream类:
ByteArrayOutputStream类:
实例:
import java.io.*;
public class ByteStreamTest {
public static void main(String[] args)throws IOException {
//定义一个12字节数组缓冲区,对流进行写操作或其他操作
ByteArrayOutputStream bOutput = new ByteArrayOutputStream(12);
//输入数据未到10个字节
while( bOutput.size()!= 10 ) {
// 一直获取用户输入值
bOutput.write(System.in.read());
}
//创建一个新分配的字节数组b,内容是用户输入数据
byte b [] = bOutput.toByteArray();
System.out.println("Print the content");
for(int x= 0 ; x < b.length; x++) {
// 打印b数组内的字符
System.out.print((char)b[x] + " ");
}
System.out.println(" ");
int c;
//创建字节数组输入流对象,对流进行读操作或其他操作
ByteArrayInputStream bInput = new ByteArrayInputStream(b);
System.out.println("Converting characters to Upper case " );
for(int y = 0 ; y < 1; y++ ) {
//只要读b里面还有东西就一直转大写输出出来
while(( c= bInput.read())!= -1) {
System.out.println(Character.toUpperCase((char)c));
}
//将此字节数组输出流的 count 字段重置为零,丢弃输出流中目前已累积的所有数据输出
bInput.reset();
}
}
}
/*
asdfghjkly
Print the content
a s d f g h j k l y
Converting characters to Upper case
A
S
D
F
G
H
J
K
L
Y
*/
/*数据输入流允许应用程序以与机器无关的方式从底层输入流中读取基本 Java 数据类型
1、创建数据输入流对象*/
DataInputStream dis = new DataInputStream(InputStream in);
/*数据输出流允许应用程序以与机器无关的方式将Java基本数据类型写到底层输出流
2、创建数据输出流对象*/
DataOutputStream out = new DataOutputStream(OutputStream out);
import java.io.*;
/**
*数据流
* 与机器无关的操作JAVA基本数据类型
*
*/
public class DataStreamDemo {
public static void main(String[] args) {
write();
read();
}
public static void read(){
File file = new File("c:/test.dat");
try {
FileInputStream inputStream = new FileInputStream(file);
DataInputStream dataInputStream = new DataInputStream(inputStream);
int i=dataInputStream.readInt();//按照写的顺序依次读出
byte j=dataInputStream.readByte();
String k=dataInputStream.readUTF();
System.out.println("i="+i+" j="+j+" k="+k);
dataInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void write(){
File file = new File("c:/test.dat");
try {
OutputStream outputStream = new FileOutputStream(file);
DataOutputStream dos = new DataOutputStream(outputStream);
dos.writeInt(10);//由于是int类型写入4个字节
dos.writeByte(1);//写入1个字节
dos.writeUTF("中");//写入utf-8类型数据
dos.close();//何时均需要关闭流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1、计算机访问外部设备非常耗时。访问外存的频率越高,造成CPU闲置的概率就越大,为了减少访问外存的次数,应该在一次对外设的访问中,读写更多的数据。为此,除了程序和流节点间交换数据必需的读写机制外,还应该增加缓冲机制;
2、缓冲流就是每一个数据流分配一个缓冲区,一个缓冲区就是一个临时存储数据的内存;这样可以减少访问硬盘的次数,提高传输效率;
3、总体来说,不同点就是在写入数据或读出时先在缓冲区操作,缓冲区满后再系统一次性将数据传输给输出设备/传回。具体请学习参考文献1获深刻理解
首先介绍 .flush()方法,其实是Java中关于缓冲区数据存储的问题。
1、java在使用流时,都会有一个缓冲区存放要发的数据,缓冲区放满以后再一次性发过去,而不是分开一次一次地发;2、.flush()表示强制将缓冲区中的数据发送出去,不必等到缓冲区满.
Java.io.OutputStream.flush() 方法刷新此输出流并强制将所有缓冲的输出字节被写出。
//1、File (String pathname) 创建文件对象f1,f1所指的文件是在当前目录下创建的FileTest1.txt
File f1=new File("FileTest1.txt");
//2、File(URI uri)通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例
//3、File (String parent , String child)
File f2=new File(“D:\\dir1","FileTest2.txt") ;//注意:D:\\dir1目录事先必须存在,否则异常
/*4、File (File parent , String child)
parent 路径名字符串和 child 路径名字符串创建一个新 File 实例*/
File f4=new File("E:\\dir3");
File f5=new File(f4,"FileTest5.txt");
//若E:\\dir3目录不存在则需要先使用f4.mkdir()先创建
序号 | 方法描述 |
1 |
public String getName() 返回由此抽象路径名表示的文件或目录的名称 |
2 | public String getParent()、 返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null |
3 | public File getParentFile() 返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null |
4 | public String getPath() 将此抽象路径名转换为一个路径名字符串 |
5 | public boolean isAbsolute() 测试此抽象路径名是否为绝对路径名 |
6 | public String getAbsolutePath() 返回抽象路径名的绝对路径名字符串 |
7 | public boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件 |
8 | public boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件 |
9 | public boolean exists() 测试此抽象路径名表示的文件或目录是否存在 |
10 | public boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录 |
11 | public boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件 |
12 | public long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间 |
13 | public long length() 返回由此抽象路径名表示的文件的长度 |
14 | public boolean createNewFile() throws IOException 当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件 |
15 | public boolean delete() 删除此抽象路径名表示的文件或目录 |
16 | public void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录 |
17 | public String[] list() 返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组 |
18 | public String[] list(FilenameFilter filter) 返回由包含在目录中的文件和目录的名称所组成的字符串数组,这一目录是通过满足指定过滤器的抽象路径名来表示的 |
19 | public File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件 |
20 | public File[] listFiles(FileFilter filter) 返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器 |
21 | public boolean mkdir() 创建此抽象路径名指定的目录 |
22 | public boolean mkdirs() 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录 |
23 | public boolean renameTo(File dest) 重新命名此抽象路径名表示的文件 |
24 | public boolean setLastModified(long time) 设置由此抽象路径名所指定的文件或目录的最后一次修改时间 |
25 | public boolean setReadOnly() 标记此抽象路径名指定的文件或目录,以便只可对其进行读操作 |
26 | public static File createTempFile(String prefix, String suffix, File directory) throws IOException 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称 |
27 | public static File createTempFile(String prefix, String suffix) throws IOException 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称 |
28 | public int compareTo(File pathname) 按字母顺序比较两个抽象路径名 |
29 | public int compareTo(Object o) 按字母顺序比较抽象路径名与给定对象 |
30 | public boolean equals(Object obj) 测试此抽象路径名与给定对象是否相等 |
31 | public String toString() 返回此抽象路径名的路径名字符串 |
//1、判断文件内是否为目录或者文件
import java.io.File;
public class DirList {
public static void main(String args[]) {
String dirname = "/java";
File f1 = new File(dirname);
if (f1.isDirectory()) {
System.out.println("Directory of " + dirname);
String s[] = f1.list();
for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]);
if (f.isDirectory()) {
System.out.println(s[i] + " is a directory");
} else {
System.out.println(s[i] + " is a file");
}
}
} else {
System.out.println(dirname + " is not a directory");
}
}
}
/*
Directory of /java
bin is a directory
lib is a directory
demo is a directory
test.txt is a file
README is a file
index.html is a file
include is a directory
*/
//2、创建 "/tmp/user/java/bin"文件夹
import java.io.File;
public class CreateDir {
public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// 现在创建目录
d.mkdirs();
}
}
//3、删除目录或文件,当删除某一目录时,必须保证该目录下没有其他文件才能正确删除,否则将删除失败
import java.io.File;
public class DeleteFileDemo {
public static void main(String[] args) {
File folder = new File("/test/java/");
deleteFolder(folder);
}
// 删除文件及目录
public static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteFolder(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
//4、输出一个目录中的所有文件名
public class Main {
public static void main(String[] args) throws IOException {
FileUtils.listDir("E:\\java");
}
public class FileUtils {
public static void listDir(String dir) throws IOException {
File file = new File(dir);
//传进来的可能不是一个目录
if (!file.isDirectory()) {
throw new IOException(dir+"不是目录");
}
//传进来的可能是一个错误的路径
else if (file == null) {
throw new IOException("没有此路径");
}
File[] files = file.listFiles();
for (File f : files) {
//有可能是一个多级目录,递归调用
if (f.isDirectory()) {
listDir(f.getAbsolutePath());
//是文件就直接输出该文件的绝对路径
}else {
System.out.println(f.getAbsolutePath());
}
}
}
}
}
FileWriter 类从 OutputStreamWriter 类继承而来。该类按字符向流中写入数据。
FileReader类从 InputStreamReader类继承而来。该类按字符读取流中数据。
1、在给出 File 对象的情况下构造一个 FileWriter /FileReader对象;
FileWriter(File file)/FileReader(File file)
2、在给出 File 对象的情况下构造一个 FileWriter 对象;3、FileWriter(File file, boolean append);
参数:
- file:要写入数据的 File 对象。
- append:如果 append 参数为 true,则将字节写入文件末尾处,相当于追加信息。如果 append 参数为 false, 则写入文件开始处。
4、在给出文件名的情况下构造 FileWriter 对象,它具有指示是否挂起写入数据的 boolean 值
FileWriter(String fileName, boolean append);同3
5、在给定从中读取数据的文件名的情况下创建一个新 FileReader;FileReader(String fileName)
import java.io.*;
public class FileRead {
public static void main(String args[]) throws IOException {
File file = new File("Hello1.txt");
// 创建文件
file.createNewFile();
// creates a FileWriter Object
FileWriter writer = new FileWriter(file);
// 向文件写入内容
writer.write("This\n is\n an\n example\n");
writer.flush();
writer.close();
// 创建 FileReader 对象
FileReader fr = new FileReader(file);
char[] a = new char[50];
fr.read(a); // 读取数组中的内容
for (char c : a)
System.out.print(c); // 一个一个打印字符
fr.close();
}
}
/*
This
is
an
example
*/
//该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
/*
1、使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream f = new FileInputStream("C:/java/hello");
2、使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:
File f = new File("C:/java/hello");
InputStream in = new FileInputStream(f);
*/
//该类用来创建一个文件并向文件中写数据。如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
/*
1、使用字符串类型的文件名来创建一个输出流对象:
OutputStream f = new FileOutputStream("C:/java/hello");
2、使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:
File f = new File("C:/java/hello");
OutputStream fOut = new FileOutputStream(f);
*/
FileInputStream类:
FileOutputStream类:
实例:
import java.io.*;
public class fileStreamTest {
public static void main(String[] args) {
try {
//定义一个byte数组存放数据
byte bWrite[] = { 1, 2, 3, 4, 5 };
//创建文件并把指定数据字节以二进制形式写入
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
//关闭流
os.close();
//创建is对象读取文件数据
InputStream is = new FileInputStream("test.txt");
//得到一个整数值,返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取的字节数,就是长度
int size = is.available();
for (int i = 0; i < size; i++) {
//read将字符一个个打印出来,记得后面加几段空格
System.out.print((char) is.read() + " ");
}
//关闭流
is.close();
//如果异常则抛出
} catch (IOException e) {
System.out.print("Exception");
}
}
}
//1 2 3 4 5
以上首先创建文件test.txt,并把给定的数字以二进制形式写进该文件,后输出到控制台上。
以上代码由于是二进制写入,可能存在乱码,可以使用以下代码实例来解决乱码问题:
import java.io.*;
public class fileStreamTest {
public static void main(String[] args) throws IOException {
File f = new File("a.txt");
FileOutputStream fop = new FileOutputStream(f);
// 构建FileOutputStream对象,文件不存在会自动新建
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
// 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk
writer.append("中文输入");
// 写入到缓冲区
writer.append("\r\n");
// 换行
writer.append("English");
// 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入
writer.close();
// 关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉
fop.close();
// 关闭输出流,释放系统资源
FileInputStream fip = new FileInputStream(f);
// 构建FileInputStream对象
InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
// 构建InputStreamReader对象,编码与写入相同
StringBuffer sb = new StringBuffer();
while (reader.ready()) {
sb.append((char) reader.read());
// 转成char加到StringBuffer对象中
}
System.out.println(sb.toString());
reader.close();
// 关闭读取流
fip.close();
// 关闭输入流,释放系统资源
}
}
/*
1. 使用 FileInputStream 输入两个音频
2. 使用 FileInputStream的skip(long n) 方法跳过特定字节长度的音频文件,比如说:输入 skip(1024*1024*3),这样就能丢弃掉音频文件前面的 3MB 的内容。
3. 截取中间特定长度的音频文件:每次输入 8KB 的内容,使用 count 记录输入次数,达到设置的次数就终止音频输入。比如说要截取 2MB 的音频,每次往输入流中输入 8KB 的内容,就要输入 1024*2/8 次。
4. 往同一个输出流 FileOutputStream 中输出音频,并生成文件,实现音频混合。
*/
public class MusicCompound
{
public static void main(String args[])
{
FileOutputStream fileOutputStream = null;
FileInputStream fileInputStream = null;
String fileNames[] = {"E:/我落泪情绪零碎.mp3","E:/稻香.mp3"};
//设置byte数组,每次往输出流中传入8K的内容
byte by[] = new byte[1024*8];
try
{
fileOutputStream = new FileOutputStream("E:/合并.mp3");
for(int i=0;i<2;i++)
{
int count = 0;
fileInputStream = new FileInputStream(fileNames[i]);
//跳过前面3M的歌曲内容
fileInputStream.skip(1024*1024*3);
while(fileInputStream.read(by) != -1)
{
fileOutputStream.write(by);
count++;
System.out.println(count);
//要截取中间2MB的内容,每次输入8k的内容,所以输入的次数是1024*2/8
if(count == (1024*2/8))
{
break;
}
}
}
}
catch(FileNotFoundException e)
{
e.printStackTrace();
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
try
{
//输出完成后关闭输入输出流
fileInputStream.close();
fileOutputStream.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
/*文件的简单复制: test.txt 内容为:
I love Java, I love JavaI love Java, I love JavaI love Java, I love JavaI love Java, I love JavaI love Java, I love Java*/
import java.io.*;
public class Test {
public static void main(String[] args) {
File sourcefile = new File("./test.txt");
File copyfile = new File("./testcopy.txt");
FileInputStream fileInputStream = null;// 从文件中读数据
FileOutputStream fileOutputStream = null;// 用于把数据写入文件
BufferedWriter bufferedWriter = null;// 用于把数据写入文件
try {
if (!sourcefile.exists()) {
sourcefile.createNewFile();
bufferedWriter = new BufferedWriter(new FileWriter(sourcefile));
// bufferedwriter 自动追加数据
String s = new String(" I love Java");
char bchar[] = s.toCharArray();
for (int i = 0; i < 5; i++) {
// 两种方式往文件中写数据
bufferedWriter.write(bchar, 0, bchar.length);
bufferedWriter.write(", " + s + "\n");
}
// 写完之后才能关闭流,
bufferedWriter.flush();
bufferedWriter.close();
}
copyfile.createNewFile();
fileInputStream = new FileInputStream(sourcefile);
fileOutputStream = new FileOutputStream(copyfile);
byte b[] = new byte[8192];
int len = b.length;
while ((len = fileInputStream.read(b, 0, len)) > 0) {
fileOutputStream.write(b, 0, len);
fileOutputStream.flush();
}
System.out.println("file copied");
fileInputStream.close();
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//执行程序,可以看到创建了一个 testcopy.txt 文件,内容相同
参考资料:菜鸟教程 - 学的不仅是技术,更是梦想!
参考文献1:java回忆录—输入输出流详细讲解(入门经典)_创建空的输出流-CSDN博客
参考文献2:Java数据流DataInputStream和DataOutputStream代码实例_datainputstream 实例化-CSDN博客
参考文献3:java,write()方法后写flush()的作用_write.flush作用-CSDN博客