Skip to content

Commit 893c4a5

Browse files
author
hollis.zhl
committed
知识完善
1 parent 455d88c commit 893c4a5

File tree

8 files changed

+988
-6
lines changed

8 files changed

+988
-6
lines changed

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Java如何实现的平台无关
4040

4141
Java的继承与实现
4242

43+
[Java的继承与组合](/basics/java-basic/inheritance-composition.md)
44+
4345
构造函数与默认构造函数
4446

4547
[类变量、成员变量和局部变量](/basics/java-basic/variable.md)
@@ -70,7 +72,9 @@ Java的继承与实现
7072

7173
replaceFirst、replaceAll、replace区别、
7274

73-
String对“+”的重载、字符串拼接的几种方式和区别
75+
String对“+”的重载
76+
77+
[字符串拼接的几种方式和区别](/basics/java-basic/string-concat.md)
7478

7579
String.valueOf和Integer.toString的区别、
7680

@@ -84,11 +88,11 @@ transient、instanceof、volatile、synchronized、final、static、const 原理
8488

8589
#### 集合类
8690

87-
常用集合类的使用、ArrayList和LinkedList和Vector的区别 、SynchronizedList和Vector的区别、HashMap、HashTable、ConcurrentHashMap区别、
91+
常用集合类的使用、ArrayList和LinkedList和Vector的区别 、[SynchronizedList和Vector的区别](/basics/java-basic/synchronizedlist-vector.md)、HashMap、HashTable、ConcurrentHashMap区别、
8892

8993
Set和List区别?Set如何保证元素不重复?
9094

91-
Java 8中stream相关用法、apache集合处理工具类的使用、不同版本的JDK中HashMap的实现的区别以及原因
95+
[Java 8中stream相关用法](/basics/java-basic/stream.md)、apache集合处理工具类的使用、不同版本的JDK中HashMap的实现的区别以及原因
9296

9397
Collection和Collections区别
9498

@@ -108,7 +112,7 @@ Java枚举如何比较
108112

109113
switch对枚举的支持
110114

111-
枚举的序列化如何实现
115+
[枚举的序列化如何实现](/basics/java-basic/enum-serializable.md)
112116

113117
枚举的线程安全性问题
114118

@@ -158,7 +162,7 @@ Spring常用注解
158162

159163
#### 泛型
160164

161-
泛型与继承、类型擦除、泛型中K T V E ? object等的含义、泛型各种用法
165+
泛型与继承、类型擦除、泛型中K T V E ? [object等的含义](/basics/java-basic/k-t-v-e.md)、泛型各种用法
162166

163167
限定通配符和非限定通配符、上下界限定符extends 和 super
164168

@@ -268,7 +272,7 @@ CAS、乐观锁与悲观锁、数据库相关锁机制、分布式锁、偏向
268272

269273
#### synchronized
270274

271-
synchronized是如何实现的?
275+
[synchronized是如何实现的?](/basics/java-basic/synchronized.md)
272276

273277
synchronized和lock之间关系、不使用synchronized如何实现一个线程安全的单例
274278

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
> 写在前面:Java SE5提供了一种新的类型-<a href="/archives/195" target="_blank">Java的枚举类型</a>,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。本文将深入分析枚举的源码,看一看枚举是怎么实现的,他是如何保证线程安全的,以及为什么用枚举实现的单例是最好的方式。
2+
3+
<!--more-->
4+
5+
### 枚举是如何保证线程安全的
6+
7+
要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关键字,他并不是一个类,那么枚举是由什么类维护的呢,我们简单的写一个枚举:
8+
9+
public enum t {
10+
SPRING,SUMMER,AUTUMN,WINTER;
11+
}
12+
13+
14+
然后我们使用反编译,看看这段代码到底是怎么实现的,反编译(<a href="/archives/58" target="_blank">Java的反编译</a>)后代码内容如下:
15+
16+
public final class T extends Enum
17+
{
18+
private T(String s, int i)
19+
{
20+
super(s, i);
21+
}
22+
public static T[] values()
23+
{
24+
T at[];
25+
int i;
26+
T at1[];
27+
System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i);
28+
return at1;
29+
}
30+
31+
public static T valueOf(String s)
32+
{
33+
return (T)Enum.valueOf(demo/T, s);
34+
}
35+
36+
public static final T SPRING;
37+
public static final T SUMMER;
38+
public static final T AUTUMN;
39+
public static final T WINTER;
40+
private static final T ENUM$VALUES[];
41+
static
42+
{
43+
SPRING = new T("SPRING", 0);
44+
SUMMER = new T("SUMMER", 1);
45+
AUTUMN = new T("AUTUMN", 2);
46+
WINTER = new T("WINTER", 3);
47+
ENUM$VALUES = (new T[] {
48+
SPRING, SUMMER, AUTUMN, WINTER
49+
});
50+
}
51+
}
52+
53+
54+
通过反编译后代码我们可以看到,`public final class T extends Enum`,说明,该类是继承了Enum类的,同时final关键字告诉我们,这个类也是不能被继承的。当我们使用`enmu`来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,所以枚举类型不能被继承,我们看到这个类中有几个属性和方法。
55+
56+
我们可以看到:
57+
58+
public static final T SPRING;
59+
public static final T SUMMER;
60+
public static final T AUTUMN;
61+
public static final T WINTER;
62+
private static final T ENUM$VALUES[];
63+
static
64+
{
65+
SPRING = new T("SPRING", 0);
66+
SUMMER = new T("SUMMER", 1);
67+
AUTUMN = new T("AUTUMN", 2);
68+
WINTER = new T("WINTER", 3);
69+
ENUM$VALUES = (new T[] {
70+
SPRING, SUMMER, AUTUMN, WINTER
71+
});
72+
}
73+
74+
75+
都是static类型的,因为static类型的属性会在类被加载之后被初始化,我们在<a href="/archives/199" target="_blank">深度分析Java的ClassLoader机制(源码级别)</a>和<a href="/archives/201" target="_blank">Java类的加载、链接和初始化</a>两个文章中分别介绍过,当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,**创建一个enum类型是线程安全的**
76+
77+
### 为什么用枚举实现的单例是最好的方式
78+
79+
在<a href="/archives/205" target="_blank">[转+注]单例模式的七种写法</a>中,我们看到一共有七种实现单例的方式,其中,**Effective Java**作者`Josh Bloch` 提倡使用枚举的方式,既然大神说这种方式好,那我们就要知道它为什么好?
80+
81+
**1\. 枚举写法简单**
82+
83+
> 写法简单这个大家看看<a hrerf="/archives/205" target="_blank">[转+注]单例模式的七种写法</a>里面的实现就知道区别了。
84+
85+
public enum EasySingleton{
86+
INSTANCE;
87+
}
88+
89+
90+
你可以通过`EasySingleton.INSTANCE`来访问。
91+
92+
**2\. 枚举自己处理序列化**
93+
94+
> 我们知道,以前的所有的单例模式都有一个比较大的问题,就是一旦实现了Serializable接口之后,就不再是单例得了,因为,每次调用 readObject()方法返回的都是一个新创建出来的对象,有一种解决办法就是使用readResolve()方法来避免此事发生。但是,**为了保证枚举类型像Java规范中所说的那样,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定。**原文如下:
95+
>
96+
> > Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant's name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant's enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream. The process by which enum constants are serialized cannot be customized: any class-specific writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods defined by enum types are ignored during serialization and deserialization. Similarly, any serialPersistentFields or serialVersionUID field declarations are also ignored--all enum types have a fixedserialVersionUID of 0L. Documenting serializable fields and data for enum types is unnecessary, since there is no variation in the type of data sent.
97+
>
98+
> 大概意思就是说,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。 我们看一下这个`valueOf`方法:
99+
100+
public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {
101+
T result = enumType.enumConstantDirectory().get(name);
102+
if (result != null)
103+
return result;
104+
if (name == null)
105+
throw new NullPointerException("Name is null");
106+
throw new IllegalArgumentException(
107+
"No enum const " + enumType +"." + name);
108+
}
109+
110+
111+
从代码中可以看到,代码会尝试从调用`enumType`这个`Class`对象的`enumConstantDirectory()`方法返回的`map`中获取名字为`name`的枚举对象,如果不存在就会抛出异常。再进一步跟到`enumConstantDirectory()`方法,就会发现到最后会以反射的方式调用`enumType`这个类型的`values()`静态方法,也就是上面我们看到的编译器为我们创建的那个方法,然后用返回结果填充`enumType`这个`Class`对象中的`enumConstantDirectory`属性。
112+
113+
所以,**JVM对序列化有保证。**
114+
115+
**3\.枚举实例创建是thread-safe(线程安全的)**
116+
117+
> 我们在<a href="/archives/199" target="_blank">深度分析Java的ClassLoader机制(源码级别)</a>和<a href="/archives/201" target="_blank">Java类的加载、链接和初始化</a>两个文章中分别介绍过,当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。所以,**创建一个enum类型是线程安全的**
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
Java是一个面向对象的语言。每一个学习过Java的人都知道,封装、继承、多态是面向对象的三个特征。每个人在刚刚学习继承的时候都会或多或少的有这样一个印象:继承可以帮助我实现类的复用。所以,很多开发人员在需要复用一些代码的时候会很自然的使用类的继承的方式,因为书上就是这么写的(老师就是这么教的)。但是,其实这样做是不对的。长期大量的使用继承会给代码带来很高的维护成本。
2+
3+
本文将介绍组合和继承的概念及区别,并从多方面分析在写代码时如何进行选择。
4+
5+
## 面向对象的复用技术
6+
7+
前面提到复用,这里就简单介绍一下面向对象的复用技术。
8+
9+
复用性是面向对象技术带来的很棒的潜在好处之一。如果运用的好的话可以帮助我们节省很多开发时间,提升开发效率。但是,如果被滥用那么就可能产生很多难以维护的代码。
10+
11+
作为一门面向对象开发的语言,代码复用是Java引人注意的功能之一。Java代码的复用有继承,组合以及代理三种具体的表现形式。本文将重点介绍继承复用和组合复用。
12+
13+
## 继承
14+
15+
继承(Inheritance)是一种联结类与类的层次模型。指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;继承是一种[`is-a`][1]关系。(图片来自网络,侵删。)
16+
17+
![Inheritance][2]
18+
19+
## 组合
20+
21+
组合(Composition)体现的是整体与部分、拥有的关系,即[`has-a`][3]的关系。
22+
23+
![Composition][4]
24+
25+
## 组合与继承的区别和联系
26+
27+
> `继承`结构中,父类的内部细节对于子类是可见的。所以我们通常也可以说通过继承的代码复用是一种`白盒式代码复用`。(如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性;)
28+
>
29+
> `组合`是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以我们也说这种方式的代码复用是`黑盒式代码复用`。(因为组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法)
30+
>
31+
> `继承`,在写代码的时候就要指名具体继承哪个类,所以,在`编译期`就确定了关系。(从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。)
32+
>
33+
> `组合`,在写代码的时候可以采用面向接口编程。所以,类的组合关系一般在`运行期`确定。
34+
35+
## 优缺点对比
36+
37+
| 组 合 关 系 | 继 承 关 系 |
38+
| -------------------------------- | -------------------------------------- |
39+
| 优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 | 缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性 |
40+
| 优点:具有较好的可扩展性 | 缺点:支持扩展,但是往往以增加系统结构的复杂度为代价 |
41+
| 优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 | 缺点:不支持动态继承。在运行时,子类无法选择不同的父类 |
42+
| 优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 | 缺点:子类不能改变父类的接口 |
43+
| 缺点:整体类不能自动获得和局部类同样的接口 | 优点:子类能自动继承父类的接口 |
44+
| 缺点:创建整体类的对象时,需要创建所有局部类的对象 | 优点:创建子类的对象时,无须创建父类的对象 |
45+
46+
## 如何选择
47+
48+
相信很多人都知道面向对象中有一个比较重要的原则『多用组合、少用继承』或者说『组合优于继承』。从前面的介绍已经优缺点对比中也可以看出,组合确实比继承更加灵活,也更有助于代码维护。
49+
50+
所以,
51+
52+
> **`建议在同样可行的情况下,优先使用组合而不是继承。`**
53+
>
54+
> **`因为组合更安全,更简单,更灵活,更高效。`**
55+
56+
注意,并不是说继承就一点用都没有了,前面说的是【在同样可行的情况下】。有一些场景还是需要使用继承的,或者是更适合使用继承。
57+
58+
> 继承要慎用,其使用场合仅限于你确信使用该技术有效的情况。一个判断方法是,问一问自己是否需要从新类向基类进行向上转型。如果是必须的,则继承是必要的。反之则应该好好考虑是否需要继承。《[Java编程思想][5]
59+
>
60+
> 只有当子类真正是超类的子类型时,才适合用继承。换句话说,对于两个类A和B,只有当两者之间确实存在[`is-a`][1]关系的时候,类B才应该继续类A。《[Effective Java][6]
61+
62+
[1]: https://zh.wikipedia.org/wiki/Is-a
63+
[2]: http://www.hollischuang.com/wp-content/uploads/2016/03/Generalization.jpg
64+
[3]: https://en.wikipedia.org/wiki/Has-a
65+
[4]: http://www.hollischuang.com/wp-content/uploads/2016/03/Composition.jpg
66+
[5]: http://s.click.taobao.com/t?e=m%3D2%26s%3DHzJzud6zOdocQipKwQzePOeEDrYVVa64K7Vc7tFgwiHjf2vlNIV67vo5P8BMUBgoEC56fBbgyn5pS4hLH%2FP02ckKYNRBWOBBey11vvWwHXSniyi5vWXIZhtlrJbLMDAQihpQCXu2JnPFYKQlNeOGCsYMXU3NNCg%2F&pvid=10_125.119.86.125_222_1458652212179
67+
[6]: http://s.click.taobao.com/t?e=m%3D2%26s%3DwIPn8%2BNPqLwcQipKwQzePOeEDrYVVa64K7Vc7tFgwiHjf2vlNIV67vo5P8BMUBgoUOZr0mLjusdpS4hLH%2FP02ckKYNRBWOBBey11vvWwHXSniyi5vWXIZvgXwmdyquYbNLnO%2BjzYQLqKnzbV%2FMLqnMYMXU3NNCg%2F&pvid=10_125.119.86.125_345_1458652241780

basics/java-basic/k-t-v-e.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
E - Element (在集合中使用,因为集合中存放的是元素)
2+
3+
T - Type(Java 类)
4+
5+
K - Key(键)
6+
7+
V - Value(值)
8+
9+
N - Number(数值类型)
10+
11+
? - 表示不确定的java类型(无限制通配符类型)
12+
13+
S、U、V - 2nd、3rd、4th types
14+
15+
Object - 是所有类的根类,任何类的对象都可以设置给该Object引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型T、E等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换。

0 commit comments

Comments
 (0)