java中的IO-01
基本介绍
所谓的IO就是Input and Output
的意思。在C++中我们学到的IO主要就是两种, Istream and Ostream
,cin和cout就是这两个流的对象。C++是这样子的,那么Java又如何呢?我们学习一门语言总是要从hello world
开始学起,也就是向控制台中输出hello world这个字符串。在C++中我们使用的是std::cout<<“Hello World”<<std::endl;
,在Java中我们使用的是System.out.println("Hello world");
。既然是输出,那么肯定是和IO流相关的。cout
是std::ostream
的一个对象,功能是向控制台输出。那么Java中的这个函数又如何理解呢?System
是Java中的一个类。
public final class System extends Object
官方文档中这样说道
System
类包含几个有用的类字段和方法。它不能被实例化。System
类提供的System
包括标准输入,标准输出和错误输出流; 访问外部定义的属性和环境变量; 一种加载文件和库的方法; 以及用于快速复制阵列的一部分的实用方法。
我们继续查阅官方文档,发现System类中有几个静态的成员!
static printStream err; // 标准错误输出流 static printStream in; // 标准输入流 static printStream out; // 标准输出流
这样一来Java中的这个输出函数就很好理解了。 System.out
是一个对象,相当于C++中的cout
。println
是System.out
的一个static method
用于向控制台输出信息,并在最后输出换行。如果不需要换行的话我们可以使用print
方法。
OutputStream与InputStream
不过这个System.out
为什么能输出字符串呢?那肯定是因为他是一个类似于std::ostream
的玩意,他的类型是printStream
。我们观察一下它的继承结构。
java.lang.Object
java.io.OutputStream
java.io.FilterOutputStream
java.io.PrintStream
最顶层的Object这个是毫无疑问的,Java当中的所有的类都是继承于java.lang.Object
的。所以说最重要的是第二个 java.io.OutputStream
, 正所谓,遇事不决先参看官方文档。
public abstract class OutputStream extends Object implements Closeable, Flushable
- 这个抽象类是表示字节输出流的所有类的超类。输出流接收输出字节并将其发送到某个接收器。
- 需要定义
OutputStream
子类的应用OutputStream
必须至少提供一个写入一个字节输出的方法。
void close(); // 关闭此输出流并释放与此流相关联的任何系统资源。 void flush(); // 刷新此输出流并强制任何缓冲的输出字节被写出。 void write(byte[] b); // 将 b.length字节从指定的字节数组写入此输出流。 void wirte(byte[] b, int off, int len); // 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。 abstract void write(int b); // 将指定的字节写入此输出流。
这个类是一个抽象的类是不能够被实例化的,也就是说他是所有的输出流的一个模子。我们要针对特殊的情形使用这个类的子类。
InputStream
和这个也是同样的道理。
FileOutputStream 与 FIleInputStream
前面说了那么多的废话,现在假如我们要输出一些东西到文件中储存能不能做到呢?答案是明显的,我们需要OutputStream
的一个合适的子类,这里我们找到了FileOutputStream
。我们继续查阅官方文档。
public class FileOutputStream extends OutputStream
- 文件输出流是用于将数据写入到输出流
File
或一个FileDescriptor
。文件是否可用或可能被创建取决于底层平台。特别是某些平台允许一次只能打开一个文件来写入一个FileOutputStream
(或其他文件写入对象)。在这种情况下,如果所涉及的文件已经打开,则此类中的构造函数将失败。FileOutputStream
用于写入诸如图像数据的原始字节流。 对于写入字符流,请考虑使用FileWriter
。
从文档中我们可以读出,这个类主要是将原始字节流写入到文件当中去的。不过既然这个类可以被实例化,我们就要看看这个类的构造函数是怎么样的。
FileOutputStream(File file) // 创建文件输出流以写入由指定的 File对象表示的文件。 FileOutputStream(File file, boolean append) // 创建文件输出流以写入由指定的 File对象表示的文件。 FileOutputStream(FileDescriptor fdObj) // 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。 FileOutputStream(String name) // 创建文件输出流以指定的名称写入文件。 FileOutputStream(String name, boolean append) // 创建文件输出流以指定的名称写入文件。
这里我们看到了一个File类,可以肯定,这个类代表的就是我们要写入的文件,不过下面的两个构造函数使用的却是String name。这两个是不一样的。
既然是写入,我们还要知道这个类的写入方法是如何的。
void write(byte[] b) // 将 b.length个字节从指定的字节数组写入此文件输出流。 void write(byte[] b, int off, int len) // 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。 void write(int b) // 将指定的字节写入此文件输出流。
代码测试
public class FileOutputStreamDemo { public static void main(String[] args) { try { FileOutputStream fileOutputStream = new FileOutputStream("sher"); String content = "sher is a pretty boy!"; fileOutputStream.write(content.getBytes()); fileOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } } }
这里我们没有使用FIle类来代表文件,使用的是一个String–“sher”,然后使用write方法将字节写入到文件中。最后使用close方法关闭这个流。这里问题来了,我们只是提供了文件的名字,那么文件放在哪儿呢??莫非是和这个类的文件同级?答案让我很懵逼,这个文件出现在我的项目文件夹的同级。其实这个位置是很沙雕的,因为我有多个项目,谁都不知道这个文件到底是属于哪一个项目的,如果是和src文件夹同级我倒是可以理解。那么我们需要修改文件的位置该如何呢?我们可以修改给定的这个文件的绝对路径,比如说我们是使用如下的代码
FileOutputStream fileOutputStream = new FileOutputStream("D:/sher");
那么这个文件就会创建在D盘下。其实我们没有给他一个绝对路径的时候,他使用的就是相对路径,这里的相对路径不是我们想象中的那个相对与代码文件的,而是相对于整个项目的。比如我们也可以这样写。
FileOutputStream fileOutputStream = new FileOutputStream("./Exercise/src/sher");
点的意思的本目录,点点指的是上一级目录,这里在我看来点是多余的,但是去除点之后就会报错。可能是没有点他就认为这个是一个绝对路径吧。通过上面的方法我们就将文件创建到了src目录下。不过之前我们提到了一个File类,那个类又如何使用呢?
我们可以尝试着使用File类。不过在使用File类之前我们还需要去了解一下File类。
public class File extends Object implements Serializable, Comparable<File>
文件和目录路径名的抽象表示,这是官方文档对File类的说明。
File类主要有如下的构战函数
File(File parent, String child) // 从父抽象路径名和子路径名字符串创建新的 File实例。 File(String pathname) // 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 File(String parent, String child) // 从父路径名字符串和子路径名字符串创建新的 File实例。
我们修改了一下代码
public class FileOutputStreamDemo { public static void main(String[] args) { try { File file = new File("./Exercise/src/sher"); System.out.println(file.getPath()); FileOutputStream fileOutputStream = new FileOutputStream(file); String content = "sher is a pretty boy!"; fileOutputStream.write(content.getBytes()); fileOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } } }
这个修改倒是不痛不痒的,只是修改了一点点而已。其实File类还有其他非常多的方法,这里我们学习的是IO,只要简单的会使用就好啦。
注意点:
你可能注意到了FileOutputStream构造函数还有第二个参数(boolean append),默认是false(其实这句话是错误的,Java中的函数没有默认值,只是如果我们没有第二个参数的话,调用的单个参数函数就是相当于第二个参数是false),如果我们将其设置为true的话,写入的文件的时候就不会将原有文件清空,而且接着后面继续写入。(如果文件不存在就会创建该文件)
write(byte[] b, int off, int len)的使用。
fileOutputStream.write(content.getBytes(), 3, 4);
写入文件的就是“r is”从第三个字符开始,写入四个字符。注意一个中文是两个字符。write(int b)的使用
这个就更简单了,这个就是写入一个字节。比如
wirte(97)
就是写入一个字符a
.flush()方法的使用
有时候写入的时候可能并不会直接写入,我们需要使用flush()方法刷新缓冲区之后才能写入。所有说可以在写入操作完成之后加上这个flush()方法。
上面还只是文件的简单的写入,下面还要学习一下简单的文件的读取操作。
public class FileInputStreamDemo { public static void main(String[] args) { try { FileInputStream fileInputStream = new FileInputStream("./Exercise/src/sher"); byte[] b = new byte[1024]; int res = fileInputStream.read(b); System.out.println(res); System.out.println(new String(b)); } catch (Exception e) { e.printStackTrace(); } } }
read函数有一个返回值,这个值的意思的读取的字符数。
其实这个FileInputStream和FileOutputStream是非常类似的。只有个别的方法存在差别。
比如说FileInputStream对象没有flush方法,不过他有一个skip方法。
long skip(long n) // 跳过并从输入流中丢弃 n字节的数据。
如果我们使用skip(1)的话,第一个字节将会被我们跳过,他的返回值是实际跳过的字节数。
总结
到这里我们就基本学完了Java io的几个最基本的类,这些类也是以后学习更加复杂的IO类的基石。