From abe92503ab537d28d812469c151c1557fab645da Mon Sep 17 00:00:00 2001 From: zhupeiquan Date: Sat, 12 Sep 2015 16:47:32 +0800 Subject: [PATCH 1/8] What_is_an_efficient_way_to_implement_a_singleton_in_Java.md --- ...nt_way_to_implement_a_singleton_in_Java.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md diff --git a/contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md b/contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md new file mode 100644 index 0000000..6349219 --- /dev/null +++ b/contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md @@ -0,0 +1,164 @@ +# 如何创建单例 ? + +## 问题 +Java 创建单例有哪些方式 ? + +## 解答 +实现单例,从加载方式来看,有两种: + +- 预加载 +- 懒加载 + +先看一下实现单例最简单的方式(预加载): +``` +public class Foo { + + private static final Foo INSTANCE = new Foo(); + + private Foo() { + if (INSTANCE != null) { + throw new IllegalStateException("Already instantiated"); + } + } + + public static Foo getInstance() { + return INSTANCE; + } +} +``` + +再来看一下懒加载的方式: +``` +class Foo { + + private static Foo INSTANCE = null; + + private Foo() { + if (INSTANCE != null) { + throw new IllegalStateException("Already instantiated"); + } + } + + public static Foo getInstance() { + if (INSTANCE == null) { + INSTANCE = new Foo(); + } + return INSTANCE; + } +} +``` + +以上方式在单线程的情况可以很好的满足需要,换言之,若是在多线程,还需要作一定的改进,如下所示: +``` +class Foo { + // 请注意 volatile 关键字的使用 + private static volatile Foo INSTANCE = null; + + private Foo() { + if (INSTANCE != null) { + throw new IllegalStateException("Already instantiated"); + } + } + + public static Foo getInstance() { + if (INSTANCE == null) { // Check 1 + synchronized (Foo.class) { + if (INSTANCE == null) { // Check 2 + INSTANCE = new Foo(); + } + } + } + return INSTANCE; + } +} +``` + +上述代码运用了 [Double-Checked Locking idiom](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)。 + +解决了多线程环境下的单例,可以进一步思考如何实现可序列化的单例 ? 反序列化可以不通过构造函数直接生成一个对象,所以反序列化时,我们需要保证其不再创建新的对象。 + +``` +class Foo implements Serializable { + + private static final long serialVersionUID = 1L; + + private static volatile Foo INSTANCE = null; + + private Foo() { + if (INSTANCE != null) { + throw new IllegalStateException("Already instantiated"); + } + } + + public static Foo getInstance() { + if (INSTANCE == null) { // Check 1 + synchronized (Foo.class) { + if (INSTANCE == null) { // Check 2 + INSTANCE = new Foo(); + } + } + } + return INSTANCE; + } + + @SuppressWarnings("unused") + private Foo readResolve() { + return INSTANCE; + } +} +``` + +readResolve 方法可以保证,即使程序在上一次运行时序列化过此单例,也只会返回全局唯一的单例。对于 Java 对象序列化机制,可参考[附录拓展](#appendix)。 + +java 创建单例的方法基本实现了,不过我们还可以作进一步的改进 —— 代码重构: +``` +public final class Foo implements Serializable { + + private static final long serialVersionUID = 1L; + + // 使用内部静态 class 实现懒加载 + private static class FooLoader { + // 保证在多线程环境下无差错运行 + private static final Foo INSTANCE = new Foo(); + } + + private Foo() { + if (INSTANCE != null) { + throw new IllegalStateException("Already instantiated"); + } + } + + public static Foo getInstance() { + return FooLoader.INSTANCE; + } + + @SuppressWarnings("unused") + private Foo readResolve() { + return FooLoader.INSTANCE; + } +} + +好了,现在已经很完美实现了单例的创建,是不是很高兴。单例实线的基本原理,我们已经基本清楚里,最后提供一种更加简洁方法,如下: +``` +public enum Foo { + INSTANCE; +} +``` + +为什么可以这么简洁?因为 Java 中每一个枚举类型都默认继承了 java.lang.Enum ,而 Enum 实现了 Serializable 接口,所以枚举类型对象都是默认可以被序列化的。通过反编译,也可以知道枚举常量本质上就是一个 +``` +public static final xxx +```` + + + +对于枚举的进一步理解,请参考[附录拓展](#appendix)。 + + +附录拓展: +[深入理解 Java 对象序列化](http://developer.51cto.com/art/201202/317181.htm) +[对象的序列化和反序列化](http://www.blogjava.net/lingy/archive/2008/10/10/233630.html) +[通过反编译字节码来理解 Java 枚举](http://unmi.cc/understand-java-enum-with-bytecode/) + +stackoverflow原址:http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java + From 5cce1f131f5d1219e1da67a5702d921669155341 Mon Sep 17 00:00:00 2001 From: zhupeiquan Date: Sat, 12 Sep 2015 16:54:08 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E5=A6=82=E4=BD=95=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E5=8D=95=E4=BE=8B=E6=A0=BC=E5=BC=8F=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...hat_is_an_efficient_way_to_implement_a_singleton_in_Java.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md b/contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md index 6349219..f81fe32 100644 --- a/contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md +++ b/contents/What_is_an_efficient_way_to_implement_a_singleton_in_Java.md @@ -137,6 +137,7 @@ public final class Foo implements Serializable { return FooLoader.INSTANCE; } } +``` 好了,现在已经很完美实现了单例的创建,是不是很高兴。单例实线的基本原理,我们已经基本清楚里,最后提供一种更加简洁方法,如下: ``` @@ -150,8 +151,6 @@ public enum Foo { public static final xxx ```` - - 对于枚举的进一步理解,请参考[附录拓展](#appendix)。 From 467a3c9879c0bf69a89a21fc2b663d837f0a7ecd Mon Sep 17 00:00:00 2001 From: zhupeiquan Date: Sat, 12 Sep 2015 18:32:39 +0800 Subject: [PATCH 3/8] What is a serialVersionUID and why should I use it? --- ...erialVersionUID_and_why_should_I_use_it.md | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md diff --git a/contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md b/contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md new file mode 100644 index 0000000..34e52dd --- /dev/null +++ b/contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md @@ -0,0 +1,141 @@ +# serialVersionUID 有什么作用?该如何使用? + +当一个对象实现 Serializable 接口时,多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识),那 serialVersionUID 到底有什么作用呢?应该如何 serialVersionUID ? + +serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则是应用于Java 对象序列化/反序列化。对象的序列化主要有两种用途: + +- 把对象序列化成字节码,保存到指定介质上(如磁盘等) +- 用于网络传输 + +现在反过来说就是,serialVersionUID 会影响到上述所提到的两种行为。那到底会造成什么影响呢? + +[java.io.Serializable](http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html) doc 文档,给出了一个相对详细解释: + serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。序列化的类可显式声明 serialVersionUID 的值,如下: + ``` + ANY-ACCESS-MODIFIER static final long serialVersionUID = 1L; + ``` + +文档上还建议 serialVersionUID 置为 private。 + +举例说明如下: +现在尝试从将一个类 Person 序列化到磁盘和反序列化来说明 serialVersionUID 的作用: Person 两日如下: +``` +public class Person implements Serializable { + + private static final long serialVersionUID = 1L; + + private String name; + private Integer age; + private String address; + + public Person() { + } + + public Person(String name, Integer age, String address) { + this.name = name; + this.age = age; + this.address = address; + } + + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", age=" + age + + ", address='" + address + '\'' + + '}'; + } +} +``` + +简单的测试一下: +``` +@Test +public void testversion1L() throws Exception { + File file = new File("person.out"); + // 序列化 + ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file)); + Person person = new Person("John", 21, "广州"); + oout.writeObject(person); + oout.close(); + // 反序列化 + ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)); + Object newPerson = oin.readObject(); + oin.close(); + System.out.println(newPerson); +} +``` + +测试发现没有什么问题。有一天,因发展需要, 需要在 Person 中增加了一个字段 email,如下: +``` +public class Person implements Serializable { + + private static final long serialVersionUID = 1L; + + private String name; + private Integer age; + private String address; + private String email; + + public Person() { + } + + public Person(String name, Integer age, String address) { + this.name = name; + this.age = age; + this.address = address; + } + + public Person(String name, Integer age, String address,String email) { + this.name = name; + this.age = age; + this.address = address; + this.email = email; + } + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", age=" + age + + ", address='" + address + '\'' + + ", email='" + email + '\'' + + '}'; + } +} +``` + +这时我们假设和之前序列化到磁盘的 Person 类是兼容的,便不修改版本标识 serialVersionUID。再次测试如下 +``` +@Test +public void testversion1LWithExtraEmail() throws Exception { + File file = new File("person.out"); + ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)); + Object newPerson = oin.readObject(); + oin.close(); + System.out.println(newPerson); +} +``` +将以前序列化到磁盘的旧 Person 反序列化到新 Person 类时,没有任何问题。 + +可当我们增加 email 字段后,不作向后兼容。即放弃原来序列化到磁盘的 Person 类,这时我们可以将版本标识提高,如下: +``` +private static final long serialVersionUID = 2L; +``` + +再次进行序列化,则会报错,如下: +``` +java.io.InvalidClassException:Person local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 +``` + +谈到这里,我们大概可以清楚,serialVersionUID 就是控制版本是否兼容的,若我们认为修改的 Person 是向后兼容的,则不修改 serialVersionUID;反之,则提高 serialVersionUID的值。再回到一开始的问题,为什么 ide 会提示声明 serialVersionUID 的值呢? + +因为若不显式定义 serialVersionUID 的值,Java 会根据类的细节自动生成 serialVersionUID 的值,如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,也有可能会导致不同的serialVersionUID。所以 ide 才会提示声明 serialVersionUID 的值。 + +附录拓展: + +- [深入理解 Java 对象序列化](http://developer.51cto.com/art/201202/317181.htm) +- [对象的序列化和反序列化](http://www.blogjava.net/lingy/archive/2008/10/10/233630.html) + +stackoverflow原址:http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it From e29dba38161adcf4a5a36c9142421356f27b98d2 Mon Sep 17 00:00:00 2001 From: zhupeiquan Date: Sat, 12 Sep 2015 18:37:25 +0800 Subject: [PATCH 4/8] =?UTF-8?q?What=5Fis=5Fa=5FserialVersionUID=5Fand=5Fwh?= =?UTF-8?q?y=5Fshould=5FI=5Fuse=5Fit.md=20=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...What_is_a_serialVersionUID_and_why_should_I_use_it.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md b/contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md index 34e52dd..c9ee44c 100644 --- a/contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md +++ b/contents/What_is_a_serialVersionUID_and_why_should_I_use_it.md @@ -1,6 +1,6 @@ # serialVersionUID 有什么作用?该如何使用? -当一个对象实现 Serializable 接口时,多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识),那 serialVersionUID 到底有什么作用呢?应该如何 serialVersionUID ? +当一个对象实现 Serializable 接口时,多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识),那 serialVersionUID 到底有什么作用呢?应该如何使用 serialVersionUID ? serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则是应用于Java 对象序列化/反序列化。对象的序列化主要有两种用途: @@ -10,15 +10,16 @@ serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则 现在反过来说就是,serialVersionUID 会影响到上述所提到的两种行为。那到底会造成什么影响呢? [java.io.Serializable](http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html) doc 文档,给出了一个相对详细解释: + serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。序列化的类可显式声明 serialVersionUID 的值,如下: ``` ANY-ACCESS-MODIFIER static final long serialVersionUID = 1L; ``` -文档上还建议 serialVersionUID 置为 private。 +文档上还建议将 serialVersionUID 置为 private。 举例说明如下: -现在尝试从将一个类 Person 序列化到磁盘和反序列化来说明 serialVersionUID 的作用: Person 两日如下: +现在尝试通过将一个类 Person 序列化到磁盘和反序列化来说明 serialVersionUID 的作用: Person 类如下: ``` public class Person implements Serializable { @@ -131,7 +132,7 @@ java.io.InvalidClassException:Person local class incompatible: stream classdesc 谈到这里,我们大概可以清楚,serialVersionUID 就是控制版本是否兼容的,若我们认为修改的 Person 是向后兼容的,则不修改 serialVersionUID;反之,则提高 serialVersionUID的值。再回到一开始的问题,为什么 ide 会提示声明 serialVersionUID 的值呢? -因为若不显式定义 serialVersionUID 的值,Java 会根据类的细节自动生成 serialVersionUID 的值,如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,也有可能会导致不同的serialVersionUID。所以 ide 才会提示声明 serialVersionUID 的值。 +因为若不显式定义 serialVersionUID 的值,Java 会根据类细节自动生成 serialVersionUID 的值,如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,也有可能会导致不同的serialVersionUID。所以 ide 才会提示声明 serialVersionUID 的值。 附录拓展: From 94fcbf241e0d21d3a6b3e778ceaf8b7927085b97 Mon Sep 17 00:00:00 2001 From: tianya Date: Sun, 13 Sep 2015 00:05:39 +0800 Subject: [PATCH 5/8] =?UTF-8?q?transient=20=E5=85=B3=E9=94=AE=E5=AD=97?= =?UTF-8?q?=E7=9A=84=E4=BD=9C=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../why-does-java-have-transient-variables.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 contents/why-does-java-have-transient-variables.md diff --git a/contents/why-does-java-have-transient-variables.md b/contents/why-does-java-have-transient-variables.md new file mode 100644 index 0000000..2b3d1fd --- /dev/null +++ b/contents/why-does-java-have-transient-variables.md @@ -0,0 +1,55 @@ +# transient 关键字的作用? + +[Java 语言规范](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3)中提到,transient 关键字用来说明指定属性不进行序列化. + +若要理解 transient 关键字的作用,自然需要对序列化有一定的认识. + +** 序列化 ** + + 序列化是用来持久化对象的状态 -- 将对象转化为字节码保存到指定的文件中.类似地,可以通过反序列化,将字节码还原为对象原有的状态.序列化是 Java 中一个比较重要的概念,因为在网络编程中会经常用到序列化与反序列化机制.一个相对若想在网络中传输,就必须转化为字节的形式.而 Serializable 接口就是用来标识某个类或接口可以转化为字节码,Serializable 可以认为是一个标识符,因为它没有任何的方法. + +Serializable 允许我们将一个类转化为字节码,进而在网络传输.可是,一个类中可能存在某些敏感的信息,我们是不想起网络中传输的,这时候我们就需要借助 transient 关键字了.被 transient 关键字标识的 field,不会进行序列化. + +下面通过一个例子说明 transient 关键字的作用.现假设我们需要在网络中传输 Person 类: +``` +public class Person implements Serializable{ + + private static final long serialVersionUID = 1L; + + private String name; + private String certNo; // 身份证号码 + private int age; + + public Person(String name, String certNo, int age) { + this.name = name; + this.certNo = certNo; + this.age = age; + } + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", certNo='" + certNo + '\'' + + ", age=" + age + + '}'; + } +} +``` + +若不使用 transient 关键字,反序列化时输出的信息是 : +``` +Person{name='tianya', certNo='12314', age=23} +``` + +我们知道,身份证号码属于敏感信息,并不想在网络中传输,这时我们就可以借助 transient 关键字,如下: +``` + private transient String certNo; +``` + +这个时候,通过反序列化获取的 Person 信息如下 : +``` +Person{name='tianya', certNo='null', age=23} +``` + +stackoverflow原址:http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables \ No newline at end of file From a9f6648ec301fcd51fc2e2541ed38db8838e3fc6 Mon Sep 17 00:00:00 2001 From: tianya Date: Sun, 13 Sep 2015 00:09:37 +0800 Subject: [PATCH 6/8] =?UTF-8?q?transient=20=E7=BB=86=E8=8A=82=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contents/why-does-java-have-transient-variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contents/why-does-java-have-transient-variables.md b/contents/why-does-java-have-transient-variables.md index 2b3d1fd..eb0e1b7 100644 --- a/contents/why-does-java-have-transient-variables.md +++ b/contents/why-does-java-have-transient-variables.md @@ -6,9 +6,9 @@ ** 序列化 ** - 序列化是用来持久化对象的状态 -- 将对象转化为字节码保存到指定的文件中.类似地,可以通过反序列化,将字节码还原为对象原有的状态.序列化是 Java 中一个比较重要的概念,因为在网络编程中会经常用到序列化与反序列化机制.一个相对若想在网络中传输,就必须转化为字节的形式.而 Serializable 接口就是用来标识某个类或接口可以转化为字节码,Serializable 可以认为是一个标识符,因为它没有任何的方法. +序列化是用来持久化对象的状态 -- 将对象转化为字节码保存到指定的文件中.类似地,可以通过反序列化,将字节码还原为对象原有的状态.序列化是 Java 中一个比较重要的概念,因为在网络编程中会经常用到序列化与反序列化机制.一个相对若想在网络中传输,就必须转化为字节的形式.而 Serializable 接口就是用来标识某个类或接口可以转化为字节码,Serializable 可以认为是一个标识符,因为它没有任何的方法. -Serializable 允许我们将一个类转化为字节码,进而在网络传输.可是,一个类中可能存在某些敏感的信息,我们是不想起网络中传输的,这时候我们就需要借助 transient 关键字了.被 transient 关键字标识的 field,不会进行序列化. +Serializable 允许我们将一个类转化为字节码,进而在网络传输.可是,一个类中可能存在某些敏感的信息,我们是不想在网络中传输的,这时候我们就需要借助 transient 关键字了.被 transient 关键字标识的 field,不会进行序列化. 下面通过一个例子说明 transient 关键字的作用.现假设我们需要在网络中传输 Person 类: ``` From 4b818bce012c269f5001b1bbc799fdd8508f739e Mon Sep 17 00:00:00 2001 From: tianya Date: Sun, 13 Sep 2015 00:24:07 +0800 Subject: [PATCH 7/8] update README.md --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index a379126..09f0ac3 100644 --- a/README.md +++ b/README.md @@ -50,9 +50,7 @@ stackoverflow-Java-top-qa - [Why is char[] preferred over String for passwords?](http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords) - [Generating random integers in a range with Java](http://stackoverflow.com/questions/363681/generating-random-integers-in-a-range-with-java) - [Why is printing “B” dramatically slower than printing “#”?](http://stackoverflow.com/questions/21947452/why-is-printing-b-dramatically-slower-than-printing) -- [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) - [Is there a unique Android device ID?](http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id) -- [How to test a class that has private methods, fields or inner classes](http://stackoverflow.com/questions/34571/how-to-test-a-class-that-has-private-methods-fields-or-inner-classes) - [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) - [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) - [How to avoid Java code in JSP files?](http://stackoverflow.com/questions/3177733/how-to-avoid-java-code-in-jsp-files) @@ -79,7 +77,6 @@ stackoverflow-Java-top-qa - [What's the simplest way to print a Java array?](http://stackoverflow.com/questions/409784/whats-the-simplest-way-to-print-a-java-array) - [Why can't I switch on a String?](http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string) - [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) -- [Why does Java have transient variables?](http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables) - [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) - [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) - [What is a JavaBean exactly?](http://stackoverflow.com/questions/3295496/what-is-a-javabean-exactly) @@ -119,7 +116,6 @@ stackoverflow-Java-top-qa - [What is the equivalent of the C++ Pair in Java?](http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java) - [What is the difference between JSF, Servlet and JSP?](http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp) - [How do I “decompile” Java class files?](http://stackoverflow.com/questions/272535/how-do-i-decompile-java-class-files) -- [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) - [Useful Eclipse Java Code Templates [closed]](http://stackoverflow.com/questions/1028858/useful-eclipse-java-code-templates) - [Which @NotNull Java annotation should I use?](http://stackoverflow.com/questions/4963300/which-notnull-java-annotation-should-i-use) - [How to call SOAP web service in Android](http://stackoverflow.com/questions/297586/how-to-call-soap-web-service-in-android) From 543df58f9bfa1755ede1987128d63eca31a04f0b Mon Sep 17 00:00:00 2001 From: tianya Date: Mon, 14 Sep 2015 02:02:40 +0800 Subject: [PATCH 8/8] =?UTF-8?q?Examples=5Fof=5FGoF=5FDesign=5FPatterns=5Fi?= =?UTF-8?q?n=5FJava\'s=5Fcore=5Flibraries.md=20How=5Fto=5Fgenerate=5Fa=5Fr?= =?UTF-8?q?andom=5Falpha-numeric=5Fstring.md=20=E5=90=8C=E6=AD=A5=E4=BB=A3?= =?UTF-8?q?=E7=A0=81,=E6=9A=82=E6=9C=AA=E7=BF=BB=E8=AF=91=E5=AE=8C?= =?UTF-8?q?=E6=88=90,=E8=AF=B7=E4=B8=8D=E8=A6=81=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=88=B0=20trunk=20=E5=88=86=E6=94=AF=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...esign_Patterns_in_Java's_core_libraries.md | 30 +++++++++++++++ ..._generate_a_random_alpha-numeric_string.md | 38 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 contents/Examples_of_GoF_Design_Patterns_in_Java's_core_libraries.md create mode 100644 contents/How_to_generate_a_random_alpha-numeric_string.md diff --git a/contents/Examples_of_GoF_Design_Patterns_in_Java's_core_libraries.md b/contents/Examples_of_GoF_Design_Patterns_in_Java's_core_libraries.md new file mode 100644 index 0000000..85585ed --- /dev/null +++ b/contents/Examples_of_GoF_Design_Patterns_in_Java's_core_libraries.md @@ -0,0 +1,30 @@ +# Java 源码里的设计模式 + +从 [维基百科](https://en.wikipedia.org/wiki/Software_design_pattern#Classification_and_list) 中,可以让你对大部分设计模式有一个概览,而且它页指出了那些设计模式是 GoF 中规范.下面列出可以从 JavaSE 和 JavaEE API 中找到的设计模式: + +## 创建型模式 + +### 抽象工厂 + +- [javax.xml.parsers.DocumentBuilderFactory#newInstance()](http://docs.oracle.com/javase/6/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#newInstance%28%29) +- [javax.xml.transform.TransformerFactory#newInstance()](http://docs.oracle.com/javase/6/docs/api/javax/xml/transform/TransformerFactory.html#newInstance%28%29) +- [javax.xml.xpath.XPathFactory#newInstance()](http://docs.oracle.com/javase/6/docs/api/javax/xml/xpath/XPathFactory.html#newInstance%28%29) + +### 建造者模式 + +- [java.lang.StringBuilder#append()](http://docs.oracle.com/javase/6/docs/api/java/lang/StringBuilder.html#append%28boolean%29)(非同步) +- [java.lang.StringBuffer#append()](http://docs.oracle.com/javase/6/docs/api/java/lang/StringBuffer.html#append%28boolean%29)(同步) +- [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)) +- [javax.swing.GroupLayout.Group#addComponent()](http://docs.oracle.com/javase/6/docs/api/javax/swing/GroupLayout.Group.html#addComponent%28java.awt.Component%29) + +### 工厂模式 + +- [java.util.Calendar#getInstance()](http://docs.oracle.com/javase/6/docs/api/java/util/Calendar.html#getInstance%28%29) +- [java.util.ResourceBundle#getBundle()](http://docs.oracle.com/javase/6/docs/api/java/util/ResourceBundle.html#getBundle%28java.lang.String%29) +- [java.text.NumberFormat#getInstance()](http://docs.oracle.com/javase/6/docs/api/java/text/NumberFormat.html#getInstance%28%29) +- [java.nio.charset.Charset#forName()](http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html#forName%28java.lang.String%29) +- [java.net.URLStreamHandlerFactory#createURLStreamHandler(String)](http://docs.oracle.com/javase/6/docs/api/java/net/URLStreamHandlerFactory.html) + +### 原型模式 + +- [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) 接口) \ No newline at end of file diff --git a/contents/How_to_generate_a_random_alpha-numeric_string.md b/contents/How_to_generate_a_random_alpha-numeric_string.md new file mode 100644 index 0000000..b15ac27 --- /dev/null +++ b/contents/How_to_generate_a_random_alpha-numeric_string.md @@ -0,0 +1,38 @@ +# 如何产生一个随机的字母数字串作为 session 的唯一标识符? + +如果允许产生的随机字符串是可猜测的(随机字符串比较都短,或者使用有缺陷的随机数生成器),进而导致攻击者可能会劫持到会话的,可以使用一个相对简单随机数生成代码,如下所示: +``` +public class RandomString { + + private static final char[] symbols; + + static { + StringBuilder tmp = new StringBuilder(); + for (char ch = '0'; ch <= '9'; ++ch) + tmp.append(ch); + for (char ch = 'a'; ch <= 'z'; ++ch) + tmp.append(ch); + symbols = tmp.toString().toCharArray(); + } + + private final Random random = new Random(); + + private final char[] buf; + + public RandomString(int length) { + if (length < 1) + throw new IllegalArgumentException("length < 1: " + length); + buf = new char[length]; + } + + public String nextString() { + for (int idx = 0; idx < buf.length; ++idx) + buf[idx] = symbols[random.nextInt(symbols.length)]; + return new String(buf); + } +} +``` + +为了安全,可以考虑使用下面这段简洁且安全的代码,不过用其作为 session 的标识符,可能略显昂贵了一点: +``` +``` \ No newline at end of file