IO流:数据相对内存的流入(In)流出(Out)
字符流:以字符为最小数据单元、java中字符是Unicode编码、一个字符占两个字节。只能处理纯文本数据。
字节流:以字节为最小数据单元。
节点流:
- File(文件读写)
- Memory Array(CharArray、ByteArray)内存数组
- Memory String(内存字符串)
- Pipe(进程间通信)
每种节点流针对字符类型的和字节类型的流的流入流出都有2个类:xxInputStream、xxOutputStream、xxReader、xxWriter。
但String只有StringReader和StringWriter,因为String本身就是字符数组,因此只有字符流、没有字节流。
处理流:
- Buffered - 缓冲流:在读入或写出时,对数据进行缓存,以减少I/O的次数
- Filter - 过滤流:在数据进行读或写时进行过滤
- Object - 对象流(复杂对象类型读写)、Data - (java基本数据类型读写:int、boolean等)
以上两种因为本身有可能是字节类型,所以只能有字节流类型的处理类 - LineNumber计数流(读入时对行计数)、PushBack预读流(通过缓存机制、进行预读)
以上两种,因为本身就是处理读入,所有只有In流的处理类 - Print打印流(包含方便打印的方法),打印是Out流,所以只有Out流的处理类
- 按照一定的编码\解码标准,将字节流转为字符流。
InputStream转Reader、OutputStream转Writer
判断使用何种流
- 读入到内存(In、Reader),从内存写出到其它目标(Out、Writer)
- 纯文本(Reader、Writer),非纯文本(InputStream、OutputStream)
- 源、目标设备(内存Array、硬盘File、键盘System.in&System.out)
- 明确是否需要其它功能:(1)提高性能,用Buffered(2)字节字符转换
例:将键盘输入的内容保存在一个文件中,输入"over"结束。
分析:
- 从键盘输入到内存System.in;
- 键盘输入内容为纯文本,但是System.in为字节流,需要转成字符流
- 从内存输出到文件,用FileWriter
- 提高性能用Buffered
java.util.Scanner
Scanner是一个工具类,仅能用来处理纯文本数据(字符为单位的数据),并且仅能处理java基本类型和String类型,不能处理其它引用类型的数据。
- 声明:implements iterator<String>, closeable
- 构造函数:Scanner(InputStream in) \ Scanner(File file) \ Scanner(Path path) \ Scanner(Readable readable)
- 实现了Iterator接口,实现了 hasNext() \ next() \ remove() \ forEachRemaining(Consumer<?> action) 方法
- 实现了 Closeable接口,实现了 close()方法
- 自定义实现了:hasNextLine() \ nextLine()
- 自定义实现了:hasNextInt() \ nextInt()
- 自定义实现了:hasNextLong() \ nextLong() short \ byte \ int \ long \ double \ float \ boolean \ BigInteger \ BigDecimal
java.nio.file.Path && Files
- Path.get(“a”, “b”, “c”…);构建一个相对路径 a/b/c…; ——工厂方法
- Path.get(“c:”,“b”,“a”…); 构建一个绝对路径 c:/b/a…
- Path.resolve(path1,“a.txt”);构建一个路径,path1/a.txt
- Files.readAllBytes(Path p);返回一个byte []数组,是文件p的所有字节
- Files.readAllLines(Path p);返回List<String>
- Files.write(Path p, byte[] contents, OpenOption…options); 将contents写入文件p
- Files.write(Path p, Iterable<? extends CharSequence> contents, OpenOptions options)
- Files.newInputStream(Path p, OpenOption…options); \ newOutputStream(
- Files.newBufferedReader(Path p,Charset charset) \ newBufferedWriter(
- Files.createDirectory(path) ——父目录必须已经存在
- Files.createDirectories(path)——父目录不存在,会自动创建所有父目录
- Files.createFile(path)——创建文件
- Files.createTmpFile(path) \ createTmpDirectory(
- Files.copy() \ move() \ delete()
- Files.exists() \ size() \ isReadable() \ isWriteable() \ isExecuteable() \ isDirectory()
BufferedInputStream && ByteArrayInputStream && ByteBuffer
- ByteArrayInputStream:是节点流。用于将数据从一个字节数组中读入到内存。
- BufferedInputStream:是处理流。缓冲流,用于为输入流提供缓冲区,装饰输入流,提高处理性能
- ByteBuffer:是NIO中的概念。代表读入的数据要被存储到的内存区域。
java.nio
Buffer
每个buffer都具有:
- capacity:容量,final的,不可变的。
- position:读写位置,下一个值在此位置读出或者写入
- limit:界限,超过此界限的读写是没有意义的
- mark:标记,用于重复一个读入或者写出操作
以下顺序必须成立:0<= mark <= position <= limit <= capacity
使用buffer的目的是执行“写入,然后读出”的循环。
- 初始position是0,limit==capacity
- 随着数据的不断写入,position不断增大,
- 当所有数据全都已经被写入到buffer、或者buffer已满后,设置limit=position,position=0,为后续应用从这个buffer读数据做准备
- 其后,当应用需要读取buffer中的数据时,初始position为0,limit为之前写操作完毕后设置的值
- 随着读操作的进行,position不断增大,直到读到limit位置,停止。这时就能理解第3部为什么要设置limit=position了,因为写操作就只写到了limit位置,对其后的位置的读取都是没有意义。
- 当读到limit位置后,读操作完成。这时需要设置position=0,limit=capacity,为其后对buffer的写操作做准备。
其中第3步的:设置limit=positoin, position=0,只需调用buffer.flip()方法即可实现。
其中第6步的:设置position=0,limit=capacity,只需调用buffer.clear()方法即可实现。
如果想重读缓冲区:buffer.rewind()——position=0; 或者 buffer.mark() + buffer.reset() 方法实现
创建缓冲区:ByteBuffer.allocate(int capacity) \ ByteBuffer.allocateDirect(int capacity)
capacity() \ clear() \ flip() \ position() \ limit() \ remaining() = limit()-position(); \ mark() \ reset() \ rewind()
limit(int l) - 设置buffer的limit为给定的参数值 \ position(int p) - 设置buffer的position为给定的参数值
hasRemaining() - 是否还有可以读写的数据 \ hasArray() \ isDirect() \ isReadOnly()
array() - 返回存储buffer的数组
ByteBuffer
- allocate(int capacity) \ allocateDirect(int capacity) \ wrap(bye[] source) \ wrap(byte[] source, int offset , int length ):构建ByteBuffer,指定了容量-capacity,或者源内容(source)
- get(): 读取positin位置的字节内容,并将position++ \ get(byte [] dest, int offset, int length):从buffer中读取,写入到dest中
- put(byte b):将b写入到posiiton位置 \ put(byte [] source, int offset , int length):将source中的数据写入buffer
- getXXX() \ getXXX(int index ):从buffer中读取一个java基本类型中的数值型数据(short char int long float double )
- putXXX() \ putXXX(int index):向buffer中写入一个java基本类型中的数值型的数据(short char int long float double )
- asXXXBuffer() :将ByteBuffer转换成其它类型的Buffer,如CharBuffer / ShortBuffer / IntBuffer / LongBuffer / FloatBuffer / DoubleBuffer
channel
一个FileChannel实例就代表了一个File或者File的一个区域。
一个ServerSocketChannel实例就代表了一个ServerSocket。
当我们操作一个Channel时,完全可以想象成我们就是在操作一个File或者Socket。
Selector && SelectableChannel
例如,NIO实现的服务器:
pulbic void startServer(){
}