2、输入输出流
1、读写字节
1.1、read()方法
InputStream是一个抽象类,有一个抽象方法
/** * Reads the next byte of data from the input stream. The value byte is * returned as an {@code int} in the range {@code 0} to * {@code 255}. If no byte is available because the end of the stream * has been reached, the value {@code -1} 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} if the end of the * stream is reached. * @throws IOException if an I/O error occurs. */ public abstract int read() throws IOException;
学习一个东西最好的方法就是读好源码和注释
Java文件操作会用到大量本地方法,都是C和操作系统交互的,我们能够用到的API,都是基于C的一个高级抽象。
read()方法读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1.
具体的输入流类,必须覆盖这个方法以提供适用的功能。例如:
- FileInputStream类中,这个方法将从某个文件中读入一个字符(具体方法在JVM中,用C实现)
- System.in(他是InputStream子类的一个预定义对象)却是从标注输入中读取信息,即控制台或者重定向文件。(考试常用,例如力扣,牛客网等)
public static void main(String[] args) { Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { System.out.println(scanner.next()); } }
Scanner 如果有空要读透源码,他是考试常用工具,毕竟很多机考都是从控制台做输入输出。
清楚文件系统本质玩转InputStream和OutputStream,以后遇到的其他实现类,或者第三方的流自定义对象都万变不离其宗了。
InputStream还有一些非抽象的方法,可以读入一个字节数组,或者跳过大量的字节。而这些方法的本质却是调用的read()方法
(length | fromIndex | size) < 0 很有意思,实际项目中可以使用
Objects.checkFromIndexSize(off, len, b.length); if ((length | fromIndex | size) < 0 || size > length - fromIndex)
public static void main(String[] args) { PrintStream printStream = new PrintStream(System.out); printStream.println(123); }
1.2、write()方法
面向流编程,Java的很多对象定义都是成对的。
与InputStream的read()方法类似,OutputStream有对应的write()方法
/** * Writes the specified byte to this output stream. The general * contract for {@code write} 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}. The 24 * high-order bits of {@code b} are ignored. * <p> * Subclasses of {@code OutputStream} must provide an * implementation for this method. * * @param b the {@code byte}. * @throws IOException if an I/O error occurs. In particular, * an {@code IOException} may be thrown if the * output stream has been closed. */ public abstract void write(int b) throws IOException;
write()方法向某个输出位置写入一个字节。
流的读写是阻塞模式的
1.3、API
InputStream
方法 | 参数 | 说明 |
---|---|---|
public abstract int read() throws IOException; | 无 | 从流中读取一个数据并返回该字节,遇到结尾返回-1 |
public int read(byte[] b) throws IOException | byte[] b | 读入一个字节数组,并返回实际读入的字节数,结尾返回-1,最大返回b.length |
public int read(byte[] b, int off, int len) throws IOException | b-数据读入的数组 off 第一个读入的字节应该被放置的位置,在b中的偏移量 len 读入字节的最大数量 |
读入一个字节数组,并返回实际读入的字节数,结尾返回-1 |
public long skip(long n) throws IOException | n 跳过数量 | 输入流中跳过n个字节 |
public int available() throws IOException | 返回在不阻塞的情况下可获取的字节数 | |
public void close() throws IOException {} | 关闭这个输入流 | |
public synchronized void mark(int readlimit) {} | readlimit | 标记 |
public synchronized void reset() throws IOException | ||
public boolean markSupported() |
OutputStream
方法 | 参数 | 说明 |
---|---|---|
public abstract void write(int b) throws IOException; | b | 写出一个字节的数据 |
public void write(byte b[]) throws IOException | b | |
public void write(byte b[], int off, int len) throws IOException | ||
public void close() throws IOException | ||
public void flush() throws IOException |
1.4、小结
public static void main(String[] args) { try (OutputStream out = new FileOutputStream(new File("D:\\workspace\\test\\test.txt"))) { for (byte b : "中国".getBytes(StandardCharsets.UTF_8)) { out.write(b); } out.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try (InputStream in = new FileInputStream(new File("D:\\workspace\\test\\test.txt"))) { while (true) { byte[] strByte = new byte[6]; int t = in.read(strByte); if (t == -1) { break; } System.out.println(new String(strByte)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
- 字节流操作的是字节,以byte为单位,自身没有任何编码格式,所以写入的内容是乱码
- 读写都是以byte为单位,可以是byte[],这才是常用方式
- 语法糖自动关闭流是项目实用的真确姿势,原理是实现了Closeable
2、完整的流家族
2.1、InputStream 、OutputStream 字节处理器两个抽象方法结构
上面了解了最基本的流操作的几个方法,下面了解下Java为我们的IO到底设计了多少工具。
处理二进制,无编码格式的输入输出流家族图谱如下:
2.2、Reader和Writer两个文本处理器抽象类家族
对于Unicode文本,可以使用抽象类Reader和Writer的子类。
abstract int read()
abstract void write(int c)
read方法返回一个Unicode元码(0~65535之间的整数),碰到文件结尾返回 -1
write方法在被调用时,需要一个Unicode元码参数。
2.3、四个功能定义接口
IT领域处理的数据核心是字符,字符串起来起来就是字符串,这个玩意才是人能读懂的东西。
而不同的国家地区有不同的文字,为了接入计算机这个虚拟世界,就要设置各种五花八门的编码,编码技术是一个核心技术,需要去吃透。
除了上述图谱,Java附加了四个接口:
- Colseable 扩展自java.lang.AutoCloseable,对于实现了colseable接口的对象可以try-with-resource自动关闭,至于到底怎么关闭,这是一个学问。
- Flushed
- Readable
- Appendable
Colseable 接口 就一个抽象方法
这个方法到底怎么实现,就没那么简单了,目前只能理解为,底层为我们做了关闭,肯定要和操作系统内核打交道。
public void close() throws IOException;
Flushed接口也就只有一个抽象方法
void flush() throws IOException;
Reader接口也就只有一个抽象方法
public int read(java.nio.CharBuffer cb) throws IOException;
CharBuffer类拥有按顺序和随机地进行读写访问的方法,它表示一个内存中的缓冲区或者一个内存映像的文件,根本原因在于数据结构用的char[]和MemorySegmentProxy
Appendable接口有两个用于添加单个字符和字符序列的方法
Appendable append(char c) throws IOException; Appendable append(CharSequence csq) throws IOException; Appendable append(CharSequence csq, int start, int end) throws IOException;
CharSequence接口描述了一个char值序列的基本属性,string,CharBuffer,StringBuffer和StringBuilder都实现了它。
public static void main(String[] args) { CharSequence cs = "中国abc"; cs.codePoints().forEach(value -> { System.out.println(value); }); System.out.println(cs.charAt(1)); }
2.4、输入输出的组合过滤器
FileInputStream和FileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流。
文件流的构造器核心参数就是文件路径,也可以使用相对路径,例如jar获取手工读取某个配置文件
路径核心秘密在于相对路径和绝对路径,相对路径就要知道程序的工作目录
System.out.println(System.getProperty("user.dir"));
public static void main(String[] args) { try (OutputStream out = new FileOutputStream("D:\\workspace\\test\\test.txt")) { for (byte b : "中国".getBytes(StandardCharsets.UTF_8)) { out.write(b); } out.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try (InputStream in = new FileInputStream("D:\\workspace\\test\\test.txt")) { while (true) { byte[] strByte = new byte[6]; int t = in.read(strByte); if (t == -1) { break; } System.out.println(new String(strByte)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
FileInputStream和FileOutputStream从家族看就是只能操作字节和字节数组。
byte t = (byte)in.read(strByte);
DataInputstream只能读入数据类型
public static void main(String[] args) throws IOException { InputStream in = new FileInputStream("D:\\workspace\\test\\test.txt"); DataInputStream din = new DataInputStream(new BufferedInputStream(in)); System.out.println(din.readLong()); }
FileInputStream核心API
方法 | 参数 | 说明 |
---|---|---|
public FileInputStream(String name) throws FileNotFoundException | name文件名 | 构造方法 |
public FileInputStream(File file) throws FileNotFoundException | file用文件对象 | 构造方法 |
FileOutputStream核心API
方法 | 参数 | 说明 |
---|---|---|
public FileOutputStream(String name) throws FileNotFoundException | name文件名 | 构造方法 |
public FileOutputStream(String name, boolean append) throws FileNotFoundException | append继续写 | 构造方法 |
public FileOutputStream(File file) throws FileNotFoundException | 构造方法 | |
public FileOutputStream(File file, boolean append) throws FileNotFoundException | file用文件对象,append继续写 | 构造方法 |
FileInputStream核心API
方法 | 参数 | 说明 |
---|---|---|
public BufferedInputStream(InputStream in) | in 输入流 | 创建一个带缓冲区的输入流,带缓冲区的输入流从流中读入字符时不需要每次都对设备访问。当缓冲区空时,回向缓冲区读入一个新的数据库。 |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析