Java 输入输出流(一)
本篇文章我们将了解什么是输入和输入,什么是流;输入输出流的应用场景,
File
类的使用,什么是文件,Java提供的输入输出流的相关API等。
1.什么是输入输出(I/O)
1.1基本概念
输入/输出这个概念,对于计算机相关专业的同学并不陌生,在计算中,输入/输出(
Input/Output
缩写 I / O) 是信息处理系统(例如计算机)与外界 (可能是人类或其他信息处理系统)之间的通信。输入是系统接收的信号或数据,输出是从系统发送的信号或者数据 。
那么在Java中,什么是输入输出呢?要理解这个概念,可将Java平台视作一个操作系统。Java平台是一个孤立的系统,系统之外的所有东西都是它的环境。系统与其环境之间的交互是一种双向对话,系统要么从其环境接收消息,要么将其消息传递给环境。当系统接收到消息时,将其为输入,与之相反的是输出。
Java 提供了两个用于 I / O 的包:较旧的java.io包(不支持符号链接)和较新的java.nio(“new io”)包,它对java.nio.file的异常处理进行了改进。
1.2 简单的Java输出——打印内容到屏幕
一直以来,我们都在向屏幕输出内容以验证我们编写的代码逻辑。向屏幕输出内容非常简单,可以由以下两种方式来完成:
// 打印 Hello World,不换行
System.out.print("Hello World");
// 打印 Hello Java,并换行
System.out.println("Hello Java");
1.3 简单的Java输入——键盘输入
java.util
包下的
Scanner
类可用于获取用户从键盘输入的内容。
package com.sean.iotest;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
// 创建扫描器对象
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您的姓名:");
// 可以将用户输入的内容扫描为字符串
String name = scanner.nextLine();
// 打印输出
System.out.println("你好 ".concat(name).concat(" ,欢迎来到seanRoom!"));
// 关闭扫描器
scanner.close();
}
}
运行结果:
请输入您的姓名:
sean001
你好 sean001 ,欢迎来到seanRoom!
2. 什么是流(Stream)
Java中最基本的输入/输出是使用流来完成的。
流也是代表数据源和数据目标的对象,怎么理解这句话呢?简单的来说,可以读取作为数据源的流。也可以写入作为数据目标的流。Java中流是长度不确定的有序字节序列,
它是一连串流动的字符
,是以先进先出的方式发送信息的通道。
3.输入输出流的应用场景
上面我们已经了解到输入输出流的基本概念,那么它具体是做什么用的呢?
在
web
产品的开发中,最常见的开发功能就是上传文件到服务器了,这个文件的读写过程就要用到输入输出流。对于计算机中文件的读写、复制和删除等操作也要用到输入输出流。输入输出流可以说是无处不在的。
4.File类
在Java中,提供了
java.io.File
类对文件和目录进行操作。
File意思是文件,文件在计算机中非常的重要,我们编写word文档,PPT演示文稿,运行游戏的
.exe
可执行文件以及我们编写的Java的源代码等等都是文件。
4.1 实例化
要实例化
File
对象,需要传入一个文件或目录的路径。
File类提供了如下4个方法:
-
:从父抽象路径名和子路径名字符串创建新的文件实例;File(File parent,String child)
-
: 通过将给定的路径名字符串转化为抽象路径名,创建一个新的文件实例(最常用);File(String pathName)
-
:从父路径名字符串和子路径名字符串创建新的文件实例;File(String parent, String child)
- File(URI uri ):通过给定的文件:URI转换为抽象路径名,创建就一个新的文件实例。
我们来实例化链各个
File
对象
package com.sean.iotest;
import java.io.File;
public class Test {
public static void main(String[] args) {
//传入目录的绝对路径
File dir = new File("E:\\test");
//传入文件的绝对路径
File file = new File("E:\\test\\a.jpg");
// 打印两个File对象
System.out.println(dir);
System.out.println(file);
}
}
我们可以直接打印File对象,File类重写了toString()方法,查看 Java 源码,toString()方法直接返回了getPath()实例方法,此方法返回构造方法传入的路径字符串:
/**
* Returns the pathname string of this abstract pathname. This is just the
* string returned by the <code>{@link #getPath}</code> method.
*
* @return The string form of this abstract pathname
*/
public String toString() {
return getPath();
}
/**
* Converts this abstract pathname into a pathname string. The resulting
* string uses the {@link #separator default name-separator character} to
* separate the names in the name sequence.
*
* @return The string form of this abstract pathname
*/
@NotNull
public String getPath() {
return path;
}
运行结果:
E:\test
E:\test\a.jpg
上面代码中,使用\表示Windows下的路径分隔符\,Linux和MacOS下使用正斜杠/作为路径分隔符。假设是同样的目录结构,在MacOS和Linux下是这样表示的:
因为
Windows
平台和其他平台路径分隔符不同,使用不同平台的开发者就难以保证路径分隔符的统一。
为了保证代码更好的兼容性,
File
类下提供了一个静态变量
separator
,用于表示当前平台的系统分隔符:
package com.sean.iotest;
import java.io.File;
public class Test {
public static void main(String[] args) {
String separator = File.separator;
//传入目录的绝对路径
File dir = new File("E:"+separator+"test");
//传入文件的绝对路径
File file = new File("E:\\test\\a.jpg");
// 打印两个File对象
System.out.println(dir);
System.out.println(file);
}
}
运行结果:
E:\test
E:\test\a.jpg
使用
File.separator;
获取的结果与
//
的效果是一样的,在Linux与
\
的结果是一样
4.2 绝对路径和相对路径
在实例化
File
对象时,可以传入相对路径也可以传入绝对路径,我们再看看什么是相对路径,什么是绝对路径。
绝对路径是以根目录开头的完整的全路径,相对路径指的是当前文件所在的路径引起的跟其它文件(或文件夹)的路径关系。
比如:全路径就是
E://test
和
E://test//a.jpg
相对于
E://test
E://test//a.jpg
的相对路径就是
a.jpg
注意:
我们在实例化 File 对象时,不会产生对磁盘的操作,因此即使传入的文件或目录不存在,代码也不会抛出异常。只有当调用 File 对象下的一些方法时,才会对磁盘进行操作。
File对象下有3个表示路径的实例方法:
-
将抽象的路径名转化为字符串;String getPath()
-
返回此抽象路径名的绝对路径名字符串;String getAbsolute()
-
返回此抽象路径名的规范路径名字符串。String getCanonicalPath()
我们可以调用这3个方法并打印结果
package com.sean.iotest;
import java.io.File;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
String separator = File.separator;
//传入目录的绝对路径
File dir = new File("E:"+separator+"test");
//传入文件的绝对路径
File file = new File(".\\1111.txt");
// 打印两个File对象
System.out.println(dir.getPath());
System.out.println(dir.getAbsolutePath());
System.out.println(dir.getCanonicalPath());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
}
}
运行结果:
E:\test
E:\test
E:\test
.\1111.txt
E:\myproject\springcloud\.\1111.txt
E:\myproject\springcloud\1111.txt
通过运行结果可以看出,规范路径名就是把.和…转换为标准的绝对路径。
4.3 判断对象是文件还是目录
我们可以通过如下两个方法判断File对象时文件还是目录:
-
测试此抽象路径名表示的文件是否为普通的文件;boolean isFile()
-
测试此抽象的路径名的文件是否为目录;boolean isDirectory()
package com.sean.iotest;
import java.io.File;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
String separator = File.separator;
//传入目录的绝对路径
File dir = new File("E:"+separator+"test");
//传入文件的绝对路径
File file = new File(".\\1111.txt");
// 打印两个File对象
System.out.println(dir.isDirectory());
System.out.println(dir.isFile());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
}
}
运行结果:
true
false
false
true
4.4创建和删除目录
4.4.1创建目录
对于一个不存在的目录,我们可以使用
boolean mkdir()
方法创建一个目录。例如,我们想要在
test
目录下创建一个
code
目录,就可以使用该方法编写一段创建目录的代码。
package com.sean.iotest;
import java.io.File;
public class Test {
public static void main(String[] args) {
String separator = File.separator;
//传入目录的绝对路径
File dir = new File("E:"+separator+"test"+separator+"code");
if(!dir.exists()){
boolean mkdirs = dir.mkdirs();
if(mkdirs){
System.out.println("创建目录成功");
}
}
}
}
代码中我们调用了File对象的boolean exists()方法,此方法用于测试由此抽象路径名表示的文件或目录是否存在。当不存在时,我们才去创建目录。
4.4.2 删除目录
如果我们想要删除刚刚创建的
code
目录,可以调用
boolean delete()
方法
package com.sean.iotest;
import java.io.File;
public class Test {
public static void main(String[] args) {
String separator = File.separator;
//传入目录的绝对路径
File dir = new File("E:"+separator+"test"+separator+"code");
if(dir.exists()){
boolean mkdirs = dir.delete();
if(mkdirs){
System.out.println("删除成功");
}
}
}
}
4.5 创建删除文件
对于文件类型的
File
对象,可以通过
boolean createNewFile()
方法创建一个新文件,使用
boolean delete()
方法删除文件。类似目录
5. InputStream 抽象类
5.1 概述
java.io.InputStream
抽象类 是Java提供的最基本的输入流,它是所有输入流的父类。其最常用的抽象方法是
int read()
/**
* Reads the next byte of data from the input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned. This method
* blocks until input data is available, the end of the stream is detected,
* or an exception is thrown.
*
* <p> A subclass must provide an implementation of this method.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
*/
@Range(from = -1,to = 255)
public abstract int read() throws IOException;
这个方法用于读取输入流的下一个字节,返回的int如果为-1,则表示已经读取到文件末尾。
InputStream
与其子类的 UML 图如下所示:
5.2 FileInputStream 实现类
我们将以最常用的
FileInputStream
实现类来介绍一下。
FileInputStream
就是从文件流中读取数据
package com.sean.iotest;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class Test {
public static void main(String[] args) throws Exception {
String separator = File.separator;
//传入目录的绝对路径
File dir = new File("E:"+separator+"test"+separator+"1.txt");
InputStream inputStream = new FileInputStream(dir);
int i;
while ((i=inputStream.read()) != -1) {
System.out.print((char) i);
}
//释放资源
inputStream.close();
}
}
6.OutputStream抽象类
6.1概述
OutputStream
抽象类是与
InputStream
对应的最基本的输出流,它是所有输出流的父类。其中常用的抽象方法
void write(int b)
/**
* Writes the specified byte to this output stream. The general
* contract for <code>write</code> is that one byte is written
* to the output stream. The byte to be written is the eight
* low-order bits of the argument <code>b</code>. The 24
* high-order bits of <code>b</code> are ignored.
* <p>
* Subclasses of <code>OutputStream</code> must provide an
* implementation for this method.
*
* @param b the <code>byte</code>.
* @exception IOException if an I/O error occurs. In particular,
* an <code>IOException</code> may be thrown if the
* output stream has been closed.
*/
public abstract void write(int b) throws IOException;
这个方法用于写入一个字节到输出流。
OutputStream
与其子类的 UML 图如下所示:
6.2 FileOutputStream 实现类
我们同样以最常用的
FileOutputStream
实现类来进行举例。
FileOutputStream
就是从文件流读取数据。
package com.sean.iotest;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
String separator = File.separator;
//传入目录的绝对路径
File dir = new File("E:"+separator+"test"+separator+"1.txt");
File dir1 = new File("E:"+separator+"test"+separator+"2.txt");
InputStream inputStream = new FileInputStream(dir);
OutputStream outputStream = new FileOutputStream(dir1);
int i;
while ((i=inputStream.read()) != -1) {
System.out.print((char) i);
outputStream.write(i);
}
outputStream.close();
inputStream.close();
}
}
运行代码后,2.txt后面成功写入了1.txt中的内容,等同于文件的复制。
7.小结
通过本小结的学习,我们知道了什么是输入输出流的概念,输入输出流经过常用于上传文件到服务器的场景。想要通过Java操作文件和目录,要学会使用
java.io.File
类,
InputStream
和
OutputStream
分别是所有输入流和所有输出流的父类,
FileInputStream
实现了文件流的输入,
FileOutputStream
实现了文件流输出。