auto commit

This commit is contained in:
CyC2018 2019-06-09 17:04:10 +08:00
parent 29ba25a9d7
commit 34db2b79db
14 changed files with 160 additions and 48 deletions

View File

@ -24,7 +24,7 @@ Git 属于分布式版本控制系统,而 SVN 属于集中式。
集中式版本控制有安全性问题当中心服务器挂了所有人都没办法工作了
集中式版本控制需要连网才能工作如果网速过慢那么提交一个文件会慢的无法让人忍受而分布式版本控制不需要连网就能工作
集中式版本控制需要连网才能工作如果网速过慢那么提交一个文件会慢的无法让人忍受而分布式版本控制不需要连网就能工作
分布式版本控制新建分支合并分支操作速度非常快而集中式版本控制新建一个分支相当于复制一份完整代码
@ -38,7 +38,7 @@ Github 就是一个中心服务器。
新建一个仓库之后当前目录就成为了工作区工作区下有一个隐藏目录 .git它属于 Git 的版本库
Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本库History 中存有所有分支使用一个 HEAD 指针指向当前分支
Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本库History 存储所有分支信息使用一个 HEAD 指针指向当前分支
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/71b97a50-a49f-4f1a-81d1-48c3364d61b3.png" width="700px"> </div><br>
@ -62,7 +62,7 @@ Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/ec4d7464-7140-46d8-827e-d63634202e1e.png" width="220px"> </div><br>
新建分支是新建一个指针指向时间线的最后一个节点并让 HEAD 指针指向新分支表示新分支成为当前分支
新建分支是新建一个指针指向时间线的最后一个节点并让 HEAD 指针指向新分支表示新分支成为当前分支
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/66d00642-ce37-466c-8f7a-143d0bf84cd6.png" width="220px"> </div><br>
@ -114,7 +114,7 @@ master 分支应该是非常稳定的,只用来发布新版本;
在一个分支上操作之后如果还没有将修改提交到分支上此时进行切换分支那么另一个分支上也能看到新的修改这是因为所有分支都共用一个工作区的缘故
可以使用 git stash 将当前分支的修改储藏起来此时当前工作区的所有修改都会被存到栈也就是说当前工作区是干净的没有任何未提交的修改此时就可以安全的切换到其它分支上了
可以使用 git stash 将当前分支的修改储藏起来此时当前工作区的所有修改都会被存到栈也就是说当前工作区是干净的没有任何未提交的修改此时就可以安全的切换到其它分支上了
```
$ git stash

View File

@ -48,6 +48,8 @@ https://leetcode.com/problems/big-countries/description/
## SQL Schema
SQL Schema 用于在本地环境下创建表结构并导入数据从而方便在本地环境解答
```sql
DROP TABLE
IF
@ -125,6 +127,8 @@ VALUES
'm' ^ 'm' ^ 'f' = 'f'
```
```sql
UPDATE salary
SET sex = CHAR ( ASCII(sex) ^ ASCII( 'm' ) ^ ASCII( 'f' ) );
@ -301,6 +305,8 @@ VALUES
## Solution
Email 进行分组如果相同 Email 的数量大于等于 2则表示该 Email 重复
```sql
SELECT
Email
@ -324,9 +330,9 @@ https://leetcode.com/problems/delete-duplicate-emails/description/
+----+---------+
| Id | Email |
+----+---------+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
| 1 | john@example.com |
| 2 | bob@example.com |
| 3 | john@example.com |
+----+---------+
```
@ -347,6 +353,8 @@ https://leetcode.com/problems/delete-duplicate-emails/description/
## Solution
只保留相同 Email Id 最小的那一个然后删除其它的
连接
```sql
@ -437,7 +445,7 @@ VALUES
## Solution
使用左外连接
涉及到 Person Address 两个表在对这两个表执行连接操作时因为要保留 Person 表中的信息使 Address 表中没有关联的信息也要保留此时可以用左外连接 Person 表放在 LEFT JOIN 的左边
```sql
SELECT
@ -797,10 +805,43 @@ VALUES
## Solution
要统计某个 score 的排名只要统计大于该 score score 数量然后加 1
| score | 大于该 score score 数量 | 排名 |
| :---: | :---: | :---: |
| 4.1 | 2 | 3 |
| 4.2 | 1 | 2 |
| 4.3 | 0 | 1 |
但是在本题中相同的 score 只算一个排名
| score | 排名 |
| :---: | :---: |
| 4.1 | 3 |
| 4.1 | 3 |
| 4.2 | 2 |
| 4.2 | 2 |
| 4.3 | 1 |
| 4.3 | 1 |
可以按 score 进行分组将同一个分组中的 score 只当成一个
但是如果分组字段只有 score 的话那么相同的 score 最后的结果只会有一个例如上面的 6 个记录最后只取出 3
| score | 排名 |
| :---: | :---: |
| 4.1 | 3 |
| 4.2 | 2 |
| 4.3 | 1 |
所以在分组中需要加入 Id每个记录显示一个结果综上需要使用 score id 两个分组字段
在下面的实现中首先将 Scores 表根据 score 字段进行自连接得到一个新表然后在新表上对 id score 进行分组
```sql
SELECT
S1.score,
COUNT( DISTINCT S2.score ) Rank
S1.score 'Score',
COUNT( DISTINCT S2.score ) 'Rank'
FROM
Scores S1
INNER JOIN Scores S2
@ -931,6 +972,8 @@ VALUES
使用多个 union
```sql
# 处理偶数 id id 1
# 例如 2,4,6,... 变成 1,3,5,...
SELECT
s1.id - 1 AS id,
s1.student
@ -938,6 +981,8 @@ FROM
seat s1
WHERE
s1.id MOD 2 = 0 UNION
# 处理奇数 id id 1但是如果最大的 id 为奇数则不做处理
# 例如 1,3,5,... 变成 2,4,6,...
SELECT
s2.id + 1 AS id,
s2.student
@ -946,6 +991,7 @@ FROM
WHERE
s2.id MOD 2 = 1
AND s2.id != ( SELECT max( s3.id ) FROM seat s3 ) UNION
# 如果最大的 id 为奇数单独取出这个数
SELECT
s4.id AS id,
s4.student

View File

@ -70,7 +70,7 @@ int c = 111; // 注释
# 为何编写注释
阅读代码首先会注意到注释如果注释没太大作用那么就会浪费代码阅读的时间那些能直接看出含义的代码不需要写注释特别是不需要为每个方法都加上注释比如那些简单的 getter setter 方法为这些方法写注释反而让代码可读性更差
阅读代码首先会注意到注释如果注释没太大作用那么就会浪费代码阅读的时间那些能直接看出含义的代码不需要写注释特别是不需要为每个方法都加上注释比如那些简单的 getter setter 方法为这些方法写注释反而让代码可读性更差
不能因为有注释就随便起个名字而是争取起个好名字而不写注释

View File

@ -32,10 +32,10 @@
# Java 主流构建工具
主要包括 AntMaven Gradle
Ant 具有编译测试和打包功能其后出现的 Maven Ant 的功能基础上又新增了依赖管理功能而最新的 Maven 又在 Maven 的功能基础上新增了对 Groovy 语言的支持
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/34751bd9-e8e4-4c20-94bc-f7217049fada.png" width="400px"> </div><br>
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/34751bd9-e8e4-4c20-94bc-f7217049fada.png" width="450px"> </div><br>
Gradle Maven 的区别是它使用 Groovy 这种特定领域语言DSL来管理构建脚本而不再使用 XML 这种标记性语言因为项目如果庞大的话XML 很容易就变得臃肿
@ -82,7 +82,7 @@ dependencies {
- 本地仓库用来存储项目的依赖库
- 中央仓库是下载依赖库的默认位置
- 远程仓库因为并非所有的库存储在中央仓库或者中央仓库访问速度很慢远程仓库是中央仓库的补充
- 远程仓库因为并非所有的依赖库都在中央仓库或者中央仓库访问速度很慢远程仓库是中央仓库的补充
## POM
@ -99,7 +99,6 @@ POM 代表项目对象模型,它是一个 XML 文件,保存在项目根目
[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标其中 groupIdartifactIdversion 必须定义packaging 可选默认为 Jarclassifier 不能直接定义的需要结合插件使用
- groupId项目组 Id必须全球唯一
- artifactId项目 Id即项目名
- version项目版本

View File

@ -36,15 +36,15 @@
有两种共享方式互斥共享和同时共享
互斥共享的资源称为临界资源例如打印机等在同一时间只允许一个进程访问需要用同步机制来实现对临界资源的访问
互斥共享的资源称为临界资源例如打印机等在同一时刻只允许一个进程访问需要用同步机制来实现互斥访问
## 3. 虚拟
虚拟技术把一个物理实体转换为多个逻辑实体
主要有两种虚拟技术分复用技术和空分复用技术
主要有两种虚拟技术时间分复用技术和空空间分复用技术
多个进程能在同一个处理器上并发执行使用了时分复用技术让每个进程轮流占处理器每次只执行一小个时间片并快速切换
多个进程能在同一个处理器上并发执行使用了时分复用技术让每个进程轮流占处理器每次只执行一小个时间片并快速切换
虚拟内存使用了空分复用技术它将物理内存抽象为地址空间每个进程都有各自的地址空间地址空间的页被映射到物理内存地址空间的页并不需要全部在物理内存中当使用到一个没有在物理内存的页时执行页面置换算法将该页置换到内存中

View File

@ -131,7 +131,7 @@ public class Singleton {
}
```
考虑下面的实现也就是只使用了一个 if 语句 uniqueInstance == null 的情况下如果两个线程都执行了 if 语句那么两个线程都会进入 if 语句块内虽然在 if 语句块内有加锁操作但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句只是先后的问题那么就会进行两次实例化因此必须使用双重校验锁也就是需要使用两个 if 语句
考虑下面的实现也就是只使用了一个 if 语句 uniqueInstance == null 的情况下如果两个线程都执行了 if 语句那么两个线程都会进入 if 语句块内虽然在 if 语句块内有加锁操作但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句只是先后的问题那么就会进行两次实例化因此必须使用双重校验锁也就是需要使用两个 if 语句第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作而第二个 if 语句进行了加锁所以只能有一个线程进入就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作
```java
if (uniqueInstance == null) {
@ -153,7 +153,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueIn
#### 静态内部类实现
Singleton 加载时静态内部类 SingletonHolder 没有被加载进内存只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` SingletonHolder 才会被加载此时初始化 INSTANCE 实例并且 JVM 能确保 INSTANCE 只被实例化一次
Singleton 加载时静态内部类 SingletonHolder 没有被加载进内存只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` SingletonHolder 才会被加载此时初始化 INSTANCE 实例并且 JVM 能确保 INSTANCE 只被实例化一次
这种方式不仅具有延迟初始化的好处而且由 JVM 提供了对线程安全的支持
@ -224,10 +224,10 @@ secondName
secondName
```
该实现在多次序列化再进行反序列化之后不会得到多个实例而其它实现需要使用 transient 修饰所有字段并且实现序列化和反序列化的方法
该实现可以防止反射攻击在其它实现中通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public然后调用构造函数从而实例化对象如果要防止这种攻击需要在构造函数中添加防止多次实例化的代码该实现是由 JVM 保证只会实例化一次因此不会出现上述的反射攻击
该实现在多次序列化和序列化之后不会得到多个实例而其它实现需要使用 transient 修饰所有字段并且实现序列化和反序列化的方法
### Examples
- Logger Classes

View File

@ -21,13 +21,13 @@
## 封装
利用抽象数据类型将数据和基于数据的操作封装在一起使其构成一个不可分割的独立实体数据被保护在抽象数据类型的内部尽可能地隐藏内部的细节只保留一些对外接口使之与外部发生联系用户无需知道对象内部的细节但可以通过对象对外提供的接口来访问该对象
利用抽象数据类型将数据和基于数据的操作封装在一起使其构成一个不可分割的独立实体数据被保护在抽象数据类型的内部尽可能地隐藏内部的细节只保留一些对外的接口使其与外部发生联系用户无需关心对象内部的细节但可以通过对象对外提供的接口来访问该对象
优点
- 减少耦合可以独立地开发测试优化使用理解和修改
- 减轻维护的负担可以更容易被程序员理解并且在调试的时候可以不影响其他模块
- 有效地调节性能可以通过剖析确定哪些模块影响了系统的性能
- 有效地调节性能可以通过剖析确定哪些模块影响了系统的性能
- 提高软件的可重用性
- 降低了构建大型系统的风险即使整个系统不可用但是这些独立的模块却有可能是可用的
@ -94,21 +94,27 @@ public class Instrument {
System.out.println("Instument is playing...");
}
}
```
```java
public class Wind extends Instrument {
public void play() {
System.out.println("Wind is playing...");
}
}
```
```java
public class Percussion extends Instrument {
public void play() {
System.out.println("Percussion is playing...");
}
}
```
```java
public class Music {
public static void main(String[] args) {
@ -122,6 +128,11 @@ public class Music {
}
```
```
Wind is playing...
Percussion is playing...
```
# 类图
以下类图使用 [PlantUML](https://www.planttext.com/) 绘制更多语法及使用请参考http://plantuml.com/

View File

@ -24,7 +24,7 @@ Git 属于分布式版本控制系统,而 SVN 属于集中式。
集中式版本控制有安全性问题当中心服务器挂了所有人都没办法工作了
集中式版本控制需要连网才能工作如果网速过慢那么提交一个文件会慢的无法让人忍受而分布式版本控制不需要连网就能工作
集中式版本控制需要连网才能工作如果网速过慢那么提交一个文件会慢的无法让人忍受而分布式版本控制不需要连网就能工作
分布式版本控制新建分支合并分支操作速度非常快而集中式版本控制新建一个分支相当于复制一份完整代码
@ -38,7 +38,7 @@ Github 就是一个中心服务器。
新建一个仓库之后当前目录就成为了工作区工作区下有一个隐藏目录 .git它属于 Git 的版本库
Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本库History 中存有所有分支使用一个 HEAD 指针指向当前分支
Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本库History 存储所有分支信息使用一个 HEAD 指针指向当前分支
<div align="center"> <img src="pics/71b97a50-a49f-4f1a-81d1-48c3364d61b3.png" width="700px"> </div><br>
@ -62,7 +62,7 @@ Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本
<div align="center"> <img src="pics/ec4d7464-7140-46d8-827e-d63634202e1e.png" width="220px"> </div><br>
新建分支是新建一个指针指向时间线的最后一个节点并让 HEAD 指针指向新分支表示新分支成为当前分支
新建分支是新建一个指针指向时间线的最后一个节点并让 HEAD 指针指向新分支表示新分支成为当前分支
<div align="center"> <img src="pics/66d00642-ce37-466c-8f7a-143d0bf84cd6.png" width="220px"> </div><br>
@ -114,7 +114,7 @@ master 分支应该是非常稳定的,只用来发布新版本;
在一个分支上操作之后如果还没有将修改提交到分支上此时进行切换分支那么另一个分支上也能看到新的修改这是因为所有分支都共用一个工作区的缘故
可以使用 git stash 将当前分支的修改储藏起来此时当前工作区的所有修改都会被存到栈也就是说当前工作区是干净的没有任何未提交的修改此时就可以安全的切换到其它分支上了
可以使用 git stash 将当前分支的修改储藏起来此时当前工作区的所有修改都会被存到栈也就是说当前工作区是干净的没有任何未提交的修改此时就可以安全的切换到其它分支上了
```
$ git stash

View File

@ -48,6 +48,8 @@ https://leetcode.com/problems/big-countries/description/
## SQL Schema
SQL Schema 用于在本地环境下创建表结构并导入数据从而方便在本地环境解答
```sql
DROP TABLE
IF
@ -125,6 +127,8 @@ VALUES
'm' ^ 'm' ^ 'f' = 'f'
```
```sql
UPDATE salary
SET sex = CHAR ( ASCII(sex) ^ ASCII( 'm' ) ^ ASCII( 'f' ) );
@ -301,6 +305,8 @@ VALUES
## Solution
Email 进行分组如果相同 Email 的数量大于等于 2则表示该 Email 重复
```sql
SELECT
Email
@ -324,9 +330,9 @@ https://leetcode.com/problems/delete-duplicate-emails/description/
+----+---------+
| Id | Email |
+----+---------+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
| 1 | john@example.com |
| 2 | bob@example.com |
| 3 | john@example.com |
+----+---------+
```
@ -347,6 +353,8 @@ https://leetcode.com/problems/delete-duplicate-emails/description/
## Solution
只保留相同 Email Id 最小的那一个然后删除其它的
连接
```sql
@ -437,7 +445,7 @@ VALUES
## Solution
使用左外连接
涉及到 Person Address 两个表在对这两个表执行连接操作时因为要保留 Person 表中的信息使 Address 表中没有关联的信息也要保留此时可以用左外连接 Person 表放在 LEFT JOIN 的左边
```sql
SELECT
@ -797,10 +805,43 @@ VALUES
## Solution
要统计某个 score 的排名只要统计大于该 score score 数量然后加 1
| score | 大于该 score score 数量 | 排名 |
| :---: | :---: | :---: |
| 4.1 | 2 | 3 |
| 4.2 | 1 | 2 |
| 4.3 | 0 | 1 |
但是在本题中相同的 score 只算一个排名
| score | 排名 |
| :---: | :---: |
| 4.1 | 3 |
| 4.1 | 3 |
| 4.2 | 2 |
| 4.2 | 2 |
| 4.3 | 1 |
| 4.3 | 1 |
可以按 score 进行分组将同一个分组中的 score 只当成一个
但是如果分组字段只有 score 的话那么相同的 score 最后的结果只会有一个例如上面的 6 个记录最后只取出 3
| score | 排名 |
| :---: | :---: |
| 4.1 | 3 |
| 4.2 | 2 |
| 4.3 | 1 |
所以在分组中需要加入 Id每个记录显示一个结果综上需要使用 score id 两个分组字段
在下面的实现中首先将 Scores 表根据 score 字段进行自连接得到一个新表然后在新表上对 id score 进行分组
```sql
SELECT
S1.score,
COUNT( DISTINCT S2.score ) Rank
S1.score 'Score',
COUNT( DISTINCT S2.score ) 'Rank'
FROM
Scores S1
INNER JOIN Scores S2
@ -931,6 +972,8 @@ VALUES
使用多个 union
```sql
# 处理偶数 id id 1
# 例如 2,4,6,... 变成 1,3,5,...
SELECT
s1.id - 1 AS id,
s1.student
@ -938,6 +981,8 @@ FROM
seat s1
WHERE
s1.id MOD 2 = 0 UNION
# 处理奇数 id id 1但是如果最大的 id 为奇数则不做处理
# 例如 1,3,5,... 变成 2,4,6,...
SELECT
s2.id + 1 AS id,
s2.student
@ -946,6 +991,7 @@ FROM
WHERE
s2.id MOD 2 = 1
AND s2.id != ( SELECT max( s3.id ) FROM seat s3 ) UNION
# 如果最大的 id 为奇数单独取出这个数
SELECT
s4.id AS id,
s4.student

View File

@ -70,7 +70,7 @@ int c = 111; // 注释
# 为何编写注释
阅读代码首先会注意到注释如果注释没太大作用那么就会浪费代码阅读的时间那些能直接看出含义的代码不需要写注释特别是不需要为每个方法都加上注释比如那些简单的 getter setter 方法为这些方法写注释反而让代码可读性更差
阅读代码首先会注意到注释如果注释没太大作用那么就会浪费代码阅读的时间那些能直接看出含义的代码不需要写注释特别是不需要为每个方法都加上注释比如那些简单的 getter setter 方法为这些方法写注释反而让代码可读性更差
不能因为有注释就随便起个名字而是争取起个好名字而不写注释

View File

@ -32,10 +32,10 @@
# Java 主流构建工具
主要包括 AntMaven Gradle
Ant 具有编译测试和打包功能其后出现的 Maven Ant 的功能基础上又新增了依赖管理功能而最新的 Maven 又在 Maven 的功能基础上新增了对 Groovy 语言的支持
<div align="center"> <img src="pics/34751bd9-e8e4-4c20-94bc-f7217049fada.png" width="400px"> </div><br>
<div align="center"> <img src="pics/34751bd9-e8e4-4c20-94bc-f7217049fada.png" width="450px"> </div><br>
Gradle Maven 的区别是它使用 Groovy 这种特定领域语言DSL来管理构建脚本而不再使用 XML 这种标记性语言因为项目如果庞大的话XML 很容易就变得臃肿
@ -82,7 +82,7 @@ dependencies {
- 本地仓库用来存储项目的依赖库
- 中央仓库是下载依赖库的默认位置
- 远程仓库因为并非所有的库存储在中央仓库或者中央仓库访问速度很慢远程仓库是中央仓库的补充
- 远程仓库因为并非所有的依赖库都在中央仓库或者中央仓库访问速度很慢远程仓库是中央仓库的补充
## POM
@ -99,7 +99,6 @@ POM 代表项目对象模型,它是一个 XML 文件,保存在项目根目
[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标其中 groupIdartifactIdversion 必须定义packaging 可选默认为 Jarclassifier 不能直接定义的需要结合插件使用
- groupId项目组 Id必须全球唯一
- artifactId项目 Id即项目名
- version项目版本

View File

@ -36,15 +36,15 @@
有两种共享方式互斥共享和同时共享
互斥共享的资源称为临界资源例如打印机等在同一时间只允许一个进程访问需要用同步机制来实现对临界资源的访问
互斥共享的资源称为临界资源例如打印机等在同一时刻只允许一个进程访问需要用同步机制来实现互斥访问
## 3. 虚拟
虚拟技术把一个物理实体转换为多个逻辑实体
主要有两种虚拟技术分复用技术和空分复用技术
主要有两种虚拟技术时间分复用技术和空空间分复用技术
多个进程能在同一个处理器上并发执行使用了时分复用技术让每个进程轮流占处理器每次只执行一小个时间片并快速切换
多个进程能在同一个处理器上并发执行使用了时分复用技术让每个进程轮流占处理器每次只执行一小个时间片并快速切换
虚拟内存使用了空分复用技术它将物理内存抽象为地址空间每个进程都有各自的地址空间地址空间的页被映射到物理内存地址空间的页并不需要全部在物理内存中当使用到一个没有在物理内存的页时执行页面置换算法将该页置换到内存中

View File

@ -131,7 +131,7 @@ public class Singleton {
}
```
考虑下面的实现也就是只使用了一个 if 语句 uniqueInstance == null 的情况下如果两个线程都执行了 if 语句那么两个线程都会进入 if 语句块内虽然在 if 语句块内有加锁操作但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句只是先后的问题那么就会进行两次实例化因此必须使用双重校验锁也就是需要使用两个 if 语句
考虑下面的实现也就是只使用了一个 if 语句 uniqueInstance == null 的情况下如果两个线程都执行了 if 语句那么两个线程都会进入 if 语句块内虽然在 if 语句块内有加锁操作但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句只是先后的问题那么就会进行两次实例化因此必须使用双重校验锁也就是需要使用两个 if 语句第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作而第二个 if 语句进行了加锁所以只能有一个线程进入就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作
```java
if (uniqueInstance == null) {
@ -153,7 +153,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueIn
#### 静态内部类实现
Singleton 加载时静态内部类 SingletonHolder 没有被加载进内存只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` SingletonHolder 才会被加载此时初始化 INSTANCE 实例并且 JVM 能确保 INSTANCE 只被实例化一次
Singleton 加载时静态内部类 SingletonHolder 没有被加载进内存只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` SingletonHolder 才会被加载此时初始化 INSTANCE 实例并且 JVM 能确保 INSTANCE 只被实例化一次
这种方式不仅具有延迟初始化的好处而且由 JVM 提供了对线程安全的支持
@ -224,10 +224,10 @@ secondName
secondName
```
该实现在多次序列化再进行反序列化之后不会得到多个实例而其它实现需要使用 transient 修饰所有字段并且实现序列化和反序列化的方法
该实现可以防止反射攻击在其它实现中通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public然后调用构造函数从而实例化对象如果要防止这种攻击需要在构造函数中添加防止多次实例化的代码该实现是由 JVM 保证只会实例化一次因此不会出现上述的反射攻击
该实现在多次序列化和序列化之后不会得到多个实例而其它实现需要使用 transient 修饰所有字段并且实现序列化和反序列化的方法
### Examples
- Logger Classes

View File

@ -21,13 +21,13 @@
## 封装
利用抽象数据类型将数据和基于数据的操作封装在一起使其构成一个不可分割的独立实体数据被保护在抽象数据类型的内部尽可能地隐藏内部的细节只保留一些对外接口使之与外部发生联系用户无需知道对象内部的细节但可以通过对象对外提供的接口来访问该对象
利用抽象数据类型将数据和基于数据的操作封装在一起使其构成一个不可分割的独立实体数据被保护在抽象数据类型的内部尽可能地隐藏内部的细节只保留一些对外的接口使其与外部发生联系用户无需关心对象内部的细节但可以通过对象对外提供的接口来访问该对象
优点
- 减少耦合可以独立地开发测试优化使用理解和修改
- 减轻维护的负担可以更容易被程序员理解并且在调试的时候可以不影响其他模块
- 有效地调节性能可以通过剖析确定哪些模块影响了系统的性能
- 有效地调节性能可以通过剖析确定哪些模块影响了系统的性能
- 提高软件的可重用性
- 降低了构建大型系统的风险即使整个系统不可用但是这些独立的模块却有可能是可用的
@ -94,21 +94,27 @@ public class Instrument {
System.out.println("Instument is playing...");
}
}
```
```java
public class Wind extends Instrument {
public void play() {
System.out.println("Wind is playing...");
}
}
```
```java
public class Percussion extends Instrument {
public void play() {
System.out.println("Percussion is playing...");
}
}
```
```java
public class Music {
public static void main(String[] args) {
@ -122,6 +128,11 @@ public class Music {
}
```
```
Wind is playing...
Percussion is playing...
```
# 类图
以下类图使用 [PlantUML](https://www.planttext.com/) 绘制更多语法及使用请参考http://plantuml.com/