IO
I/O 是什么?
- 程序内部和外部进行数据交互的过程,就叫输入输出。
- 程序内部是谁?内存
- 程序外部是谁?
- 一般来说是两类:本地文件和网络。
- 也有别的情况,比如你和别的程序做交互,和你交互的程序也属于 外部,但一般来说,就是文件和网络这么两种。
- 从文件里或者从网络上读数据到内存里,就叫输入;从内存里写到文件 里或者发送到网络上,就叫输出
- Java I/O 作用只有一个:和外界做数据交互
用法
- 使用流,例如 FileInputStream / FileOutputStream
- 可以用 Reader 和 Writer 来对字符进行读写
- 流的外面还可以套别的流,层层嵌套都可以
- BufferedXXXX 可以给流加上缓冲。对于输入流,是每次多读一些放在内存 里面,下次再去数据就不用再和外部做交互(即不必做 IO 操作);对于输 出流,是把数据先在内存里面攒一下,攒够一波了再往外部去写。 通过缓存的方式减少和和外部的交互,从而可以提高效率
- 文件的关闭:close()
- 需要用到的写过的数据,flush() 一下可以保证数据真正写到外部去(读数据 没有这样的担忧)
- 这个就是 Java 的 I/O,它的原理就是内存和外界的交互
- Java I/O 涉及的类非常多,但你用到哪个再去关注它就行了,不要背类 的继承关系图
FileOutputStream写操作
//写
private static void writeIO() {
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream("text.txt");
outputStream.write('a');
outputStream.write('b');
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
FileInputStream读操作
//字符读取
private static void readIO() {
InputStream inputStream = null;
try{
inputStream = new FileInputStream("text.txt");
char content = (char) inputStream.read();
char content2 = (char) inputStream.read();
System.out.println(content);//a
System.out.println(content2);//b
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
InputStreamReader字符串读取操作
//字符串读取
private static void stringIO() {
//这样写流将自动关闭
try( InputStream inputStream = new FileInputStream("text.txt");
Reader reader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(reader)){
// FileReader reader = new FileReader("text.txt");
System.out.println(bufferedReader.readLine());//ab
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
BufferedOutputStream 读操作
private static void bosIO() {
try( OutputStream os = new FileOutputStream("text.txt");
BufferedOutputStream bos = new BufferedOutputStream(os)){
bos.write('c');
bos.write('d');
//如果bos不在try的括号内,需要手动冲一下
// bos.flush();//或 bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
文件拷贝,InputStream读OutputStream写
//文件拷贝
public static void fileCopy(){
try (InputStream is = new FileInputStream("text.txt");
OutputStream os = new FileOutputStream("text_copy.txt")){
//笨方法 一个一个地写入
// os.write(is.read());
byte[] data = new byte[1024];
int readCount ;
while ((readCount = is.read(data))!=-1){
os.write(data,0,readCount);
}
//性能优化,BufferedXXX给流加上缓冲,减少交互频率
// InputStream is2 = new BufferedInputStream(new FileInputStream("text.txt"));
// OutputStream os2 = new BufferedOutputStream(new FileOutputStream("text_copy.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
客户端请求SocketIO
//socket请求方式
public static void socketIO(){
try {
Socket socket = new Socket("hencoder.com",80);
OutputStream os = socket.getOutputStream();//发送请求报文
InputStream is = socket.getInputStream();//接收响应报文
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
writer.write("GET / HTTP/1.1\n"+
"Host: www.example.com\n\n");
writer.flush();
String message;
while ((message = reader.readLine())!=null){
System.out.println(message);//整个http的响应报文
}
System.out.println(reader.readLine());//HTTP/1.1 200 OK
} catch (IOException e) {
e.printStackTrace();
}
}
服务端serverSocketIO
//serverSocket
public static void serverSocketIO(){
try (ServerSocket serverSocket = new ServerSocket(100);
Socket socket = serverSocket.accept();
OutputStream os = socket.getOutputStream();//发送请求报文
InputStream is = socket.getInputStream();//接收响应报文
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os));
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
){
writer.write("HTTP/1.1 200 OK\n" +
"Date: Mon, 23 May 2005 22:38:34 GMT\n" +
"Content-Type: text/html; charset=UTF-8\n" +
"Content-Length: 138\n" +
"Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT\n" +
"Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)\n" +
"ETag: \"3f80f-1b6-3e1cb03b\"\n" +
"Accept-Ranges: bytes\n" +
"Connection: close\n" +
"\n" +
"<html>\n" +
" <head>\n" +
" <title>An Example Page</title>\n" +
" </head>\n" +
" <body>\n" +
" <p>Hello World, this is a very simple HTML document.</p>\n" +
" </body>\n" +
"</html>\n\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
NIO
NIO 和 IO 的区别有几点:
- 传统IO用的是插管道的方式,用的是Stream;
- NIO用的也是插管道的方式,用的是 Channel。
- NIO 的 Channel 是双向的
- NIO也用到buffer
- 它的 Buffer 可以被操作
- 它强制使用 Buffer
- 它的 buffer 不好用
- NIO有非阻塞式的支持
- 只是支持非阻塞式,而不是全是非阻塞式。默认是阻塞式的
- 而且就算是非阻塞式,也只是网络交互支持,文件交互是不支持的
使用
- NIO 的 Buffer 模型:
- 用 NIO 来读文件的写法:
- 使用 fine.getChannel() 获取到 Channel
- 然后创建一个 Buffer
- 再用 channel.read(buffer),把文件内容读进去
- 读完以后,用 flip() 翻⻚
- 开始使用 Buffer
- 使用完之后记得 clear() 一下
public class NIOMain {
public static void main(String[] args) {
// nio1();//NIO
nio2();//非阻塞式NIO
}
//阻塞式
private static void nio1() {
try {
RandomAccessFile file = new RandomAccessFile("text.txt","r");
FileChannel channel = file.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
channel.read(byteBuffer);
//limit 和 position 可以用flip一句代码解决
// byteBuffer.limit(byteBuffer.position());
// byteBuffer.position(0);
byteBuffer.flip();
System.out.println(Charset.defaultCharset().decode(byteBuffer));
byteBuffer.clear();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void nio2() {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(99));
//非阻塞 start -> 以下代码为非阻塞配置代码
serverSocketChannel.configureBlocking(false);//非阻塞
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞
for (SelectionKey key:selector.selectedKeys()) {
SocketChannel socketChannel = serverSocketChannel.accept();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while (socketChannel.read(byteBuffer)!=-1){
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
OKIO
特点:
- 它也是基于插管的,而且是单向的,输入源叫 Source,输出目标叫 Sink
- 支持 Buffer
- 像 NIO 一样,可以对 Buffer 进行操作
- 但不强制使用 Buffer
public class OKIOMain {
public static void main(String[] args) {
try (BufferedSource source = Okio.buffer(Okio.source(new File("text.txt")))){
System.out.println(source.readUtf8Line());//ab
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//不带buffer的
//try (Source source = Okio.source(new File("text.txt"))){
// Buffer buffer = new Buffer();
// source.read(buffer,1024);
// System.out.println(buffer.readUtf8Line());//ab
//} catch (FileNotFoundException e) {
// e.printStackTrace();
//} catch (IOException e) {
// e.printStackTrace();
//}
}
}