Skip to content

Commit 1e63bf6

Browse files
committed
Merge pull request giantray#11 from peiquan/master
如何创建单例、 transient 关键字的作用、serialVersionUID 有什么作用?该如何使用?
2 parents bd69384 + 543df58 commit 1e63bf6

6 files changed

+428
-3
lines changed

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ stackoverflow-Java-top-qa
5757
- [Creating a memory leak with Java [closed]](http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java)
5858
- [Why is char[] preferred over String for passwords?](http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords)
5959
- [Why is printing “B” dramatically slower than printing “#”?](http://stackoverflow.com/questions/21947452/why-is-printing-b-dramatically-slower-than-printing)
60-
- [What is a serialVersionUID and why should I use it?](http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it)
6160
- [Is there a unique Android device ID?](http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id)
6261
- [Why does this code using random strings print “hello world”?](http://stackoverflow.com/questions/15182496/why-does-this-code-using-random-strings-print-hello-world)
6362
- [How can I create an executable jar with dependencies using Maven?](http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven)
@@ -82,7 +81,6 @@ stackoverflow-Java-top-qa
8281
- [What's the simplest way to print a Java array?](http://stackoverflow.com/questions/409784/whats-the-simplest-way-to-print-a-java-array)
8382
- [Why can't I switch on a String?](http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string)
8483
- [How to create a Java String from the contents of a file?](http://stackoverflow.com/questions/326390/how-to-create-a-java-string-from-the-contents-of-a-file)
85-
- [Why does Java have transient variables?](http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables)
8684
- [How can I convert a stack trace to a string?](http://stackoverflow.com/questions/1149703/how-can-i-convert-a-stack-trace-to-a-string)
8785
- [How do you assert that a certain exception is thrown in JUnit 4 tests?](http://stackoverflow.com/questions/156503/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests)
8886
- [What exactly is Apache Camel?](http://stackoverflow.com/questions/8845186/what-exactly-is-apache-camel)
@@ -120,7 +118,6 @@ stackoverflow-Java-top-qa
120118
- [What is the equivalent of the C++ Pair<L,R> in Java?](http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java)
121119
- [What is the difference between JSF, Servlet and JSP?](http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp)
122120
- [How do I “decompile” Java class files?](http://stackoverflow.com/questions/272535/how-do-i-decompile-java-class-files)
123-
- [What is an efficient way to implement a singleton pattern in Java?](http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java)
124121
- [Useful Eclipse Java Code Templates [closed]](http://stackoverflow.com/questions/1028858/useful-eclipse-java-code-templates)
125122
- [Which @NotNull Java annotation should I use?](http://stackoverflow.com/questions/4963300/which-notnull-java-annotation-should-i-use)
126123
- [How to call SOAP web service in Android](http://stackoverflow.com/questions/297586/how-to-call-soap-web-service-in-android)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Java 源码里的设计模式
2+
3+
[维基百科](https://en.wikipedia.org/wiki/Software_design_pattern#Classification_and_list) 中,可以让你对大部分设计模式有一个概览,而且它页指出了那些设计模式是 GoF 中规范.下面列出可以从 JavaSE 和 JavaEE API 中找到的设计模式:
4+
5+
## 创建型模式
6+
7+
### 抽象工厂
8+
9+
- [javax.xml.parsers.DocumentBuilderFactory#newInstance()](http://docs.oracle.com/javase/6/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#newInstance%28%29)
10+
- [javax.xml.transform.TransformerFactory#newInstance()](http://docs.oracle.com/javase/6/docs/api/javax/xml/transform/TransformerFactory.html#newInstance%28%29)
11+
- [javax.xml.xpath.XPathFactory#newInstance()](http://docs.oracle.com/javase/6/docs/api/javax/xml/xpath/XPathFactory.html#newInstance%28%29)
12+
13+
### 建造者模式
14+
15+
- [java.lang.StringBuilder#append()](http://docs.oracle.com/javase/6/docs/api/java/lang/StringBuilder.html#append%28boolean%29)(非同步)
16+
- [java.lang.StringBuffer#append()](http://docs.oracle.com/javase/6/docs/api/java/lang/StringBuffer.html#append%28boolean%29)(同步)
17+
- [java.nio.ByteBuffer#put()](http://docs.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html#put%28byte%29)(类似的还有, [CharBuffer](http://docs.oracle.com/javase/6/docs/api/java/nio/CharBuffer.html#put%28char%29), [ShortBuffer](http://docs.oracle.com/javase/6/docs/api/java/nio/ShortBuffer.html#put%28short%29), [IntBuffer](http://docs.oracle.com/javase/6/docs/api/java/nio/IntBuffer.html#put%28int%29), [LongBuffer](http://docs.oracle.com/javase/6/docs/api/java/nio/LongBuffer.html#put%28long%29), [FloatBuffer](http://docs.oracle.com/javase/6/docs/api/java/nio/FloatBuffer.html#put%28float%29)[DoubleBuffer](http://docs.oracle.com/javase/6/docs/api/java/nio/DoubleBuffer.html#put%28double%29))
18+
- [javax.swing.GroupLayout.Group#addComponent()](http://docs.oracle.com/javase/6/docs/api/javax/swing/GroupLayout.Group.html#addComponent%28java.awt.Component%29)
19+
20+
### 工厂模式
21+
22+
- [java.util.Calendar#getInstance()](http://docs.oracle.com/javase/6/docs/api/java/util/Calendar.html#getInstance%28%29)
23+
- [java.util.ResourceBundle#getBundle()](http://docs.oracle.com/javase/6/docs/api/java/util/ResourceBundle.html#getBundle%28java.lang.String%29)
24+
- [java.text.NumberFormat#getInstance()](http://docs.oracle.com/javase/6/docs/api/java/text/NumberFormat.html#getInstance%28%29)
25+
- [java.nio.charset.Charset#forName()](http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html#forName%28java.lang.String%29)
26+
- [java.net.URLStreamHandlerFactory#createURLStreamHandler(String)](http://docs.oracle.com/javase/6/docs/api/java/net/URLStreamHandlerFactory.html)
27+
28+
### 原型模式
29+
30+
- [java.lang.Object#clone()](http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#clone%28%29)(类需要实现 [java.lang.Cloneable](http://docs.oracle.com/javase/6/docs/api/java/lang/Cloneable.html) 接口)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# 如何产生一个随机的字母数字串作为 session 的唯一标识符?
2+
3+
如果允许产生的随机字符串是可猜测的(随机字符串比较都短,或者使用有缺陷的随机数生成器),进而导致攻击者可能会劫持到会话的,可以使用一个相对简单随机数生成代码,如下所示:
4+
```
5+
public class RandomString {
6+
7+
private static final char[] symbols;
8+
9+
static {
10+
StringBuilder tmp = new StringBuilder();
11+
for (char ch = '0'; ch <= '9'; ++ch)
12+
tmp.append(ch);
13+
for (char ch = 'a'; ch <= 'z'; ++ch)
14+
tmp.append(ch);
15+
symbols = tmp.toString().toCharArray();
16+
}
17+
18+
private final Random random = new Random();
19+
20+
private final char[] buf;
21+
22+
public RandomString(int length) {
23+
if (length < 1)
24+
throw new IllegalArgumentException("length < 1: " + length);
25+
buf = new char[length];
26+
}
27+
28+
public String nextString() {
29+
for (int idx = 0; idx < buf.length; ++idx)
30+
buf[idx] = symbols[random.nextInt(symbols.length)];
31+
return new String(buf);
32+
}
33+
}
34+
```
35+
36+
为了安全,可以考虑使用下面这段简洁且安全的代码,不过用其作为 session 的标识符,可能略显昂贵了一点:
37+
```
38+
```
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# serialVersionUID 有什么作用?该如何使用?
2+
3+
当一个对象实现 Serializable 接口时,多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识),那 serialVersionUID 到底有什么作用呢?应该如何使用 serialVersionUID ?
4+
5+
serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则是应用于Java 对象序列化/反序列化。对象的序列化主要有两种用途:
6+
7+
- 把对象序列化成字节码,保存到指定介质上(如磁盘等)
8+
- 用于网络传输
9+
10+
现在反过来说就是,serialVersionUID 会影响到上述所提到的两种行为。那到底会造成什么影响呢?
11+
12+
[java.io.Serializable](http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html) doc 文档,给出了一个相对详细解释:
13+
14+
serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。序列化的类可显式声明 serialVersionUID 的值,如下:
15+
```
16+
ANY-ACCESS-MODIFIER static final long serialVersionUID = 1L;
17+
```
18+
19+
文档上还建议将 serialVersionUID 置为 private。
20+
21+
举例说明如下:
22+
现在尝试通过将一个类 Person 序列化到磁盘和反序列化来说明 serialVersionUID 的作用: Person 类如下:
23+
```
24+
public class Person implements Serializable {
25+
26+
private static final long serialVersionUID = 1L;
27+
28+
private String name;
29+
private Integer age;
30+
private String address;
31+
32+
public Person() {
33+
}
34+
35+
public Person(String name, Integer age, String address) {
36+
this.name = name;
37+
this.age = age;
38+
this.address = address;
39+
}
40+
41+
42+
@Override
43+
public String toString() {
44+
return "Person{" +
45+
"name='" + name + '\'' +
46+
", age=" + age +
47+
", address='" + address + '\'' +
48+
'}';
49+
}
50+
}
51+
```
52+
53+
简单的测试一下:
54+
```
55+
@Test
56+
public void testversion1L() throws Exception {
57+
File file = new File("person.out");
58+
// 序列化
59+
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
60+
Person person = new Person("John", 21, "广州");
61+
oout.writeObject(person);
62+
oout.close();
63+
// 反序列化
64+
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
65+
Object newPerson = oin.readObject();
66+
oin.close();
67+
System.out.println(newPerson);
68+
}
69+
```
70+
71+
测试发现没有什么问题。有一天,因发展需要, 需要在 Person 中增加了一个字段 email,如下:
72+
```
73+
public class Person implements Serializable {
74+
75+
private static final long serialVersionUID = 1L;
76+
77+
private String name;
78+
private Integer age;
79+
private String address;
80+
private String email;
81+
82+
public Person() {
83+
}
84+
85+
public Person(String name, Integer age, String address) {
86+
this.name = name;
87+
this.age = age;
88+
this.address = address;
89+
}
90+
91+
public Person(String name, Integer age, String address,String email) {
92+
this.name = name;
93+
this.age = age;
94+
this.address = address;
95+
this.email = email;
96+
}
97+
98+
@Override
99+
public String toString() {
100+
return "Person{" +
101+
"name='" + name + '\'' +
102+
", age=" + age +
103+
", address='" + address + '\'' +
104+
", email='" + email + '\'' +
105+
'}';
106+
}
107+
}
108+
```
109+
110+
这时我们假设和之前序列化到磁盘的 Person 类是兼容的,便不修改版本标识 serialVersionUID。再次测试如下
111+
```
112+
@Test
113+
public void testversion1LWithExtraEmail() throws Exception {
114+
File file = new File("person.out");
115+
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
116+
Object newPerson = oin.readObject();
117+
oin.close();
118+
System.out.println(newPerson);
119+
}
120+
```
121+
将以前序列化到磁盘的旧 Person 反序列化到新 Person 类时,没有任何问题。
122+
123+
可当我们增加 email 字段后,不作向后兼容。即放弃原来序列化到磁盘的 Person 类,这时我们可以将版本标识提高,如下:
124+
```
125+
private static final long serialVersionUID = 2L;
126+
```
127+
128+
再次进行序列化,则会报错,如下:
129+
```
130+
java.io.InvalidClassException:Person local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
131+
```
132+
133+
谈到这里,我们大概可以清楚,serialVersionUID 就是控制版本是否兼容的,若我们认为修改的 Person 是向后兼容的,则不修改 serialVersionUID;反之,则提高 serialVersionUID的值。再回到一开始的问题,为什么 ide 会提示声明 serialVersionUID 的值呢?
134+
135+
因为若不显式定义 serialVersionUID 的值,Java 会根据类细节自动生成 serialVersionUID 的值,如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,也有可能会导致不同的serialVersionUID。所以 ide 才会提示声明 serialVersionUID 的值。
136+
137+
附录拓展:
138+
139+
- [深入理解 Java 对象序列化](http://developer.51cto.com/art/201202/317181.htm)
140+
- [对象的序列化和反序列化](http://www.blogjava.net/lingy/archive/2008/10/10/233630.html)
141+
142+
stackoverflow原址:http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it

0 commit comments

Comments
 (0)