提交 87cde1a7 编写于 作者: C CyC2018

auto commit

上级 e31f79d0
......@@ -82,7 +82,7 @@
容器的一些总结,包含容器源码的分析。
> [Java IO](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20IO.md)
> [Java I/O](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20IO.md)
File, InputStream OutputStream, Reader Writer, Serializable, Socket, NIO
......
此差异已折叠。
<div align="center"> <img src="../pics//98fb8a54-e916-440e-ab4f-73b2955838c9.jpg" width="100%"/> </div><br>
<img src="index_files/98fb8a54-e916-440e-ab4f-73b2955838c9.jpg" width="100%"/>
<!-- GFM-TOC -->
* [学习资料](#学习资料)
* [集中式与分布式](#集中式与分布式)
* [Git 的中心服务器](#git-的中心服务器)
* [Git 工作流](#git-工作流)
* [分支实现](#分支实现)
* [冲突](#冲突)
* [Fast forward](#fast-forward)
* [分支管理策略](#分支管理策略)
* [储藏(Stashing)](#储藏stashing)
* [SSH 传输设置](#ssh-传输设置)
* [.gitignore 文件](#gitignore-文件)
* [Git 命令一览](#git-命令一览)
<!-- GFM-TOC -->
[TOC]
# 学习资料
# 学习资料
[Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html)
[图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)
[廖雪峰 : Git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)
[Learn Git Branching](https://learngitbranching.js.org/)
- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html)
- [图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)
- [廖雪峰 : Git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000)
- [Learn Git Branching](https://learngitbranching.js.org/)
# 集中式与分布式
# 集中式与分布式
Git 属于分布式版本控制系统,而 SVN 属于集中式。
Git 属于分布式版本控制系统,而 SVN 属于集中式。
集中式版本控制只有中心服务器拥有一份代码,而分布式版本控制每个人的电脑上就有一份完整的代码。
......@@ -36,124 +22,124 @@ Git 属于分布式版本控制系统,而 SVN 属于集中式。
分布式版本控制新建分支、合并分支操作速度非常快,而集中式版本控制新建一个分支相当于复制一份完整代码。
# Git 的中心服务器
# Git 的中心服务器
Git 的中心服务器用来交换每个用户的修改。没有中心服务器也能工作,但是中心服务器能够 24 小时保持开机状态,这样就能更方便的交换修改。Github 就是一种 Git 中心服务器。
Git 的中心服务器用来交换每个用户的修改。没有中心服务器也能工作,但是中心服务器能够 24 小时保持开机状态,这样就能更方便的交换修改。Github 就是一种 Git 中心服务器。
# Git 工作流
# Git 工作流
<div align="center"> <img src="../pics//a1198642-9159-4d88-8aec-c3b04e7a2563.jpg"/> </div><br>
![](index_files/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg)
新建一个仓库之后,当前目录就成为了工作区,工作区下有一个隐藏目录 .git,它属于 Git 的版本库。
新建一个仓库之后,当前目录就成为了工作区,工作区下有一个隐藏目录 .git,它属于 Git 的版本库。
Git 版本库有一个称为 stage 的暂存区,还有自动创建的 master 分支以及指向分支的 HEAD 指针。
Git 版本库有一个称为 stage 的暂存区,还有自动创建的 master 分支以及指向分支的 HEAD 指针。
<div align="center"> <img src="../pics//46f66e88-e65a-4ad0-a060-3c63fe22947c.png"/> </div><br>
![](index_files/46f66e88-e65a-4ad0-a060-3c63fe22947c.png)
- git add files 把文件的修改添加到暂存区
- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了
- git reset -- files 使用当前分支上的修改覆盖暂缓区,用来撤销最后一次 git add files
- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改
- git add files 把文件的修改添加到暂存区
- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了
- git reset -- files 使用当前分支上的修改覆盖暂缓区,用来撤销最后一次 git add files
- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改
<div align="center"> <img src="../pics//17976404-95f5-480e-9cb4-250e6aa1d55f.png"/> </div><br>
![](index_files/17976404-95f5-480e-9cb4-250e6aa1d55f.png)
可以跳过暂存区域直接从分支中取出修改或者直接提交修改到分支中
- git commit -a 直接把所有文件的修改添加到暂缓区然后执行提交
- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作
- git commit -a 直接把所有文件的修改添加到暂缓区然后执行提交
- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作
# 分支实现
# 分支实现
Git 把每次提交都连成一条时间线。分支使用指针来实现,例如 master 分支指针指向时间线的最后一个节点,也就是最后一次提交。HEAD 指针指向的是当前分支。
Git 把每次提交都连成一条时间线。分支使用指针来实现,例如 master 分支指针指向时间线的最后一个节点,也就是最后一次提交。HEAD 指针指向的是当前分支。
<div align="center"> <img src="../pics//fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg"/> </div><br>
![](index_files/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg)
新建分支是新建一个指针指向时间线的最后一个节点,并让 HEAD 指针指向新分支表示新分支成为当前分支。
新建分支是新建一个指针指向时间线的最后一个节点,并让 HEAD 指针指向新分支表示新分支成为当前分支。
<div align="center"> <img src="../pics//bc775758-89ab-4805-9f9c-78b8739cf780.jpg"/> </div><br>
![](index_files/bc775758-89ab-4805-9f9c-78b8739cf780.jpg)
每次提交只会让当前分支向前移动,而其它分支不会移动。
<div align="center"> <img src="../pics//5292faa6-0141-4638-bf0f-bb95b081dcba.jpg"/> </div><br>
![](index_files/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg)
合并分支也只需要改变指针即可。
<div align="center"> <img src="../pics//1164a71f-413d-494a-9cc8-679fb6a2613d.jpg"/> </div><br>
![](index_files/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg)
# 冲突
# 冲突
当两个分支都对同一个文件进行了修改,在分支合并时就会产生冲突。
<div align="center"> <img src="../pics//58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg"/> </div><br>
![](index_files/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg)
Git 会使用 <<<<<<< ,======= >>>>>>> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。
Git 会使用 <<<<<<< ,======= ,>>>>>>> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。
```
<<<<<<< HEAD
Creating a new branch is quick & simple.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
Creating a new branch is quick AND simple.
>>>>>>> feature1
```
# Fast forward
# Fast forward
"快进式合并"(fast-farward merge),会直接将 master 分支指向合并的分支,这种模式下进行分支合并会丢失分支信息,也就不能在分支历史上看出分支信息。
"快进式合并"(fast-farward merge),会直接将 master 分支指向合并的分支,这种模式下进行分支合并会丢失分支信息,也就不能在分支历史上看出分支信息。
可以在合并时加上 --no-ff 参数来禁用 Fast forward 模式,并且加上 -m 参数让合并时产生一个新的 commit。
可以在合并时加上 --no-ff 参数来禁用 Fast forward 模式,并且加上 -m 参数让合并时产生一个新的 commit。
```
$ git merge --no-ff -m "merge with no-ff" dev
$ git merge --no-ff -m "merge with no-ff" dev
```
<div align="center"> <img src="../pics//dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg"/> </div><br>
![](index_files/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg)
# 分支管理策略
# 分支管理策略
master 分支应该是非常稳定的,只用来发布新版本;
master 分支应该是非常稳定的,只用来发布新版本;
日常开发在开发分支 dev 上进行。
日常开发在开发分支 dev 上进行。
<div align="center"> <img src="../pics//245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg"/> </div><br>
![](index_files/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg)
# 储藏(Stashing)
# 储藏(Stashing)
在一个分支上操作之后,如果还没有将修改提交到分支上,此时进行切换分支,那么另一个分支上也能看到新的修改。这是因为所有分支都共用一个工作区的缘故。
可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区的所有修改都会被存到栈上,也就是说当前工作区是干净的,没有任何未提交的修改。此时就可以安全的切换到其它分支上了。
可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区的所有修改都会被存到栈上,也就是说当前工作区是干净的,没有任何未提交的修改。此时就可以安全的切换到其它分支上了。
```
$ git stash
Saved working directory and index state \ "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file (To restore them type "git stash apply")
$ git stash
Saved working directory and index state \ "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file (To restore them type "git stash apply")
```
该功能可以用于 bug 分支的实现。如果当前正在 dev 分支上进行开发,但是此时 master 上有个 bug 需要修复,但是 dev 分支上的开发还未完成,不想立即提交。在新建 bug 分支并切换到 bug 分支之前就需要使用 git stash 将 dev 分支的未提交修改储藏起来。
该功能可以用于 bug 分支的实现。如果当前正在 dev 分支上进行开发,但是此时 master 上有个 bug 需要修复,但是 dev 分支上的开发还未完成,不想立即提交。在新建 bug 分支并切换到 bug 分支之前就需要使用 git stash 将 dev 分支的未提交修改储藏起来。
# SSH 传输设置
# SSH 传输设置
Git 仓库和 Github 中心仓库之间是通过 SSH 加密。
Git 仓库和 Github 中心仓库之间是通过 SSH 加密。
如果工作区下没有 .ssh 目录,或者该目录下没有 id_rsa 和 id_rsa.pub 这两个文件,可以通过以下命令来创建 SSH Key:
如果工作区下没有 .ssh 目录,或者该目录下没有 id_rsa 和 id_rsa.pub 这两个文件,可以通过以下命令来创建 SSH Key:
```
$ ssh-keygen -t rsa -C "youremail@example.com"
$ ssh-keygen -t rsa -C "youremail@example.com"
```
然后把公钥 id_rsa.pub 的内容复制到 Github "Account settings" 的 SSH Keys 中。
然后把公钥 id_rsa.pub 的内容复制到 Github "Account settings" 的 SSH Keys 中。
# .gitignore 文件
# .gitignore 文件
忽略以下文件:
1. 操作系统自动生成的文件,比如缩略图;
2. 编译生成的中间文件,比如 Java 编译产生的 .class 文件;
3. 自己的敏感信息,比如存放口令的配置文件。
1. 操作系统自动生成的文件,比如缩略图;
2. 编译生成的中间文件,比如 Java 编译产生的 .class 文件;
3. 自己的敏感信息,比如存放口令的配置文件。
不需要全部自己编写,可以到 [https://github.com/github/gitignore](https://github.com/github/gitignore) 中进行查询。
不需要全部自己编写,可以到 [https://github.com/github/gitignore](https://github.com/github/gitignore) 中进行查询。
# Git 命令一览
# Git 命令一览
<div align="center"> <img src="../pics//7a29acce-f243-4914-9f00-f2988c528412.jpg"/> </div><br>
![](index_files/7a29acce-f243-4914-9f00-f2988c528412.jpg)
比较详细的地址:http://www.cheat-sheets.org/saved-copy/git-cheat-sheet.pdf
此差异已折叠。
<!-- GFM-TOC -->
* [一、创建型](#一创建型)
* [1. 单例模式](#1-单例模式)
* [2. 简单工厂模式](#2-简单工厂模式)
* [3. 工厂方法模式](#3-工厂方法模式)
* [4. 抽象工厂](#4-抽象工厂)
* [5. 生成器模式](#5-生成器模式)
* [6. 原型模式](#6-原型模式)
* [二、行为型](#二行为型)
* [1. 责任链](#1-责任链)
* [2. 命令模式](#2-命令模式)
* [3. 解释器模式](#3-解释器模式)
* [4. 迭代器](#4-迭代器)
* [5. 中间人模式](#5-中间人模式)
* [6. 备忘录模式](#6-备忘录模式)
* [7. 观察者模式](#7-观察者模式)
* [8. 策略模式](#8-策略模式)
* [9. 模板方法](#9-模板方法)
* [10. 访问者模式](#10-访问者模式)
* [11. 空对象模式](#11-空对象模式)
* [三、结构型](#三结构型)
* [1. 适配器](#1-适配器)
* [2. 桥接模式](#2-桥接模式)
* [3. 组合模式](#3-组合模式)
* [4. 装饰者模式](#4-装饰者模式)
* [5. 蝇量模式](#5-蝇量模式)
* [6. 动态代理](#6-动态代理)
* [参考资料](#参考资料)
<!-- GFM-TOC -->
# 一、创建型
## 1. 单例模式
[TOC]
# 一、创建型
## 1. 单例模式
确保只实例化一个对象,并提供一个对象的全局访问点。
......@@ -42,11 +13,11 @@ java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()
java.awt.Desktop#getDesktop()
```
## 2. 简单工厂模式
## 2. 简单工厂模式
在不对用户暴露对象内部逻辑的前提下创建对象;使用通用的接口来创建对象;
## 3. 工厂方法模式
## 3. 工厂方法模式
定义创建对象的接口,但是让子类来决定应该使用哪个类来创建;使用通用的接口来创建对象;
......@@ -60,7 +31,7 @@ java.lang.Boolean#valueOf(String)
java.lang.Class#forName()
```
## 4. 抽象工厂
## 4. 抽象工厂
提供一个创建相关对象家族的接口,而没有明确指明它们的类。
......@@ -75,7 +46,7 @@ java.text.NumberFormat#getInstance()
javax.xml.transform.TransformerFactory#newInstance()
```
## 5. 生成器模式
## 5. 生成器模式
定义一个新的类来构造另一个类的实例,以创建一个复杂的对象。
......@@ -88,7 +59,7 @@ java.sql.PreparedStatement
javax.swing.GroupLayout.Group#addComponent()
```
## 6. 原型模式
## 6. 原型模式
使用原型实例指定要创建对象的类型;通过复制这个原型来创建新对象。
......@@ -97,9 +68,9 @@ java.lang.Object#clone()
java.lang.Cloneable
```
# 二、行为型
# 二、行为型
## 1. 责任链
## 1. 责任链
避免将请求的发送者附加到其接受者,从而使其它对象也可以处理请求;将请求以对象的方式把发送到链上直到请求被处理完毕。
......@@ -108,7 +79,7 @@ java.util.logging.Logger#log()
javax.servlet.Filter#doFilter()
```
## 2. 命令模式
## 2. 命令模式
将命令封装进对象中;允许使用命令对象对客户对象进行参数化;允许将命令对象存放到队列中。
......@@ -117,7 +88,7 @@ java.lang.Runnable
javax.swing.Action
```
## 3. 解释器模式
## 3. 解释器模式
为语言创建解释器,通常由语言的语法和语法分析来定义。
......@@ -127,7 +98,7 @@ java.text.Normalizer
java.text.Format
```
## 4. 迭代器
## 4. 迭代器
提供一种一致的访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。
......@@ -136,7 +107,7 @@ java.util.Iterator
java.util.Enumeration
```
## 5. 中间人模式
## 5. 中间人模式
使用中间人对象来封装对象之间的交互。中间人模式可以让降低交互对象之间的耦合程度。
......@@ -147,7 +118,7 @@ java.util.concurrent.ExecutorService#submit()
java.lang.reflect.Method#invoke()
```
## 6. 备忘录模式
## 6. 备忘录模式
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
......@@ -156,7 +127,7 @@ java.util.Date
java.io.Serializable
```
## 7. 观察者模式
## 7. 观察者模式
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
......@@ -167,7 +138,7 @@ javax.servlet.http.HttpSessionAttributeListener
javax.faces.event.PhaseListener
```
## 8. 策略模式
## 8. 策略模式
定义一系列算法,封装每个算法,并使它们可以互换。策略可以让算法独立于使用它的客户端。
......@@ -177,7 +148,7 @@ javax.servlet.http.HttpServlet
javax.servlet.Filter#doFilter()
```
## 9. 模板方法
## 9. 模板方法
定义算法框架,并将一些步骤的实现延迟到子类。通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
......@@ -188,24 +159,24 @@ java.io.InputStream#read()
java.util.AbstractList#indexOf()
```
## 10. 访问者模式
## 10. 访问者模式
提供便捷的维护方式来操作一组对象。它使你在不改变操作对象的前提下,可以修改或扩展对象的行为。
例如集合,它可以包含不同类型的元素,访问者模式允许在不知道具体元素类型的前提下对集合元素进行一些操作。
```java
javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor
javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor
javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor
javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor
```
## 11. 空对象模式
## 11. 空对象模式
使用什么都不做的空对象来替代 NULL。
使用什么都不做的空对象来替代 NULL。
# 三、结构型
# 三、结构型
## 1. 适配器
## 1. 适配器
把一个类接口转换成另一个用户需要的接口。
......@@ -218,16 +189,16 @@ javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()
```
## 2. 桥接模式
## 2. 桥接模式
将抽象与实现分离开来,使它们可以独立变化。
```java
AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
JDBC
```
## 3. 组合模式
## 3. 组合模式
将对象组合成树形结构来表示整理-部分层次关系,允许用户以相同的方式处理单独对象和组合对象。
......@@ -239,7 +210,7 @@ java.util.List#addAll(Collection)
java.util.Set#addAll(Collection)
```
## 4. 装饰者模式
## 4. 装饰者模式
为对象动态添加功能。
......@@ -251,11 +222,11 @@ java.util.zip.ZipOutputStream(OutputStream)
java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()
```
## 5. 蝇量模式
## 5. 蝇量模式
利用共享的方式来支持大量的对象,这些对象一部分内部状态时相同的,而另一份状态可以变化。
Java 利用缓存来加速大量小对象的访问时间。
Java 利用缓存来加速大量小对象的访问时间。
```java
java.lang.Integer#valueOf(int)
......@@ -264,7 +235,7 @@ java.lang.Byte#valueOf(byte)
java.lang.Character#valueOf(char)
```
## 6. 动态代理
## 6. 动态代理
提供一个占位符来控制对象的访问。
......@@ -275,9 +246,9 @@ java.lang.reflect.Proxy
RMI
```
# 参考资料
# 参考资料
- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html)
- [Design Patterns](http://www.oodesign.com/)
- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html)
- [Design Patterns](http://www.oodesign.com/)
此差异已折叠。
<!-- GFM-TOC -->
* [一、概览](#一概览)
* [二、磁盘操作](#二磁盘操作)
* [三、字节操作](#三字节操作)
* [四、字符操作](#四字符操作)
* [五、对象操作](#五对象操作)
* [六、网络操作](#六网络操作)
* [InetAddress](#inetaddress)
* [URL](#url)
* [Sockets](#sockets)
* [Datagram](#datagram)
* [七、NIO](#七nio)
* [流与块](#流与块)
* [通道与缓冲区](#通道与缓冲区)
* [缓冲区状态变量](#缓冲区状态变量)
* [文件 NIO 实例](#文件-nio-实例)
* [阻塞与非阻塞](#阻塞与非阻塞)
* [套接字 NIO 实例](#套接字-nio-实例)
* [内存映射文件](#内存映射文件)
* [八、参考资料](#八参考资料)
<!-- GFM-TOC -->
# 一、概览
Java 的 I/O 大概可以分成以下几类:
1. 磁盘操作:File
2. 字节操作:InputStream 和 OutputStream
3. 字符操作:Reader 和 Writer
4. 对象操作:Serializable
5. 网络操作:Socket
6. 新的输入/输出:NIO
# 二、磁盘操作
File 类可以用于表示文件和目录,但是它只用于表示文件的信息,而不表示文件的内容。
# 三、字节操作
<div align="center"> <img src="../pics//8143787f-12eb-46ea-9bc3-c66d22d35285.jpg"/> </div><br>
Java I/O 使用了装饰者模式来实现。以 InputStream 为例,InputStream 是抽象组件,FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作。FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。
实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。
```java
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
```
DataInputStream 装饰者提供了对更多数据类型进行输入的操作,比如 int、double 等基本类型。
批量读入文件内容到字节数组:
```java
byte[] buf = new byte[20*1024];
int bytes = 0;
// 最多读取 buf.length 个字节,返回的是实际读取的个数,返回 -1 的时候表示读到 eof,即文件尾
while((bytes = in.read(buf, 0 , buf.length)) != -1) {
// ...
}
```
# 四、字符操作
不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符,所以 I/O 操作的都是字节而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。
InputStreamReader 实现从文本文件的字节流解码成字符流;OutputStreamWriter 实现字符流编码成为文本文件的字节流。它们继承自 Reader 和 Writer。
编码就是把字符转换为字节,而解码是把字节重新组合成字符。
```java
byte[] bytes = str.getBytes(encoding); // 编码
String str = new String(bytes, encoding) // 解码
```
GBK 编码中,中文占 2 个字节,英文占 1 个字节;UTF-8 编码中,中文占 3 个字节,英文占 1 个字节;Java 使用双字节编码 UTF-16be,中文和英文都占 2 个字节。
如果编码和解码过程使用不同的编码方式那么就出现了乱码。
# 五、对象操作
序列化就是将一个对象转换成字节序列,方便存储和传输。
序列化:ObjectOutputStream.writeObject()
反序列化:ObjectInputStream.readObject()
序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现。
transient 关键字可以使一些属性不会被序列化。
**ArrayList 序列化和反序列化的实现** :ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
```java
private transient Object[] elementData;
```
# 六、网络操作
Java 中的网络支持:
1. InetAddress:用于表示网络上的硬件资源,即 IP 地址;
2. URL:统一资源定位符,通过 URL 可以直接读取或者写入网络上的数据;
3. Sockets:使用 TCP 协议实现网络通信;
4. Datagram:使用 UDP 协议实现网络通信。
## InetAddress
没有公有构造函数,只能通过静态方法来创建实例。
```java
InetAddress.getByName(String host);
InetAddress.getByAddress(byte[] addr);
```
## URL
可以直接从 URL 中读取字节流数据
```java
URL url = new URL("http://www.baidu.com");
InputStream is = url.openStream(); // 字节流
InputStreamReader isr = new InputStreamReader(is, "utf-8"); // 字符流
BufferedReader br = new BufferedReader(isr);
String line = br.readLine();
while (line != null) {
System.out.println(line);
line = br.readLine();
}
br.close();
isr.close();
is.close();
```
## Sockets
- ServerSocket:服务器端类
- Socket:客户端类
- 服务器和客户端通过 InputStream 和 OutputStream 进行输入输出。
<div align="center"> <img src="../pics//fa4101d7-19ce-4a69-a84f-20bbe64320e5.jpg"/> </div><br>
## Datagram
- DatagramPacket:数据包类
- DatagramSocket:通信类
# 七、NIO
新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。
## 流与块
I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
面向流的 I/O 一次处理一个字节数据,一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。
一个面向块的 I/O 系统以块的形式处理数据,一次处理数据块。按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如, java.io.\* 包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。
## 通道与缓冲区
### 1. 通道
通道 Channel 是对原 I/O 包中的流的模拟,可以通过它读取和写入数据。
通道与流的不同之处在于,流只能在一个方向上移动,(一个流必须是 InputStream 或者 OutputStream 的子类),而通道是双向的,可以用于读、写或者同时用于读写。
通道包括以下类型:
- FileChannel:从文件中读写数据;
- DatagramChannel:通过 UDP 读写网络中数据;
- SocketChannel:通过 TCP 读写网络中数据;
- ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
### 2. 缓冲区
发送给一个通道的所有对象都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要读到缓冲区中。也就是说,不会直接对通道进行读写数据,而是要先经过缓冲区。
缓冲区实质上是一个数组,但它不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。
缓冲区包括以下类型:
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
## 缓冲区状态变量
- capacity:最大容量;
- position:当前已经读写的字节数;
- limit:还可以读写的字节数。
状态变量的改变过程:
1. 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 9。capacity 变量不会改变,下面的讨论会忽略它。
<div align="center"> <img src="../pics//1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png"/> </div><br>
2. 从输入通道中读取 3 个字节数据写入缓冲区中,此时 position 移动设为 3,limit 保持不变。
<div align="center"> <img src="../pics//4628274c-25b6-4053-97cf-d1239b44c43d.png"/> </div><br>
3. 以下图例为已经从输入通道读取了 5 个字节数据写入缓冲区中。在将缓冲区的数据写到输出通道之前,需要先调用 flip() 方法,这个方法将 limit 设置为当前 position,并将 position 设置为 0。
<div align="center"> <img src="../pics//952e06bd-5a65-4cab-82e4-dd1536462f38.png"/> </div><br>
4. 从缓冲区中取 4 个字节到输出缓冲中,此时 position 设为 4。
<div align="center"> <img src="../pics//b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png"/> </div><br>
5. 最后需要调用 clear() 方法来清空缓冲区,此时 position 和 limit 都被设置为最初位置。
<div align="center"> <img src="../pics//67bf5487-c45d-49b6-b9c0-a058d8c68902.png"/> </div><br>
## 文件 NIO 实例
1\. 为要读取的文件创建 FileInputStream,之后通过 FileInputStream 获取输入 FileChannel;
```java
FileInputStream fin = new FileInputStream("readandshow.txt");
FileChannel fic = fin.getChannel();
```
2\. 创建一个容量为 1024 的 Buffer;
```java
ByteBuffer buffer = ByteBuffer.allocate(1024);
```
3\. 将数据从输入 FileChannel 写入到 Buffer 中,如果没有数据的话, read() 方法会返回 -1;
```java
int r = fcin.read(buffer);
if (r == -1) {
break;
}
```
4\. 为要写入的文件创建 FileOutputStream,之后通过 FileOutputStream 获取输出 FileChannel
```java
FileOutputStream fout = new FileOutputStream("writesomebytes.txt");
FileChannel foc = fout.getChannel();
```
5\. 调用 flip() 切换读写
```java
buffer.flip();
```
6\. 把 Buffer 中的数据读取到输出 FileChannel 中
```java
foc.write(buffer);
```
7\. 最后调用 clear() 重置缓冲区
```java
buffer.clear();
```
## 阻塞与非阻塞
应当注意,FileChannel 不能切换到非阻塞模式,套接字 Channel 可以。
### 1. 阻塞式 I/O
阻塞式 I/O 在调用 InputStream.read() 方法时会一直等到数据到来时(或超时)才会返回,在调用 ServerSocket.accept() 方法时,也会一直阻塞到有客户端连接才会返回。
服务端都会为每个连接的客户端创建一个线程来处理读写请求,阻塞式的特点会造成服务器会创建大量线程,并且大部分线程处于阻塞的状态,因此对服务器的性能会有很大的影响。
<div align="center"> <img src="../pics//edc23f99-c46c-4200-b64e-07516828720d.jpg"/> </div><br>
### 2. 非阻塞式 I/O
由一个专门的线程来处理所有的 I/O 事件,并负责分发。
事件驱动机制:事件到的时候触发,而不是同步地监视事件。
线程通信:线程之间通过 wait()、notify() 等方式通信,保证每次上下文切换都是有意义的,减少无谓的线程切换。
<div align="center"> <img src="../pics//7fcb2fb0-2cd9-4396-bc2d-282becf963c3.jpg"/> </div><br>
## 套接字 NIO 实例
### 1. ServerSocketChannel
每一个监听端口都需要有一个 ServerSocketChannel 用来监听连接。
```java
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false); // 设置为非阻塞
ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress(ports[i]);
ss.bind(address); // 绑定端口号
```
### 2. Selectors
异步 I/O 通过 Selector 注册对特定 I/O 事件的兴趣 ― 可读的数据的到达、新的套接字连接等等,在发生这样的事件时,系统将会发送通知。
创建 Selectors 之后,就可以对不同的通道对象调用 register() 方法。register() 的第一个参数总是这个 Selector。第二个参数是 OP_ACCEPT,这里它指定我们想要监听 ACCEPT 事件,也就是在新的连接建立时所发生的事件。
SelectionKey 代表这个通道在此 Selector 上的这个注册。当某个 Selector 通知您某个传入事件时,它是通过提供对应于该事件的 SelectionKey 来进行的。SelectionKey 还可以用于取消通道的注册。
```java
Selector selector = Selector.open();
SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);
```
### 3. 主循环
首先,我们调用 Selector 的 select() 方法。这个方法会阻塞,直到至少有一个已注册的事件发生。当一个或者更多的事件发生时,select() 方法将返回所发生的事件的数量。
接下来,我们调用 Selector 的 selectedKeys() 方法,它返回发生了事件的 SelectionKey 对象的一个集合。
我们通过迭代 SelectionKeys 并依次处理每个 SelectionKey 来处理事件。对于每一个 SelectionKey,您必须确定发生的是什么 I/O 事件,以及这个事件影响哪些 I/O 对象。
```java
int num = selector.select();
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
// ... deal with I/O event ...
}
```
### 4. 监听新连接
程序执行到这里,我们仅注册了 ServerSocketChannel,并且仅注册它们“接收”事件。为确认这一点,我们对 SelectionKey 调用 readyOps() 方法,并检查发生了什么类型的事件:
```java
if ((key.readyOps() & SelectionKey.OP_ACCEPT)
== SelectionKey.OP_ACCEPT) {
// Accept the new connection
// ...
}
```
可以肯定地说,readOps() 方法告诉我们该事件是新的连接。
### 5. 接受新的连接
因为我们知道这个服务器套接字上有一个传入连接在等待,所以可以安全地接受它;也就是说,不用担心 accept() 操作会阻塞:
```java
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();
```
下一步是将新连接的 SocketChannel 配置为非阻塞的。而且由于接受这个连接的目的是为了读取来自套接字的数据,所以我们还必须将 SocketChannel 注册到 Selector 上,如下所示:
```java
sc.configureBlocking(false);
SelectionKey newKey = sc.register(selector, SelectionKey.OP_READ);
```
注意我们使用 register() 的 OP_READ 参数,将 SocketChannel 注册用于读取而不是接受新连接。
### 6. 删除处理过的 SelectionKey
在处理 SelectionKey 之后,我们几乎可以返回主循环了。但是我们必须首先将处理过的 SelectionKey 从选定的键集合中删除。如果我们没有删除处理过的键,那么它仍然会在主集合中以一个激活的键出现,这会导致我们尝试再次处理它。我们调用迭代器的 remove() 方法来删除处理过的 SelectionKey:
```java
it.remove();
```
现在我们可以返回主循环并接受从一个套接字中传入的数据 (或者一个传入的 I/O 事件) 了。
### 7. 传入的 I/O
当来自一个套接字的数据到达时,它会触发一个 I/O 事件。这会导致在主循环中调用 Selector.select(),并返回一个或者多个 I/O 事件。这一次, SelectionKey 将被标记为 OP_READ 事件,如下所示:
```java
} else if ((key.readyOps() & SelectionKey.OP_READ)
== SelectionKey.OP_READ) {
// Read the data
SocketChannel sc = (SocketChannel)key.channel();
// ...
}
```
## 内存映射文件
内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。
只有文件中实际读取或者写入的部分才会映射到内存中。
现代操作系统一般根据需要将文件的部分映射为内存的部分,从而实现文件系统。Java 内存映射机制不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。
向内存映射文件写入可能是危险的,仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,您可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行行映射。
```java
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
```
# 八、参考资料
- Eckel B, 埃克尔, 昊鹏, 等. Java 编程思想 [M]. 机械工业出版社, 2002.
- [IBM: NIO 入门](https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html)
- [深入分析 Java I/O 的工作机制](https://www.ibm.com/developerworks/cn/java/j-lo-javaio/index.html)
- [NIO 与传统 IO 的区别](http://blog.csdn.net/shimiso/article/details/24990499)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# Google Java Style Guide
# Google Java Style Guide
- http://www.hawstein.com/posts/google-java-style.html
- http://google.github.io/styleguide/javaguide.html
- http://www.hawstein.com/posts/google-java-style.html
- http://google.github.io/styleguide/javaguide.html
# Google C++ Style Guide
# Google C++ Style Guide
- http://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/
- http://google.github.io/styleguide/cppguide.html
- http://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/contents/
- http://google.github.io/styleguide/cppguide.html
# Google Python Style Guide
# Google Python Style Guide
- http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/contents/
- http://google.github.io/styleguide/pyguide.html
- http://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/contents/
- http://google.github.io/styleguide/pyguide.html
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册