├── README.md ├── SUMMARY.md ├── contents ├── Eclipse-set-maximun-line-length-for-auto-formatting.md ├── android-sdk-installation-doesnt-find-jdk.md ├── avoiding-null-statements-in-java.md ├── breaking-out-of-nested-loops-in-java.md ├── check-if-at-least-two-out-of-three-booleans-are-true.md ├── comparing-java-enum-members-or-equals.md ├── convert-a-string-to-an-enum-in-java.md ├── converting-string-to-int-in-java.md ├── create-arraylist-from-array.md ├── creating-a-memory-leak-with-java.md ├── dealing-with-java-lang-outofmemoryerror-permgen-space-error.md ├── declare-array-in-java.md ├── difference-between-wait-and-sleep.md ├── differences-between-hashmap-and-hashtable.md ├── does-finally-always-execute-in-java.md ├── does-java-support-default-parameter-values.md ├── download-a-file-with-android-and-showing-the-progress-in-a-progressdialog.md ├── efficiency-of-java-double-brace-initialization.md ├── examples-of-gof-design-patterns-in-javas-core-libraries.md ├── failed-to-load-the-JNI-shared-library(JDK).md ├── generating-random-integers-in-a-range-with-Java.md ├── get-current-stack-trace-in-java.md ├── how-can-i-convert-a-stack-trace-to-a-string.md ├── how-can-i-create-an-executable-jar-with-dependencies-using-maven.md ├── how-can-i-generate-an-md5-hash.md ├── how-can-i-initialize-a-static-map.md ├── how-can-i-pad-an-integers-with-zeros-on-the-left.md ├── how-can-i-permanently-have-line-numbers-in-intellij.md ├── how-can-i-test-if-an-array-contains-a-certain-value.md ├── how-do-i-call-one-constructor-from-another-in-java.md ├── how-do-i-compare-strings-in-java.md ├── how-do-i-create-a-java-string-from-the-contents-of-a-file.md ├── how-do-i-decompile-java-class-files.md ├── how-do-servlets-work-instantiation-shared-variables-and-multithreading.md ├── how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests.md ├── how-does-the-java-for-each-loop-work.md ├── how-to-avoid-java-code-in-jsp-files.md ├── how-to-concatenate-two-arrays-in-java.md ├── how-to-create-a-file-and-write-to-a-file-in-java.md ├── how-to-create-a-generic-array-in-java.md ├── how-to-create-a-generic-array2-in-java.md ├── how-to-generate-a-random-alpha-numeric-string.md ├── how-to-get-an-enum-value-from-a-string-value-in-java.md ├── how-to-sort-a-mapkey-value-on-the-values-in-java.md ├── how-to-split-a-string-in-java.md ├── how-to-test-a-class-that-has-private-methods-fields-or-inner-classes.md ├── implements-runnable-vs-extends-thread.md ├── in-java-whats-the-difference-between-public-default-protected-and-private.md ├── initialization-of-an-arraylist-in-one-line.md ├── is-null-check-needed-before-calling-instanceof.md ├── is-there-a-unique-android-device-id.md ├── iterate-through-a-hashmap.md ├── iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re.md ├── java-inner-class-and-static-nested-class.md ├── java-operator.md ├── lookup-enum-by-string-value.md ├── lookup-enum-by-string-value2.md ├── must-override-a-superclass-method-errors-after-importing-a-project-into-eclips.md ├── proper-use-cases-for-android-usermanager-isuseragoat.md ├── read-convert-an-inputstream-to-a-string.md ├── setting-multiple-jars-in-java-classpath.md ├── sort-arraylist-of-custom-objects-by-property.md ├── stringbuilder-and-stringbuffer.md ├── using-java-net-urlconnection-to-fire-and-handle-http-requests.md ├── what-exactly-is-apache-camel.md ├── what-is-a-javabean-exactly.md ├── what-is-a-serialversionuid-and-why-should-i-use-it.md ├── what-is-an-efficient-way-to-implement-a-singleton-in-java.md ├── what-is-reflection-and-why-is-it-useful.md ├── what-is-the-difference-between-a-soft-reference-and-a-weak-reference-in-java.md ├── what-is-the-difference-between-jsf-servlet-and-jsp.md ├── what-is-the-equivalent-of-the-c++pair-in-java.md ├── what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java.md ├── whats-the-difference-between-component-repository-service-annotations-in.md ├── whats-the-simplest-way-to-print-a-java-array.md ├── when-and-how-should-i-use-a-threadlocal-variable.md ├── when-to-use-linkedlist-over-arraylist.md ├── which-notnull-java-annotation-should-i-use.md ├── why-cant-i-switch-on-a-string.md ├── why-does-java-have-transient-variables.md ├── why-does-math-round0-49999999999999994-return-1.md ├── why-does-math.round-(0.49999999999999994)-return-1.md ├── why-does-this-code-using-random-strings-print-hello-world.md ├── why-does-this-go-into-an-infinite-loop.md ├── why-is-char-preferred-over-string-for-passwords-in-java.md ├── why-is-java-vector-class-considered-obsolete-or-deprecated.md ├── why-is-printing-b-dramatically-slower-than-printing.md ├── why-is-processing-a-sorted-array-faster-than-an-unsorted-array.md ├── why-is-subtracting-these-two-times-in-1927-giving-a-strange-result.md └── 目录.md └── 目录.md /SUMMARY.md: -------------------------------------------------------------------------------- 1 | \# Summary* [前言](README.md) -------------------------------------------------------------------------------- /contents/Eclipse-set-maximun-line-length-for-auto-formatting.md: -------------------------------------------------------------------------------- 1 | 为自动代码调整设置最大的行数? 2 | 问题:我正在学习Java。如果我在Eclipse Helios里使用ctrl+shift+f的组合键,它会自动调整我的代码。一定程度下,它会改变行数。我想增加行数的最大值。应该怎么做? 3 | 4 | 回答,在偏好设置里,分别点击Java->Code Style->Fomatter->edit,在菜单栏Line Wrapping下会有行的宽度选择(Maximun line width).你将需要编辑你的代码轮廓。 5 | 6 | stackoverflow原址:http://stackoverflow.com/questions/3697287/eclipse-set-maximum-line-length-for-auto-formatting -------------------------------------------------------------------------------- /contents/android-sdk-installation-doesnt-find-jdk.md: -------------------------------------------------------------------------------- 1 | ##安装Android SDK的时候找不到JDK 2 | 3 | ###问题 4 | 我在我的win7 64位的系统上安装Android SDK时,jdk-6u23-windows-x64.exe已经安装上了,但是Android SDK的安装程序却因为找不到已安装的JDK无法继续下去。 5 | 这个问题出现过吗?有没有办法解决呢? 6 | 7 | ![](http://ww2.sinaimg.cn/large/0060lm7Tgw1f72ny3m6oaj30ds0a0gmi.jpg) 8 | 9 | ###回答1: 10 | 当你看到这个提示(找不到jdk)的时候按Back(返回),然后再点Next(下一步)。这个时候,它将会去寻找JDK 11 | 12 | ###回答2: 13 | 实际安装: 14 | 15 | - 系统:windows 8.1 16 | - JDK文件: jdk-8u11-windows-x64.exe 17 | - ADT文件:installer_r23.0.2-windows.exe 18 | 安装64位JDK,然后尝试第一个回答中的back-next的方法。然后尝试设置JAVA_HOME 根据错误信息的提示,但是,仍旧对我没有用处,然后,尝试如下解决办法: 19 | 20 | 按照它说的做,设置JAVA_HOME在你的系统环境变量中,这个路径要使用正斜杠(/)而非反斜杠(\) 21 | 22 | **注意:** 23 | 当我把JAVA_HOME设置为C:\Program Files\Java\jdk1.6.0_31的时候还是不行,但是当我设置成C:/Program Files/Java/jdk1.6.0_31的时候就ok了。快把我逼疯了。 24 | 25 | 如果还不行,就把 %JAVA_HOME%加在环境变量Path的头部。 26 | 27 | 下面是我的环境变量的配置: 28 | - JAVA_HOME=C:/Program Files/Java/jdk1.8.0_11 29 | - JRE_HOME=C:/Program Files/Java/jre8 30 | - Path=%JAVA_HOME%;C:... 31 | 32 | stackoverflow链接: 33 | http://stackoverflow.com/questions/4382178/android-sdk-installation-doesnt-find-jdk -------------------------------------------------------------------------------- /contents/avoiding-null-statements-in-java.md: -------------------------------------------------------------------------------- 1 | ##去掉烦人的“!=null"(判空语句) 2 | 3 | ###问题 4 | 为了避免空指针调用,我们经常会看到这样的语句 5 | ```java 6 | if (someobject != null) { 7 | someobject.doCalc(); 8 | } 9 | ``` 10 | 最终,项目中会存在大量判空代码,多么丑陋繁冗!如何避免这种情况?我们是否滥用了判空呢? 11 | 12 | ###回答 13 | 14 | 这是初、中级程序猿经常会遇到的问题。他们总喜欢在方法中返回null,因此,在调用这些方法时,也不得不去判空。另外,也许受此习惯影响,他们总潜意识地认为,所有的返回都是不可信任的,为了保护自己程序,就加了大量的判空。 15 | 16 | 吐槽完毕,回到这个题目本身,进行判空前,请区分以下两种情况: 17 | 18 | 1. null 是一个有效有意义的返回值(Where null is a valid response in terms of the contract; and) 19 | 2. null是无效有误的(Where it isn't a valid response.) 20 | 21 | 你可能还不明白这两句话的意思,不急,继续往下看,接下来将详细讨论这两种情况 22 | 23 | ####先说第2种情况 24 | null就是一个不合理的参数,就应该明确地中断程序,往外抛错误。这种情况常见于api方法。例如你开发了一个接口,id是一个必选的参数,如果调用方没传这个参数给你,当然不行。你要感知到这个情况,告诉调用方“嘿,哥们,你传个null给我做甚"。 25 | 26 | 相对于判空语句,更好的检查方式有两个 27 | 28 | 1. assert语句,你可以把错误原因放到assert的参数中,这样不仅能保护你的程序不往下走,而且还能把错误原因返回给调用方,岂不是一举两得。(原文介绍了assert的使用,这里省略) 29 | 2. 也可以直接抛出空指针异常。上面说了,此时null是个不合理的参数,有问题就是有问题,就应该大大方方往外抛。 30 | 31 | ####第1种情况会更复杂一些。 32 | 这种情况下,null是个”看上去“合理的值,例如,我查询数据库,某个查询条件下,就是没有对应值,此时null算是表达了“空”的概念。 33 | 34 | 这里给一些实践建议: 35 | 36 | - 假如方法的返回类型是collections,当返回结果是空时,你可以返回一个空的collections(empty list),而不要返回null.这样调用侧就能大胆地处理这个返回,例如调用侧拿到返回后,可以直接print list.size(),又无需担心空指针问题。(什么?想调用这个方法时,不记得之前实现该方法有没按照这个原则?所以说,代码习惯很重要!如果你养成习惯,都是这样写代码(返回空collections而不返回null),你调用自己写的方法时,就能大胆地忽略判空) 37 | - 返回类型不是collections,又怎么办呢? 38 | 那就返回一个空对象(而非null对象),下面举个“栗子”,假设有如下代码 39 | 40 | ```java 41 | public interface Action { 42 | void doSomething();} 43 | 44 | public interface Parser { 45 | Action findAction(String userInput);} 46 | ``` 47 | 48 | 其中,Parse有一个接口FindAction,这个接口会依据用户的输入,找到并执行对应的动作。假如用户输入不对,可能就找不到对应的动作(Action),因此findAction就会返回null,接下来action调用doSomething方法时,就会出现空指针。 49 | 解决这个问题的一个方式,就是使用Null Object pattern(空对象模式) 50 | 51 | 我们来改造一下 52 | 53 | 类定义如下,这样定义findAction方法后,确保无论用户输入什么,都不会返回null对象: 54 | ```java 55 | public class MyParser implements Parser { 56 | private static Action DO_NOTHING = new Action() { 57 | public void doSomething() { /* do nothing */ } 58 | }; 59 | 60 | public Action findAction(String userInput) { 61 | // ... 62 | if ( /* we can't find any actions */ ) { 63 | return DO_NOTHING; 64 | } 65 | }} 66 | ``` 67 | 68 | 对比下面两份调用实例 69 | 1. 冗余: 每获取一个对象,就判一次空 70 | ```java 71 | Parser parser = ParserFactory.getParser(); 72 | if (parser == null) { 73 | // now what? 74 | // this would be an example of where null isn't (or shouldn't be) a valid response 75 | } 76 | Action action = parser.findAction(someInput); 77 | if (action == null) { 78 | // do nothing} 79 | else { 80 | action.doSomething();} 81 | ``` 82 | 83 | 2. 精简 84 | ```java 85 | ParserFactory.getParser().findAction(someInput).doSomething(); 86 | ``` 87 | 因为无论什么情况,都不会返回空对象,因此通过findAction拿到action后,可以放心地调用action的方法。 88 | 89 | 90 | 91 | ####其他回答精选: 92 | - 如果要用equal方法,请用object<不可能为空>.equal(object<可能为空>)) 93 | 例如: 94 | 使用 95 | `"bar".equals(foo) ` 96 | 而不是 97 | `foo.equals("bar") ` 98 | - Java8或者guava lib中,提供了Optional类,这是一个元素容器,通过它来封装对象,可以减少判空。不过代码量还是不少。不爽。 99 | - 如果你想返回null,请停下来想一想,这个地方是否更应该抛出一个异常 100 | 101 | stackoverflow链接: 102 | http://stackoverflow.com/questions/271526/avoiding-null-statements?page=1&tab=votes#tab-top 103 | -------------------------------------------------------------------------------- /contents/breaking-out-of-nested-loops-in-java.md: -------------------------------------------------------------------------------- 1 | ##从一个多层嵌套循环中直接跳出 2 | 3 | ###问题 4 | Java中如何从一个多层嵌套循环中退出,例如下面,有两个循环,break只能退出一个for循环,不能直接跳过第二个for循环 5 | ```java 6 | for (Type type : types) { 7 | for (Type t : types2) { 8 | if (some condition) { 9 | // Do something and break... 10 | break; // 这样只退出了最里的for循环 11 | } 12 | }} 13 | ``` 14 | 15 | ###回答 16 | 17 | 可以用break+label的语法,例子如下 18 | ```java 19 | public class Test { 20 | public static void main(String[] args) { 21 | outerloop: 22 | for (int i=0; i < 5; i++) { 23 | for (int j=0; j < 5; j++) { 24 | if (i * j > 6) { 25 | System.out.println("Breaking"); 26 | break outerloop; 27 | } 28 | System.out.println(i + " " + j); 29 | } 30 | } 31 | System.out.println("Done"); 32 | } 33 | } 34 | ``` 35 | 36 | 首先在for循环前加标签,如例子中的outerloop,然后在for循环内break label(如本例的outerloop),就会跳出该label指定的for循环。 37 | 38 | stackoverflow链接: 39 | http://stackoverflow.com/questions/886955/breaking-out-of-nested-loops-in-java 40 | -------------------------------------------------------------------------------- /contents/check-if-at-least-two-out-of-three-booleans-are-true.md: -------------------------------------------------------------------------------- 1 | ##给3个布尔变量,当其中有2个或者2个以上为true才返回true 2 | 3 | ###问题 4 | 给3个boolean变量,a,b,c,当其中有2个或2个以上为true时才返回true? 5 | * 最笨的方法: 6 | ```java 7 | boolean atLeastTwo(boolean a, boolean b, boolean c) 8 | { 9 | if ((a && b) || (b && c) || (a && c)) 10 | { 11 | return true; 12 | } 13 | else 14 | { 15 | return false; 16 | } 17 | } 18 | ``` 19 | * 优雅解法1 20 | ```java 21 | return a ? (b || c) : (b && c); 22 | ``` 23 | 24 | * 优雅解法2 25 | ```java 26 | return (a==b) ? a : c; 27 | ``` 28 | 29 | * 优雅解法3 30 | ```java 31 | return a ^ b ? c : a 32 | ``` 33 | 34 | * 优雅解法4 35 | ```java 36 | return a ? (b || c) : (b && c); 37 | ``` 38 | 39 | 40 | stackoverflow链接: http://stackoverflow.com/questions/3076078/check-if-at-least-two-out-of-three-booleans-are-true 41 | -------------------------------------------------------------------------------- /contents/comparing-java-enum-members-or-equals.md: -------------------------------------------------------------------------------- 1 | ## 比较java枚举成员使用equal还是== 2 | 3 | ### 问题 4 | 我知道Java枚举会被编译成一个包含私有构造参数和一堆静态方法的类,当去比较两个枚举的时候,总是使用equals()方法,例如: 5 | ```java 6 | public useEnums(SomeEnum a) 7 | { 8 | if(a.equals(SomeEnum.SOME_ENUM_VALUE)) 9 | { 10 | ... 11 | } 12 | ... 13 | } 14 | ``` 15 | 除此之外,我也可以使用 == 替代equals() 方法 16 | ```java 17 | public useEnums2(SomeEnum a) 18 | { 19 | if(a == SomeEnum.SOME_ENUM_VALUE) 20 | { 21 | ... 22 | } 23 | ... 24 | } 25 | ``` 26 | 我有5年以上的java编程经验,并且我想我也懂得 == 和 equals() 之间的区别,但是我仍然觉得很困惑,哪一个操作符才是我该使用的。 27 | 28 | ### 答案 29 | 30 | 二者皆对,如果你看过枚举的源码,你会发现在源码中,equals也仅仅非常简单的 == 。 31 | 我使用 == ,因为无论如何,这个左值是可以为 null的 32 | 33 | 34 | 译者补充 java.lang.Enum 中Equals 代码: 35 | ```java 36 | public final boolean equals(Object other) { 37 | return this==other; 38 | } 39 | ``` 40 | 41 | 42 | ### 额外答案 43 | #### 能在枚举中使用 == 进行判断? 44 | 答案是肯定的,因为枚举有着严格的实例化控制,所以你可以用 == 去做比较符,这个用法,在官方文档中也有明确的说明。 45 | 46 | >JLS 8.9 Enums 47 | 一个枚举类型除了定义的那些枚举常量外没有其他实例了。 48 | 试图明确地说明一种枚举类型是会导致编译期异常。在枚举中final clone方法确保枚举常量从不会被克隆,而且序列化机制会确保从不会因为反序列化而创造复制的实例。枚举类型的反射实例化也是被禁止的。总之,以上内容确保了除了定义的枚举常量之外,没有枚举类型实例。 49 | 50 | 因为每个枚举常量只有一个实例,所以如果在比较两个参考值,至少有一个涉及到枚举常量时,允许使用“==”代替equals()。(equals()方法在枚举类中是一个final方法,在参数和返回结果时,很少调用父类的equals()方法,因此是一种恒等的比较。) 51 | #### 什么时候 == 和 equals 不一样? 52 | As a reminder, it needs to be said that generally, == is NOT a viable alternative to equals. When it is, however (such as with enum), there are two important differences to consider: 53 | 通常来说 == 不是一个 equals的一个备选方案,无论如何有2个重要的不同处需要考虑: 54 | 55 | ##### == 不会抛出 NullPointerException 56 | ```java 57 | enum Color { BLACK, WHITE }; 58 | 59 | Color nothing = null; 60 | if (nothing == Color.BLACK); // runs fine 61 | if (nothing.equals(Color.BLACK)); // throws NullPointerException 62 | ``` 63 | ##### == 在编译期检测类型兼容性 64 | ```java 65 | enum Color { BLACK, WHITE }; 66 | enum Chiral { LEFT, RIGHT }; 67 | 68 | if (Color.BLACK.equals(Chiral.LEFT)); // compiles fine 69 | if (Color.BLACK == Chiral.LEFT); // DOESN'T COMPILE!!! Incompatible types! 70 | ``` 71 | 72 | #### 什么时候使用 == ? 73 | Bloch specifically mentions that immutable classes that have proper control over their instances can guarantee to their clients that == is usable. enum is specifically mentioned to exemplify. 74 | 具体来说,那些提供恰当实例控制的不可变类能够保证 == 是可用的,枚举刚好符合这个条件。 75 | 76 | 考虑静态工厂方法代替构造器 77 | 它使得不可变的类可以确保不会存在两个相等的实例,即当且仅当a==b的时候才有a.equals(b)为true。如果类保证了这一点,它的客户端可以使用“==”操作符来代替equals(Object)方法,这样可以提升性能。枚举类型保证了这一点 78 | 79 | 总而言之,在枚举比较上使用 == , 因为: 80 | 1. 能正常工作 81 | 2. 更快 82 | 3. 运行时是安全的 83 | 4. 编译期也是安全的 84 | 85 | stackoverlfow链接:http://stackoverflow.com/questions/1750435/comparing-java-enum-members-or-equals 86 | -------------------------------------------------------------------------------- /contents/convert-a-string-to-an-enum-in-java.md: -------------------------------------------------------------------------------- 1 | ##如何将String转换为enum 2 | 3 | ### 问题 4 | 假设定义了如下的enum(枚举): 5 | 6 | ```java 7 | public enum Blah { 8 | A, B, C, D 9 | } 10 | ``` 11 | 已知枚举对应的String值,希望得到对应的枚举值。例如,已知"A",希望得到对应的枚举——Blah.A,应该怎么做? 12 | Enum.valueOf()是否能实现以上目的,如果是,那我如何使用? 13 | 14 | 15 | ### 答案 16 | 是的,Blah.valueOf("A") 将会得到 Blah.A 17 | 18 | 静态方法valueOf() 和 values() 不存在于源码中,而是在编译时创建,我们也可以在JavaDoc查看到它们,比如 [Dialog.ModalityTyp](http://docs.oracle.com/javase/7/docs/api/java/awt/Dialog.ModalityType.html) 就中出现这两个方法。 19 | 20 | ### 其他答案 21 | 22 | 当文本和枚举值不同时,可以采用这种方式: 23 | ```java 24 | public enum Blah { 25 | A("text1"), 26 | B("text2"), 27 | C("text3"), 28 | D("text4"); 29 | 30 | private String text; 31 | 32 | Blah(String text) { 33 | this.text = text; 34 | } 35 | 36 | public String getText() { 37 | return this.text; 38 | } 39 | 40 | public static Blah fromString(String text) { 41 | for (Blah b : Blah.values()) { 42 | if (b.text.equalsIgnoreCase(text)) { 43 | return b; 44 | } 45 | } 46 | return null; 47 | } 48 | } 49 | ``` 50 | fromString方法中,throw new IllegalArgumentException("No constant with text " + text + " found") 会比直接返回null更优秀. 51 | 52 | ### 其他答案 53 | 54 | 我有一个挺赞的工具方法: 55 | ```java 56 | /** 57 | * A common method for all enums since they can't have another base class 58 | * @param Enum type 59 | * @param c enum type. All enums must be all caps. 60 | * @param string case insensitive 61 | * @return corresponding enum, or null 62 | */ 63 | public static > T getEnumFromString(Class c, String string) { 64 | if( c != null && string != null ) { 65 | try { 66 | return Enum.valueOf(c, string.trim().toUpperCase()); 67 | } catch(IllegalArgumentException ex) { 68 | } 69 | } 70 | return null; 71 | } 72 | ``` 73 | 74 | 你可以这么使用: 75 | 76 | ```java 77 | public static MyEnum fromString(String name) { 78 | return getEnumFromString(MyEnum.class, name); 79 | } 80 | ``` 81 | 82 | stackoverflow链接:http://stackoverflow.com/questions/604424/convert-a-string-to-an-enum-in-java 83 | -------------------------------------------------------------------------------- /contents/converting-string-to-int-in-java.md: -------------------------------------------------------------------------------- 1 | ##如何将String转换为Int 2 | 3 | 有两种方式 4 | 5 | ```java 6 | Integer x = Integer.valueOf(str); 7 | // or 8 | int y = Integer.parseInt(str); 9 | ``` 10 | 这两种方式有一点点不同: 11 | - `valueOf`返回的是`java.lang.Integer`的实例 12 | - `parseInt`返回的是基本数据类型 int 13 | 14 | `Short.valueOf/parseShort`,`Long.valueOf/parseLong`等也是有类似差别。 15 | 16 | 另外还需注意的是,在做int类型转换时,可能会抛出NumberFormatException,因此要做好异常捕获 17 | ```java 18 | int foo; 19 | String StringThatCouldBeANumberOrNot = "26263Hello"; //will throw exception 20 | String StringThatCouldBeANumberOrNot2 = "26263"; //will not throw exception 21 | try { 22 | foo = Integer.parseInt(StringThatCouldBeANumberOrNot); 23 | } catch (NumberFormatException e) { 24 | //Will Throw exception! 25 | //do something! anything to handle the exception. 26 | } 27 | 28 | try { 29 | foo = Integer.parseInt(StringThatCouldBeANumberOrNot2); 30 | } catch (NumberFormatException e) { 31 | //No problem this time but still it is good practice to care about exceptions. 32 | //Never trust user input :) 33 | //do something! anything to handle the exception. 34 | } 35 | ``` 36 | 37 | stackoverflow链接:http://stackoverflow.com/questions/5585779/converting-string-to-int-in-java -------------------------------------------------------------------------------- /contents/create-arraylist-from-array.md: -------------------------------------------------------------------------------- 1 | ##将数组转换为List 2 | 3 | ###问题 4 | 假设有数组 5 | ```java 6 | Element[] array = {new Element(1),new Element(2),new Element(3)}; 7 | ``` 8 | 如何将其转换为ArrayList`` arraylist = ??? 9 | 10 | ### 回答1 ### 11 | 12 | `new ArrayList(Arrays.asList(array))` 13 | 14 | ###回答2 15 | 16 | Arrays.asList(array)或者Arrays.asList(new Element(1),new Element(2),new Element(3)) 17 | 18 | 不过,这样做有些坑要注意: 19 | 20 | 1. 这样做生成的list,是定长的。也就是说,如果你对它做add或者remove,都会抛UnsupportedOperationException。 21 | 2. 如果修改数组的值,list中的对应值也会改变! 22 | 23 | **Arrays.asList() 返回的是Arrays内部静态类,而不是Java.util.ArrayList的类。这个java.util.Arrays.ArrayList有set(),get(),contains()方法,但是没有任何add() 方法,所以它是固定大小的** 24 | 25 | 26 | 如果希望避免这两个坑,请改用这个方式 27 | ```java 28 | Collections.addAll(arraylist, array); 29 | ``` 30 | 31 | stackoverflow原址: 32 | http://stackoverflow.com/questions/157944/how-to-create-arraylist-arraylistt-from-array-t -------------------------------------------------------------------------------- /contents/creating-a-memory-leak-with-java.md: -------------------------------------------------------------------------------- 1 | # 如何使用Java创建一个内存泄漏的程序 2 | 3 | ## 问题: 4 | 我在一个面试的过程中被问到如何使用Java创建一个内存泄漏的程序。毫无疑问地说,我当时哑口无言,根本不知道如何开始编码。 5 | 6 | ## 解答 7 | 在Java下有一个很好的方法来创建内存泄漏程序--通过使得对象不可访问但任然存储在内存中。 8 | 9 | 1. 应用程序创建一个长期运行的线程A 或者 使用一个线程池来加快泄漏的速度。 10 | 11 | 2. 线程A使用ClassLoader(用户可以自定义)加载一个类 B。 12 | 13 | 3. 在类B申请一块很大的连续内存(例如:new byte[1000000]), 14 | 并使用一个静态成员变量保存该空间的一个强引用,之后在一个ThreadLocal对象中存储类B对象的引用。 15 | 虽然泄漏这个类的一个实例就足够了,但是也可以通过申请多个实例的方法来加快内存泄漏的速度。 16 | 17 | 4. 线程A清理所有指向自定义类或者通过ClassLoadeer加载的引用。 18 | 19 | 5. 重复上述步骤 20 | 21 | 上述方式可以达到内存泄漏的目的,因为 ThreadLocal 存储了一个指向类B对象的引用, 22 | 而该对象又保存了一个指向其类的引用,这个类又保存了一个指向其ClassLoader的引用, 23 | 而ClassLoader又保存了一个通过它加载的所有类的引用。 24 | 这种方法在许多的JVM的实现中表现更糟糕,因为Classes和ClassLoader被直接存储在老年代(permgen)并且永远都不会被GC处理。 25 | 26 | ******************************下方为个人理解************************************ 27 | 28 | 通过一个简单的图来描述上述关系:
29 | ThreadLocal.obj ---> B.obj ---> B.class <--> ClassLoader.obj
30 | 注:上图的\*.obj表示\*类的一个实例对象,B.class表示类B的Class对象 31 | 32 | ******************************上方为个人理解************************************ 33 | 34 | 这个模式的一个变形:如果频繁的重新部署那些可能使用ThreadLocals的应用,应用容器(例如Tomcat)就会像筛子一样泄漏内存。 35 | 因为应用容器使用上述所说的线程,每次重新部署应用时,应用容器都会使用一个新的ClassLoader。 36 | 37 | 具体代码可以参考:https://gist.github.com/dpryden/b2bb29ee2d146901b4ae 38 | 39 | 参考:http://frankkieviet.blogspot.com/2006/10/classloader-leaks-dreaded-permgen-space.html 40 | 41 | stackoverflow原址:http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java 42 | -------------------------------------------------------------------------------- /contents/dealing-with-java-lang-outofmemoryerror-permgen-space-error.md: -------------------------------------------------------------------------------- 1 | ##如何处理 java.lang.outOfMemoryError PermGen space error 2 | 3 | ###问题 4 | 5 | 最近,我在过运行我的web应用时得到:java.lang.OutOfMemoryError: PermGen space。 6 | 我的应用是一个典型的 Hibernate/JPA + IceFaces/JSF的应用.运行于Tomcat6.0和jdk1.6.我发布了多次以后,产生了这个错误。 7 | 8 | 是什么原因造成的,我如何避免?我怎样修复? 9 | 10 | 11 | #回答 12 | 13 | 14 | 解决的方案是当TomeCat启时,在jvm的的命令行添加参数 15 | 16 | -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 17 | 18 | 19 | 你也可以停止tomcat的服务,直接进入Tomcat/bin目录,运行tomcat6w.exe.在Java的标签下,加好上面的参数。单击"OK",重新启动Tomcat的服务. 20 | 21 | 22 | 如果系统返回错误,提示指定的服务不存在,你可以运行: 23 | 24 | tomcat6w //ES//servicename 25 | 26 | servicename的名字你可以在services.msc中查看。 27 | 28 | stackoverflow链接: 29 | http://stackoverflow.com/questions/88235/dealing-with-java-lang-outofmemoryerror-permgen-space-error 30 | 31 | 32 | -------------------------------------------------------------------------------- /contents/declare-array-in-java.md: -------------------------------------------------------------------------------- 1 | ##在java中声明数组 2 | 3 | ###问题描述: 4 | 你是如何在Java中声明数组的。 5 | 6 | ###回答: 7 | 你可以直接用数组声明,或者通过数组的字面常量(array literal )声明 8 | 9 | 对于原始类型(primitive types): 10 | ``` 11 | int[] myIntArray = new int[3]; 12 | int[] myIntArray = {1, 2, 3}; 13 | int[] myIntArray = new int[]{1, 2, 3}; 14 | ``` 15 | 16 | 对于其他类,比如String类,也是相同的: 17 | ``` 18 | String[] myStringArray = new String[3]; 19 | String[] myStringArray = {"a", "b","c"}; 20 | String[] myStringArray = new String[]{"a", "b", "c"}; 21 | ``` 22 | [stackoverflow链接:Declare array in Java?](http://stackoverflow.com/questions/1200621/declare-array-in-java) 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /contents/difference-between-wait-and-sleep.md: -------------------------------------------------------------------------------- 1 | ## wait()和sleep()的区别 2 | 3 | ### 问题: 4 | 在线程里 `wait()` 和 `sleep()` 的区别? 5 | 6 | 我的理解是执行 `wait()` 语句后,该线程仍是运行态,并且会占用CPU,但是执行 `sleep()`后,该线程则不会占用CPU,对吗? 7 | 8 | 为什么需要 `sleep()` 和 `wait()` 两条语句:他们底层是如何实现的? 9 | 10 | ### 回答: 11 | 线程 在[`wait`](http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait%28%29) 后,可以被另一个拥有相同 `synchronized ` 对象的线程,通过调用[ `notify` ](http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#notify%28%29)唤醒,而 `sleep` 不行。`wait` 和 `notify` 能正常执行的条件是(否则会抛异常):多个线程的代码,都包在`synchronized `块中,并且 `synchronized` 锁的对象需要是同一个。如下所示: 12 | ``` 13 | Object mon = ...; 14 | synchronized (mon) { 15 | mon.wait(); 16 | } 17 | ``` 18 | 上面这个线程调用了 `wait`后,会进入等待状态。这时另外一个线程可以这样做: 19 | ``` 20 | synchronized (mon) { mon.notify(); } 21 | ``` 22 | 可以看到,`synchronized`锁对象,都是mon。因此,当第二个线程调用了 `notify()` 方法,第一个线程就会唤醒(假设有且仅有一个线程是被包在 `synchronized (mon)` 中且处于等待状态)。 23 | 24 | 如果有多个线程在等待(且`synchronized` 锁对象是同一个,如上例中的mon),则可以调用[ `notifyAll` ](http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#notifyAll%28%29)来唤醒。但是,只有其中一个线程能抢到锁并继续执行(因为 `wait` 的线程都是在 `synchronized` 块内,需要争夺 `synchronized` 锁)。其他的线程会被锁住,直到他们依次获得锁。 25 | 26 | 再补充几点: 27 | 28 | - `wait` 方法由 `Object` 对象调用(例如:你可以让 `synchronized` 锁对象调用 `wait` ,如上面例子的mon.wait()),而 `sleep` 则由线程调用。 29 | 30 | - `wait` 之后,可能会伪唤醒(`spurious wakeups`)(正在waiting的线程,无故就被唤醒了,如遇到interrupted, timing out等情况)。因此,你需要多设置一些检查,如果不满足实际的运行条件,则继续等待,如下: 31 | ``` 32 | synchronized { 33 | while (!condition) { mon.wait(); } 34 | } 35 | ``` 36 | 37 | - 当线程调用 `sleep` 时,并没有释放对象锁,而 `wait` 则释放了对象锁: 38 | ``` 39 | synchronized(LOCK) { 40 | Thread.sleep(1000); // LOCK is held 41 | } 42 | synchronized(LOCK) { 43 | LOCK.wait(); // LOCK is not held 44 | } 45 | ``` 46 | 47 | 最后,再小结一下: 48 | - ` sleep() `:“我已经完成了一个时间片,**在n微秒**前,请不要再给我一个时间片”。这时操作系统不会让这个线程做任何事情,直到sleep时间结束。 49 | - ` wait() `:"我已经完成了一个时间片,**在其他线程调用` notify() `前**,请不要再给我一个时间片)。这时操作系统不会安排这个线程继续运行,直到有人调用了` notify() ` 50 | 51 | 52 | 53 | **stackoverflow链接:** 54 | http://stackoverflow.com/questions/1036754/difference-between-wait-and-sleep 55 | 56 | ### 相关问题及链接: 57 | 58 | 1. [Java: notify() vs. notifyAll() all over again](http://stackoverflow.com/questions/37026/java-notify-vs-notifyall-all-over-again) 59 | 2. [线程通信](http://ifeve.com/thread-signaling/) 60 | 3. [最简实例说明wait、notify、notifyAll的使用方法](http://longdick.iteye.com/blog/453615) -------------------------------------------------------------------------------- /contents/differences-between-hashmap-and-hashtable.md: -------------------------------------------------------------------------------- 1 | ## HashMap和Hashtable的区别 2 | ### 问题 3 | 在Java中`HashMap`和`Hashtable`的区别? 4 | 哪一个对于多线程应用程序更好? 5 | 6 | ### 回答 7 | 1. `Hashtable`是同步的,加了`synchronized`锁,而`HashMap`不是。没有加`synchronized`锁的对象,性能通常比加了`synchronized`锁的对象要更好一些,因此,如果是非多线程程序,不需要考虑锁、同步等问题,那么使用`HashMap`更好。 8 | 2. `Hashtable`不允许有空的键或值。`HashMap`允许空键和空值。 9 | 3. HashMap有一个子类[LinkedHashMap](http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html),对这个类对象进行迭代时,它的顺序是有序的(按插入顺序排序)。如有需要,你也能轻易的从`LinkedHashMap`转化成`HashMap`。`Hashtable`就没那么简单了, 10 | 11 | 总之,如果你无需关心同步(synchronized)问题,我会建议用`HashMap`。反之,你可以考虑使用[ConcurrentHashMap](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html) 12 | 13 | ### stackoverflow链接: 14 | http://stackoverflow.com/questions/40471/differences-between-hashmap-and-hashtable 15 | 16 | ### 相关推荐: 17 | importnew:[HashMap和Hashtable的区别](http://www.importnew.com/7010.html) -------------------------------------------------------------------------------- /contents/does-finally-always-execute-in-java.md: -------------------------------------------------------------------------------- 1 | ##问题 2 | 3 | 有一个 try/catch 代码块,其中包含一个打印语句。finally代码块总会被执行么? 4 | 5 | 示例: 6 | 7 | ``` java 8 | try { 9 | something(); 10 | return success; 11 | } 12 | catch (Exception e) { 13 | return failure; 14 | } 15 | finally { 16 | System.out.println("i don't know if this will get printed out."); 17 | } 18 | ``` 19 | 20 | ##回答 21 | 22 | 1. ```finally``` 将会被调用。 23 | 只有以下情况 ```finally``` 不会被调用: 24 | 25 | - 当你使用 ```System.exit()``` 后 26 | - 其他线程干扰了现在运行的线程(通过 ```interrupt``` 方法) 27 | - JVM 崩溃( crash )了 28 | 29 | Answered by [Jodonnell](http://stackoverflow.com/users/4223/jodonnell), edited by [jpaugh](http://stackoverflow.com/users/712526/jpaugh). 30 | 31 | 2. //示例代码 32 | 33 | ``` java 34 | class Test 35 | { 36 | public static void main(String args[]) 37 | { 38 | System.out.println(Test.test()); 39 | } 40 | 41 | public static int test() 42 | { 43 | try { 44 | return 0; 45 | } 46 | finally { 47 | System.out.println("finally trumps return."); 48 | } 49 | } 50 | } 51 | ``` 52 | 输出: 53 | 54 | ``` java 55 | finally trumps return. 56 | 0 57 | ``` 58 | Answered by [Kevin](http://stackoverflow.com/users/1058366/kevin) 59 | 60 | --- 61 | 原文链接:http://stackoverflow.com/questions/65035/does-finally-always-execute-in-java?page=1&tab=votes#tab-top 62 | -------------------------------------------------------------------------------- /contents/does-java-support-default-parameter-values.md: -------------------------------------------------------------------------------- 1 | # Java 是否支持默认的参数值? 2 | 3 | 在 c++ 中,常见到如下的方法定义(param3 默认为 false): 4 | 5 | ``` 6 | void MyParameterizedFunction(String param1, int param2, bool param3=false); 7 | ``` 8 | 9 | 那在 java 中,是否也支持这样的定义方式? 10 | 11 | 答案是否定的,不过我们可以通过多种方式处理这种参数默认值的情况。 12 | 13 | ### [创建者模式](http://en.wikipedia.org/wiki/Builder_pattern) 14 | 使用创建者模式,你可以设定部分参数是有默认值,部分参数是可选的。如: 15 | 16 | ``` 17 | Student s1 = new StudentBuilder().name("Eli").buildStudent(); 18 | Student s2 = new StudentBuilder() 19 | .name("Spicoli") 20 | .age(16) 21 | .motto("Aloha, Mr Hand") 22 | .buildStudent(); 23 | ``` 24 | 25 | 26 | ### 方法(构造函数)重载 27 | 如: 28 | 29 | ``` 30 | void foo(String a, Integer b) { 31 | //... 32 | } 33 | 34 | void foo(String a) { 35 | foo(a, 0); // here, 0 is a default value for b 36 | } 37 | 38 | foo("a", 2); 39 | foo("a"); 40 | ``` 41 | 构造函数重载,对于参数比较少的情况下,比较适合;当参数相对多的时候,可以考虑使用静态工厂方法,或添加一个参数辅助对象。 42 | 43 | 如果是常规方法重载,可以考虑使用 参数辅助对象,或者重命名多种情况(比如说,有多个开银行卡的重载方法,可以根据需要重命名为 开交行卡,开招行卡 等多种方法)。 44 | 45 | ### null 的传递 46 | 当有多个默认参数时,可以考虑传递 null,当参数为 null 时,将参数设为 默认值。如: 47 | 48 | ``` 49 | void foo(String a, Integer b, Integer c) { 50 | b = b != null ? b : 0; 51 | c = c != null ? c : 0; 52 | //... 53 | } 54 | 55 | foo("a", null, 2); 56 | ``` 57 | 58 | ### 多参数方式 59 | 当有多个参数,且某些参数可以忽略不设置的情况下,可以考虑使用多参数方式。 60 | 61 | - 可选的参数类型的一致 62 | 63 | ``` 64 | void foo(String a, Integer... b) { 65 | Integer b1 = b.length > 0 ? b[0] : 0; 66 | Integer b2 = b.length > 1 ? b[1] : 0; 67 | //... 68 | } 69 | 70 | foo("a"); 71 | foo("a", 1, 2); 72 | ``` 73 | 74 | - 可选参数类型不一致 75 | 76 | ``` 77 | void foo(String a, Object... b) { 78 | Integer b1 = 0; 79 | String b2 = ""; 80 | if (b.length > 0) { 81 | if (!(b[0] instanceof Integer)) { 82 | throw new IllegalArgumentException("..."); 83 | } 84 | b1 = (Integer)b[0]; 85 | } 86 | if (b.length > 1) { 87 | if (!(b[1] instanceof String)) { 88 | throw new IllegalArgumentException("..."); 89 | } 90 | b2 = (String)b[1]; 91 | //... 92 | } 93 | //... 94 | } 95 | 96 | foo("a"); 97 | foo("a", 1); 98 | foo("a", 1, "b2"); 99 | ``` 100 | 101 | ### 使用 Map 作为方法中的参数 102 | 当参数很多,且大部分参数都会使用默认值的情况,可以使用 Map 作为方法中的参数。 103 | 104 | ``` 105 | void foo(Map parameters) { 106 | String a = ""; 107 | Integer b = 0; 108 | if (parameters.containsKey("a")) { 109 | if (!(parameters.get("a") instanceof Integer)) { 110 | throw new IllegalArgumentException("..."); 111 | } 112 | a = (String)parameters.get("a"); 113 | } 114 | if (parameters.containsKey("b")) { 115 | //... 116 | } 117 | //... 118 | } 119 | 120 | foo(ImmutableMap.of( 121 | "a", "a", 122 | "b", 2, 123 | "d", "value")); 124 | ``` 125 | 126 | 127 | stackoverflow原址:https://stackoverflow.com/questions/997482/does-java-support-default-parameter-values -------------------------------------------------------------------------------- /contents/download-a-file-with-android-and-showing-the-progress-in-a-progressdialog.md: -------------------------------------------------------------------------------- 1 | ## 在Android里面下载文件,并在ProgressDialog显示进度 2 | 3 | ### 问题 4 | 尝试写一个可以进行“应用更新”的APP。为了达到这个效果,我写了一个可以下载文件并且在一个`ProgressDialog`里面显示进度的简单方法。我知道怎么使用`ProgressDialog`,但是我不太确定怎么显示当前进度和下载文件。 5 | 6 | ### 回答 7 | 8 | 有很多方式去下载文件。我给出一些最常用的方法;由你来选择选择哪一个最适合你的应用。 9 | 10 | #### 1. 使用AsyncTask,并且在一个dialog里面显示进度 11 | 这种方法允许你执行一些后台任务,并且同时更新UI(在这里,我们是更新进度条progress bar)。 12 | 13 | 首先是实例代码 14 | ```java 15 | // 定义一个dialog为Activity的成员变量 16 | ProgressDialog mProgressDialog; 17 | 18 | // 在OnCreate()方法里面初始化 19 | mProgressDialog = new ProgressDialog(YourActivity.this); 20 | mProgressDialog.setMessage("A message"); 21 | mProgressDialog.setIndeterminate(true); 22 | mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 23 | mProgressDialog.setCancelable(true); 24 | 25 | // 执行下载器 26 | final DownloadTask downloadTask = new DownloadTask(YourActivity.this); 27 | downloadTask.execute("你要下载文件的Url"); 28 | 29 | mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { 30 | @Override 31 | public void onCancel(DialogInterface dialog) { 32 | downloadTask.cancel(true); 33 | } 34 | }); 35 | ``` 36 | 37 | `AsyncTask`看起来像这样 38 | ```java 39 | // 一般我们把AsyncTask的子类定义在Activity的内部 40 | // 通过这种方式,我们就可以轻松地在这里更改UI线程 41 | private class DownloadTask extends AsyncTask { 42 | 43 | private Context context; 44 | private PowerManager.WakeLock mWakeLock; 45 | 46 | public DownloadTask(Context context) { 47 | this.context = context; 48 | } 49 | 50 | @Override 51 | protected String doInBackground(String... sUrl) { 52 | InputStream input = null; 53 | OutputStream output = null; 54 | HttpURLConnection connection = null; 55 | try { 56 | URL url = new URL(sUrl[0]); 57 | connection = (HttpURLConnection) url.openConnection(); 58 | connection.connect(); 59 | 60 | // 避免因为接收到非HTTP 200 OK状态,而导致只或者错误代码,而不是要下载的文件 61 | if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { 62 | return "Server returned HTTP " + connection.getResponseCode() 63 | + " " + connection.getResponseMessage(); 64 | } 65 | 66 | // 这对显示下载百分比有帮助 67 | // 当服务器没有返回文件的大小时,数字可能为-1 68 | int fileLength = connection.getContentLength(); 69 | 70 | // 下载文件 71 | input = connection.getInputStream(); 72 | output = new FileOutputStream("/sdcard/file_name.extension"); 73 | 74 | byte data[] = new byte[4096]; 75 | long total = 0; 76 | int count; 77 | while ((count = input.read(data)) != -1) { 78 | // 允许用返回键取消下载 79 | if (isCancelled()) { 80 | input.close(); 81 | return null; 82 | } 83 | total += count; 84 | // 更新下载进度 85 | if (fileLength > 0) // 只有当 fileLength>0 的时候才会调用 86 | publishProgress((int) (total * 100 / fileLength)); 87 | output.write(data, 0, count); 88 | } 89 | } catch (Exception e) { 90 | return e.toString(); 91 | } finally { 92 | try { 93 | if (output != null) 94 | output.close(); 95 | if (input != null) 96 | input.close(); 97 | } catch (IOException ignored) { 98 | } 99 | 100 | if (connection != null) 101 | connection.disconnect(); 102 | } 103 | return null; 104 | } 105 | ``` 106 | 107 | 上面的`doInBackground`方法总是在后台线程中运行。你不能在这里做任何UI线程相关的任务。另一方面,`onProgressUpdate`和`onPreExecute`是在UI线程里面运行的,所以你可以在这里更改进度条。 108 | 109 | ```java 110 | @Override 111 | protected void onPreExecute() { 112 | super.onPreExecute(); 113 | // 取得CPU锁,避免因为用户在下载过程中按了电源键而导致的失效 114 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 115 | mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 116 | getClass().getName()); 117 | mWakeLock.acquire(); 118 | mProgressDialog.show(); 119 | } 120 | 121 | @Override 122 | protected void onProgressUpdate(Integer... progress) { 123 | super.onProgressUpdate(progress); 124 | // 如果到了这里,文件长度是确定的,设置indeterminate为false 125 | mProgressDialog.setIndeterminate(false); 126 | mProgressDialog.setMax(100); 127 | mProgressDialog.setProgress(progress[0]); 128 | } 129 | 130 | @Override 131 | protected void onPostExecute(String result) { 132 | mWakeLock.release(); 133 | mProgressDialog.dismiss(); 134 | if (result != null) 135 | Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show(); 136 | else 137 | Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show(); 138 | } 139 | ``` 140 | 141 | 为了可正常运行,你还要取得WAKE_LOCK权限 142 | ``` 143 | 144 | ``` 145 | 146 | #### 2. 从服务器上下载文件 147 | 148 | 这里有个最大的问题:*我怎么从service来更新我的activity?*。 149 | 在下一个例子当中我们会使用两个你可能不熟悉的类:`ResultReceiver`和`IntentService`。`ResultReceiver`是一个可以允许我们用Service来更新线程的类;`IntentService`是一个可以生成用来处理后台任务的线程的`Service`子类(你需要知道,`Service`实际上是和你的应用运行在同一个线程的;当你继承了`Service`之后,你必须手动生成一个新的线程来处理费时操作)。 150 | 151 | 一个提供下载功能的`Service`看起来像这样: 152 | 153 | ```java 154 | public class DownloadService extends IntentService { 155 | public static final int UPDATE_PROGRESS = 8344; 156 | public DownloadService() { 157 | super("DownloadService"); 158 | } 159 | @Override 160 | protected void onHandleIntent(Intent intent) { 161 | String urlToDownload = intent.getStringExtra("url"); 162 | ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver"); 163 | try { 164 | URL url = new URL(urlToDownload); 165 | URLConnection connection = url.openConnection(); 166 | connection.connect(); 167 | // 这对你在进度条上面显示百分比很有用 168 | int fileLength = connection.getContentLength(); 169 | 170 | // download the file 171 | InputStream input = new BufferedInputStream(connection.getInputStream()); 172 | OutputStream output = new FileOutputStream("/sdcard/BarcodeScanner-debug.apk"); 173 | 174 | byte data[] = new byte[1024]; 175 | long total = 0; 176 | int count; 177 | while ((count = input.read(data)) != -1) { 178 | total += count; 179 | // 更新进度条.... 180 | Bundle resultData = new Bundle(); 181 | resultData.putInt("progress" ,(int) (total * 100 / fileLength)); 182 | receiver.send(UPDATE_PROGRESS, resultData); 183 | output.write(data, 0, count); 184 | } 185 | 186 | output.flush(); 187 | output.close(); 188 | input.close(); 189 | } catch (IOException e) { 190 | e.printStackTrace(); 191 | } 192 | 193 | Bundle resultData = new Bundle(); 194 | resultData.putInt("progress" ,100); 195 | receiver.send(UPDATE_PROGRESS, resultData); 196 | } 197 | } 198 | ``` 199 | 200 | 把这个`Service`添加到清单文件中: 201 | ``` 202 | 203 | ``` 204 | 205 | activity里面的代码: 206 | 207 | ```java 208 | // 像第一个例子里面一样初始化ProgressBar 209 | 210 | // 在这里启动下载 211 | mProgressDialog.show(); 212 | Intent intent = new Intent(this, DownloadService.class); 213 | intent.putExtra("url", "url of the file to download"); 214 | intent.putExtra("receiver", new DownloadReceiver(new Handler())); 215 | startService(intent); 216 | ``` 217 | 218 | 然后像这样来使用`ResultReceiver`: 219 | 220 | ```java 221 | private class DownloadReceiver extends ResultReceiver{ 222 | public DownloadReceiver(Handler handler) { 223 | super(handler); 224 | } 225 | 226 | @Override 227 | protected void onReceiveResult(int resultCode, Bundle resultData) { 228 | super.onReceiveResult(resultCode, resultData); 229 | if (resultCode == DownloadService.UPDATE_PROGRESS) { 230 | int progress = resultData.getInt("progress"); 231 | mProgressDialog.setProgress(progress); 232 | if (progress == 100) { 233 | mProgressDialog.dismiss(); 234 | } 235 | } 236 | } 237 | } 238 | ``` 239 | 240 | ##### 2.1 使用Groundy库 241 | [Groundy](http://casidiablo.github.com/groundy)是一个可以帮助你在后台服务中运行代码片段的库,它是基于`ResultReceiver`这一概念。但是这个库现在已经被标记为**过时**了(deprecated)。下面是**完整**代码的样子。 242 | 243 | 你要展示dialog的Activity: 244 | 245 | ```java 246 | public class MainActivity extends Activity { 247 | 248 | private ProgressDialog mProgressDialog; 249 | 250 | @Override 251 | public void onCreate(Bundle savedInstanceState) { 252 | super.onCreate(savedInstanceState); 253 | setContentView(R.layout.main); 254 | 255 | findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() { 256 | public void onClick(View view) { 257 | String url = ((EditText) findViewById(R.id.edit_url)).getText().toString().trim(); 258 | Bundle extras = new Bundler().add(DownloadTask.PARAM_URL, url).build(); 259 | Groundy.create(DownloadExample.this, DownloadTask.class) 260 | .receiver(mReceiver) 261 | .params(extras) 262 | .queue(); 263 | 264 | mProgressDialog = new ProgressDialog(MainActivity.this); 265 | mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 266 | mProgressDialog.setCancelable(false); 267 | mProgressDialog.show(); 268 | } 269 | }); 270 | } 271 | 272 | private ResultReceiver mReceiver = new ResultReceiver(new Handler()) { 273 | @Override 274 | protected void onReceiveResult(int resultCode, Bundle resultData) { 275 | super.onReceiveResult(resultCode, resultData); 276 | switch (resultCode) { 277 | case Groundy.STATUS_PROGRESS: 278 | mProgressDialog.setProgress(resultData.getInt(Groundy.KEY_PROGRESS)); 279 | break; 280 | case Groundy.STATUS_FINISHED: 281 | Toast.makeText(DownloadExample.this, R.string.file_downloaded, Toast.LENGTH_LONG); 282 | mProgressDialog.dismiss(); 283 | break; 284 | case Groundy.STATUS_ERROR: 285 | Toast.makeText(DownloadExample.this, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show(); 286 | mProgressDialog.dismiss(); 287 | break; 288 | } 289 | } 290 | }; 291 | } 292 | ``` 293 | 294 | **Groundy**使用一个`GroundyTask`的实现类来下载文件和显示进度: 295 | 296 | ```java 297 | public class DownloadTask extends GroundyTask { 298 | public static final String PARAM_URL = "com.groundy.sample.param.url"; 299 | 300 | @Override 301 | protected boolean doInBackground() { 302 | try { 303 | String url = getParameters().getString(PARAM_URL); 304 | File dest = new File(getContext().getFilesDir(), new File(url).getName()); 305 | DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this)); 306 | return true; 307 | } catch (Exception pokemon) { 308 | return false; 309 | } 310 | } 311 | } 312 | ``` 313 | 314 | 添加这行代码到清单文件中: 315 | ``` 316 | 317 | ``` 318 | 319 | 这实在是太简单了!只需要从[Github](https://github.com/casidiablo/groundy/downloads)上下载最新的jar文件就可以开始了。但是要记住,Groundy的主要用途是在后台服务中调用外部的REST API,然后更简单地在UI上更新结果。如果你要在你的应用里面做类似的事情,这个库将非常有帮助。 320 | 321 | ##### 2.2 使用[ion](https://github.com/koush/ion) 322 | 323 | #### 3. 使用`DownloadManager`类(只适用于GingerBread及其以上的系统) 324 | 325 | 这个方法很酷炫,你不需要担心手动下载文件、处理线程和流之类等乱七八糟的东西。GingerBread带来一项新功能:`DownloadManager`。`DownloadManager`允许你轻松地下载文件和把复杂计算的任务委托给系统。 326 | 327 | 首先,我们来看一下工具方法: 328 | 329 | ```java 330 | /** 331 | * @param 使用context来检查设备信息和 DownloadManager 的信息 332 | * @return 如果downloadmanager可用则返回 true 333 | */ 334 | public static boolean isDownloadManagerAvailable(Context context) { 335 | 336 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { 337 | return true; 338 | } 339 | return false; 340 | } 341 | ``` 342 | 343 | 方法的名字就已经告诉了我们一切,只有当你确保可以使用`DownloadManager`的时候,你才可以做下面的事情: 344 | 345 | ```java 346 | String url = "url you want to download"; 347 | DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); 348 | request.setDescription("Some descrition"); 349 | request.setTitle("Some title"); 350 | // in order for this if to run, you must use the android 3.2 to compile your app 351 | // 为了保证这个if语句会运行,你必须使用android 3.2来编译 (译者注:应该是大于android 3.2的版本) 352 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 353 | request.allowScanningByMediaScanner(); 354 | request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); 355 | } 356 | request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "name-of-the-file.ext"); 357 | 358 | // 获得下载服务和队列文件 359 | DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); 360 | manager.enqueue(request); 361 | ``` 362 | 363 | #### 最后的一些思考 364 | 365 | 第一个和第二个方法只是冰山一角。如果你想你的应用更加健壮,你得留意许多事情。这里是一些建议: 366 | 367 | + 你必须检查用户是否有Internet连接。 368 | + 确保你有正确的权限(`Internet`和`WRITE_EXTERNAL_STORAGE`),如果要检查网络可用性,你还需要`ACCESS_NETWORK_STATE`权限。 369 | + 确保你要保存下载文件的目录存在,并且有相应的写入权限。 370 | + 如果下载的文件太大,你可能需要实现一种方法来确保上次的请求失败后,可以接着从来。 371 | + 如果可以有暂停或者取消下载的选项,用户会很感激你的! 372 | 373 | 除非你想对下载过程有绝对的控制权,否则我强烈推荐你使用`DownloadManager`。因为他已经处理好了上面的大部分建议。 374 | 375 | stackoverflow链接:http://stackoverflow.com/questions/3028306/download-a-file-with-android-and-showing-the-progress-in-a-progressdialog 376 | -------------------------------------------------------------------------------- /contents/efficiency-of-java-double-brace-initialization.md: -------------------------------------------------------------------------------- 1 | ## "Double Brace Initialization"的效率问题 2 | 3 | #### 问题 4 | 5 | `Double Brace Initialization`是java的隐藏特性,它有着如下诱人的语法: 6 | 7 | ```java 8 | Set flavors = new HashSet() {{ 9 | add("vanilla"); 10 | add("strawberry"); 11 | add("chocolate"); 12 | add("butter pecan"); 13 | }}; 14 | ``` 15 | 16 | 但是,这个特性听说不是很高效率,是否要限制一次性使用? 17 | 18 | #### 回答 19 | 20 | 当我使用匿名内部类时出现了如下的问题: 21 | 22 | ```Auto 23 | 2009/05/27 16:35 1,602 DemoApp2$1.class 24 | 2009/05/27 16:35 1,976 DemoApp2$10.class 25 | 2009/05/27 16:35 1,919 DemoApp2$11.class 26 | 2009/05/27 16:35 2,404 DemoApp2$12.class 27 | 2009/05/27 16:35 1,197 DemoApp2$13.class 28 | 29 | /* snip */ 30 | 31 | 2009/05/27 16:35 1,953 DemoApp2$30.class 32 | 2009/05/27 16:35 1,910 DemoApp2$31.class 33 | 2009/05/27 16:35 2,007 DemoApp2$32.class 34 | 2009/05/27 16:35 926 DemoApp2$33$1$1.class 35 | 2009/05/27 16:35 4,104 DemoApp2$33$1.class 36 | 2009/05/27 16:35 2,849 DemoApp2$33.class 37 | 2009/05/27 16:35 926 DemoApp2$34$1$1.class 38 | 2009/05/27 16:35 4,234 DemoApp2$34$1.class 39 | 2009/05/27 16:35 2,849 DemoApp2$34.class 40 | 41 | /* snip */ 42 | 43 | 2009/05/27 16:35 614 DemoApp2$40.class 44 | 2009/05/27 16:35 2,344 DemoApp2$5.class 45 | 2009/05/27 16:35 1,551 DemoApp2$6.class 46 | 2009/05/27 16:35 1,604 DemoApp2$7.class 47 | 2009/05/27 16:35 1,809 DemoApp2$8.class 48 | 2009/05/27 16:35 2,022 DemoApp2$9.class 49 | ``` 50 | 51 | 这是在我的一个简单应用中所产生的类信息。在这个应用中,使用了大量的匿名内部类,这些类会被单独地编译成`class`文件。 52 | 53 | `Double Brace Initialization`是一个带有实例初始化块的匿名内部类。这就意味着每一个新的类的产生都会执行一次实例块,这样的目的通常是为了创建一个简单的对象。 54 | 55 | java虚拟机在使用类之前需要去读取其classes信息,然后执行字节码校验等流程。所以为了保存这些`class`文件,所需要的磁盘空间会增大。 56 | 57 | 这个可以说是`Double Brace Initialization`的开销。所以尽量不要过分使用。 58 | 59 | --- 60 | 61 | 在java的介绍中,`Double Brace Initialization`有着如下的写法: 62 | 63 | ```java 64 | List list = new ArrayList() {{ 65 | add("Hello"); 66 | add("World!"); 67 | }}; 68 | ``` 69 | 70 | 看起来像是java的隐藏特性,其实它只是下面代码的一个重写: 71 | 72 | ```java 73 | List list = new ArrayList() { 74 | 75 | // 实例初始化块 76 | { 77 | add("Hello"); 78 | add("World!"); 79 | } 80 | }; 81 | ``` 82 | 83 | 所以,它只是在匿名内部类中加上了实例初始化块而已。 84 | 85 | --- 86 | 87 | Joshua Bloch希望以后的集合代码能够像这样简介: 88 | 89 | ```java 90 | List intList = [1, 2, 3, 4]; 91 | 92 | Set strSet = {"Apple", "Banana", "Cactus"}; 93 | 94 | Map truthMap = { "answer" : 42 }; 95 | ``` 96 | 97 | 但目前还没有这样的语法。 98 | 99 | --- 100 | 101 | 实践 102 | 103 | 做一个简单的试验:创建1000个带着`"Hello"`和`"World!"`元素的`ArrayList` 104 | 105 | * 方法1:Double Brace Initialization 106 | 107 | ``` 108 | List l = new ArrayList() {{ 109 | add("Hello"); 110 | add("World!"); 111 | }}; 112 | ``` 113 | 114 | * 方法2:初始化`ArrayList`并调用`add`方法 115 | 116 | ```java 117 | List l = new ArrayList(); 118 | l.add("Hello"); 119 | l.add("World!"); 120 | ``` 121 | 122 | 我修改了java的源文件使之能够为每种上述方法分别创建出1000个实例 123 | 124 | * 方法1的测试 125 | 126 | ```java 127 | class Test1 { 128 | public static void main(String[] s) { 129 | long st = System.currentTimeMillis(); 130 | 131 | List l0 = new ArrayList() {{ 132 | add("Hello"); 133 | add("World!"); 134 | }}; 135 | 136 | List l1 = new ArrayList() {{ 137 | add("Hello"); 138 | add("World!"); 139 | }}; 140 | 141 | /* snip */ 142 | 143 | List l999 = new ArrayList() {{ 144 | add("Hello"); 145 | add("World!"); 146 | }}; 147 | 148 | System.out.println(System.currentTimeMillis() - st); 149 | } 150 | ``` 151 | 152 | * 方法2的测试 153 | 154 | ```java 155 | class Test2 { 156 | public static void main(String[] s) { 157 | long st = System.currentTimeMillis(); 158 | 159 | List l0 = new ArrayList(); 160 | l0.add("Hello"); 161 | l0.add("World!"); 162 | 163 | List l1 = new ArrayList(); 164 | l1.add("Hello"); 165 | l1.add("World!"); 166 | 167 | /* snip */ 168 | 169 | List l999 = new ArrayList(); 170 | l999.add("Hello"); 171 | l999.add("World!"); 172 | 173 | System.out.println(System.currentTimeMillis() - st); 174 | } 175 | } 176 | ``` 177 | 178 | 然后得出了下面的测试时间: 179 | 180 | ```Auto 181 | Test1 Times (ms) Test2 Times (ms) 182 | ---------------- ---------------- 183 | 187 0 184 | 203 0 185 | 203 0 186 | 188 0 187 | 188 0 188 | 187 0 189 | 203 0 190 | 188 0 191 | 188 0 192 | 203 0 193 | ``` 194 | 195 | 从上面我们可以看到,`Double Brace Initialization`平均时间花费了190ms左右。 196 | 同时,另外一种方法平均只用了0ms。 197 | 198 | 所以,在第一个方法测试程序中,即`Double Brace Initialization`产生了1000个`class`文件。 199 | 200 | ## StackOverflow地址 201 | 202 | [http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization](http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization) -------------------------------------------------------------------------------- /contents/failed-to-load-the-JNI-shared-library(JDK).md: -------------------------------------------------------------------------------- 1 | 加载JNI共享库失败(JDK) 2 | 问题:当我试图打开Eclipse时,会弹出一个提示写着: 3 | Failed to load the JNI shared library "C:/JDK/bin/client/jvm.dll"`. 4 | 然后,Eclipse会强制关闭。 5 | 我做了以下几点: 6 | ·我检查那个路径有没有存在什么,真的有存在。 7 | ·我的Eclipse和Java SE Development Kit都是64位的。我检查我的系统,它能处理64位。 8 | ·我也在Google和Stack Overflow搜索解决方法,我找到唯一的方法是下载一个32位版本的JDK和Eclipse。 9 | 下载32位版本是我没办法下的办法。但还有其他的解决方法吗? 10 | 11 | 回答: 12 | 你需要一个三件套: 13 | ·64位的操作系统 14 | ·64位的Java 15 | ·64位的Eclipse 16 | 17 | - stackoverflow原址: 18 | http://stackoverflow.com/questions/7352493/failed-to-load-the-jni-shared-library-jdk 19 | 20 | -------------------------------------------------------------------------------- /contents/generating-random-integers-in-a-range-with-Java.md: -------------------------------------------------------------------------------- 1 | # java 产生指定范围的随机数 2 | 问题,如何使用 java 产生 0~10,5~10 之间的随机数? 3 | 4 | ## [Math.random()](http://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#random--) 5 | 6 | Math.random() 可以产生一个 **大于等于** 0 且 **小于** 1 的双精度伪随机数,假设需要产生 ”0《= 随机数 <=10” 的随机数,可以这样做: 7 | 8 | ``` 9 | int num =(int)(Math.random() * 11); 10 | 11 | ``` 12 | 13 | 那如何产生 “5 <= 随机数 <= 10” 的随机数呢? 14 | 15 | ``` 16 | int num = 5 + (int)(Math.random() * 6); 17 | 18 | ``` 19 | 20 | 生成 “min <= 随机数 <= max ” 的随机数 21 | 22 | ``` 23 | int num = min + (int)(Math.random() * (max-min+1)); 24 | 25 | ``` 26 | 27 | 28 | ## [java.util.Random](http://docs.oracle.com/javase/8/docs/api/java/util/Random.html) 29 | 30 | Random 是 java 提供的一个伪随机数生成器。 31 | 32 | 生成 “ min <= 随机数 <= max ” 的随机数: 33 | 34 | ``` 35 | import java.util.Random; 36 | 37 | /** 38 | * Returns a pseudo-random number between min and max, inclusive. 39 | * The difference between min and max can be at most 40 | * Integer.MAX_VALUE - 1. 41 | * 42 | * @param min Minimum value 43 | * @param max Maximum value. Must be greater than min. 44 | * @return Integer between min and max, inclusive. 45 | * @see java.util.Random#nextInt(int) 46 | */ 47 | public static int randInt(int min, int max) { 48 | 49 | // NOTE: Usually this should be a field rather than a method 50 | // variable so that it is not re-seeded every call. 51 | Random rand = new Random(); 52 | 53 | // nextInt is normally exclusive of the top value, 54 | // so add 1 to make it inclusive 55 | int randomNum = rand.nextInt((max - min) + 1) + min; 56 | 57 | return randomNum; 58 | } 59 | 60 | ``` 61 | 62 | ## 标准库 63 | 64 | 在实际使用中,没有必要区重新写一次这些随机数的生成规则,可以借助一些标准库完成。如 [commons-lang](https://commons.apache.org/proper/commons-lang/index.html). 65 | 66 | org.apache.commons.lang3.RandomUtils 提供了如下产生指定范围的随机数方法: 67 | 68 | ``` 69 | // 产生 start <= 随机数 < end 的随机整数 70 | public static int nextInt(final int startInclusive, final int endExclusive); 71 | // 产生 start <= 随机数 < end 的随机长整数 72 | public static long nextLong(final long startInclusive, final long endExclusive); 73 | // 产生 start <= 随机数 < end 的随机双精度数 74 | public static double nextDouble(final double startInclusive, final double endInclusive); 75 | // 产生 start <= 随机数 < end 的随机浮点数 76 | public static float nextFloat(final float startInclusive, final float endInclusive); 77 | 78 | ``` 79 | 80 | org.apache.commons.lang3.RandomStringUtils 提供了生成随机字符串的方法,简单介绍一下: 81 | 82 | ``` 83 | // 生成指定个数的随机数字串 84 | public static String randomNumeric(final int count); 85 | // 生成指定个数的随机字母串 86 | public static String randomAlphabetic(final int count); 87 | // 生成指定个数的随机字母数字串 88 | public static String randomAlphanumeric(final int count); 89 | 90 | ``` 91 | 92 | stackoverflow原址:http://stackoverflow.com/questions/363681/generating-random-integers-in-a-range-with-java 93 | 文章若有写得不正确或不通顺的地方,恳请你指出,谢谢。 94 | 95 | -------------------------------------------------------------------------------- /contents/get-current-stack-trace-in-java.md: -------------------------------------------------------------------------------- 1 | ##获取完整的堆栈信息 2 | 3 | ###问题 4 | 捕获了异常后,如何获取完整的堆栈轨迹(stack trace) 5 | 6 | ###回答 7 | ```java 8 | String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e) 9 | ``` 10 | 11 | ````java 12 | Thread.currentThread().getStackTrace(); 13 | ```` 14 | 15 | stackoverflow原址: 16 | http://stackoverflow.com/questions/1069066/how-can-i-get-the-current-stack-trace -------------------------------------------------------------------------------- /contents/how-can-i-convert-a-stack-trace-to-a-string.md: -------------------------------------------------------------------------------- 1 | #怎样将堆栈追踪信息转换为字符串 2 | ##问题 3 | 将`Throwable.getStackTrace()`的结果转换为一个字符串来来描述堆栈信息的最简单的方法是什么 4 | 5 | 6 | ###最佳答案 7 | 可以用下面的方法将异常堆栈信息转换为字符串类型。该类在Apache commons-lang-2.2.jar中可以找到: 8 | [`org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)`](org.apache.commons.lang.exception.ExceptionUtils.getStackTrace\(Throwable\)) 9 | 10 | ###答案二 11 | 用 [`Throwable.printStackTrace(PrintWriter pw)`](https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html#printStackTrace-java.io.PrintWriter-)可以输出堆栈信息: 12 | ````java 13 | StringWriter sw = new StringWriter(); 14 | PrintWriter pw = new PrintWriter(sw); 15 | t.printStackTrace(pw); 16 | sw.toString(); // stack trace as a string 17 | ```` 18 | 19 | ###答案三 20 | ````java 21 | StringWriter sw = new StringWriter(); 22 | e.printStackTrace(new PrintWriter(sw)); 23 | String exceptionAsString = sw.toString(); 24 | ```` 25 | 26 | ###答案四 27 | ````java 28 | public String stackTraceToString(Throwable e) { 29 | StringBuilder sb = new StringBuilder(); 30 | for (StackTraceElement element : e.getStackTrace()) { 31 | sb.append(element.toString()); 32 | sb.append("\n"); 33 | } 34 | return sb.toString(); 35 | } 36 | ```` 37 | 38 | stackoverflow链接: 39 | http://stackoverflow.com/questions/1149703/how-can-i-convert-a-stack-trace-to-a-string -------------------------------------------------------------------------------- /contents/how-can-i-create-an-executable-jar-with-dependencies-using-maven.md: -------------------------------------------------------------------------------- 1 | ## 如何使用maven把项目及其依赖打包为可运行jar包 2 | 3 | #### 问题 4 | 5 | 我想把java项目打包为可运行的分布式jar包。我该怎样做,才能把项目中maven所依赖的jar包导入到我的项目jar包中? 6 | 7 | #### 回答 8 | 9 | 在`pom.xml`文件中,加入如下的插件: 10 | 11 | ```xml 12 | 13 | 14 | 15 | maven-assembly-plugin 16 | 17 | 18 | 19 | 20 | fully.qualified.MainClass 21 | 22 | 23 | 24 | jar-with-dependencies 25 | 26 | 27 | 28 | 29 | 30 | ``` 31 | 32 | 之后,运行maven命令: 33 | 34 | > mvn clean compile assembly:single 35 | 36 | `clean`,`compile`,`assembly:single`任务将会依次被执行;`compile`任务必须写在`assembly:single`之前,否则打包后的jar包内将不会有你的编译代码。 37 | 38 | (译注:执行完后,会在你的maven项目的target目录下,生成想要的jar包,而不再需要使用`mvn package`命令进行打包) 39 | 40 | 通常情况下,上述maven命令执行后会自动绑定到项目的构建阶段,从而保证了以后在执行`mvn install`命令时的jar包也会被构建。 41 | (译注:下面是实际上完整的默认的`pom.xml`配置,只不过``可以被省略,若省略则按照下述默认的配置执行) 42 | 43 | ```xml 44 | 45 | maven-assembly-plugin 46 | 47 | 48 | 49 | fully.qualified.MainClass 50 | 51 | 52 | 53 | jar-with-dependencies 54 | 55 | 56 | 57 | 58 | make-assembly 59 | package 60 | 61 | single 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | #### 拓展 69 | 70 | 怎样去运行打包后的可运行jar包? 71 | 72 | * 对上述配置中已经指定了`main`函数所在类的jar包,打开命令行窗口,输入命令: 73 | 74 | ```java 75 | java -jar jar包的路径/jar包的名字.jar 76 | ``` 77 | 78 | 例如: 79 | 80 | ```Auto 81 | java -jar D:\my_java_project\maven_test.jar 82 | ``` 83 | 84 | * 若在pom.xml并没有指定`main`方法所在类,那么该jar的运行应采取如下命令: 85 | 86 | ```java 87 | java -cp jar包的路径/jar包的名字.jar main方法所在类的全限定名 88 | ``` 89 | 90 | 例如: 91 | 92 | ```java 93 | java -cp D:\my_java_project\maven_test.jar com.my.path.MainClass 94 | ``` 95 | 96 | 97 | #### StackOverflow地址 98 | 99 | [http://stackoverflow.com/questions/574594/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) -------------------------------------------------------------------------------- /contents/how-can-i-generate-an-md5-hash.md: -------------------------------------------------------------------------------- 1 | ##如何计算MD5值 2 | 3 | ###问题 4 | Java中有没有方法可以计算一个String的MD5值? 5 | 6 | 7 | ###回答 8 | 你可以用 ```MessageDigest``` 的MD5实例来计算String的MD5值。 9 | 10 | 使用 ```MessageDigest``` 和 String 时,一定要显式声明你的数据编码类型。如果你使用无参的 ```String.getBytes()``` , 它会以当前平台的默认编码来转换数据。不同平台的默认编码可能是不同的,这可能会导致你的数据不一致。 11 | 12 | ``` java 13 | import java.security.*; 14 | 15 | .. 16 | 17 | byte[] bytesOfMessage = yourString.getBytes("UTF-8"); 18 | MessageDigest md = MessageDigest.getInstance("MD5"); 19 | byte[] thedigest = md.digest(bytesOfMessage); 20 | ``` 21 | 22 | 如果你的要计算的数据量很大,你可以循环使用 ```.update(byte[])``` 方法来加载数据。加载完毕后用 ```.digest()``` 方法来得到计算出的MD5值。 23 | 24 | stackoverflow链接 25 | http://stackoverflow.com/questions/415953/how-can-i-generate-an-md5-hash 26 | -------------------------------------------------------------------------------- /contents/how-can-i-initialize-a-static-map.md: -------------------------------------------------------------------------------- 1 | # 初始化静态map # 2 | 3 | ## 问题 ## 4 | 5 | 怎么在Java中初始化一个静态的map 6 | 7 | 我想到的两种方法如下,大家是否有更好的建议呢? 8 | 9 | **方法一**:static初始化器 10 | 11 | **方法二**:实例初始化(匿名子类) 12 | 13 | 下面是描述上面两种方法的例子 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | public class Test{ 18 | private static final Map myMap = new HashMap(); 19 | static { 20 | myMap.put(1, "one"); 21 | myMap.put(2, "two"); 22 | } 23 | 24 | private static final Map myMap2 = new HashMap(){ 25 | { 26 | put(1, "one"); 27 | put(2, "two"); 28 | } 29 | }; 30 | } 31 | 32 | ## 答案 ## 33 | 34 | ### 答案1 ### 35 | 36 | 匿名子类初始化器是java的语法糖,我搞不明白为什么要用匿名子类来初始化,而且,如果类是final的话,它将不起作用 37 | 38 | 我使用static初始化器来创建一个固定长度的静态map 39 | 40 | public class Test{ 41 | private static final Map myMap; 42 | static{ 43 | Map aMap = ...; 44 | aMap.put(1,"one"); 45 | aMap.put(2,"two"); 46 | myMap = Collections.unmodifiableMap(aMap); 47 | } 48 | } 49 | 50 | 51 | ### 答案2 ### 52 | 53 | 我喜欢用Guava(是 Collection 框架的增强)的方法初始化一个静态的,不可改变的map 54 | 55 | static final Map MY_MAP = ImmutableMap.of( 56 | 1, "one", 57 | 2, "two" 58 | ); 59 | 60 | · 61 | 当map的 entry个数超过5个时,你就不能使用`ImmutableMap.of`。可以试试`ImmutableMap.bulider()` 62 | 63 | static final Map MY_MAP = ImmutableMap.builder() 64 | .put(1, "one") 65 | .put(2, "two") 66 | // ... 67 | .put(15, "fifteen") 68 | .build(); 69 | 70 | 71 | 72 | # 原文链接 # 73 | 74 | http://stackoverflow.com/questions/507602/how-can-i-initialize-a-static-map 75 | -------------------------------------------------------------------------------- /contents/how-can-i-pad-an-integers-with-zeros-on-the-left.md: -------------------------------------------------------------------------------- 1 | ## 如何在整数左填充0 2 | 3 | ### 问题 4 | 如何在整数左填充0 5 | 举例 1 = "0001" 6 | 7 | 8 | ### 答案一,String.format 9 | 10 | String.format("%05d", yournumber); 11 | 12 | 用0填充,总长度为5 13 | https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html 14 | 15 | ### 答案二,ApacheCommonsLanguage 16 | 如果需要在Java 1.5前使用,可以利用 Apache Commons Language 方法 17 | 18 | org.apache.commons.lang.StringUtils.leftPad(String str, int size, '0') 19 | 20 | ### 答案三,DecimalFormat 21 | import java.text.DecimalFormat; 22 | class TestingAndQualityAssuranceDepartment 23 | { 24 | public static void main(String [] args) 25 | { 26 | int x=1; 27 | DecimalFormat df = new DecimalFormat("00"); 28 | System.out.println(df.format(x)); 29 | } 30 | } 31 | 32 | ### 答案四,自己实现 33 | 如果效率很重要的话,相比于 String.format 函数的可以自己实现 34 | 35 | /** 36 | * @param in The integer value 37 | * @param fill The number of digits to fill 38 | * @return The given value left padded with the given number of digits 39 | */ 40 | public static String lPadZero(int in, int fill){ 41 | 42 | boolean negative = false; 43 | int value, len = 0; 44 | 45 | if(in >= 0){ 46 | value = in; 47 | } else { 48 | negative = true; 49 | value = - in; 50 | in = - in; 51 | len ++; 52 | } 53 | 54 | if(value == 0){ 55 | len = 1; 56 | } else{ 57 | for(; value != 0; len ++){ 58 | value /= 10; 59 | } 60 | } 61 | 62 | StringBuilder sb = new StringBuilder(); 63 | 64 | if(negative){ 65 | sb.append('-'); 66 | } 67 | 68 | for(int i = fill; i > len; i--){ 69 | sb.append('0'); 70 | } 71 | 72 | sb.append(in); 73 | 74 | return sb.toString(); 75 | } 76 | 77 | 效率对比 78 | 79 | public static void main(String[] args) { 80 | Random rdm; 81 | long start; 82 | 83 | // Using own function 84 | rdm = new Random(0); 85 | start = System.nanoTime(); 86 | 87 | for(int i = 10000000; i != 0; i--){ 88 | lPadZero(rdm.nextInt(20000) - 10000, 4); 89 | } 90 | System.out.println("Own function: " + ((System.nanoTime() - start) / 1000000) + "ms"); 91 | 92 | // Using String.format 93 | rdm = new Random(0); 94 | start = System.nanoTime(); 95 | 96 | for(int i = 10000000; i != 0; i--){ 97 | String.format("%04d", rdm.nextInt(20000) - 10000); 98 | } 99 | System.out.println("String.format: " + ((System.nanoTime() - start) / 1000000) + "ms"); 100 | } 101 | 102 | 结果 103 | 自己的实现:1697ms 104 | String.format:38134ms 105 | 106 | ### 答案,Google Guava 107 | Maven: 108 | 109 | 110 | guava 111 | com.google.guava 112 | 14.0.1 113 | 114 | 样例: 115 | 116 | Strings.padStart("7", 3, '0') returns "007" 117 | Strings.padStart("2020", 3, '0') returns "2020" 118 | 注意: 119 | Guava 是非常有用的库,它提供了很多有用的功能,包括了Collections, Caches, Functional idioms, Concurrency, Strings, Primitives, Ranges, IO, Hashing, EventBus等 120 | 121 | 122 | stackoverflow原址: 123 | http://stackoverflow.com/questions/473282/how-can-i-pad-an-integers-with-zeros-on-the-left -------------------------------------------------------------------------------- /contents/how-can-i-permanently-have-line-numbers-in-intellij.md: -------------------------------------------------------------------------------- 1 | ## 如何让IntelliJ编辑器永久性显示代码行数 2 | 3 | #### 问题 4 | 5 | 如何让IntelliJ编辑器永久性显示代码行数 6 | 7 | #### 回答 8 | 9 | ##### IntelliJ 14.0之后的版本 10 | 11 | 打开软件的菜单`File`->`Settings`->`Editor`->`General`->`Appearance`,在右侧的配置`Show line numbers`打勾: 12 | ![image1][1] 13 | 14 | ##### IntelliJ 8.1.2 - 13.X的版本 15 | 16 | 打开软件的菜单`File`->`Settings`->`Editor`->`Appearance`,在右侧的配置`Show line numbers`打勾: 17 | ![images2][2] 18 | 19 | #### 拓展 20 | 21 | [IntelliJ IDEA 使用教程](http://www.phperz.com/article/15/0923/159068.html) 22 | 23 | #### StackOverflow地址 24 | 25 | http://stackoverflow.com/questions/13751/how-can-i-permanently-have-line-numbers-in-intellij 26 | 27 | 28 | [1]: http://i.stack.imgur.com/9DL9q.png 29 | [2]: http://i.stack.imgur.com/JVZlJ.jpg -------------------------------------------------------------------------------- /contents/how-can-i-test-if-an-array-contains-a-certain-value.md: -------------------------------------------------------------------------------- 1 | # 如何测试一个数组是否包含指定的值 2 | 3 | 指定数组,如: 4 | ``` 5 | public static final String[] VALUES = new String[] {"AB","BC","CD","AE"}; 6 | ``` 7 | 现在制定一个值 s,有哪些比较好的方式,判断这个数组 VALUES 是否包含值 s? 8 | 9 | ## 简单且优雅的方法: 10 | 11 | 1. Arrays.asList(...).contains(...) 12 | 13 | 2. 使用 Apache Commons Lang包中的ArrayUtils.contains 14 | 15 | 16 | ```java 17 | String[] fieldsToInclude = { "id", "name", "location" }; 18 | 19 | if ( ArrayUtils.contains( fieldsToInclude, "id" ) ) { 20 | // Do some stuff. 21 | } 22 | ``` 23 | 24 | 25 | ## 自己写逻辑 26 | 问题的本质,其实是一个查找的问题,即查找一个数组是否包含某个值。对于原始类型,若是无序的数组,可以直接写一个 for 循环: 27 | ``` 28 | public static boolean useLoop(String[] arr, String targetValue) { 29 | for(String s: arr){ 30 | if(s.equals(targetValue)) 31 | return true; 32 | } 33 | return false; 34 | } 35 | ``` 36 | 若是有序的数组,可以考虑二分查找或者其他查找算法: 37 | ``` 38 | public static boolean useArraysBinarySearch(String[] arr, String targetValue) { 39 | int a = Arrays.binarySearch(arr, targetValue); 40 | if(a >= 0) 41 | return true; 42 | else 43 | return false; 44 | } 45 | ``` 46 | 47 | 若数组里包含的是一个个对象,实际上比较就是引用是否相等(String 类型是判断 值是否相等),本质就是比较 hashcode 和 equal 方法,可以考虑使用 List 或者 Set,如下 48 | ``` 49 | public static boolean useList(String[] arr, String targetValue) { 50 | return Arrays.asList(arr).contains(targetValue); 51 | } 52 | ``` 53 | 54 | ``` 55 | public static boolean useLoop(String[] arr, String targetValue) { 56 | for(String s: arr){ 57 | if(s.equals(targetValue)) 58 | return true; 59 | } 60 | return false; 61 | } 62 | ``` 63 | 64 | stackoverflow原址:http://stackoverflow.com/questions/1128723/how-can-i-test-if-an-array-contains-a-certain-value 65 | -------------------------------------------------------------------------------- /contents/how-do-i-call-one-constructor-from-another-in-java.md: -------------------------------------------------------------------------------- 1 | ##能否在一个构造器中调用另一个构造器 2 | 3 | ###问题 4 | 能否在一个构造器中调用另一个构造器(在同一个类中,不是子类)?如果可以,怎么做? 5 | 调用另一个构造器的最好方法是什么(如果有几种方法可以选择的话)? 6 | 7 | 8 | ###回答 9 | 可以这样做: 10 | ```java 11 | public class Foo 12 | { 13 | private int x; 14 | 15 | public Foo() 16 | { 17 | this(1); 18 | } 19 | 20 | public Foo(int x) 21 | { 22 | this.x = x; 23 | } 24 | } 25 | ``` 26 | 如果你想调用一个特定的父类构造器,而不是本类的构造器,应该使用super,而不是this. 27 | 请注意,在构造器中,你只能调用一次其他的构造器。并且调用其他构造器的语句,必须是这个构造器的第一个语句。 28 | 29 | stackoverflow原址: 30 | http://stackoverflow.com/questions/285177/how-do-i-call-one-constructor-from-another-in-java 31 | -------------------------------------------------------------------------------- /contents/how-do-i-compare-strings-in-java.md: -------------------------------------------------------------------------------- 1 | ##在java中如何对比(compare)string 2 | 3 | - `==`对应的是指针相等,也就是他们是否为同一个对象 4 | - `.equals()`对应的是值相等,也就是逻辑相等 5 | 6 | 因此,如果你想检查两个字符串是否为相同值,那么应该用`.equals()`方法 7 | ```java 8 | //值是相等的 9 | new String("test").equals("test") // --> true 10 | 11 | // ... 值相等,但不是同个对象(指向不同的地址空间) 12 | new String("test") == "test" // --> false 13 | 14 | // ... 同上 15 | new String("test") == new String("test") // --> false 16 | 17 | // 这个返回true,是因为这种写法属于字符串字面量,编译器会维护一个常量池,相同的字面量,都会指向相同的一个对象 18 | "test" == "test" // --> true 19 | 20 | ``` 21 | 因此, 值的对比,一般都是用equals方法。字符串字面量之间的对比,也可以用==(大家知其所以然即可,但没必要用==) 22 | 23 | 下面多举个字符串字面量的例子,下面代码中,前四个对比,返回true,最后一个返回false。 24 | ``` java 25 | public static final String test1 = "test"; 26 | public static final String test2 = "test"; 27 | 28 | @Test 29 | public void test() { 30 | 31 | String test3 = "test"; 32 | String test = "test"; 33 | 34 | System.out.println(test3.equals(test)); 35 | System.out.println(test3 == test); 36 | System.out.println(test1.equals(test2)); 37 | System.out.println(test1 == test2); 38 | System.out.println(test1 == new String("test")); 39 | } 40 | ``` 41 | 42 | ###其他 43 | - 如果你重写了equal方法,记得相对应地修改hashcode方法,否则将会违反这两个方法的对等关系,如果两个对象是相等(equal)的,那么两个对象调用hashCode必须产生相同的整数结果,即:equal为true,hashCode必须为true,equal为false,hashCode也必须为false 44 | - 如果要忽略大小写进行对比,可以用equalsIgnoreCase()方法 45 | -------------------------------------------------------------------------------- /contents/how-do-i-create-a-java-string-from-the-contents-of-a-file.md: -------------------------------------------------------------------------------- 1 | ##如何从文件里读取字符串 2 | 3 | ###从文件里读取所有文本: 4 | 5 | 代码: 6 | ```java 7 | static String readFile(String path, Charset encoding) 8 | throws IOException 9 | { 10 | byte[] encoded = Files.readAllBytes(Paths.get(path)); 11 | return new String(encoded, encoding); 12 | } 13 | 14 | ``` 15 | 16 | ###一行一行读入文本: 17 | 18 | Java 7 提供了一个方便的方法可以直接将文件中的文本一行一行读入,存放在一个List容器里。 19 | ```JAVA 20 | List lines = Files.readAllLines(Paths.get(path), encoding); 21 | ``` 22 | 23 | ###内存使用率 24 | 25 | 第一个方法,一次读取所有文本的方法,占用内存较多,因为它一次性保留了文件的所有原始信息,包括换行符之类的“无用”字符。 26 | 27 | 第二个方法,按行读入,比起一次性全部读入,要消耗更少的内存。因为它每次只将一行的文件信息放在缓存中。然而,如果文本文件很大,这种方法依然会占用很多内存。 28 | 29 | 如果你的程序需要处理很大的文本文件,在设计的时候就要考虑,分配一块固定的缓存,每次从流中读入文件的一部分放入缓存,处理,然后清空缓存,把下一部分读入缓存,直到处理完所有的数据。 30 | 31 | 这里的“很大”是相对于计算机性能的。一般来说,几十个G的文件应当算是大文件。 32 | 33 | ###字符编码 34 | 35 | 还有一件事需要注意,就是字符编码。不同的平台有自己的默认编码,所以有时候你的程序需要指定编码,来保持平台无关/跨平台。 36 | 37 | ```StandardCharsets``` 类定义了常用的编码类型,你可以用如下方法调用: 38 | 39 | ```java 40 | String content = readFile("test.txt", StandardCharsets.UTF_8); 41 | ``` 42 | 43 | 可以通过```Charset```类来获得平台默认的字符编码。 44 | 45 | ```java 46 | String content = readFile("test.txt", Charset.defaultCharset()); 47 | ``` 48 | 49 | 注: 这个答案与之前Java6版本时的答案完全不同。Java 7 新增的工具类极大的优化了字符处理,文件读取等功能。Java 6 常用的内存映射方法已不适合在Java 7 以后的版本使用。 50 | 51 | ### 原文链接 52 | http://stackoverflow.com/questions/326390/how-do-i-create-a-java-string-from-the-contents-of-a-file 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /contents/how-do-i-decompile-java-class-files.md: -------------------------------------------------------------------------------- 1 | ## 如何对Java class文件进行反编译 ## 2 | 3 | 可以用什么程序来编译class文件 会得到java代码还是JVM编译的代码? 4 | 在这个网站上性能讨论的问题上经常看到进行反编译文件来看编译器如何优化一些东西 5 | 6 | “反编译”的艺术也可以被认为是逆向工程。虽然有时在逆向工程时你并不总是能够访问二进制文件。 7 | 8 | 9 | 没人提到 bytecodeviewer.com吗,它可以反编译为java源码和字节码(对于java源码是基于JAD) 10 | 11 | **www.javadecompilers.com** 12 | 13 | 它是最流行的Java编译器,用c++编写,速度很快 14 | 比较过时,且不提供支持,不支持Java5及以后的版本 15 | 这个网站也列出了其他的工具。 16 | 17 | 正如Salvador Valencia在评论(2017年9月)中所说 javadecompiler提供了一个SaaS,您可以将.cl​​ass文件上传到云端,并返回反编译代码。 18 | (原答案:2008年10月) 19 | 定义J2SE 5.0(Java SE 5)主要功能的JSR 176的最终版本已于2004年9月30日发布。 20 | 由Pavel Kouznetsov先生编写的着名Java反编译器JAD支持的最新Java版本是JDK 1.3。 21 | 22 | 23 | Java Decompiler(一个快速Java反编译器)具有: 24 | 显式支持反编译和分析Java 5+“.class”文件。 25 | 一个很好的GUI: 26 | ![](https://i.imgur.com/iXIWcl8.png) 27 | 28 | 它适用于从JDK 1.1.8到JDK 1.7.0以及其他(Jikes,JRockit等)的编译器。 29 | 它具有在线实时演示版本,实际上功能齐全!你可以在页面上删除一个jar文件,看看反编译的源代码而不安装任何东西。 30 | 31 | 32 | 比如: 33 | Procyon:开源(Apache 2) 34 | Krakatau:开源(GPLv3) 35 | CFR:开源(MIT) 36 | JAD 37 | DJ Java Decompiler 38 | Mocha 39 | 还有很多。 40 | 41 | 这些产生Java代码,可以让你看到JVM字节码。 42 | 要查看Java源代码,请检查一些反编译器。去搜索jad。 43 | 如果要查看字节码,只需使用JDK附带的javap即可。 44 | 45 | 46 | 47 | 我试了几个,Procyon对我来是最好的。它正在积极开发中,并支持最新版Java的许多功能。 48 | 49 | 以下是我试过的其他的: 50 | CFR 51 | 还可以,但是经常反编译失败。我会密切注意这个。还积极开发支持最新的Java功能。 52 | Krakatau 53 | 采用不同的方法,它尝试输出等效的Java代码,而不是尝试重建原始源,这有可能使混淆代码更好。根据我的测试,它与Procyon大致相当,但仍然很高兴有不同的东西。我确实必须使用-skip命令行标志,因此它不会停止错误。积极开发,有趣的是它是用Python编写的。 54 | JD-GUI 55 | 工作,但Procyon的输出要好得多。这是一个将Procyon输出与原始和JD-GUI进行比较的页面。 JD-GUI也可以作为Eclipse插件使用,它根本不适用于我。似乎不是开源的,发展似乎是零星的。 56 | JAD 57 | 工作,但只支持Java 1.4及更低版本。也可以作为Eclipse插件使用。不再在开发中。 58 | 59 | 60 | 我使用JAD Decompiler。 61 | 有一个Eclipse插件,jadeclipse。这挺好用的。 62 | 63 | 64 | JD-GUI真的很棒。你可以打开一个jar文件并浏览代码,就好像正在使用IDE一样。好东西。 65 | 66 | ### 原文链接 67 | http://stackoverflow.com/questions/272535/how-do-i-decompile-java-class-files -------------------------------------------------------------------------------- /contents/how-do-servlets-work-instantiation-shared-variables-and-multithreading.md: -------------------------------------------------------------------------------- 1 | ## How do servlets work? Instantiation, shared variables and multithreading 2 | ### 问题: 3 | 假设,我有一个web服务器可以支持无数的servlets,对于通过这些servlets的信息,我正在获取这些servlets的上下文环境,并设置session变量。 4 | 现在,如果有两个或者更多的user用户发送请求到这个服务器,session变量会发生什么变化?session对于所有的user是公共的还是不同的user拥有不同的session。如果用户彼此之间的session是不同的,那么服务器怎么区分辨别不同的用户呢? 5 | 另外一些相似的问题,如果有N个用户访问一个具体的servlets,那么这个servlets是只在第一个用户第一次访问的时候实例化,还是为每一个用户各自实例化呢? 6 | 7 | ### 答案: 8 | #### ServletContext 9 | 当servletcontainer(像tomcat)启动的时候,它会部署和加载所有的webapplications,当一个webapplication加载完成后,servletcontainer就会创建一个ServletContext,并且保存在服务器的内存中。这个webapp的web.xml会被解析,web.xml中的每个```, and ```或者通过注解```@WebServlet, @WebFilter and @WebListener```,都会被创建一次并且也保存在服务器的内存中。对于所有filter,```init()```方法会被直接触发,当servletcontainer关闭的时候,它会unload所有的webapplications,触发所有实例化的servlets和filters的```destroy()```方法,最后,servletcontext和所有的servlets,filter和listener实例都会被销毁。 10 | 11 | #### HttpServletRequest and HttpServletResponse 12 | servletcontainer 是附属于webserver的,而这个webserver会持续监听一个目标端口的```HTTP request```请求,这个端口在开发中经常会被设置成8080,而在生产环境会被设置成80。当一个客户端(比如用户的浏览器)发送一个HTTP request,servletcontainer就会创建新的HttpServletRequest对象和HttpServletResponse对象。。。。 13 | 14 | 在有filter的情况下,```doFilter()```方法会被触发。当代码调用```chain.doFilter(request, response)```时候,请求会经过下一个过滤器filter,如果没有了过滤器,会到达servlet。在servlets的情况下,```service()```触发,然后根据```request.getMethod()```确定执行doGet()还是```doPost()```,如果当前servlet找不到请求的方法,返回405error。 15 | 16 | request对象提供了HTTP请求所有的信息,比如request headers和request body,response对象提供了控制和发送HTTP响应的的能力,并且以你想要的方式,比如设置headers和body。当HTTP响应结束,请求和响应对象会被销毁(实际上,大多数container将会清洗到这些对象的状态然后回收这些事例以重新利用) 17 | ####httpSession 18 | 当客户端第一次访问webapp或者通过```request.getSession()```方法第一次获取httpSession 19 | ,servletcontainer 将会创建一个新的HttpSession 对象,产生一个长的唯一的ID标记session(可以通过session.getId()),并且将这个session存储在server内存中。servletcontainer 同时会在HTTP response的Header中设置```Set-Cookie```cookie值,其中cookie name为JSESSIONID,cookie value为唯一的长ID值。 20 | 21 | 在接下来的连续请求中,客户端浏览器都要cookie通过header带回,然后servletcontainer 会根据cookie中的JSESSIONID 值,获得server内存中的对应的httpSession。 22 | 23 | 只要没超过``````设定的值,httpSession对象会一直存在,``````大小可以在web.xml中设定,默认是30分钟。所以如果连续30分钟之内客户端不再访问webapp,servletcontainer就会销毁对应的session。接下来的request请求即使cookies依旧存在,但是却不再有对应的session了。servletcontainer 会创建新的session。 24 | 25 | 另外一方面,session cookie在浏览器端有默认的生命时长,就是只要浏览器一直在运行,所以当浏览器关闭,浏览器端的cookie会被销毁。 26 | #### 总结 27 | - 只要webapp存在,ServletContext 一定会存在。并且ServletContext 是被所有session和request共享的。 28 | - 只要客户端用同一个浏览器和webapp交互并且该session没有在服务端超时,HttpSession 就会一直存在。并且在同一个会话中所有请求都是共享的。 29 | - 只有当完整的response响应到达,HttpServletRequest 和 HttpServletResponse才不再存活,并且不被共享。 30 | - 只要webapp存在,servlet、filter和listener就会存在。他们被所有请求和会话共享。 31 | - 只要问题中的对象存在,任何设置在ServletContext, HttpServletRequest 和 HttpSession中的属性就会存在。 32 | 33 | #### 线程安全 34 | 就是说,你主要关注的是线程安全性。你应该了解到,servlets和filter是被所有请求共享的。这正是Java的美妙之处,它的多线程和不同的线程可以充分利用同样的实例instance,否则对于每一个request请求都要重复创建和调用init()和destroy()开销太大。 35 | 36 | 但是你也应该注意到,你不应该把任何请求或会话作用域的数据作为一个servlet或过滤器的实例变量。这样会被其他会话的请求共享,并且那是线程不安全的!下面的例子阐明的这点: 37 | ``` 38 | public class ExampleServlet extends HttpServlet { 39 | 40 | private Object thisIsNOTThreadSafe; 41 | 42 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 43 | Object thisIsThreadSafe; 44 | 45 | thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests! 46 | thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe. 47 | } 48 | } 49 | ``` 50 | 51 | stackoverflow链接: 52 | 53 | http://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-shared-variables-and-multithreading 54 | -------------------------------------------------------------------------------- /contents/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests.md: -------------------------------------------------------------------------------- 1 | ## JUnit4如何断言确定异常的抛出 2 | 3 | #### 问题 4 | 5 | 在JUnit4单元测试中,我要怎样做才能测试出有特定的异常抛出?我能想到的就只有下面的方法: 6 | 7 | ```java 8 | @Test 9 | public void testFooThrowsIndexOutOfBoundsException() { 10 | boolean thrown = false; 11 | 12 | try { 13 | foo.doStuff(); 14 | } catch (IndexOutOfBoundsException e) { 15 | thrown = true; 16 | } 17 | 18 | assertTrue(thrown); 19 | } 20 | ``` 21 | 22 | #### 回答1 23 | 24 | 在JUnit4后支持下面的写法: 25 | ```java 26 | @Test(expected=IndexOutOfBoundsException.class) 27 | public void testIndexOutOfBoundsException() { 28 | ArrayList emptyList = new ArrayList(); 29 | Object o = emptyList.get(0); 30 | } 31 | ``` 32 | (译者:在`@Test`注解内提供了`expected`属性,你可以用它来指定一个`Throwble`类型,如果方法调用中抛出了这个异常,那么这条测试用例就相当于通过了) 33 | 34 | #### 回答2 35 | 36 | 如果你使用的是JUnit4.7,你可以使用如下的期望异常规则来验证异常信息: 37 | 38 | ```java 39 | public class FooTest { 40 | @Rule 41 | public final ExpectedException exception = ExpectedException.none(); 42 | 43 | @Test 44 | public void doStuffThrowsIndexOutOfBoundsException() { 45 | Foo foo = new Foo(); 46 | 47 | exception.expect(IndexOutOfBoundsException.class); 48 | foo.doStuff(); 49 | } 50 | } 51 | ``` 52 | 53 | 这种方式比`@Test(expected=IndexOutOfBoundsException.class)`要更好,如果是在调用`foo.doStuff()`方法之前就已经抛出异常的话,测试结果就不是我们想要的了。 54 | (译者:同时,`ExpectedException`还能够验证异常信息,如`exception.expectMessage("there is an exception!");` 55 | 56 | #### 拓展阅读 57 | 58 | 1. [JUnit:使用ExpectedException进行异常测试](http://www.tuicool.com/articles/ANviIz) 59 | 2. [JUnit4 用法详解](http://www.blogjava.net/jnbzwm/archive/2010/12/15/340801.html) 60 | 61 | #### StackOverflow地址: 62 | 63 | [http://stackoverflow.com/questions/156503/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) -------------------------------------------------------------------------------- /contents/how-does-the-java-for-each-loop-work.md: -------------------------------------------------------------------------------- 1 | ## Java的foreach循环是如何工作的? 2 | ### 问题 3 | ````java 4 | List someList = new ArrayList(); 5 | // add "monkey", "donkey", "skeleton key" to someList 6 | for (String item : someList) { 7 | System.out.println(item); 8 | } 9 | ```` 10 | 如果不用for each语法,等价的循环语句是什么样的? 11 | ### 回答 12 | ````java 13 | for(Iterator i = someList.iterator(); i.hasNext(); ) { 14 | String item = i.next(); 15 | System.out.println(item); 16 | } 17 | ```` 18 | 记住,如果需要在循环中使用i.remove;或者以某种方式获取实际的iterator,你不能使用for(:)语法,因为实际的Iterator很难被推断出来。 19 | 正如Denis Bueno写的那样,这种代码对任何实现了Iterable接口的对象都奏效。 20 | 此外,如果for(:)句法中右侧是一个数组而不是一个可迭代对象,那么内部代码用一个int型的计数器来防止数组越界。详见Java Language Specification: 21 | http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2 22 | 23 | stackoverflow链接:http://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work 24 | -------------------------------------------------------------------------------- /contents/how-to-avoid-java-code-in-jsp-files.md: -------------------------------------------------------------------------------- 1 | ##如何避免在JSP文件中使用Java代码 2 | 3 | ###问题 4 | 如何避免在JSP文件中使用Java代码? 5 | 6 | 我对Java EE不是很熟悉,我知道类似如下的三行代码 7 | ```jsp 8 | <%= x+1 %> 9 | <%= request.getParameter("name") %> 10 | <%! counter++; %> 11 | ``` 12 | 这三行代码是学校教的老式代码。在JSP 2,存在一些方法可以避免在JSP文件中使用Java代码。有人可以告诉我在JSP 2中如何避免使用Java代码吗,这些方法该如何使用? 13 | 14 | ###回答 15 | 在大约十年前,taglibs(比如JSTL)和EL(EL表达式,`${}`)诞生的时候,在JSP中使用scriptlets(类似`<% %>`)这种做法,就确实已经是不被鼓励使用的做法了。 16 | 17 | scriptlets 主要的缺点有: 18 | 1. **重用性** :你不可以重用scriptlets 19 | 2. **可替换性** :你不可以让scriptlets抽象化 20 | 3. **面向对象能力** :你不可以使用继承或组合 21 | 4. **调试性** :如果scriptlets中途抛出了异常,你只能获得一个空白页 22 | 5. **可测试性** :scriptlets不能进行单元测试 23 | 6. **可维护性** :(这句有些词语不确定)需要更多的时间去维护混合的/杂乱的/冲突的 代码逻辑 24 | 25 | Oracle自己也在 [JSP coding conventions](http://www.oracle.com/technetwork/articles/javase/code-convention-138726.html)一文中推荐在功能可以被标签库所替代的时候避免使用scriptlets语法。以下引用它提出的几个观点: 26 | 27 | > 在JSP 1.2规范中,强烈推荐使用JSTL来减少JSP scriptlets语法的使用。一个使用JSTL的页面,总得来说会更加地容易阅读和维护。 28 | 29 | >... 30 | 31 | >在任何可能的地方,当标签库能够提供相同的功能时,尽量避免使用JSP scriptlets语法。这会让页面更加容易阅读和维护,帮助将 业务逻辑 从 表现层逻辑 中分离,也会让页面往更符合JSP 2.0风格的方向发展(JSP 2.0规范中,支持但是极大弱化了JSP scriptlets语法) 32 | 33 | >... 34 | 35 | >本着适应 模型-显示层-控制器(MVC) 设计模式中关于减少业务逻辑层与显示层之间的耦合的精神,**JSP scriptlets语法不应该**被用来编写业务逻辑。相应的,JSP scriptlets语法在传送一些服务端返回的处理客户端请求的数据(也称为value objects)的时候会被使用。尽管如此,使用一个controller servlet来处理或者用自定义标签来处理会更好。 36 | 37 | ----------- 38 | 39 | **如何替换scriptlets语句,取决于代码/逻辑的目的。更常见的是,被替换的语句会被放在另外的一些更值得放的Java类里**(这里翻译得不一定清楚) 40 | 41 | * 如果你想在每个请求、每个页面请求都运行**相同的**Java代码,,比如说 检查一个用户是否在登录状态,就要实现一个 过滤器,在doFilter()方法中编写正确的代码,例如 42 | 43 | ```java 44 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { 45 | if (((HttpServletRequest) request).getSession().getAttribute("user") == null) { 46 | ((HttpServletResponse) response).sendRedirect("login"); // Not logged in, redirect to login page. 47 | } else { 48 | chain.doFilter(request, response); // Logged in, just continue request. 49 | } 50 | } 51 | ``` 52 | 当你在``中做好恰当的地址映射,覆盖所有应该被覆盖的JSP文件,也就不需要再JSP文件中添加这些相同的Java代码 53 | 54 | ---------------- 55 | 56 | * 如果你想执行一些Java代码来**预处理**一个请求,例如,预加载某些从数据库加载的数据来显示在一些表格里,可能还会有一些查询参数,那么,实现一个Servlet,在doGet()方法里编写正确的代码,例如 57 | 58 | ```java 59 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 60 | try { 61 | List products = productService.list(); // Obtain all products. 62 | request.setAttribute("products", products); // Store products in request scope. 63 | request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); // Forward to JSP page to display them in a HTML table. 64 | } catch (SQLException e) { 65 | throw new ServletException("Retrieving products failed!", e); 66 | } 67 | } 68 | ``` 69 | 这个方法能够更方便地处理异常。这样会在渲染、展示JSP页面时访问数据库。在数据库抛出异常的时候,你可以根据情况返回不同的响应或页面。在上面的例子,出错时默认会展示500页面,你也可以改变`web.xml`的``来自定义异常处理错误页。 70 | 71 | ---------- 72 | 73 | * 如果你想执行一些Java代码来**后置处理(postprocess)**一个请求,例如处理表单提交,那么,实现一个Servlet,在doPost()里写上正确的代码: 74 | 75 | ```java 76 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 77 | String username = request.getParameter("username"); 78 | String password = request.getParameter("password"); 79 | User user = userService.find(username, password); 80 | 81 | if (user != null) { 82 | request.getSession().setAttribute("user", user); // Login user. 83 | response.sendRedirect("home"); // Redirect to home page. 84 | } else { 85 | request.setAttribute("message", "Unknown username/password. Please retry."); // Store error message in request scope. 86 | request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); // Forward to JSP page to redisplay login form with error. 87 | } 88 | } 89 | ``` 90 | 这个处理不同目标结果页的方法会比原来更加简单: 可以显示一个带有表单验证错误提示的表单(在这个特别的例子中,你可以用EL表达式`${message}`来显示错误提示),或者仅仅跳转到成功的页面 91 | 92 | --------- 93 | 94 | * 如果你想执行一些Java代码来**控制**执行计划(control the execution plan) 和/或 request和response的跳转目标,用[MVC模式](http://stackoverflow.com/questions/3541077/design-patterns-web-based-applications/3542297#3542297)实现一个Servlet,例如: 95 | ```java 96 | protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 97 | try { 98 | Action action = ActionFactory.getAction(request); 99 | String view = action.execute(request, response); 100 | 101 | if (view.equals(request.getPathInfo().substring(1)) { 102 | request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response); 103 | } else { 104 | response.sendRedirect(view); 105 | } 106 | } catch (Exception e) { 107 | throw new ServletException("Executing action failed.", e); 108 | } 109 | } 110 | ``` 111 | 或者使用一些MVC框架例如[JSF](http://stackoverflow.com/tags/jsf/info), [Spring MVC](http://stackoverflow.com/tags/spring-mvc/info), [Wicket](http://stackoverflow.com/tags/wicket/info) 这样你就不用自定义servlet,只要写一些页面和javabean class就可以了。 112 | 113 | --------- 114 | 115 | * 如果你想执行一些Java代码来**控制JSP页面的数据渲染流程(control the flow inside a JSP page)**,那么你需要使用一些(已经存在的)流程控制标签库,比如[JSTL core](http://docs.oracle.com/javaee/5/jstl/1.1/docs/tlddocs/c/tld-summary.html),例如,在一个表格显示`List` 116 | 117 | ```java 118 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 119 | ... 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 |
${product.name}${product.description}${product.price}
129 | ``` 130 | 这些XML风格的标签可以很好地适应HTML代码,代码变得更好阅读(也因此更好地维护),相比于杂乱无章的scriptlets 的分支大括号(Where the heck does this closing brace belong to?"(到底这个结束大括号是属于哪个代码段的?))。一个简单的设置可以配置你的Web程序让在使用scriptlets 的时候自动抛出异常 131 | 132 | ```xml 133 | 134 | 135 | *.jsp 136 | true 137 | 138 | 139 | ``` 140 | 141 | 在JSP的继承者[Facelets](http://stackoverflow.com/tags/facelets/info)里(Java EE提供的MVC框架[JSF](http://stackoverflow.com/tags/jsf/info)),已经**不**可能使用scriptlets语法了。这是一个让你强制使用“正确的方法”的方法 142 | 143 | ----------- 144 | 145 | * 如果你想执行一些Java代码来在JSP中 **访问和显示** 一些“后端”数据,你需要使用EL(表达式),`${}`,例如,显示已经提交了的数值: 146 | 147 | ```jsp 148 | 149 | ``` 150 | 151 | `${param.foo}`会显示`request.getParameter("foo")`这句话的输出结果。 152 | 153 | -------------- 154 | 155 | * 如果你想在JSP直接执行一些工具类Java代码(典型的,一些public static方法),你需要定义它,并使用EL表达式函数。这是JSTL里的标准函数标签库,但是你也可以[轻松地创建自己需要的功能](http://docs.oracle.com/javaee/5/tutorial/doc/bnahq.html#bnaiq),下面是一个使用有用的`fn:escapeXml`来避免XSS攻击的例子。 156 | 157 | ```java 158 | <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> 159 | ... 160 | 161 | ``` 162 | 163 | 注意,XSS并不是Java/JSP/JSTL/EL/任何技术相关的东西,这个问题是**任何**Web应用程序都需要关心的问题,scriptlets 并没有为这个问题提供良好的解决方案,至少没有标准的Java API的解决方案。JSP的继承者Facelets内含了HTML转义功能,所以在Facelets里你不用担心XSS攻击的问题。 164 | 165 | See Also: 166 | * [JSP, Servlet, JSF的不同点在哪里?](http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp/2097732#2097732) 167 | * [Servlet, ServletContext, HttpSession 和 HttpServletRequest/Response 是如何工作的?](http://stackoverflow.com/questions/3106452/java-servlet-instantiation-and-session-variables/3106909#3106909) 168 | * [JSP, Servlet and JDBC的基本MVC例子](http://stackoverflow.com/questions/5003142/jsp-using-mvc-and-jdbc) 169 | * [Java Web应用程序中的设计模式](http://stackoverflow.com/questions/3541077/design-patterns-web-based-applications/) 170 | * [JSP/Servlet中的隐藏功能](http://balusc.blogspot.com/2010/01/hidden-features-of-jspservlet.html) 171 | 172 | 173 | stackoverflow原址:http://stackoverflow.com/questions/3177733/how-to-avoid-java-code-in-jsp-files -------------------------------------------------------------------------------- /contents/how-to-concatenate-two-arrays-in-java.md: -------------------------------------------------------------------------------- 1 | ##如何便捷地将两个数组合到一起 2 | 3 | ###一行代码搞定 4 | Apache Commons Lang library [`ArrayUtils.addAll(T[], T...)`](http://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/ArrayUtils.html#addAll%28T%5B%5D,%20T...%29)就是专门干这事的 5 | 6 | 代码: 7 | ```java 8 | String[] both = ArrayUtils.addAll(first, second); 9 | ``` 10 | 11 | ###不借助依赖包 12 | 13 | ####非泛型 14 | 把下面的`Foo`替换成你自己的类名 15 | ```java 16 | public Foo[] concat(Foo[] a, Foo[] b) { 17 | int aLen = a.length; 18 | int bLen = b.length; 19 | Foo[] c= new Foo[aLen+bLen]; 20 | System.arraycopy(a, 0, c, 0, aLen); 21 | System.arraycopy(b, 0, c, aLen, bLen); 22 | return c; 23 | } 24 | ``` 25 | 26 | ####泛型 27 | ```java 28 | public T[] concatenate (T[] a, T[] b) { 29 | int aLen = a.length; 30 | int bLen = b.length; 31 | 32 | @SuppressWarnings("unchecked") 33 | T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen+bLen); 34 | System.arraycopy(a, 0, c, 0, aLen); 35 | System.arraycopy(b, 0, c, aLen, bLen); 36 | 37 | return c; 38 | } 39 | ``` 40 | 注意,泛型的方案不适用于基本数据类型(int,boolean……) -------------------------------------------------------------------------------- /contents/how-to-create-a-file-and-write-to-a-file-in-java.md: -------------------------------------------------------------------------------- 1 | ## 用java怎么创建一个文件并向该文件写文本内容 2 | 3 | ### 问:在java里最简单的创建文件写文件的方法是什么 4 | 5 | ### 最佳答案: 6 | 创建一个文本文件(注意:如果该文件存在,则会覆盖该文件) 7 | ````java 8 | PrintWriter writer = new PrintWriter("the-file-name.txt", "UTF-8"); 9 | writer.println("The first line"); 10 | writer.println("The second line"); 11 | writer.close(); 12 | ```` 13 | 创建一个二进制文件(同样会覆盖这文件) 14 | ````java 15 | byte data[] = ... 16 | FileOutputStream out = new FileOutputStream("the-file-name"); 17 | out.write(data); 18 | out.close(); 19 | ```` 20 | 21 | Java 7+ 用户可以用[`File`](http://docs.oracle.com/javase/7/docs/api/index.html?java/nio/file/Files.html)类来写文件 22 | 创建一个文本文件 23 | ````java 24 | List lines = Arrays.asList("The first line", "The second line"); 25 | Path file = Paths.get("the-file-name.txt"); 26 | Files.write(file, lines, Charset.forName("UTF-8")); 27 | //Files.write(file, lines, Charset.forName("UTF-8"), StandardOpenOption.APPEND); 28 | ```` 29 | 创建一个二进制文件 30 | ````java 31 | byte data[] = ... 32 | Path file = Paths.get("the-file-name"); 33 | Files.write(file, data); 34 | //Files.write(file, data, StandardOpenOption.APPEND); 35 | ```` 36 | 37 | ### 其他的答案(1): 38 | 在Java 7+中 39 | ````java 40 | try (Writer writer = new BufferedWriter(new OutputStreamWriter( 41 | new FileOutputStream("filename.txt"), "utf-8"))) { 42 | writer.write("something"); 43 | } 44 | ```` 45 | 还有一些实用的方法如下: 46 | * [`FileUtils.writeStringtoFile(..)`](https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/FileUtils.html#writeStringToFile%28java.io.File,%20java.lang.String,%20java.nio.charset.Charset%29) 来自于 commons-io 包 47 | * [`Files.write(..)`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/io/Files.html#write%28java.lang.CharSequence,%20java.io.File,%20java.nio.charset.Charset%29) 来自于 guava 48 | 49 | Note also that you can use a FileWriter, but it uses the default encoding, 50 | which is often a bad idea - it's best to specify the encoding explicitly. 51 | 还要注意可以使用 `FileWriter`,但是它使用的是默认编码,这不是很好的方法,最好是明确指定编码 52 | 53 | 54 | 下面是来自于prior-to-java-7的原始方法 55 | ````java 56 | Writer writer = null; 57 | 58 | try { 59 | writer = new BufferedWriter(new OutputStreamWriter( 60 | new FileOutputStream("filename.txt"), "utf-8")); 61 | writer.write("Something"); 62 | } catch (IOException ex) { 63 | // report 64 | } finally { 65 | try {writer.close();} catch (Exception ex) {/*ignore*/} 66 | } 67 | ```` 68 | 可以看[`Reading, Writing, and Creating Files`](http://docs.oracle.com/javase/tutorial/essential/io/file.html)(包含NIO2) 69 | 70 | ### 其他答案(2): 71 | ````java 72 | public class Program { 73 | public static void main(String[] args) { 74 | String text = "Hello world"; 75 | BufferedWriter output = null; 76 | try { 77 | File file = new File("example.txt"); 78 | output = new BufferedWriter(new FileWriter(file)); 79 | output.write(text); 80 | } catch ( IOException e ) { 81 | e.printStackTrace(); 82 | } finally { 83 | if ( output != null ) output.close(); 84 | } 85 | } 86 | } 87 | ```` 88 | 89 | ### 其他答案(3): 90 | 如果已经有想要写到文件中的内容,[`java.nio.file.Files`](https://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html) 作为 Java 7 附加部分的native I/O,提供了简单高效的方法来实现你的目标 91 | 92 | 基本上创建文件,写文件只需要一行,而且是只需一个方法调用! 93 | 下面的例子创建并且写了6个不同的文件来展示是怎么使用的 94 | 95 | ````java 96 | Charset utf8 = StandardCharsets.UTF_8; 97 | List lines = Arrays.asList("1st line", "2nd line"); 98 | byte[] data = {1, 2, 3, 4, 5}; 99 | 100 | try { 101 | Files.write(Paths.get("file1.bin"), data); 102 | Files.write(Paths.get("file2.bin"), data, 103 | StandardOpenOption.CREATE, StandardOpenOption.APPEND); 104 | Files.write(Paths.get("file3.txt"), "content".getBytes()); 105 | Files.write(Paths.get("file4.txt"), "content".getBytes(utf8)); 106 | Files.write(Paths.get("file5.txt"), lines, utf8); 107 | Files.write(Paths.get("file6.txt"), lines, utf8, 108 | StandardOpenOption.CREATE, StandardOpenOption.APPEND); 109 | } catch (IOException e) { 110 | e.printStackTrace(); 111 | } 112 | ```` 113 | 114 | ### 其他答案(4): 115 | 下面是一个小程序来创建和写文件。该版本的代码比较长,但是可以容易理解 116 | ````java 117 | import java.io.BufferedWriter; 118 | import java.io.File; 119 | import java.io.FileOutputStream; 120 | import java.io.IOException; 121 | import java.io.OutputStreamWriter; 122 | import java.io.Writer; 123 | 124 | public class writer { 125 | public void writing() { 126 | try { 127 | //Whatever the file path is. 128 | File statText = new File("E:/Java/Reference/bin/images/statsTest.txt"); 129 | FileOutputStream is = new FileOutputStream(statText); 130 | OutputStreamWriter osw = new OutputStreamWriter(is); 131 | Writer w = new BufferedWriter(osw); 132 | w.write("POTATO!!!"); 133 | w.close(); 134 | } catch (IOException e) { 135 | System.err.println("Problem writing to the file statsTest.txt"); 136 | } 137 | } 138 | 139 | public static void main(String[]args) { 140 | writer write = new writer(); 141 | write.writing(); 142 | } 143 | } 144 | ```` 145 | 146 | 147 | 148 | 149 | stackoverflow链接: 150 | http://stackoverflow.com/questions/2885173/how-to-create-a-file-and-write-to-a-file-in-java 151 | -------------------------------------------------------------------------------- /contents/how-to-create-a-generic-array-in-java.md: -------------------------------------------------------------------------------- 1 | ## 如何创建泛型java数组 2 | 3 | #### 问题 4 | 5 | 数组是不能通过泛型创建的,因为我们不能创建不可具体化的类型的数组。如下面的代码: 6 | 7 | ```java 8 | public class GenSet { 9 | private E a[]; 10 | 11 | public GenSet() { 12 | a = new E[INITIAL_ARRAY_LENGTH]; //编译期就会报错:不能创建泛型数组 13 | } 14 | } 15 | ``` 16 | 17 | #### 采纳答案 18 | 19 | * 检查:强类型。`GenSet`明确知道数组中包含的类型是什么(例如通过构造器传入`Class`,当方法中传入类型不是`E`将抛出异常) 20 | 21 | ```java 22 | public class GenSet { 23 | 24 | private E[] a; 25 | 26 | public GenSet(Class c, int s) { 27 | // 使用原生的反射方法,在运行时知道其数组对象类型 28 | @SuppressWarnings("unchecked") 29 | final E[] a = (E[]) Array.newInstance(c, s); 30 | this.a = a; 31 | } 32 | 33 | E get(int i) { 34 | return a[i]; 35 | } 36 | 37 | //...如果传入参数不为E类型,那么强制添加进数组将会抛出异常 38 | void add(E e) {...} 39 | } 40 | ``` 41 | 42 | * 未检查:弱类型。数组内对象不会有任何类型检查,而是作为Object类型传入。 43 | 44 | 在这种情况下,你可以采取如下写法: 45 | 46 | ```java 47 | public class GenSet { 48 | 49 | private Object[] a; 50 | 51 | public GenSet(int s) { 52 | a = new Object[s]; 53 | } 54 | 55 | E get(int i) { 56 | @SuppressWarnings("unchecked") 57 | final E e = (E) a[i]; 58 | return e; 59 | } 60 | } 61 | ``` 62 | 63 | 上述代码在编译期能够通过,但因为泛型擦除的缘故,在程序执行过程中,数组的类型有且仅有`Object`类型存在,这个时候如果我们强制转化为`E`类型的话,在运行时会有`ClassCastException`抛出。所以,要确定好泛型的上界,将上边的代码重写一下: 64 | 65 | ```java 66 | public class GenSet { // E has an upper bound of Foo 67 | 68 | private Foo[] a; // E 泛型在运行期会被擦除为Foo类型,所以这里使用Foo[] 69 | 70 | public GenSet(int s) { 71 | a = new Foo[s]; 72 | } 73 | 74 | //... 75 | } 76 | ``` 77 | 78 | #### StackOverflow地址: 79 | 80 | [http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java](http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java) -------------------------------------------------------------------------------- /contents/how-to-create-a-generic-array2-in-java.md: -------------------------------------------------------------------------------- 1 | 如何创建一个java泛型数组 2 | 问题: 3 | 由于Java泛型的实现机制,你不能这样写代码: 4 | pulic class GenSet{ 5 | private E a[]; 6 | public GetSet(){ 7 | a=new E[INITIAL_ARRAY_LENGTH]; //error:generic array creation 8 | } 9 | } 10 | 在保证类型安全的情况下,我该如何实现创建一个Java泛型数组? 11 | 我在一个Java论坛上看到是这样解决的: 12 | import java.lang.reflect.Array; 13 | 14 | class Stack { 15 | public Stack(Class clazz, int capacity) { 16 | array = (T[])Array.newInstance(clazz, capacity); 17 | } 18 | 19 | private final T[] array; 20 | } 21 | 但我不懂发生了什么。有人能帮我吗? 22 | 23 | 回答: 24 | 在回答之前,我得问你一个问题,你的GetSet是"checked"还是"unchecked"? 25 | 什么意思呢? 26 | Checked的话,是强类型。GetSet明确地知道包含了什么类型的对象。 27 | 比如,当要传递不是E类型的实参时,它的构造器会被Class引数明确地调用,方法会抛出一个异常。参阅Collections.checkedCollection。 28 | 在这种情况下,你应该这样写: 29 | public class GenSet { 30 | 31 | private E[] a; 32 | 33 | public GenSet(Class c, int s) { 34 | // Use Array native method to create array 35 | // of a type only known at run time 36 | @SuppressWarnings("unchecked") 37 | final E[] a = (E[]) Array.newInstance(c, s); 38 | this.a = a; 39 | } 40 | 41 | E get(int i) { 42 | return a[i]; 43 | } 44 | } 45 | Unchecked的话:弱类型。实际上要传递任何对象的实参时 46 | 是没有类型检查的。 47 | 在这种情况下,你应当这样写: 48 | public class GenSet { 49 | 50 | private Object[] a; 51 | 52 | public GenSet(int s) { 53 | a = new Object[s]; 54 | } 55 | 56 | E get(int i) { 57 | @SuppressWarnings("unchecked") 58 | final E e = (E) a[i]; 59 | return e; 60 | } 61 | } 62 | 注意数组里的元素类型应当是可擦除的形参。 63 | public class GenSet { // E has an upper bound of Foo 64 | 65 | private Foo[] a; // E erases to Foo, so use Foo[] 66 | 67 | public GenSet(int s) { 68 | a = new Foo[s]; 69 | } 70 | 71 | ... 72 | } 73 | 所有的这些结果来自Java一个有名,存心,不足的泛型:它通过使用 74 | erasure实现,所以“泛型”类在运行时创建是不知道它的实参类型的, 75 | 所以不能提供类型安全,除非某些明确的机制(比如类型检查)已经实现了。 76 | StackOverflow地址: 77 | http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java -------------------------------------------------------------------------------- /contents/how-to-generate-a-random-alpha-numeric-string.md: -------------------------------------------------------------------------------- 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 | import java.security.SecureRandom; 39 | 40 | public final class SessionIdentifierGenerator { 41 | private SecureRandom random = new SecureRandom(); 42 | 43 | public String nextSessionId() { 44 | return new BigInteger(130, random).toString(32); 45 | } 46 | } 47 | ``` 48 | 49 | 其工作原理就是,使用一个 130 位的安全的随机数生成器生成一个随机数,接着转化为 32 进制。我们知道,128 位安全随机数的生成已经是足够安全的,不过以 32 进制编码的每一个数字可编码 5 位,所以需要取大于 128 且是 5 的倍数,所以就选择了 130 位。相对于 随机 UUID 来说(在标准输出中,每个字符使用 3.4 bit,共 122 bit),每个字符使用 5 个随机的 bit 来编码的方式,显得更为简洁和高效。 50 | 51 | 译者注:上面两段代码,生成26位随机字符串,第一段代码每次耗时不到1ms,第二段耗时约100ms。也就是说第一段代码更快,但第二段代码更安全,但更耗时。 52 | 53 | stackoverflow原链接: 54 | http://stackoverflow.com/questions/41107/how-to-generate-a-random-alpha-numeric-string -------------------------------------------------------------------------------- /contents/how-to-get-an-enum-value-from-a-string-value-in-java.md: -------------------------------------------------------------------------------- 1 | ## 怎样得到指定名称的枚举类型对应的的字符串类型的枚举常量 2 | 3 | ### 问题: 4 | 假如说我有一个如下的枚举类 5 | 6 | ```java 7 | public enum Blah { 8 | A, B, C, D 9 | } 10 | ``` 11 | 而我想要找出具有指定名称的枚举类型对应的的字符串类型的枚举常量,打个比方``"A"``对应的值为``Blah.A``。 12 | 此时我应该怎么做? 13 | 我是不是应该使用``Enum.valueOf()``这个方法?如果是的话,我又该怎么使用它? 14 | 15 | ### 回答: 16 | 是的,``Blah.valueOf("A")``将会给你你想要的``Blah.A`` 17 | 不过需要注意的是,你输入的名字必须是绝对匹配的,像``Blah.valueOf("a")``和``Blah.valueOf("A ")``都会抛出``IllegalArgumentException`` 18 | 注:第一个表达式``a`` 与``A``不匹配 19 | 第二个表达式``A ``后面多了一个空格 20 | 21 | 静态方法``valueOf()``和``values()``在编译时创建并且不会出现在编译后的源码里。但尽管如此,这两个方法还是确实出现在了Java文档里,[文档连接](https://docs.oracle.com/javase/7/docs/api/java/awt/Dialog.ModalityType.html) 22 | 23 | stackoverflow讨论地址 [https://stackoverflow.com/questions/604424/how-to-get-an-enum-value-from-a-string-value-in-java](https://stackoverflow.com/questions/604424/how-to-get-an-enum-value-from-a-string-value-in-java) 24 | 25 | 26 | -------------------------------------------------------------------------------- /contents/how-to-sort-a-mapkey-value-on-the-values-in-java.md: -------------------------------------------------------------------------------- 1 | ## `Map`基于Value值排序 2 | 3 | ### 方法1: 4 | 使用TreeMap,可以参考下面的代码 5 | ```java 6 | public class Testing { 7 | 8 | public static void main(String[] args) { 9 | 10 | HashMap map = new HashMap(); 11 | ValueComparator bvc = new ValueComparator(map); 12 | TreeMap sorted_map = new TreeMap(bvc); 13 | 14 | map.put("A",99.5); 15 | map.put("B",67.4); 16 | map.put("C",67.4); 17 | map.put("D",67.3); 18 | 19 | System.out.println("unsorted map: "+map); 20 | 21 | sorted_map.putAll(map); 22 | 23 | System.out.println("results: "+sorted_map); 24 | } 25 | } 26 | 27 | class ValueComparator implements Comparator { 28 | 29 | Map base; 30 | public ValueComparator(Map base) { 31 | this.base = base; 32 | } 33 | 34 | // Note: this comparator imposes orderings that are inconsistent with equals. 35 | public int compare(String a, String b) { 36 | if (base.get(a) >= base.get(b)) { 37 | return -1; 38 | } else { 39 | return 1; 40 | } // returning 0 would merge keys 41 | } 42 | } 43 | ``` 44 | 译注:如果不自己写Comparator,treemap默认是用key来排序 45 | 46 | ### 方法2: 47 | 48 | 先通过linkedlist排好序,再放到LinkedHashMap中 49 | ```java 50 | public class MapUtil 51 | { 52 | public static > Map 53 | sortByValue( Map map ) 54 | { 55 | List> list = 56 | new LinkedList>( map.entrySet() ); 57 | Collections.sort( list, new Comparator>() 58 | { 59 | public int compare( Map.Entry o1, Map.Entry o2 ) 60 | { 61 | return (o1.getValue()).compareTo( o2.getValue() ); 62 | } 63 | } ); 64 | 65 | Map result = new LinkedHashMap(); 66 | for (Map.Entry entry : list) 67 | { 68 | result.put( entry.getKey(), entry.getValue() ); 69 | } 70 | return result; 71 | } 72 | } 73 | ``` 74 | 译注:这两种方法,我简单测试了下,如果map的size在十万级别以上,两者的耗时都是几百毫秒,第二个方法会快一些。否则,第一个方法快一些。因此,如果你处理的map,都是几十万级别以下的大小,两种方式随意使用,看个人喜欢了。 75 | 76 | stackoverflow链接: 77 | http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java 78 | -------------------------------------------------------------------------------- /contents/how-to-split-a-string-in-java.md: -------------------------------------------------------------------------------- 1 | ##如何分割(split)string字符串 2 | 使用[`String#split()`](http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#split-java.lang.String-)方法 3 | 4 | 如下所示: 5 | ```java 6 | String string = "004-034556"; 7 | String[] parts = string.split("-"); 8 | String part1 = parts[0]; // 004 9 | String part2 = parts[1]; // 034556 10 | ``` 11 | 需要注意的是,该方法的参数是个[正则表达式](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#sum),要注意对某些字符做转码。例如,`.`在正则表达式中表示任意字符,因此,如果你要通过`.`号做分割,需要这样写,`split("\\.")`或者`split(Pattern.quote("."))` 12 | 13 | 如果只是为了验证字符串中是否包含某个字符,使用[`String#contains`](http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#contains-java.lang.CharSequence-)方法就行。注意该方法的参数,不是正则表达式 14 | 15 | stackoverflow链接: 16 | http://stackoverflow.com/questions/3481828/how-to-split-a-string-in-java 17 | -------------------------------------------------------------------------------- /contents/how-to-test-a-class-that-has-private-methods-fields-or-inner-classes.md: -------------------------------------------------------------------------------- 1 | # 如何测试 private 方法,变量或者内部类 2 | 3 | 当你需要测试一个遗留的应用程序,且不能更改方法的可见性时,那么,测试私有方法/属性的最好方式就是使用[反射](https://en.wikipedia.org/wiki/Reflection_%28computer_programming%29)。 4 | 5 | 实际测试时,可以通过一些反射辅助类,设置和获取私有(静态)的变量、调用私有(静态)方法。遵循下面的窍门,你可以很好地处理私有方法和变量的测试。 6 | 7 | ``` 8 | Method method = targetClass.getDeclaredMethod(methodName, argClasses); 9 | method.setAccessible(true); 10 | return method.invoke(targetObject, argObjects); 11 | ``` 12 | 13 | 私有变量: 14 | 15 | ``` 16 | Field field = targetClass.getDeclaredField(fieldName); 17 | field.setAccessible(true); 18 | field.set(object, value); 19 | 20 | ``` 21 | > note: 22 | > 1. `targetClass.getDeclaredMethod(methodName, argClasses)`这个方法能让你获取到私有方法。`getDeclaredField`让你获取到私有变量 23 | > 2. 在对私有变量(方法)进行处理前,需要先`setAccessible(true)` 24 | 25 | stackoverflow原址:http://stackoverflow.com/questions/34571/how-to-test-a-class-that-has-private-methods-fields-or-inner-classes -------------------------------------------------------------------------------- /contents/implements-runnable-vs-extends-thread.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/giantray/stackoverflow-java-top-qa/db68803d271ce43b7fdf4e42f6680154c91f3d9a/contents/implements-runnable-vs-extends-thread.md -------------------------------------------------------------------------------- /contents/in-java-whats-the-difference-between-public-default-protected-and-private.md: -------------------------------------------------------------------------------- 1 | ##Java修饰符:public,protected,private,不加修饰符。有什么区别呢? 2 | 3 | 4 | 如下表所示,Y表示能访问(可见性),N表示不能访问,例如第一行的第3个Y,表示类的变量/方法如果是用public修饰,它的子类能访问这个变量/方法 5 | 6 | 7 | 修饰符 | 类内部 | 同个包(package) | 子类 | 其他范围 8 | ------------- | ------------- | -------------| -------------| ------------- 9 | public | Y | Y | Y | Y 10 | protected | Y | Y | Y | N 11 | 无修饰符 | Y | Y | N or Y(见说明) | N 12 | private | Y | N | N | N 13 | 14 | 15 | 说明: 16 | 需要特别说明“无修饰符”这个情况,子类能否访问父类中无修饰符的变量/方法,取决于子类的位置。如果子类和父类在同一个包中,那么子类可以访问父类中的无修饰符的变量/方法,否则不行。 17 | 18 | 译注:本来觉得很简单一个问题,没想记录的,但看到答案,才发现自己以前错了。我以前一直以为无修饰符和private是一样的,如果没给变量加修饰符,java就默认为private。 19 | 20 | stackoverflow链接: 21 | http://stackoverflow.com/questions/215497/in-java-whats-the-difference-between-public-default-protected-and-private -------------------------------------------------------------------------------- /contents/initialization-of-an-arraylist-in-one-line.md: -------------------------------------------------------------------------------- 1 | 如何用一行代码初始化一个ArrayList 2 | 3 | ###问题 4 | 为了测试,我需要临时快速创建一个list。一开始我这样做: 5 | ```java 6 | ArrayList places = new ArrayList(); 7 | places.add("Buenos Aires"); 8 | places.add("Córdoba"); 9 | places.add("La Plata"); 10 | ``` 11 | 之后我重构了下 12 | ```java 13 | ArrayList places = new ArrayList( 14 | Arrays.asList("Buenos Aires", "Córdoba", "La Plata")); 15 | ``` 16 | 是否有更加简便的方法呢? 17 | 18 | ###回答 19 | 20 | ####常见方式 21 | 实际上,也许“最好”的方式,就是你写的这个方式,因为它不用再创建新的`List`: 22 | ``` 23 | ArrayList list = new ArrayList(); 24 | list.add("A"); 25 | list.add("B"); 26 | list.add("C"); 27 | ``` 28 | 只是这个方式看上去要多写些代码,让人郁闷 29 | 30 | ####匿名内部类 31 | 当然,还有其他方式,例如,写一个匿名内部类,然后在其中做初始化(也被称为 brace initialization): 32 | ``` 33 | ArrayList list = new ArrayList() {{ 34 | add("A"); 35 | add("B"); 36 | add("C"); 37 | }}; 38 | ``` 39 | 但是,我不喜欢这个方式。只是为了做个初始化,却要在`ArrayList`的同一行后面加这么一坨代码。 40 | 41 | ####Arrays.asList 42 | ``` 43 | List places = Arrays.asList("Buenos Aires", "Córdoba", "La Plata"); 44 | ``` 45 | ####Collections.singletonList 46 | ``` 47 | List places = Collections.singletonList("Buenos Aires"); 48 | ``` 49 | 注意:后面的这两种方式,得到的是一个定长的`List`(如果add操作会抛异常)。如果你需要一个不定长的`List`,可以这样做: 50 | ``` 51 | ArrayList places = new ArrayList<>(Arrays.asList("Buenos Aires", "Córdoba", "La Plata")); 52 | 53 | ``` 54 | 55 | stackoverflow链接: 56 | http://stackoverflow.com/questions/1005073/initialization-of-an-arraylist-in-one-line 57 | 58 | -------------------------------------------------------------------------------- /contents/is-null-check-needed-before-calling-instanceof.md: -------------------------------------------------------------------------------- 1 | ## 在调用 instanceof 前需要进行null检查吗 2 | 3 | 4 | ### 问题: 5 | 6 | null instanceof SomeClass 会返回 null 还是抛出 NullPointerException 异常 7 | 8 | ### 答案一 9 | 在调用 instanceof 前不要进行null检查 10 | null instanceof SomeClass 会返回 null 11 | 在 Java Language Specification 中 http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2 12 | 13 | ``` 14 | 在运行时,如果该instanceof运算符的关系表达式(RelationExpression)不为 null,且这个引用可以被成功转型( §15.16),不抛出ClassCastException,则结果为true; 15 | 否则结果为false。 16 | ``` 17 | 18 | ### 答案二 19 | public class IsInstanceOfTest { 20 | 21 | public static void main(final String[] args) { 22 | 23 | String s; 24 | 25 | s = ""; 26 | 27 | System.out.println((s instanceof String)); 28 | System.out.println(String.class.isInstance(s)); 29 | 30 | s = null; 31 | 32 | System.out.println((s instanceof String)); 33 | System.out.println(String.class.isInstance(s)); 34 | } 35 | } 36 | 打印出 37 | 38 | true 39 | true 40 | false 41 | false 42 | 43 | ### 原文链接 44 | http://stackoverflow.com/questions/2950319/is-null-check-needed-before-calling-instanceof 45 | 46 | 47 | -------------------------------------------------------------------------------- /contents/is-there-a-unique-android-device-id.md: -------------------------------------------------------------------------------- 1 | ##如何获取Android设备唯一ID? 2 | 3 | ###问题 4 | 每一个android设备都有唯一ID吗?如果有?怎么用java最简单取得呢? 5 | 6 | ###回答1(最佳) 7 | 8 | 如何取得android唯一码? 9 | 10 | 好处: 11 | - 1.不需要特定权限. 12 | - 2.在99.5% Android装置(包括root过的)上,即API => 9,保证唯一性. 13 | - 3.重装app之后仍能取得相同唯一值. 14 | 15 | 伪代码: 16 | 17 | ``` 18 | if API => 9/10: (99.5% of devices) 19 | 20 | return unique ID containing serial id (rooted devices may be different) 21 | 22 | else 23 | 24 | return unique ID of build information (may overlap data - API < 9) 25 | ``` 26 | 27 | 代码: 28 | 29 | ```java 30 | 31 | /** 32 | * Return pseudo unique ID 33 | * @return ID 34 | */public static String getUniquePsuedoID() { 35 | // If all else fails, if the user does have lower than API 9 (lower 36 | // than Gingerbread), has reset their device or 'Secure.ANDROID_ID' 37 | // returns 'null', then simply the ID returned will be solely based 38 | // off their Android device information. This is where the collisions 39 | // can happen. 40 | // Thanks http://www.pocketmagic.net/?p=1662! 41 | // Try not to use DISPLAY, HOST or ID - these items could change. 42 | // If there are collisions, there will be overlapping data 43 | String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10); 44 | 45 | // Thanks to @Roman SL! 46 | // http://stackoverflow.com/a/4789483/950427 47 | // Only devices with API >= 9 have android.os.Build.SERIAL 48 | // http://developer.android.com/reference/android/os/Build.html#SERIAL 49 | // If a user upgrades software or roots their device, there will be a duplicate entry 50 | String serial = null; 51 | try { 52 | serial = android.os.Build.class.getField("SERIAL").get(null).toString(); 53 | 54 | // Go ahead and return the serial for api => 9 55 | return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString(); 56 | } catch (Exception exception) { 57 | // String needs to be initialized 58 | serial = "serial"; // some value 59 | } 60 | 61 | // Thanks @Joe! 62 | // http://stackoverflow.com/a/2853253/950427 63 | // Finally, combine the values we have found by using the UUID class to create a unique identifier 64 | return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();} 65 | ``` 66 | ###回答2 67 | 好处: 68 | - 1.不需要特定权限. 69 | - 2.在100% Android装置(包括root过的)上,保证唯一性. 70 | 71 | 坏处 72 | - 1.重装app之后不能取得相同唯一值. 73 | 74 | ```java 75 | private static String uniqueID = null; 76 | private static final String PREF_UNIQUE_ID = "PREF_UNIQUE_ID"; 77 | 78 | public synchronized static String id(Context context) { 79 | if (uniqueID == null) { 80 | SharedPreferences sharedPrefs = context.getSharedPreferences( 81 | PREF_UNIQUE_ID, Context.MODE_PRIVATE); 82 | uniqueID = sharedPrefs.getString(PREF_UNIQUE_ID, null); 83 | if (uniqueID == null) { 84 | uniqueID = UUID.randomUUID().toString(); 85 | Editor editor = sharedPrefs.edit(); 86 | editor.putString(PREF_UNIQUE_ID, uniqueID); 87 | editor.commit(); 88 | } 89 | } 90 | return uniqueID; 91 | } 92 | ``` 93 | 94 | ###回答3(需要有电话卡) 95 | 96 | 好处: 97 | 1.重装app之后仍能取得相同唯一值. 98 | 99 | 代码: 100 | 101 | ```java 102 | final TelephonyManager tm = (TelephonyManager) getBaseContext().getSystemService(Context.TELEPHONY_SERVICE); 103 | final String tmDevice, tmSerial, androidId; 104 | tmDevice = "" + tm.getDeviceId(); 105 | tmSerial = "" + tm.getSimSerialNumber(); 106 | androidId = "" + android.provider.Settings.Secure.getString(getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); 107 | UUID deviceUuid = new UUID(androidId.hashCode(), ((long)tmDevice.hashCode() << 32) | tmSerial.hashCode()); 108 | String deviceId = deviceUuid.toString(); 109 | ``` 110 | 111 | 谨记:要取得以下权限 112 | ``` 113 | 114 | ``` 115 | stackoverflow链接: 116 | http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id -------------------------------------------------------------------------------- /contents/iterate-through-a-hashmap.md: -------------------------------------------------------------------------------- 1 | # HashMap遍历 # 2 | 3 | 在Java中有多种遍历HashMap的方法。让我们回顾一下最常见的方法和它们各自的优缺点。由于所有的Map都实现了Map接口,所以接下来方法适用于所有Map(如:HaspMap,TreeMap,LinkedMap,HashTable,etc) 4 | 5 | ## 方法#1 使用For-Each迭代entries ## 6 | 7 | 这是最常见的方法,并在大多数情况下更可取的。当你在循环中需要使用Map的键和值时,就可以使用这个方法 8 | 9 | Map map = new HashMap(); 10 | for(Map.Entry entry : map.entrySet()){ 11 | System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue()) 12 | } 13 | 14 | 注意:For-Each循环是Java5新引入的,所以只能在Java5以上的版本中使用。如果你遍历的map是null的话,For-Each循环会抛出NullPointerException异常,所以在遍历之前你应该判断是否为空引用。 15 | 16 | ## 方法#2 使用For-Each迭代keys和values ## 17 | 18 | 如果你只需要用到map的keys或values时,你可以遍历KeySet或者values代替entrySet 19 | 20 | Map map = new HashMap(); 21 | 22 | //iterating over keys only 23 | for (Integer key : map.keySet()) { 24 | System.out.println("Key = " + key); 25 | } 26 | 27 | //iterating over values only 28 | for (Integer value : map.values()) { 29 | System.out.println("Value = " + value); 30 | } 31 | 32 | 这个方法比entrySet迭代具有轻微的性能优势(大约快10%)并且代码更简洁 33 | 34 | ## 方法#3 使用Iterator迭代 ## 35 | 36 | 使用泛型 37 | 38 | Map map = new HashMap(); 39 | Iterator> entries = map.entrySet().iterator(); 40 | while (entries.hasNext()) { 41 | Map.Entry entry = entries.next(); 42 | System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); 43 | } 44 | 45 | 不使用泛型 46 | 47 | Map map = new HashMap(); 48 | Iterator entries = map.entrySet().iterator(); 49 | while (entries.hasNext()) { 50 | Map.Entry entry = (Map.Entry) entries.next(); 51 | Integer key = (Integer)entry.getKey(); 52 | Integer value = (Integer)entry.getValue(); 53 | System.out.println("Key = " + key + ", Value = " + value); 54 | } 55 | 56 | 你可以使用同样的技术迭代keyset或者values 57 | 58 | 这个似乎有点多余但它具有自己的优势。首先,它是遍历老java版本map的唯一方法。另外一个重要的特性是可以让你在迭代的时候从map中删除entries的(通过调用iterator.remover())唯一方法.如果你试图在For-Each迭代的时候删除entries,你将会得到unpredictable resultes 异常。 59 | 60 | 从性能方法看,这个方法等价于使用For-Each迭代 61 | 62 | ## 方法#4 迭代keys并搜索values(低效的) ## 63 | 64 | Map map = new HashMap(); 65 | for (Integer key : map.keySet()) { 66 | Integer value = map.get(key); 67 | System.out.println("Key = " + key + ", Value = " + value); 68 | } 69 | 70 | 这个方法看上去比方法#1更简洁,但是实际上它更慢更低效,通过key得到value值更耗时(这个方法在所有实现map接口的map中比方法#1慢20%-200%)。如果你安装了FindBugs,它将检测并警告你这是一个低效的迭代。这个方法应该避免 71 | 72 | ## 总结 ## 73 | 74 | 如果你只需要使用key或者value使用方法#2,如果你坚持使用java的老版本(java 5 以前的版本)或者打算在迭代的时候移除entries,使用方法#3。其他情况请使用#1方法。避免使用#4方法。 75 | 76 | stackoverflow链接: 77 | http://stackoverflow.com/questions/1066589/iterate-through-a-hashmap 78 | -------------------------------------------------------------------------------- /contents/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re.md: -------------------------------------------------------------------------------- 1 | ## 遍历集合时移除元素,怎样避免ConcurrentModificationException异常抛出 2 | 3 | #### 问题: 4 | 5 | 在遍历集合的过程中,不会总出现`ConcurrentModificationException`异常的抛出,但是在下面的代码块中: 6 | 7 | ```java 8 | public static void main(String[] args) { 9 | Collection l = new ArrayList(); 10 | 11 | for (int i=0; i < 10; ++i) { 12 | l.add(new Integer(4)); 13 | l.add(new Integer(5)); 14 | l.add(new Integer(6)); 15 | } 16 | 17 | //遍历的过程中移除部分集合元素 18 | for (Integer i : l) { 19 | if (i.intValue() == 5) { 20 | l.remove(i); 21 | } 22 | } 23 | 24 | System.out.println(l); 25 | } 26 | ``` 27 | 28 | 运行之后,结果显而易见,总是会抛出异常: 29 | 30 | ```java 31 | Exception in thread "main" java.util.ConcurrentModificationException 32 | ``` 33 | 34 | 所以,遍历集合时移除元素,怎样避免ConcurrentModificationException异常的产生?有什么好的解决办法? 35 | 36 | #### 回答: 37 | 38 | `Iterator.remove()`是线程安全的,所以你的代码可以这样写: 39 | 40 | ```java 41 | List list = new ArrayList<>(); 42 | 43 | for (Iterator iterator = list.iterator(); iterator.hasNext();) { 44 | String string = iterator.next(); 45 | if (string.isEmpty()) { 46 | 47 | // 从迭代器中移除集合元素,集合中相应的集合元素也会安全地被移除 48 | // 在这里,如果继续调用的是list.remove(string),那么仍会抛出异常 49 | iterator.remove(); 50 | } 51 | } 52 | ``` 53 | 54 | 在遍历集合时修改集合的结构或内容的情况中,`Iterator.remove()`是唯一线程安全的方法。 55 | 56 | #### 问题原因: 57 | 58 | fail-fast, 快速失败机制,是java集合类的一种错误检查机制。当有多个线程同时对集合进行遍历以及内容或者结构的修改时,就有可能产生fail-fast机制。这意味着,当它们发现容器在迭代的过程中被修改时,就会抛出一个ConcurrentModificationException异常。 59 | 60 | 迭代器的快速失败行为无法得到保证,它不能保证一定会出现该错误,但是快速失败操作会尽最大努力抛出ConcurrentModificationException异常,这个异常仅用于检测bug。这种迭代器并不是完备的处理机制,而只是作为并发问题的一个预警指示器。 61 | 62 | 63 | #### 拓展阅读: 64 | 65 | [fail-fast机制的原理解析](https://github.com/AcceptedBoy/backstage-vacation-plan/blob/master/chapter1/concurrency/fail-fast.md) 66 | 67 | #### StackOverFlow地址: 68 | 69 | [http://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re](http://stackoverflow.com/questions/223918/iterating-through-a-collection-avoiding-concurrentmodificationexception-when-re) -------------------------------------------------------------------------------- /contents/java-inner-class-and-static-nested-class.md: -------------------------------------------------------------------------------- 1 | ## Java内部类和嵌套静态类 2 | 3 | ### 问题 4 | Java 当中的内部类和静态嵌套类有什么主要区别? 在这两者中有什么设计或者实现么? 5 | 6 | ### 回答 7 | 嵌套类分为两类: 静态和非静态. 用`static`装饰的嵌套类叫做静态类, 非静态的嵌套类叫做内部类. 8 | 9 | 静态嵌套类使用外围类名来访问: 10 | ```java 11 | OuterClass.StaticNestedClass 12 | ``` 13 | 例如, 实例化一个静态嵌套类的对象就要使用这种语法: 14 | ```java 15 | OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass(); 16 | ``` 17 | 18 | 内部类对象的存在需要依靠一个外部类的对象. 看看下面的类: 19 | ```java 20 | class OuterClass { 21 | ... 22 | class InnerClass { 23 | ... 24 | } 25 | } 26 | ``` 27 | 内部类对象只有当外部类对象存在时才有效, 并且可以直接访问他的包裹对象(外部类对象)的方法以及成员. 28 | 29 | 因此, 要实例化一个内部类对象, 必须先实例化外部类对象. 然后用这种语法来创建内部类对象: 30 | ```java 31 | OuterClass.InnerClass innerObject = outerObject.new InnerClass(); 32 | ``` 33 | 参考: [Java Tutorial - Nested Classes](http://download.oracle.com/javase/tutorial/java/javaOO/nested.html) 34 | 35 | 提醒一下, 还有一种不用外部类对象来创建内部类对象的方法: [inner class without an enclosing ](http://stackoverflow.com/questions/20468856/is-it-true-that-every-inner-class-requires-an-enclosing-instance) 36 | ```java 37 | class A { 38 | int t() { return 1; } 39 | static A a = new A() { int t() { return 2; } }; 40 | } 41 | ``` 42 | 在这里, `new A() { ... }`是一个定义在静态上下文的内部类对象, 并没有一个外围对象. 43 | 44 | stackoverflow链接: [Java inner class and static nested class](http://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class) 45 | -------------------------------------------------------------------------------- /contents/java-operator.md: -------------------------------------------------------------------------------- 1 | ## Java += 操作符实质 2 | 3 | ### 问题 4 | 我之前以为: 5 | i += j 等同于 i = i + j; 6 | 但假设有: 7 | ```java 8 | int i = 5; 9 | long j = 8; 10 | ``` 11 | 这时 i = i + j 不能编译,但 i += j 却可以编译。这说明两者还是有差别的 12 | 这是否意味着,i += j,实际是等同于 i= (type of i) (i + j)呢? 13 | 14 | ### 回答 15 | 这个问题,其实官方文档中已经解答了。 请看这里 [§15.26.2 Compound Assignment Operators](http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.26.2) 16 | 17 | 18 | 再照搬下官方文档的说明 19 | 20 | 21 | 对复合赋值表达式来说,E1 op= E2 (诸如 i += j; i -= j 等等),其实是等同于 E1 = (T)((E1) op (E2)),其中,T是E1这个元素的类型。 22 | 23 | 举例来说,如下的代码 24 | ```java 25 | short x = 3; 26 | x += 4.6; 27 | ``` 28 | 等同于 29 | ```java 30 | short x = 3; 31 | x = (short)(x + 4.6); 32 | ``` 33 | 34 | 35 | stackoverflow链接 36 | http://stackoverflow.com/questions/8710619/java-operator 37 | -------------------------------------------------------------------------------- /contents/lookup-enum-by-string-value.md: -------------------------------------------------------------------------------- 1 | # Java 中如何将 String 转换为 enum 2 | 3 | ### 问题 4 | enum 类 5 | ```java 6 | public enum Blah { 7 | A, B, C, D 8 | } 9 | ``` 10 | 如何根据枚举类型的值(比如 "A" ) 得到 `Blah.A`? 11 | 12 | --- 13 | 14 | ### A1 15 | 16 | 是的, `Blah.valueOf("A")` 将会给你 `Blah.A`. 17 | 18 | 静态方法 `valueof()` 和 `values()` 在编译时期被插入,并不存在于源码中。 19 | 但是在Javadoc中会显示;例如,[`Dialog.ModalityType`](http://docs.oracle.com/javase/7/docs/api/java/awt/Dialog.ModalityType.html "Dialog.ModalityType")中显示了这两个方法。 20 | 21 | 22 | ### A2 23 | 24 | 另一个解答,如果文本和 `enumeration` 值不一致 25 | 26 | ``` java 27 | public enum Blah { 28 | A("text1"), 29 | B("text2"), 30 | C("text3"), 31 | D("text4"); 32 | 33 | private String text; 34 | 35 | Blah(String text) { 36 | this.text = text; 37 | } 38 | 39 | public String getText() { 40 | return this.text; 41 | } 42 | 43 | public static Blah fromString(String text) { 44 | if (text != null) { 45 | for (Blah b : Blah.values()) { 46 | if (text.equalsIgnoreCase(b.text)) { 47 | return b; 48 | } 49 | } 50 | } 51 | return null; 52 | } 53 | } 54 | ``` 55 | _评论区在讨论是应该返回null还是抛出异常,个人认为视具体情况,允许转换失败就返回null,不允许就抛出异常,或许创建一个特殊的空对象是个好的选择 -译者注_ 56 | 57 | ### A3 58 | 59 | 这是我使用的一个工具类: 60 | 61 | ``` java 62 | /** 63 | * 一个对于所有Enum类通用的方法,因为他们不能有另一个基类 64 | * @param Enum type 65 | * @param c enum type. All enums must be all caps. 66 | * @param string case insensitive 67 | * @return corresponding enum, or null 68 | */ 69 | public static > T getEnumFromString(Class c, String string) { 70 | if( c != null && string != null ) { 71 | try { 72 | return Enum.valueOf(c, string.trim().toUpperCase()); 73 | } catch(IllegalArgumentException ex) { 74 | } 75 | } 76 | return null; 77 | } 78 | ``` 79 | 之后,在我的enum类中通常如此使用来减少代码量: 80 | ``` java 81 | public static MyEnum fromString(String name) { 82 | return getEnumFromString(MyEnum.class, name); 83 | } 84 | ``` 85 | 如果的enums不是全部大写,只需要修改 `Enum.valueOf` 这一行。 86 | 很遗憾,我不能使用 `T.class` 传给 `Enum.valueOf`,因为 `T`会被擦出。 87 | 88 | _评论区对于答主的异常处理一片指责 -译者注_ 89 | 90 | ### A4 91 | 如果你不想编写自己的工具类,可以使用 Google的 [Google guava](https://github.com/google/guava) 库: 92 | ``` java 93 | Enums.getIfPresent(Blah.class, "A") 94 | ``` 95 | 它让你检查是否 `Blan`中存在 `A`并且不抛出异常 96 | 97 | _完整方法签名 `Optional getIfPresent(Class enumClass, String value)` , `Optional` 对象可以优雅的解决null值问题 -译者注_ 98 | 99 | > 注意: 返回的是 `Google Optional` 而不是 `Java Optional` 100 | 101 | --- 102 | _其他的答案都大同小异,感兴趣的可以看原帖_ 103 | stackoverflow链接 104 | [Lookup enum by string value 105 | ](https://stackoverflow.com/questions/604424/lookup-enum-by-string-value) 106 | _译者:[MagicWolf](https://github.com/DaiDongLiang)_ -------------------------------------------------------------------------------- /contents/lookup-enum-by-string-value2.md: -------------------------------------------------------------------------------- 1 | 如何将枚举转换成数组 2 | 问题: 3 | 假设我有一个枚举类是这样的: 4 | public enum Blah { 5 | A, B, C, D 6 | } 7 | 我想要将枚举类的值转化成一个数组,比如"A"怎么可能是Blah.A.怎么有可能做到这点?我需要Enum.valueOf()这个方法吗?如果是这样,我应该怎么用他? 8 | 9 | 回答: 10 | 是的,Blah.valuOf("A")将会给你Blah.A。 11 | valueOf()和values()这些静态方法是在编译时创建的,而且不会出现在源代码里。不过他们确实有出现在Javadoc,比如Dialog.ModalityType有这两个方法。 12 | stackoverflow链接: 13 | http://stackoverflow.com/questions/604424/lookup-enum-by-string-value 14 | -------------------------------------------------------------------------------- /contents/must-override-a-superclass-method-errors-after-importing-a-project-into-eclips.md: -------------------------------------------------------------------------------- 1 |  2 | ##问题 3 | 大多数情况下,当我重新导入项目到eclipse的时候,我重写的方法都不能被正确格式化,导致这样的错误: 4 | > The method must override a superclass method. 5 | 6 | 需要说明的是这是一个Android项目,不知道什么原因,方法的参数被篡改了,因此,我不得不手动的把他们改回来。 7 | 例如: 8 | ```java 9 | list.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { 10 | //这儿的参数名是正确的 11 | public void onCreateContextMenu(ContextMenu menu, View v, 12 | ContextMenuInfo menuInfo) { 13 | } 14 | }); 15 | ``` 16 | 初始化的时候被篡改成了这样: 17 | ```java 18 | list.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { 19 | //这儿的参数被篡改成了这样 20 | public void onCreateContextMenu(ContextMenu arg1, View arg2, 21 | ContextMenuInfo arg3) { 22 | } 23 | }); 24 | ``` 25 | 奇怪的是,如果我移除我的代码并使用eclipse自动创建方法的话,它还是会是相同的参数(被篡改的)。因此,我真不知道那儿的问题,它本应该自动格式化代码的。 26 | 要是手动的去修改被篡改的参数名,那是一个非常痛苦的过程。要是有人能解释为什么会出现这样的情况以及怎样去解决它,我感激不尽。 27 | 是不是因为我格式化的这个方法,是另一个方法里面的参数而导致的这样的问题呢? 28 | 29 | ##回答 30 | Eclipse的默认执行环境是java 1.5况且你使用了类的声明接口方法(在java 1.6中能使用@Ovrride注释,但是在java 1.5中一个方法只能重写父类的方法) 31 | 32 | 打开你的项目,然后找到preference并且设置java的编译版本为1.6,同时也确保你的eclipse是使用JRE 1.6 来执行你的程序的。 33 | 34 | Stack Overflow原地址:http://stackoverflow.com/questions/1678122/must-override-a-superclass-method-errors-after-importing-a-project-into-eclips 35 | 36 | -------------------------------------------------------------------------------- /contents/proper-use-cases-for-android-usermanager-isuseragoat.md: -------------------------------------------------------------------------------- 1 | ##安卓中“UserManger.isUserAGoat()”的合适案例? 2 | 3 | ###问题描述: 4 | 我正在看在安卓4.2里介绍的新API。当看到“UserManger”类时,我遇到了如下的“method”: 5 | public boolean isUserAGoat() 6 | Used to determine whether the user making this call is subject to teleportations. 7 | 8 | Returns whether the user making this call is a goat. 9 | 这个应该怎样使用和什么时候使用? 10 | 11 | ###回答: 12 | 根据他们的资料,在API21前,这个“method”用来返回“false” 13 | /** 14 | * Used to determine whether the user making this call is subject to 15 | * teleportations. 16 | * @return whether the user making this call is a goat 17 | */ 18 | public boolean isUserAGoat() { 19 | return false; 20 | } 21 | 看起来这个“method”对我们开发者没有真正的用途。一些人之前认为它可能是个复活节彩蛋。 22 | 再次编辑: 23 | 在API21它改为判断是否存在有包“com.coffeestainstudios.goatsimulator”的已安装app。 24 | /** 25 | * Used to determine whether the user making this call is subject to 26 | * teleportations. 27 | * 28 | *

As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this method can 29 | * now automatically identify goats using advanced goat recognition technology.

30 | * 31 | * @return Returns true if the user making this call is a goat. 32 | */ 33 | public boolean isUserAGoat() { 34 | return mContext.getPackageManager() 35 | .isPackageAvailable("com.coffeestainstudios.goatsimulator"); 36 | } 37 | 38 | [stackoverflow链接:Proper use cases for Android UserManager.isUserAGoat()](http://stackoverflow.com/questions/13375357/proper-use-cases-for-android-usermanager-isuseragoat) 39 | -------------------------------------------------------------------------------- /contents/read-convert-an-inputstream-to-a-string.md: -------------------------------------------------------------------------------- 1 | ##将InputStream转换为String 2 | 3 | ###使用Apache库 4 | 不重复造轮子。最靠谱的方法,还是用Apache commons IOUtils 5 | 这样简单几行代码就搞定了 6 | ```java 7 | StringWriter writer = new StringWriter(); 8 | IOUtils.copy(inputStream, writer, encoding); 9 | String theString = writer.toString(); 10 | ``` 11 | 或者 12 | ```java 13 | String theString = IOUtils.toString(inputStream, encoding)//这个方法其实封装了上面的方法,减少了一个参数 14 | ``` 15 | ###使用原生库 16 | 如果不想引入Apache库,也可以这样做 17 | ```java 18 | static String convertStreamToString(java.io.InputStream is) { 19 | java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); 20 | return s.hasNext() ? s.next() : ""; 21 | } 22 | ``` 23 | 24 | stackoverflow讨论地址 25 | http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string -------------------------------------------------------------------------------- /contents/setting-multiple-jars-in-java-classpath.md: -------------------------------------------------------------------------------- 1 | ##如何在classpath中设置多个jar包? 2 | 3 | ###问题 4 | 是否有一个方法可以在classpath选项中包含一个文件夹(目录)下的所有jar包? 5 | 我尝试运行`java -classpath lib/*.jar:. my.package.Program`,但是无法找到相应的类文件,可是这些类文件确实存在于命令中的jar包中。我是否需要在classpath中分别指定所有的jar包? 6 | 7 | ###回答 8 | 在使用Java6或者以上版本时,classpath选项可以支持通配符(wildcards)。使用方法如下: 9 | * 使用直接引用(`"`) 10 | * 使用 `*` 而不是 `*.jar` 11 | 12 | **Windows平台** 13 | 14 | `java -cp "Test.jar;lib/*" my.package.MainClass` 15 | 16 | **Unix平台** 17 | 18 | `java -cp "Test.jar:lib/*" my.package.MainClass` 19 | 20 | Unix平台与Windows平台基本一样,除了使用冒号 `:` 替代分号 `;` 之外。如果你不能使用通配符,也可以使用`bash`完成上述功能,命令如下(其中lib是一个包含所有jar包的目录): 21 | `java -cp $(echo lib/*.jar | tr ' ' ':')` 22 | 23 | 注意事项:classpath选项与-jar选项并不能兼容。可以查看:[Execute jar file with multiple classpath libraries from command prompt](http://stackoverflow.com/questions/13018100/execute-jar-file-with-multiple-classpath-libraries-from-command-prompt) 24 | 25 | **对于通配符的理解** 26 | 来自[Classpath](http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html)文档: 27 | 28 | 类路径可以包含一个基本文件名通配符`*`,其等价于指定一个特定目录下的所有以.jar或.JAR为后缀的文件的列表。例如,一个类路径的条目为`foo/*`,其指定了foo目录下的所有jar文件。一个仅仅包含`*`的classpath条目(entry)指定了当前目录下的所有jar包。 29 | 30 | 一个包含了`*`的classpath条目不能匹配特定目录下的class文件。为了既能匹配foo目录下的类文件,也能匹配jar包,可以使用`foo;foo/*`或`foo/*;foo`。对于前者而言,类文件和资源选择的顺序先是foo目录下的类文件和资源,之后才是jar包;而后者则正好相反。 31 | 32 | 通配符并不能递归地搜索子目录下的jar包。例如,`foo/*`只找`foo`目录下的jar包,而不会寻找`foo/bar`,`foo/baz`等目录下的jar包。 33 | 34 | 一个目录中的jar包枚举顺序并不固定,这不仅和平台有关,甚至可能会在同一个机器上因为时间不同而表现不同。一个结构良好(well-constructed)的应用不应该依赖于某个特定的顺序。如果特定顺序是不可避免的时候,就需要在classpath中显示枚举所有的jar包了。 35 | 36 | 在类加载进程中,通配符的扩展在早期完成,优先于程序main函数的调用,而不是在其后。每个包含通配符的类路径都被替换为所在目录下所有jar包的序列。例如,如果目录`foo`包含`a.jar`,`b.jar`以及`c.jar`,因此类路径`foo/*`被扩展为`foo/a.jar;foo/b.jar;foo/c.jar`,并且以上字符串被作为系统属性`java.class.path`的值。 37 | 38 | 环境变量`CLASSPATH`与命令行选项-classpath或者-cp并没有什么不同。也就是说,通配符既可以应用于命令行`-classpath/-cp`选项中,也可以应用于环境变量`CLASSPATH`中。 39 | 40 | stackoverflow原地址:http://stackoverflow.com/questions/219585/setting-multiple-jars-in-java-classpath -------------------------------------------------------------------------------- /contents/sort-arraylist-of-custom-objects-by-property.md: -------------------------------------------------------------------------------- 1 |  2 | 3 | ##通过对象属性对常规对象的ArrayList进行排序 4 | 5 | ###问题 6 | 我读过使用Comparator对常规类的ArrayList进行排序的示例,但是它们大多数使用comparedTo(),据我了解这是一个对字符串进行操作的方法。 7 | 我想要对一个由常规对象构成的ArrayList,通过它的属性(一个Date对象,getStartDate())对ArrayList进行排序。通常情况下我这样比较它们: 8 | ```java 9 | item1.getStartDate().before(item2.getStartDate()) 10 | ``` 11 | 所以我能不能像下面一样: 12 | ```java 13 | public class CustomComparator { 14 | public boolean compare(Object object1, Object object2) { 15 | return object1.getStartDate().before(object2.getStartDate()); 16 | } 17 | } 18 | 19 | public class RandomName { 20 | ... 21 | Collections.sort(Database.arrayList, new CustomComparator); 22 | ... 23 | } 24 | ``` 25 | 26 | ###回答 27 | 以前Date声明了Comparable,它有一个像处理字符串操作那样的compareTo方法。因此你可以这样: 28 | ```java 29 | public class CustomComparator implements Comparator { 30 | @Override 31 | public int compare(MyObject o1, MyObject o2) { 32 | return o1.getStartDate().compareTo(o2.getStartDate()); 33 | } 34 | } 35 | ``` 36 | 这儿的compare()方法必须返回int,所以不能像你预期那样直接返回boolean. 37 | 你的代码看起来应该是这样: 38 | ```java 39 | Collections.sort(Database.arrayList, new CustomComparator()); 40 | ``` 41 | 如果你不需要重复使用comparetor的话,一种简单的方法是,把它写成一个内部类的样子: 42 | ```java 43 | Collections.sort(Database.arrayList, new Comparator() { 44 | @Override 45 | public int compare(MyObject o1, MyObject o2) { 46 | return o1.getStartDate().compareTo(o2.getStartDate()); 47 | } 48 | }); 49 | ``` 50 | 自java8 开始,你可以使用lambda表达式一种简洁的方式来写Comparator() 51 | ```java 52 | Collections.sort(Database.arrayList, 53 | (o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate())); 54 | ``` 55 | 并且List有一个sort(Comparator)方法,所以你可以进一步简化它 56 | ```java 57 | 58 | Database.arrayList.sort((o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate())); 59 | ``` 60 | 这是一种司空见惯的方法,使用Comparable为一个类创建一个Comprator 61 | ```java 62 | Database.arrayList.sort(Comparator.comparing(MyObject::getStartDate)); 63 | All of these are equivalent forms. 64 | ``` 65 | 66 | stackoverflow原地址:http://stackoverflow.com/questions/2784514/sort-arraylist-of-custom-objects-by-property 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /contents/stringbuilder-and-stringbuffer.md: -------------------------------------------------------------------------------- 1 | ##StringBuilder和StringBuffer有哪些区别呢 2 | 3 | ###最主要的区别,StringBuffer的实现用了synchronized(锁),而StringBuilder没有。 4 | 5 | 因此,StringBuilder会比StringBuffer快。 6 | 7 | 如果你 8 | - 非常非常追求性能(其实这两个都不慢,比直接操作String,要快非常多了) 9 | - 不需要考虑线程安全问题, 10 | - JRE是1.5+ 11 | 12 | 可以用StringBuilder,反之,请用StringBuffer。 13 | 14 | 性能测试例子: 15 | 16 | 如下这个例子,使用StringBuffer,耗时2241ms,而StringBuilder是753ms 17 | ```java 18 | public class Main { 19 | public static void main(String[] args) { 20 | int N = 77777777; 21 | long t; 22 | 23 | { 24 | StringBuffer sb = new StringBuffer(); 25 | t = System.currentTimeMillis(); 26 | for (int i = N; i --> 0 ;) { 27 | sb.append(""); 28 | } 29 | System.out.println(System.currentTimeMillis() - t); 30 | } 31 | 32 | { 33 | StringBuilder sb = new StringBuilder(); 34 | t = System.currentTimeMillis(); 35 | for (int i = N; i --> 0 ;) { 36 | sb.append(""); 37 | } 38 | System.out.println(System.currentTimeMillis() - t); 39 | } 40 | } 41 | } 42 | ``` 43 | 44 | 45 | stackoverflow讨论原址 46 | http://stackoverflow.com/questions/355089/stringbuilder-and-stringbuffer 47 | -------------------------------------------------------------------------------- /contents/using-java-net-urlconnection-to-fire-and-handle-http-requests.md: -------------------------------------------------------------------------------- 1 | ##如何使用java.net.URLConnection接收及发送HTTP请求 2 | 3 | 首先声明,下面的代码,都是基本的例子。更严谨的话,还应加入处理各种异常的代码(如IOExceptions、NullPointerException、ArrayIndexOutOfBoundsException) 4 | 5 | ###准备 6 | 首先,需要设置请求的URL以及charset(编码);额外的参数,则取决于各自url的要求。 7 | ```java 8 | String url = "http://example.com"; 9 | String charset = "UTF-8"; 10 | String param1 = "value1"; 11 | String param2 = "value2"; 12 | // ... 13 | String query = String.format("param1=%s¶m2=%s", 14 | URLEncoder.encode(param1, charset), 15 | URLEncoder.encode(param2, charset)); 16 | ``` 17 | url中附带的请求参数,必须是name=value这样的格式,每个参数间用&连接。一般来说,你还得用 [URLEncoder#encode()](http://docs.oracle.com/javase/6/docs/api/java/net/URLEncoder.html)对参数做[编码](http://en.wikipedia.org/wiki/Percent-encoding) 18 | 19 | 上面例子还用到了String#format()。字符拼接方式,看个人喜好,我更喜欢用这个方式。 20 | 21 | ###发送一个[HTTP GET](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3)请求(可选:带上参数) 22 | 这依然是个繁琐的事情。默认的方式如下: 23 | ```java 24 | URLConnection connection = new URL(url + "?" + query).openConnection(); 25 | connection.setRequestProperty("Accept-Charset", charset); 26 | InputStream response = connection.getInputStream(); 27 | ``` 28 | url和参数之间,要用?号连接。请求头(header)中的[Accept-Charset](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2),用于告诉服务器,你所发送参数的编码。如果你不带送任何参数,也可以不管Accept-Charset。另外如果你无需设置header,也可以用[URL#openStream()](http://docs.oracle.com/javase/6/docs/api/java/net/URL.html#openStream%28%29) 而非openConnection。 29 | 不管那种方式,假设服务器端是 [HttpServlet](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServlet.html),那么你的get请求将会触发它的doGet()方法,它能通过[HttpServletRequest#getParameter()](http://docs.oracle.com/javaee/6/api/javax/servlet/ServletRequest.html#getParameter%28java.lang.String%29)获取你传递的参数。 30 | 31 | ###发送一个[HTTP POST](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5)请求,并带上参数 32 | 设置[URLConnection#setDoOutput()](http://docs.oracle.com/javase/6/docs/api/java/net/URLConnection.html#setDoOutput%28boolean%29),等于隐式地将请求方法设为POST。标准的HTTP POST 表单,其Content-Tyep为application/x-www-form-urlencoded,请求的内容放到到body中。也就是如下代码: 33 | ```java 34 | URLConnection connection = new URL(url).openConnection(); 35 | connection.setDoOutput(true); // Triggers POST. 36 | connection.setRequestProperty("Accept-Charset", charset); 37 | connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset); 38 | 39 | try (OutputStream output = connection.getOutputStream()) { 40 | output.write(query.getBytes(charset)); 41 | } 42 | 43 | InputStream response = connection.getInputStream(); 44 | ``` 45 | 46 | 提醒: 47 | 48 | 当你要提交一个HTML表单时,务必要把``这类元素的值,也以name=value的形式提交,因为,服务端通常也需要这个信息,已确认哪一个按钮触发了这个提交动作。 49 | 50 | 也可以使用[HttpURLConnection](http://docs.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html) 来代替[URLConnection](http://docs.oracle.com/javase/6/docs/api/java/net/URLConnection.html) ,然后调用[HttpURLConnection#setRequestMethod()](http://docs.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html#setRequestMethod%28java.lang.String%29)来将请求设为POST类型。 51 | 52 | ```java 53 | HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection(); 54 | httpConnection.setRequestMethod("POST"); 55 | ``` 56 | 57 | 同样的,如果服务端是[HttpServlet](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServlet.html),将会触发它的[doPost()](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServlet.html#doPost%28javax.servlet.http.HttpServletRequest,%20javax.servlet.http.HttpServletResponse%29)方法,可以通过[HttpServletRequest#getParameter()](http://docs.oracle.com/javaee/6/api/javax/servlet/ServletRequest.html#getParameter%28java.lang.String%29)获取post参数 58 | 59 | ###触发HTTP请求的发送 60 | 你可以显式地通过[URLConnection#connect()](http://docs.oracle.com/javase/6/docs/api/java/net/URLConnection.html#connect%28%29)来发送请求,但是,当你调用获取响应信息的方法时,一样将自动发送请求。例如当你使用[URLConnection#getInputStream()](http://docs.oracle.com/javase/6/docs/api/java/net/URLConnection.html#getInputStream%28%29)时,就会自动触发请求,因此,connect()方法往往都是多余的。上面我的例子,也都是直接调用getInputStream()方法。 61 | 62 | 获取HTTP响应信息 63 | 1. HTTP响应码: 64 | 首先默认你使用了 [HttpURLConnection](http://docs.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html) 65 | ```java 66 | int status = httpConnection.getResponseCode(); 67 | ``` 68 | 2. HTTP 响应头(headers) 69 | ```java 70 | for (Entry> header : connection.getHeaderFields().entrySet()) { 71 | System.out.println(header.getKey() + "=" + header.getValue()); 72 | } 73 | ``` 74 | 3. HTTP响应编码: 75 | 当Content-Type中包含charset参数时,说明响应内容是基于charset参数指定的编码。因此,解码响应信息时,也要按照这个编码格式来。 76 | 77 | ```java 78 | String contentType = connection.getHeaderField("Content-Type"); 79 | String charset = null; 80 | 81 | for (String param : contentType.replace(" ", "").split(";")) { 82 | if (param.startsWith("charset=")) { 83 | charset = param.split("=", 2)[1]; 84 | break; 85 | } 86 | } 87 | 88 | if (charset != null) { 89 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) { 90 | for (String line; (line = reader.readLine()) != null;) { 91 | // ... System.out.println(line) ? 92 | } 93 | } 94 | } 95 | else { 96 | // It's likely binary content, use InputStream/OutputStream. 97 | } 98 | ``` 99 | 100 | 101 | ###session的维护 102 | 服务端session,通常是基于cookie实现的。你可以通过[CookieHandlerAPI](http://docs.oracle.com/javase/8/docs/api/java/net/CookieHandler.html)来管理cookie。在发送HTTP请求前,初始化一个[CookieManager](http://docs.oracle.com/javase/6/docs/api/java/net/CookieManager.html), 然后设置参数为[CookiePolicy](http://docs.oracle.com/javase/6/docs/api/java/net/CookiePolicy.html).[CCEPT_ALL](http://docs.oracle.com/javase/6/docs/api/java/net/CookiePolicy.html#ACCEPT_ALL)。 103 | ```java 104 | // First set the default cookie manager. 105 | CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL)); 106 | // All the following subsequent URLConnections will use the same cookie manager. 107 | URLConnection connection = new URL(url).openConnection(); 108 | // ... 109 | connection = new URL(url).openConnection(); 110 | // ... 111 | connection = new URL(url).openConnection(); 112 | // ... 113 | ``` 114 | 115 | 请注意,这个方式并非适用于所有场景。如果使用这个方式失败了,你可以尝试自己设置cookie:你需要从响应头中拿到Set-Cookie参数,然后再把cookie设置到接下来的其他请求中。 116 | ```java 117 | // Gather all cookies on the first request. 118 | URLConnection connection = new URL(url).openConnection(); 119 | List cookies = connection.getHeaderFields().get("Set-Cookie"); 120 | // ... 121 | 122 | // Then use the same cookies on all subsequent requests. 123 | connection = new URL(url).openConnection(); 124 | for (String cookie : cookies) { 125 | connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]); 126 | } 127 | // ... 128 | ``` 129 | 上面的split(";", 2)[0],作用是去掉一些跟服务端无关的cookie信息(例如expores,path等)。也可用cookie.substring(0, cookie.indexOf(';'))达到同样的目的 130 | 131 | ###流的处理 132 | 不管你是否通过connection.setRequestProperty("Content-Length", contentLength)方法,为content设置了定长, [HttpURLConnection](http://docs.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html)在发送请求前,默认都会缓存整个请求的body。如果发送一个比较大的post请求(例如上传文件),有可能会导致OutOfMemoryException。为了避免这个问题,可以设置[HttpURLConnection#setFixedLengthStreamingMode()](http://docs.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html#setFixedLengthStreamingMode%28int%29) 133 | ```java 134 | httpConnection.setFixedLengthStreamingMode(contentLength); 135 | ``` 136 | 但如果content长度是未知的,则可以用[HttpURLConnection#setChunkedStreamingMode()](http://docs.oracle.com/javase/6/docs/api/java/net/HttpURLConnection.html#setChunkedStreamingMode%28int%29)。这样,header中Transfer-Encoding会变成chunked,你的请求将会分块发送,例如下面的例子,请求的body,将会按1KB一块,分块发送 137 | ```java 138 | httpConnection.setChunkedStreamingMode(1024); 139 | ``` 140 | 141 | ###User-Agent 142 | 有时候,你发送的请求,可能只有在浏览器下才能正常返回,而其他方式却不行。这可能跟请求头中的User-Agent有关。通过URLConnection发送的请求,默认会带上的User-Agent信息是Java/1.6.0_19,也就是java+jre的版本。你可以重写这个信息: 143 | ```java 144 | connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401"); // Do as if you're using Firefox 3.6.3. 145 | ``` 146 | 这里有一份更全的浏览器[User-Agent清单](http://www.useragentstring.com/pages/useragentstring.php) 147 | 148 | ###错误处理 149 | 如果HTTP的响应码是4xx(客户端异常)或者5xx(服务端异常),你可以通过HttpURLConnection#getErrorStream()获取信息,服务端可能会将一些有用的错误信息放到这里面。 150 | ```java 151 | InputStream error = ((HttpURLConnection) connection).getErrorStream(); 152 | ``` 153 | 154 | ###上传文件 155 | 一般来说,你需要将post的内容设为[multipart/form-data](http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2)(相关的RFC文档:[RFC2388](http://www.faqs.org/rfcs/rfc2388.html)) 156 | ```java 157 | String param = "value"; 158 | File textFile = new File("/path/to/file.txt"); 159 | File binaryFile = new File("/path/to/file.bin"); 160 | String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value. 161 | String CRLF = "\r\n"; // Line separator required by multipart/form-data. 162 | URLConnection connection = new URL(url).openConnection(); 163 | connection.setDoOutput(true); 164 | connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); 165 | 166 | try ( 167 | OutputStream output = connection.getOutputStream(); 168 | PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true); 169 | ) { 170 | // Send normal param. 171 | writer.append("--" + boundary).append(CRLF); 172 | writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF); 173 | writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); 174 | writer.append(CRLF).append(param).append(CRLF).flush(); 175 | 176 | // Send text file. 177 | writer.append("--" + boundary).append(CRLF); 178 | writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF); 179 | writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset! 180 | writer.append(CRLF).flush(); 181 | Files.copy(textFile.toPath(), output); 182 | output.flush(); // Important before continuing with writer! 183 | writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary. 184 | 185 | // Send binary file. 186 | writer.append("--" + boundary).append(CRLF); 187 | writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF); 188 | writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF); 189 | writer.append("Content-Transfer-Encoding: binary").append(CRLF); 190 | writer.append(CRLF).flush(); 191 | Files.copy(binaryFile.toPath(), output); 192 | output.flush(); // Important before continuing with writer! 193 | writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary. 194 | 195 | // End of multipart/form-data. 196 | writer.append("--" + boundary + "--").append(CRLF).flush(); 197 | } 198 | ``` 199 | 200 | 假设服务端还是一个[HttpServlet](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServlet.html),它的doPost()方法将会处理这个请求,服务端通过[HttpServletRequest#getPart()](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getPart%28java.lang.String%29)获取你发送的内容(注意了,不是getParameter())。getPart()是个比较新的方法,是在Servlet 3.0后才引入的。如果你是Servlet 3.0之前的版本,则可以选用[Apache Commons FileUpload](http://commons.apache.org/fileupload]来解析multipart/form-data的请求。可以参考这里的[例子](http://stackoverflow.com/questions/2422468/upload-big-file-to-servlet/2424824#2424824) 201 | 202 | ###最后的话 203 | 上面啰嗦了很多,Apache提供了工具包,帮助我们更方便地完成这些事情 204 | [Apache HttpComponents HttpClient](http://stackoverflow.com/questions/2422468/upload-big-file-to-servlet/2424824#2424824): 205 | - [HttpClient Tutorial](http://hc.apache.org/httpcomponents-client-ga/tutorial/html/) 206 | - [HttpClient Examples](http://hc.apache.org/httpcomponents-client-ga/examples.html) 207 | 208 | 209 | google也有类似的[工具包](https://code.google.com/p/google-http-java-client/) 210 | 211 | 解析、提取HTML内容 212 | 如果你是想解析提取html的内容,你可以用[Jsoup](http://jsoup.org/)等解析器 213 | - [一些比较有名的HTML解析器的优缺点](http://stackoverflow.com/questions/3152138/what-are-the-pros-and-cons-of-the-leading-java-html-parsers/3154281#3154281) 214 | - [用java如何扫描和解析网页](http://stackoverflow.com/questions/2835505/how-to-scan-a-website-or-page-for-info-and-bring-it-into-my-program/2835555#2835555) 215 | 216 | 217 | 218 | 219 | stackoverflow原址: 220 | http://stackoverflow.com/questions/2793150/using-java-net-urlconnection-to-fire-and-handle-http-requests -------------------------------------------------------------------------------- /contents/what-exactly-is-apache-camel.md: -------------------------------------------------------------------------------- 1 | ### 问题描述 2 | 3 | 我不明白Camel到底是干什么的. 4 | 希望你能在101字以内介绍一下Camel: 5 | 它到底是什么? 6 | 如何在java中使用它? 7 | 它是和服务器相关的么? 8 | 它是一个独立的程序? 9 | 请解释一下Camel是什么. 10 | 11 | ### 答案 12 | 如果你有5-10分钟时间,我建议你读一下Jonathan Anstey关于Apache Camel的文章.这是一篇非常棒的文章,简要的介绍了一些Apache Camel的概念,以及用java实现了一个 13 | 实例.Jonathan Anstey是这样描述的: 14 | Apache Camel是一个开源的Java框架,其整合的目的主要是为了使开发人员更容易、方便的开发程序.它提供了如下内容: 15 | 16 | (1)所有被广泛使用的`企业集成模式`的具体实现(`EIPs`) 17 | 18 | (2)连接不同的数据传输和API 19 | 20 | (3)容易使用`领域特定语言(DSL)`建立EIPs和高效的数据传输 21 | 22 | ### 术语解析 23 | 24 | `EIPs`:企业集成模式的简称,使用消息传递进行企业应用集成,比如消息中间件,将不同程序之间连接在一起. 25 | 26 | `DSL`:DSL编程又称为声明式编程,DSL是在模型之上建立的一种更加灵活的对模型化的理解和使用方式,通俗点说你只需要告诉程序你想要什么,不必每一步都指挥它如何 27 | 执行,SQL语句就是其中的代表. 28 | 29 | ### 通俗点讲 30 | 31 | _Camel:将数据从一方获得,该数据可以是消息、文件流、JSON的多种形式的数据,然后处理,再发送,整合了多种数据获取、处理、发送方式,方便开发者使用_ 32 | 33 | 34 | stackoverflow链接 35 | http://stackoverflow.com/questions/8845186/what-exactly-is-apache-camel 36 | _译者:[王小过](https://github.com/whp1473)_ 37 | -------------------------------------------------------------------------------- /contents/what-is-a-javabean-exactly.md: -------------------------------------------------------------------------------- 1 | ## JavaBean 到底是什么? 2 | 3 | ### 问题 4 | 5 | 按照我的理解: “Bean” 是一个带有属性和getters/setter方法的Java类。它是不是和C的结构体是相似的呢,对吗? 6 | 一个“Bean"类与普通的类相比是不是语法的不同呢?还是有特殊的定义和接口? 7 | 为什么会出现这个术语呢,这让我很困惑? 8 | 如果你很好心告诉我一些关于`Serializable`接口的信息,对于你的答案那到底是什么意思,我会非常感谢你的。 9 | 10 | ### 回答 11 | 12 | JavaBean 只是一个[标准](http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html) 13 | 14 | 1. 所有的属性是私有的(通过[getters/setters](https://en.wikipedia.org/wiki/Mutator_method)处理属性) 15 | 2. 一个公有的无参数的构造器 16 | 3. 实现了[序列化(Serializable)](http://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html) 17 | 18 | 就这些,它只是一个规范。但是很多的类库都是依赖于这些预定。 19 | 20 | 对于`Serializable`,看一下[API文档的解释](http://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html) 21 | 22 | ``` 23 | 实现java.io.Serializable接口的类能串行化。 24 | 不实现此接口的类不会有任何状态的序列化和反序列化。 25 | 可序列化类的所有子类型本身都是可序列化。 26 | 序列化接口没有方法或字段,仅用于标识的可序列化的语义。 27 | ``` 28 | 换句话说,序列化的对象可以被写入流,文件,对象数据库等。 29 | 30 | 另外,一个JavaBean类和一个普通的类没有语法区别,如果遵循上面的标准的话,一个类可以认为成JavaBean类。 31 | 32 | 之所以需要JavaBean,是因为这样预定义了一种类的格式,一些库能依据这个约定的格式,来做一些自动化处理。举个例子,如果一个类库需要通过流来处理你传递的任何对象,它知道它可以正常处理,因为这个对象是可序列化的。(假设这个类库要求你的对象是JavaBeans) 33 | 34 | **stackoverflow链接**: 35 | http://stackoverflow.com/questions/3295496/what-is-a-javabean-exactly 36 | 37 | ### 关于序列化相关博客 38 | 39 | 40 | 1. [我对Java Serializable(序列化)的理解和总结](http://xiebh.iteye.com/blog/121311) 41 | 2. [理解Java对象序列化](http://www.blogjava.net/jiangshachina/archive/2012/02/13/369898.html) 42 | -------------------------------------------------------------------------------- /contents/what-is-a-serialversionuid-and-why-should-i-use-it.md: -------------------------------------------------------------------------------- 1 | # serialVersionUID 有什么作用?该如何使用? 2 | ##问题 3 | 4 | 当一个对象实现 Serializable 接口时,多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识),那 serialVersionUID 到底有什么作用呢?应该如何使用 serialVersionUID ? 5 | 6 | ##回答 7 | serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则是应用于Java 对象序列化/反序列化。对象的序列化主要有两种用途: 8 | 9 | - 把对象序列化成字节码,保存到指定介质上(如磁盘等) 10 | - 用于网络传输 11 | 12 | 现在反过来说就是,serialVersionUID 会影响到上述所提到的两种行为。那到底会造成什么影响呢? 13 | 14 | [java.io.Serializable](http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html) doc 文档,给出了一个相对详细解释: 15 | 16 | serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。序列化的类可显式声明 serialVersionUID 的值,如下: 17 | ``` 18 | ANY-ACCESS-MODIFIER static final long serialVersionUID = 1L; 19 | ``` 20 | 当显式定义 serialVersionUID 的值时,Java 根据类的多个方面(具体可参考 Java 序列化规范)动态生成一个默认的 serialVersionUID 。尽管这样,还是建议你在每一个序列化的类中显式指定 serialVersionUID 的值,因为不同的 jdk 编译很可能会生成不同的 serialVersionUID 默认值,进而导致在反序列化时抛出 InvalidClassExceptions 异常。所以,为了保证在不同的 jdk 编译实现中,其 serialVersionUID 的值也一致,可序列化的类必须显式指定 serialVersionUID 的值。另外,serialVersionUID 的修饰符最好是 private,因为 serialVersionUID 不能被继承,所以建议使用 private 修饰 serialVersionUID。 21 | 22 | 23 | 举例说明如下: 24 | 现在尝试通过将一个类 Person 序列化到磁盘和反序列化来说明 serialVersionUID 的作用: Person 类如下: 25 | ``` 26 | public class Person implements Serializable { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | private String name; 31 | private Integer age; 32 | private String address; 33 | 34 | public Person() { 35 | } 36 | 37 | public Person(String name, Integer age, String address) { 38 | this.name = name; 39 | this.age = age; 40 | this.address = address; 41 | } 42 | 43 | 44 | @Override 45 | public String toString() { 46 | return "Person{" + 47 | "name='" + name + '\'' + 48 | ", age=" + age + 49 | ", address='" + address + '\'' + 50 | '}'; 51 | } 52 | } 53 | ``` 54 | 55 | 简单的测试一下: 56 | ``` 57 | @Test 58 | public void testversion1L() throws Exception { 59 | File file = new File("person.out"); 60 | // 序列化 61 | ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file)); 62 | Person person = new Person("John", 21, "广州"); 63 | oout.writeObject(person); 64 | oout.close(); 65 | // 反序列化 66 | ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)); 67 | Object newPerson = oin.readObject(); 68 | oin.close(); 69 | System.out.println(newPerson); 70 | } 71 | ``` 72 | 73 | 测试发现没有什么问题。有一天,因发展需要, 需要在 Person 中增加了一个字段 email,如下: 74 | ``` 75 | public class Person implements Serializable { 76 | 77 | private static final long serialVersionUID = 1L; 78 | 79 | private String name; 80 | private Integer age; 81 | private String address; 82 | private String email; 83 | 84 | public Person() { 85 | } 86 | 87 | public Person(String name, Integer age, String address) { 88 | this.name = name; 89 | this.age = age; 90 | this.address = address; 91 | } 92 | 93 | public Person(String name, Integer age, String address,String email) { 94 | this.name = name; 95 | this.age = age; 96 | this.address = address; 97 | this.email = email; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "Person{" + 103 | "name='" + name + '\'' + 104 | ", age=" + age + 105 | ", address='" + address + '\'' + 106 | ", email='" + email + '\'' + 107 | '}'; 108 | } 109 | } 110 | ``` 111 | 112 | 这时我们假设和之前序列化到磁盘的 Person 类是兼容的,便不修改版本标识 serialVersionUID。再次测试如下 113 | ``` 114 | @Test 115 | public void testversion1LWithExtraEmail() throws Exception { 116 | File file = new File("person.out"); 117 | ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file)); 118 | Object newPerson = oin.readObject(); 119 | oin.close(); 120 | System.out.println(newPerson); 121 | } 122 | ``` 123 | 将以前序列化到磁盘的旧 Person 反序列化到新 Person 类时,没有任何问题。 124 | 125 | 可当我们增加 email 字段后,不作向后兼容。即放弃原来序列化到磁盘的 Person 类,这时我们可以将版本标识提高,如下: 126 | ``` 127 | private static final long serialVersionUID = 2L; 128 | ``` 129 | 130 | 再次进行反序列化,则会报错,如下: 131 | ``` 132 | java.io.InvalidClassException:Person local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 133 | ``` 134 | 135 | 谈到这里,我们大概可以清楚,serialVersionUID 就是控制版本是否兼容的,若我们认为修改的 Person 是向后兼容的,则不修改 serialVersionUID;反之,则提高 serialVersionUID的值。再回到一开始的问题,为什么 ide 会提示声明 serialVersionUID 的值呢? 136 | 137 | 因为若不显式定义 serialVersionUID 的值,Java 会根据类细节自动生成 serialVersionUID 的值,如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,也有可能会导致不同的serialVersionUID。所以 ide 才会提示声明 serialVersionUID 的值。 138 | 139 | 附录拓展: 140 | 141 | - [深入理解 Java 对象序列化](http://developer.51cto.com/art/201202/317181.htm) 142 | - [对象的序列化和反序列化](http://www.blogjava.net/lingy/archive/2008/10/10/233630.html) 143 | 144 | stackoverflow原址:http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it 145 | -------------------------------------------------------------------------------- /contents/what-is-an-efficient-way-to-implement-a-singleton-in-java.md: -------------------------------------------------------------------------------- 1 | # 如何创建单例 ? 2 | 3 | ## 问题 4 | Java 创建单例有哪些方式 ? 5 | 6 | ## 解答 7 | 实现单例,从加载方式来看,有两种: 8 | 9 | - 预加载 10 | - 懒加载 11 | 12 | 先看一下实现单例最简单的方式(预加载): 13 | ``` 14 | public class Foo { 15 | 16 | private static final Foo INSTANCE = new Foo(); 17 | 18 | private Foo() { 19 | if (INSTANCE != null) { 20 | throw new IllegalStateException("Already instantiated"); 21 | } 22 | } 23 | 24 | public static Foo getInstance() { 25 | return INSTANCE; 26 | } 27 | } 28 | ``` 29 | 30 | 再来看一下懒加载的方式: 31 | ``` 32 | class Foo { 33 | 34 | private static Foo INSTANCE = null; 35 | 36 | private Foo() { 37 | if (INSTANCE != null) { 38 | throw new IllegalStateException("Already instantiated"); 39 | } 40 | } 41 | 42 | public static Foo getInstance() { 43 | if (INSTANCE == null) { 44 | INSTANCE = new Foo(); 45 | } 46 | return INSTANCE; 47 | } 48 | } 49 | ``` 50 | 51 | 以上方式在单线程的情况可以很好的满足需要,换言之,若是在多线程,还需要作一定的改进,如下所示: 52 | ``` 53 | class Foo { 54 | // 请注意 volatile 关键字的使用 55 | private static volatile Foo INSTANCE = null; 56 | 57 | private Foo() { 58 | if (INSTANCE != null) { 59 | throw new IllegalStateException("Already instantiated"); 60 | } 61 | } 62 | 63 | public static Foo getInstance() { 64 | if (INSTANCE == null) { // Check 1 65 | synchronized (Foo.class) { 66 | if (INSTANCE == null) { // Check 2 67 | INSTANCE = new Foo(); 68 | } 69 | } 70 | } 71 | return INSTANCE; 72 | } 73 | } 74 | ``` 75 | 76 | 上述代码运用了 [Double-Checked Locking idiom](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)。 77 | 78 | 解决了多线程环境下的单例,可以进一步思考如何实现可序列化的单例 ? 反序列化可以不通过构造函数直接生成一个对象,所以反序列化时,我们需要保证其不再创建新的对象。 79 | 80 | ``` 81 | class Foo implements Serializable { 82 | 83 | private static final long serialVersionUID = 1L; 84 | 85 | private static volatile Foo INSTANCE = null; 86 | 87 | private Foo() { 88 | if (INSTANCE != null) { 89 | throw new IllegalStateException("Already instantiated"); 90 | } 91 | } 92 | 93 | public static Foo getInstance() { 94 | if (INSTANCE == null) { // Check 1 95 | synchronized (Foo.class) { 96 | if (INSTANCE == null) { // Check 2 97 | INSTANCE = new Foo(); 98 | } 99 | } 100 | } 101 | return INSTANCE; 102 | } 103 | 104 | @SuppressWarnings("unused") 105 | private Foo readResolve() { 106 | return INSTANCE; 107 | } 108 | } 109 | ``` 110 | 111 | readResolve 方法可以保证,即使程序在上一次运行时序列化过此单例,也只会返回全局唯一的单例。对于 Java 对象序列化机制,可参考[附录拓展](#appendix)。 112 | 113 | java 创建单例的方法基本实现了,不过我们还可以作进一步的改进 —— 代码重构: 114 | ``` 115 | public final class Foo implements Serializable { 116 | 117 | private static final long serialVersionUID = 1L; 118 | 119 | // 使用内部静态 class 实现懒加载 120 | private static class FooLoader { 121 | // 保证在多线程环境下无差错运行 122 | private static final Foo INSTANCE = new Foo(); 123 | } 124 | 125 | private Foo() { 126 | if (INSTANCE != null) { 127 | throw new IllegalStateException("Already instantiated"); 128 | } 129 | } 130 | 131 | public static Foo getInstance() { 132 | return FooLoader.INSTANCE; 133 | } 134 | 135 | @SuppressWarnings("unused") 136 | private Foo readResolve() { 137 | return FooLoader.INSTANCE; 138 | } 139 | } 140 | ``` 141 | 142 | 好了,现在已经很完美实现了单例的创建,是不是很高兴。单例实线的基本原理,我们已经基本清楚里,最后提供一种更加简洁方法,如下: 143 | ``` 144 | public enum Foo { 145 | INSTANCE; 146 | } 147 | ``` 148 | 08 年 google 开发者年会中,Joshua Bloch 149 | Joshua Bloch 在 [高效 Java 话题中](http://sites.google.com/site/io/effective-java-reloaded) 解释了这种方法,视频请戳 [这里](http://www.youtube.com/watch?v=pi_I7oD_uGI#t=28m50s).在 他[演讲的ppt](https://14b1424d-a-62cb3a1a-s-sites.googlegroups.com/site/io/effective-java-reloaded/effective_java_reloaded.pdf?attachauth=ANoY7crKCOet2NEUGW7RV1XfM-Jn4z8YJhs0qJM11OhLRnFW_JbExkJtvJ3UJvTE40dhAciyWcRIeGJ-n3FLGnMOapHShHINh8IY05YViOJoZWzaohMtM-s4HCi5kjREagi8awWtcYD0_6G7GhKr2BndToeqLk5sBhZcQfcYIyAE5A4lGNosDCjODcBAkJn8EuO6572t2wU1LMSEUgjvqcf4I-Fp6VDhDvih_XUEmL9nuVJQynd2DRpxyuNH1SpJspEIdbLw-WWZ&attredirects=0) 30-32 页提到: 150 | 151 | 实现单例正确的方式如下: 152 | ``` 153 | public enum Elvis { 154 | INSTANCE; 155 | private final String[] favoriteSongs = 156 | { "Hound Dog", "Heartbreak Hotel" }; 157 | public void printFavorites() { 158 | System.out.println(Arrays.toString(favoriteSongs)); 159 | } 160 | } 161 | ``` 162 | 163 | 在 [高效 Java 线上部分](http://www.ddj.com/java/208403883?pgno=3) 有说到: 164 | 165 | 上述实现单例的方式,其实等同于,将 INSTANCE 设置为 public static final 的方式,不同之处在于,使用枚举的方式显得更为简洁,且默认提供了序列化机制,也保证了多线程访问的安全。虽然这种单例的实现方式还未被广泛使用,可实现单例的最好方式就是使用一个单元素的枚举。 166 | 167 | 为什么可以这么简洁?因为 Java 中每一个枚举类型都默认继承了 java.lang.Enum ,而 Enum 实现了 Serializable 接口,所以枚举类型对象都是默认可以被序列化的。通过反编译,也可以知道枚举常量本质上就是一个 168 | ``` 169 | public static final xxx 170 | ```` 171 | 172 | 对于枚举的进一步理解,请参考[附录拓展](#appendix)。 173 | 174 | 175 | 附录拓展: 176 | [深入理解 Java 对象序列化](http://developer.51cto.com/art/201202/317181.htm) 177 | [对象的序列化和反序列化](http://www.blogjava.net/lingy/archive/2008/10/10/233630.html) 178 | [通过反编译字节码来理解 Java 枚举](http://unmi.cc/understand-java-enum-with-bytecode/) 179 | 180 | stackoverflow原址:http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java 181 | 182 | -------------------------------------------------------------------------------- /contents/what-is-reflection-and-why-is-it-useful.md: -------------------------------------------------------------------------------- 1 | # 反射(reflection)是什么及其用途? 2 | 3 | ##问题描述 4 | 反射是什么,为什么它是有用的? 5 | 我特别感兴趣的是java,但我认为任何语言的原理都是相同的。 6 | 7 | ##回答 8 | 反射的概念,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。在java中,通过反射,能够在"运行态"动态获得任意一个类的所有属性和方法,动态地调用对象的方法。 9 | 10 | 举个例子,假设你有一个不知道具体类的对象,并且你想调用它的"dosomething"方法(如果存在的话)。java的静态类型系统只能调用一个已知类对象对应的已知接口,在未指定对象类型时,无法调用它的方法。但是通过反射,你的代码能检查这个未知类对象,并试图找出这个dosomething方法。如果存在这个方法,你可以通过反射调用这个方法。 11 | 12 | 为了进一步说明,请看下面的例子(下面的对象foo,就是上文提到的,我们不知道它对应的类是什么): 13 | ``` 14 | Method method = foo.getClass().getMethod("dosomething",null); 15 | method.invoke(foo,null); //调用foo的dosomething方法 16 | ``` 17 | 反射这个特性,经常会用于各种注解中(annotations)。举个例子,Junit4将使用反射来遍历你的代码,查找所有加了@test注解的类方法,之后运行测试单元时就调用这些方法。 18 | 19 | [有很多好的反射例子,可以用来入门](http://docs.oracle.com/javase/tutorial/reflect/index.html) 20 | 21 | 最后,其概念在其他支持反射的静态类型语言中也是非常相似的。在动态语言中,无需用到上面说的第一种用法场景——调用未知类的方法(因为动态语言编允许任意对象调用任意方法,如果不存在对应方法,在运行时就会失败),但是第二种情况,查找做了指定标记的方法,这种场景还是很常见的 22 | 23 | [stackoverflow链接:What is reflection, and why is it useful?](http://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-it-useful) 24 | 25 | 26 | -------------------------------------------------------------------------------- /contents/what-is-the-difference-between-a-soft-reference-and-a-weak-reference-in-java.md: -------------------------------------------------------------------------------- 1 | # Java中软引用和弱引用的区别 2 | ## 问题 3 | 题目就是问题 4 | 5 | ## 解答 6 | ### 回答1 7 | 从Ethan Nicholas的《Understanding Weak References》中 8 | 9 | 弱引用: 10 | 放置一个弱引用的作用,不是强有力强制一个对象保存在内存中。弱引用允许利用垃圾收集者的能力去决定可达性,所以你不需要自己做,你只需要创建一个软引用: 11 | 12 | WeakReference weakWidgt = new WeakReference(widgt); 13 | 14 | 然后在代码别的地方你可以使用 `weakWidget.get()` 来获取真实的 `Widgt` 对象,当然弱引用足以强大能抵制垃圾收集器,所以你也许发现(如果没有强引用指向widget)`weakWidget.get()`突然开始返回null 15 | 16 | 软引用 17 | 18 | 软引用就像弱引用一样,除了它不会着急将引用的对象扔出去。只有弱可达性的对象(这样的对象最强的引用只能是弱引用)将在下一次垃圾收集处理中被抛弃,但是软可达性的对象通常可以坚持一会。 19 | 20 | 软引用不要求与弱引用有什么不同,但是实际中,只要内存足够,软可达的对象通常会维持下去。对于缓存来说,这是个不错的基础,就像以上图像缓存描述,虽然可以让垃圾收集者担心对象是如何可达(一个强可达性的对象从不会从缓存中移除)和她们需要消耗多少内存 21 | 22 | 而且Peter Kessler备注到 23 | 24 | Sun JRE 对待软引用和弱引用是不同的。如果内存是够用的。我们应坚持用软引用引用对象。一个细节是:对于客户端和服务器,JRE的政策是不同的:客户端,JRE试图保持通过清除软引用而不是扩大堆内存来使改变小点,而服务器端,JRE通过扩大堆内存让性能更好。没有一种通用的方法。 25 | 26 | ### 回答2 27 | 弱引用对象很快被收集。如果GC发现一个对象是弱引用(只能通过弱引用可达),它会立刻清除弱引用对象。同样的,对于在程序保持关联信息的对象保持一个引用是不错的,像关于类的缓存存储的反射信息或一个对象的包装器等等。没有意义地跟随相连对象的任何事物都会被清除掉。当弱引用清除掉时,它会进入到引用队列中,同时丢弃关联的对象。你保持关于对象额外的信息,但是一旦对象引用不要了,信息也就不需要了。总之,在某些情境下,你可以创建WeakReference的子类,保持在WeakReference的子类中对象的额外信息。WeakReference的其他典型应用是与Map连接,以保持规范化的例子。 28 | 29 | 在另一方面,软引用有利于外部缓存,再创造资源,因为GC会延迟清理他们。它能保证所有软引用会在内存溢出之前被清除,所以它们不会造成内存溢出。 30 | 31 | 典型的使用例子是保持从一个文件内容解析形式。在你载入文件,解析和与解析过代表的根对象保持一个软引用的地方扩展系统。在你下次需要文件时,你试图通过软引用恢复。如果可以恢复,你会在其他地方载入、解析你分享的文件,如果同时GC清理掉,你也可以重新载入。这样的话,你利用空内存可以做到性能最优化,但是不要内存溢出。 32 | 光保持一个软引用不会造成溢出。如果在另一方面你误用软引用,且弱引用被使用了(也就是说,你保持与较强引用的对象相连的信息,然后当引用对象被清除,你也丢弃信息),你可能会内存溢出,因为在进入引用队列时,也许碰巧没有及时丢弃相连的对象。 33 | 34 | 所以,使用软引用还是弱引用是取决于用法的。如果你的信息构造起来较为复杂,但是尽管如此仍想从别的数据再构造信息,使用软引用。如果你对一些数据的规范化实例保持引用,或者你想对一个“不拥有的”对象保持引用(就是防止被垃圾回收),这样就使用弱引用。 35 | 36 | 37 | 原文: 38 | > http://stackoverflow.com/questions/299659/what-is-the-difference-between-a-soft-reference-and-a-weak-reference-in-java -------------------------------------------------------------------------------- /contents/what-is-the-difference-between-jsf-servlet-and-jsp.md: -------------------------------------------------------------------------------- 1 | ## JSF, Servlet 和 JSP (三种技术)有什么区别? 2 | 3 | ###问题 4 | JSP 和 Servlet 有什么关系?JSP 是某种 Servlet 吗?JSP 和 JSF 又有什么关系?JSF 是某种基于JSP的,预构建好的 UI 吗,像 5 | ASP.NET-MVC 那样? 6 | 7 | 8 | ###回答1 9 | 10 | #### JSP(Java Server Pages) 11 | JSP 是一种运行在服务器上的Java 视图技术,它允许你写入模版化的文本(例如客户端代码 HTML, CSS, JavaScript等)。JSP 支持标签库(taglibs),标签库由Java 代码实现,让你可以动态地控制页面输出。JSTL 便是一种比较有名的标签库。JSP 同样支持表达式语言(expression language),表达式语言可以用来访问后台数据(页面上可用的属性,request/session 对象等等), 通常与标签库结合使用。 12 | 13 | 当一个 JSP 第一次被访问或者 webapp 启动时,servlet 容器会将 JSP 编译成一个继承了 HttpServlet 的类,然后在整个 webapp 生命周期内使用被编译后的类。可以在 servlet 容器的 work 目录下找到 JSP 对应的源代码。例如Tomcat 的 CATALINA.BASE/work 目录。 14 | 当收到一个 JSP 请求时,servlet 容器会执行编译 JSP 生成的类,并将该类的输出(通常是 HTML/CSS/JS)发送到 客户端,客户端(WEB 浏览器) 会展示从服务端收到的内容。 15 | 16 | #### Servlet 17 | Servlet 是一种针对服务器端的 API,它用来响应客户端请求,并生成响应。比较有名的例子是 HttpServlet,它提供了响应 HTTP 请求(例如 GET POST)的方法。你可以从 web.xml 配置 HttpServlet 来监听某种 HTTP URL pattern 的请求,或者使用较新的 Java EE 6 @WebServlet 注解。 18 | 19 | 当 Servlet 第一次被请求,或者 webapp 启动时,servlet 容器会创建该 Servlet 的实例,并在整个 webapp 的生命周期维持该实例在内存中。同一个实例会被复用,来响应匹配到 URL pattern 的请求。可以通过 HttpServletRequest 访问请求里的数据,通过 HttpServletResponse 控制响应。上边两个对象会是 HttpServlet 的重载方法 doGet()和 doPost() 的参数。 20 | 21 | #### JSF (JavaServer Faces) 22 | JSF 是一个基于组件的MVC框架,建立在 Servlet API 基础上,JSF 通过标签库提供组件,标签库又可以用于 JSP 或者其它 Java 视图技术例如 Facelets. Facelets 更适合JSF。即它提供了很厉害的模版功能例如组合组件,而JSP 基本上只提供了 `` 来支持模版,所以 23 | 当你想用一个组件替换一组重复出现的组件时,你不得不使用原生的 Java 代码来创建自定义组件(这在 JSF 里并不那么清晰明了,而且带来很多冗余工作)。为了推进 Facelets,自从 JSF 2.0 之后,JSP 这种视图技术已经被废弃了。 24 | 作为一种 MVC(Model-View-Controller)框架,JSF 提供了唯一的 FacesServlet 请求/响应控制器。它负责所有的 HTTP 请求/响应工作, 25 | 例如 收集/校验/转换用户输入,将输入设置到 model 对象里,调用处理逻辑并输出响应。这样你基本上 只有一个 JSP或者 Facelets(XHTML) 页面用作视图,再加一个 Javabean 类当作 模型。 JSF 组件用来将模型和视图绑定起来(类似 ASP.NET web control 做的),然后 FacesServlet 使用 JSF 组件树来完成整个工作。 26 | 27 | ### 其它答案选编 28 | 29 | 参考以下链接 30 | 31 | [http://www.oracle.com/technetwork/java/faq-137059.html](http://www.oracle.com/technetwork/java/faq-137059.html) 32 | 33 | [https://jcp.org/en/introduction/faq](https://jcp.org/en/introduction/faq) 34 | 35 | JSP 是一种特殊的Servlet。 36 | 37 | JSF 是一个可以配合 JSP 使用的标签集。 38 | 39 | 40 | ### stackoverflow原文链接: 41 | [http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp](http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp) 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /contents/what-is-the-equivalent-of-the-c++pair-in-java.md: -------------------------------------------------------------------------------- 1 | Java里什么是与C++的Pair相等的? 2 | 问题: 3 | Java里没有Pair是不是一个好理由?那什么会和C++这个结构相等呢?似乎1.6版本提供了一些类似的(比如AbstractMap.SimpleEntry),但这看起来很费解。 4 | 5 | 回答: 6 | 在comp.lang.java.help的一个论坛上,Hunter Gratzner对于java的Pair结构给出了一些论点。主要的论点是Pair类不能传达出两个值的关系的语义(你怎么能知道第一和第二代表什么?) 7 | 一个最好的校验是写一个非常简单的类,像Mike建议的,每一个应用上的pair类,Map.Entry都能很好的取代。 8 | 总的来说,我建议是最好是一个Position(x,y)类,一个Range(begin,end)和一个Entry(key,value)类,而不是一个不能告诉我应该怎么做的广泛Pair(first,second)类。 9 | 10 | stackoverflow的地址: 11 | http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java 12 | 13 | -------------------------------------------------------------------------------- /contents/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java.md: -------------------------------------------------------------------------------- 1 | ##重写(Override)equals和hashCode方法时应考虑的问题 2 | 3 | ###理论上讲(编程语言、数学层面) 4 | equals() 定义了对象的相等关系(自反性、对称性、传递性)(有点抽象,更详细说明,请参考[javadoc](http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object))) 。 5 | 另外,它还具有一致性(也就是说,如果一个对象没有修改,那么对象的equals方法,应总是返回相同的值),此外,o.equals(null)应当总是返回false。 6 | hashCode()([javadoc](http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()))也必须具备一致性的(也就是说,如果equal的结果没有变,那么hashcode()也应总是返回相同的值) 7 | 8 | 总的来说,这两个方法的关系: 9 | 10 | **假如a.equals(b),那么a.hashCode() 应等于b.hashCode()** 11 | 12 | ###实践上讲 13 | 14 | **如果你重写了其中一个方法,那么务必重写另外一个方法** 15 | 16 | equals()和hashCode()所计算的属性集(set of fields)应当是一样的 17 | 如何更快地重写这两个方法呢? 18 | 1. 使用[Apache Commons Lang library](http://commons.apache.org/lang/)中的[EqualsBuilder](http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/builder/EqualsBuilder.html)、[HashCodeBuilder](http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/builder/HashCodeBuilder.html) 19 | ```java 20 | public class Person { 21 | private String name; 22 | private int age; 23 | // ... 24 | 25 | public int hashCode() { 26 | return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers 27 | // if deriving: appendSuper(super.hashCode()). 28 | append(name). 29 | append(age). 30 | toHashCode(); 31 | } 32 | 33 | public boolean equals(Object obj) { 34 | if (!(obj instanceof Person)) 35 | return false; 36 | if (obj == this) 37 | return true; 38 | 39 | Person rhs = (Person) obj; 40 | return new EqualsBuilder(). 41 | // if deriving: appendSuper(super.equals(obj)). 42 | append(name, rhs.name). 43 | append(age, rhs.age). 44 | isEquals(); 45 | } 46 | } 47 | ``` 48 | 49 | 2. 如果你是用eclipse,可以在代码编辑区右键,然后选择 Source > Generate hashCode() and equals() 50 | 51 | **另外请记得** 52 | 53 | 54 | 当你使用一些基于Hash的 Collection 、 Map,例如HashSet, LinkedHashSet, HashMap, Hashtable, 、WeakHashMap等。在键值对被放到集合中之后,请确保其key值所对应的hashCode()是保持不变的。比较可靠的一个办法,是保持这些key是不可变的,这也能带来不少好处 55 | 56 | 57 | stackoverflow链接: 58 | http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java 59 | -------------------------------------------------------------------------------- /contents/whats-the-difference-between-component-repository-service-annotations-in.md: -------------------------------------------------------------------------------- 1 | ## @Component, @Repository, @Service的区别 2 | 3 | #### 问题 4 | 5 | 在spring集成的框架中,注解在类上的`@Component`,`@Repository`,`@Service`等注解能否被互换?或者说这些注解有什么区别? 6 | 7 | #### 回答1 8 | 9 | 引用spring的官方文档中的一段描述: 10 | 11 | 在Spring2.0之前的版本中,`@Repository`注解可以标记在任何的类上,用来表明该类是用来执行与数据库相关的操作(即dao对象),并支持自动处理数据库操作产生的异常 12 | 13 | 在Spring2.5版本中,引入了更多的Spring类注解:`@Component`,`@Service`,`@Controller`。`Component`是一个通用的Spring容器管理的单例bean组件。而`@Repository`, `@Service`, `@Controller`就是针对不同的使用场景所采取的特定功能化的注解组件。 14 | 15 | 因此,当你的一个类被`@Component`所注解,那么就意味着同样可以用`@Repository`, `@Service`, `@Controller`来替代它,同时这些注解会具备有更多的功能,而且功能各异。 16 | 17 | 最后,如果你不知道要在项目的业务层采用`@Service`还是`@Component`注解。那么,`@Service`是一个更好的选择。 18 | 19 | 就如上文所说的,`@Repository`早已被支持了在你的持久层作为一个标记可以去自动处理数据库操作产生的异常(译者注:因为原生的java操作数据库所产生的异常只定义了几种,但是产生数据库异常的原因却有很多种,这样对于数据库操作的报错排查造成了一定的影响;而Spring拓展了原生的持久层异常,针对不同的产生原因有了更多的异常进行描述。所以,在注解了`@Repository`的类上如果数据库操作中抛出了异常,就能对其进行处理,转而抛出的是翻译后的spring专属数据库异常,方便我们对异常进行排查处理)。 20 | 21 | | 注解 | 含义 | 22 | | ------------- |:-------------:| 23 | | @Component | 最普通的组件,可以被注入到spring容器进行管理 | 24 | | @Repository | 作用于持久层 | 25 | | @Service | 作用于业务逻辑层 | 26 | | @Controller | 作用于表现层(spring-mvc的注解) | 27 | 28 | #### 回答2 29 | 30 | 这几个注解几乎可以说是一样的:因为被这些注解修饰的类就会被Spring扫描到并注入到Spring的bean容器中。 31 | 32 | 这里,有两个注解是不能被其他注解所互换的: 33 | 34 | * `@Controller` 注解的bean会被spring-mvc框架所使用。 35 | * `@Repository` 会被作为持久层操作(数据库)的bean来使用 36 | 37 | 如果想使用自定义的组件注解,那么只要在你定义的新注解中加上`@Component`即可: 38 | 39 | ```java 40 | @Component 41 | @Scope("prototype") 42 | public @interface ScheduleJob {...} 43 | ``` 44 | 45 | 这样,所有被`@ScheduleJob`注解的类就都可以注入到spring容器来进行管理。我们所需要做的,就是写一些新的代码来处理这个自定义注解(译者注:可以用反射的方法),进而执行我们想要执行的工作。 46 | 47 | #### 回答3 48 | 49 | `@Component`就是跟``一样,可以托管到Spring容器进行管理。 50 | 51 | @Service, @Controller , @Repository = {@Component + 一些特定的功能}。这个就意味着这些注解在部分功能上是一样的。 52 | 53 | 当然,下面三个注解被用于为我们的应用进行分层: 54 | 55 | * `@Controller`注解类进行前端请求的处理,转发,重定向。包括调用Service层的方法 56 | * `@Service`注解类处理业务逻辑 57 | * `@Repository`注解类作为DAO对象(数据访问对象,Data Access Objects),这些类可以直接对数据库进行操作 58 | 59 | 有这些分层操作的话,代码之间就实现了松耦合,代码之间的调用也清晰明朗,便于项目的管理;假想一下,如果只用`@Controller`注解,那么所有的请求转发,业务处理,数据库操作代码都糅合在一个地方,那这样的代码该有多难拓展和维护。 60 | 61 | #### 总结 62 | 63 | * `@Component`, `@Service`, `@Controller`, `@Repository`是spring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理 64 | * `@Component`是通用注解,其他三个注解是这个注解的拓展,并且具有了特定的功能 65 | * `@Repository`注解在持久层中,具有将数据库操作抛出的原生异常翻译转化为spring的持久层异常的功能。 66 | * `@Controller`层是spring-mvc的注解,具有将请求进行转发,重定向的功能。 67 | * `@Service`层是业务逻辑层注解,这个注解只是标注该类处于业务逻辑层。 68 | * 用这些注解对应用进行分层之后,就能将请求处理,义务逻辑处理,数据库操作处理分离出来,为代码解耦,也方便了以后项目的维护和开发。 69 | 70 | #### Stackoverflow链接: 71 | 72 | [http://stackoverflow.com/questions/6827752/whats-the-difference-between-component-repository-service-annotations-in](http://stackoverflow.com/questions/6827752/whats-the-difference-between-component-repository-service-annotations-in) 73 | 74 | #### 拓展 75 | 76 | 1. [Spring注解@Component、@Repository、@Service、@Controller区别](http://www.cnblogs.com/JAYIT/p/5593169.html) 77 | 2. [Spring注解@Autowired、@Resource区别](http://www.cnblogs.com/leiOOlei/p/3713779.html) -------------------------------------------------------------------------------- /contents/whats-the-simplest-way-to-print-a-java-array.md: -------------------------------------------------------------------------------- 1 | 输出 Java 数组最简单的方式 2 | === 3 | 问题 4 | --- 5 | 因为 Java 数组中没有 toString() 方法,所以我如果直接调用数组toStrign()方法的话,只会得到它的内存地址。像这样,显得并不人性化: 6 | ```java 7 | int[] intArray = new int[] {1, 2, 3, 4, 5}; 8 | System.out.println(intArray); // 有时候会输出 '[I@3343c8b3' 9 | ``` 10 | 11 | 所以输出一个数组最简单的方法是什么?我想要的效果是 12 | ```java 13 | // 数字数组: 14 | int[] intArray = new int[] {1, 2, 3, 4, 5}; 15 | //输出: [1, 2, 3, 4, 5] 16 | 17 | // 对象数组: 18 | String[] strArray = new String[] {"John", "Mary", "Bob"}; 19 | //输出: [John, Mary, Bob] 20 | ``` 21 | 22 | 回答 23 | --- 24 | 在 Java 5+ 以上中使用 Arrays.toString(arr) 或 Arrays.deepToString(arr)来打印(输出)数组。 25 | 26 | 不要忘了引入import java.util.Arrays; 27 | ```java 28 | package packageName; 29 | import java.util.Arrays; 30 | ``` 31 | 32 | ```java 33 | int[] intArray = new int[] {1, 2, 3, 4, 5}; 34 | System.out.println(Arrays.toString(intArray)); 35 | //输出: [1, 2, 3, 4, 5] 36 | 37 | String[] strArray = new String[] {"John", "Mary", "Bob"}; 38 | System.out.println(Arrays.deepToString(strArray)); 39 | *//输出: [John, Mary, Bob] 40 | ``` 41 | Arrays.deepToString与Arrays.toString不同之处在于,Arrays.deepToString更适合打印多维数组
42 | 比如:
43 | 44 | ```java 45 | String[][] b = new String[3][4]; 46 | for (int i = 0; i < 3; i++) 47 | { 48 | for (int j = 0; j < 4; j++) 49 | { 50 | b[i][j] = "A" + j; 51 | } 52 | } 53 | System.out.println(Arrays.toString(b)); 54 | //输出[[Ljava.lang.String;@55e6cb2a, [Ljava.lang.String;@23245e75, [Ljava.lang.String;@28b56559] 55 | System.out.println(Arrays.deepToString(b)); 56 | //输出[[A0, A1, A2, A3], [A0, A1, A2, A3], [A0, A1, A2, A3]] 57 | 58 | ``` 59 | stackoverflow链接: http://stackoverflow.com/questions/409784/whats-the-simplest-way-to-print-a-java-array 60 | -------------------------------------------------------------------------------- /contents/when-and-how-should-i-use-a-threadlocal-variable.md: -------------------------------------------------------------------------------- 1 | ## 该什么时候使用 ThreadLocal变量,它是如何工作的? 2 | ### 回答1 3 | 一种可能的(也是常见的)使用情形是你不想通过同步方式(synchronized)访问非线程安全的对象(说的就是SimpleDateFormat),而是想给每个线程一个对象实例的时候。 4 | 例如 5 | ````java 6 | public class Foo 7 | { 8 | // SimpleDateFormat is not thread-safe, so give one to each thread 9 | private static final ThreadLocal formatter = new ThreadLocal(){ 10 | @Override 11 | protected SimpleDateFormat initialValue() 12 | { 13 | return new SimpleDateFormat("yyyyMMdd HHmm"); 14 | } 15 | }; 16 | 17 | public String formatIt(Date date) 18 | { 19 | return formatter.get().format(date); 20 | } 21 | } 22 | ```` 23 | ### 回答2 24 | 因为ThreadLocal是一个既定线程内部的数据引用,你可能在使用线程池的应用服务器上因此引起类加载时候的内存泄漏。你需要使用remove()方法很小心地清理TheadLocal中get()或者set()的变量。 25 | 如果程序执行完毕没有清理的话,它持有的任何对类的引用将作为部署的Web应用程序的一部分仍保持在永久堆,永远无法得到回收。重新部署/取消部署也无法清理对应用程序类的引用,因为线程不是被你的应用程序所拥有的。 26 | 每次成功部署都会创建一个永远不会被垃圾回收类的实例。 27 | 28 | 最后将会遇到内存不足的异常-java.lang.java.lang.OutOfMemoryError: PermGen space -XX:MaxPermSize,在google了很多答案之后你可能只是增加了-XX:MaxPermSize,而不是修复这个bug。 29 | 倘若你的确遇到这种问题,可以通过[Eclipse's Memory Analyzer](http://www.eclipse.org/mat/)或根据[Frank Kieviet's guide](https://blogs.oracle.com/fkieviet/entry/classloader_leaks_the_dreaded_java) 和 [followup](https://blogs.oracle.com/fkieviet/entry/how_to_fix_the_dreaded)来判断哪些线程和类保留了那些引用。 30 | 31 | 更新:又发现了[Alex Vasseur's blog entry](http://avasseur.blogspot.jp/2003/11/threadlocal-and-memory-leaks.html),它帮助我查清楚了一些ThreadLocal的问题。 32 | 33 | stackoverflow链接:http://stackoverflow.com/questions/817856/when-and-how-should-i-use-a-threadlocal-variable 34 | -------------------------------------------------------------------------------- /contents/when-to-use-linkedlist-over-arraylist.md: -------------------------------------------------------------------------------- 1 | ##LinkedList、ArrayList各自的使用场景,如何确认应该用哪一个呢? 2 | 3 | 一言以蔽之,在大部分情况下,使用ArrayList会好一些。 4 | 5 | ###耗时上各有优缺点。ArrayList稍有优势 6 | List只是一个接口,而LinkedList、ArrayList是List的不同实现。LinkedList的模型是双向链表,而ArrayList则是动态数组 7 | 8 | 首先对比下常用操作的算法复杂度 9 | #####LinkedList 10 | - get(int index) : O(n) 11 | - add(E element) : O(1) 12 | - add(int index, E element) : O(n) 13 | - remove(int index) : O(n) 14 | - Iterator.remove() : O(1) <--- LinkedList的主要优点 15 | - ListIterator.add(E element) is O(1) <--- LinkedList的主要优点 16 | 17 | #####ArrayList 18 | - get(int index) : O(1) <--- ArrayList的主要优点 19 | - add(E element) : 基本是O(1) , 因为动态扩容的关系,最差时是 O(n) 20 | - add(int index, E element) : 基本是O( n - index) , 因为动态扩容的关系,最差时是 O(n) 21 | - remove(int index) : O(n - index) (例如,移除最后一个元素,是 O(1)) 22 | - Iterator.remove() : O(n - index) 23 | - ListIterator.add(E element) : O(n - index) 24 | - LinkedList,因为本质是个链表,所以通过Iterator来插入和移除操作的耗时,都是个恒量,但如果要获取某个位置的元素,则要做指针遍历。因此,get操作的耗时会跟List长度有关 25 | 26 | 对于ArrayList来说,得益于快速随机访问的特性,获取任意位置元素的耗时,是常量的。但是,如果是add或者remove操作,要分两种情况,如果是在尾部做add,也就是执行add方法(没有index参数),此时不需要移动其他元素,耗时是O(1),但如果不是在尾部做add,也就是执行add(int index, E element),这时候在插入新元素的同时,也要移动该位置后面的所有元素,以为新元素腾出位置,此时耗时是O(n-index)。另外,当List长度超过初始化容量时,会自动生成一个新的array(长度是之前的1.5倍),此时会将旧的array移动到新的array上,这种情况下的耗时是O(n)。 27 | 28 | **总之,get操作,ArrayList快一些。而add操作,两者差不多**。(除非是你希望在List中间插入节点,且维护了一个Iterator指向指定位置,这时候linkedList能快一些,但是,我们更多时候是直接在尾部插入节点,这种特例的情况并不多) 29 | 30 | ###空间占用上,ArrayList完胜 31 | 看下两者的内存占用图 32 | ![](http://img.blog.csdn.net/20141017095352885?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl6ZXlhbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) 33 | 34 | 这三个图,横轴是list长度,纵轴是内存占用值。**两条蓝线是LinkedList,两条红线是ArrayList** 35 | 36 | 可以看到,LinkedList的空间占用,要远超ArrayList。LinkedList的线更陡,随着List长度的扩大,所占用的空间要比同长度的ArrayList大得多。 37 | 注:从mid JDK6之后,默认启用了CompressedOops ,因此64位及32位下的结果没有差异,LinkedList x64和LinkedList x32的线是一样的。 38 | 39 | stackoverflow原址: 40 | http://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist 41 | -------------------------------------------------------------------------------- /contents/which-notnull-java-annotation-should-i-use.md: -------------------------------------------------------------------------------- 1 | 我应该用哪一个@NotNull注解? 2 | === 3 | 我希望能通过注解的方式,尽量避免程序中出现空指针问题,同时既能保障代码的可读性,又能和IDE的代码检查,静态代码扫描工具结合起来。相关的注解,我看到有好多种@NotNull/@NonNull/@Nonnull,而他们彼此间又有冲突,不能共用,下面是我找到的一些注解,哪个是最好的选择呢? 4 | 5 | 1.javax.validation.constraints.NotNull 6 | 7 | 运行时进验证,不静态分析 8 | 9 | 2.edu.umd.cs.findbugs.annotations.NonNull 10 | 11 | 用于finbugs和Sonar静态分析 12 | 13 | 3.javax.annotation.Nonnull 14 | 15 | 只适用FindBugs,JSR-305不适用 16 | 17 | 4.org.jetbrains.annotations.NotNull 18 | 19 | 适用用于IntelliJ IDEA静态分析 20 | 21 | 5.lombok.NonNull 22 | 23 | 适用Lombok项目中代码生成器。不是一个标准的占位符注解. 24 | 25 | 6.android.support.annotation.NonNull 26 | 27 | 适用于Android项目的标记注解,位于support-annotations包中 28 | 29 | 回答 30 | --- 31 | 32 | 我推荐用javax命名空间下的注解(虽然我喜欢Lombok和Intelij做的事情),使用其他命名空间的注解,等于你还需要引入其他依赖。 33 | 34 | 我用javax.validation.constraints.NotNull,因为它已经在Java EE 6中定义 35 | 36 | javax.annotation.NonNull可能直到java 8都不存在(正如Stephen指出)。其他的都不是标准的注解 . 37 | 38 | 如果注解是可扩展的,那将是一件美好的事情.你可以自己写一个`non-null`注解,然后继承上面说的这些注解。如果标准的注解不支持某个特性,你就可以在自己定义的注解里面扩展。 39 | 40 | 41 | 42 | 43 | 44 | stackoverflow链接: http://stackoverflow.com/questions/4963300/which-notnull-java-annotation-should-i-use 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /contents/why-cant-i-switch-on-a-string.md: -------------------------------------------------------------------------------- 1 | # 为什么不能用string类型进行switch判断 2 | 3 | ##问题描述 4 | 为什么不能用string类型进行switch判断? 5 | 在java的后续版本中,是否会增加这个新特性? 6 | 有人能给我一篇文章,解释一下为什么不能这样做,或者进一步说明java中switch语句的运行方式? 7 | 8 | ##回答 9 | 在switch语句中用string作为case,这个特性已经在java SE7 中被实现了,距离 [这个'bug'](http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179) 被提出至少也有16年了。为何迟迟不提供这个特性,原因不明。但可以推测,可能跟性能有关。 10 | 11 | **Implementtation in JDK 7** 12 | 13 | 在JDK7中,这个特性已经实现了。在编译阶段,以string作为case值的代码,会按照特定的模式,被转换为更加复杂的代码。最终的执行代码将是一些使用了JVM指令的代码。 14 | 15 | 究竟是如何转换的呢?我们直接看看源码及编译后的代码。源代码: 16 | ``` 17 | public class StringInSwitchCase { 18 | public static void main(String[] args) { 19 | String mode = args[0]; 20 | switch (mode) { 21 | case "ACTIVE": 22 | System.out.println("Application is running on Active mode"); 23 | break; 24 | case "PASSIVE": 25 | System.out.println("Application is running on Passive mode"); 26 | break; 27 | case "SAFE": 28 | System.out.println("Application is running on Safe mode"); 29 | } 30 | } 31 | } 32 | ``` 33 | 编译后再反编译的代码: 34 | ``` 35 | import java.io.PrintStream; 36 | 37 | public class StringInSwitchCase{ 38 | public StringInSwitchCase() { } 39 | 40 | public static void main(string args[]) { 41 | String mode = args[0]; 42 | String s; switch ((s = mode).hashCode()) { 43 | default: break; 44 | case -74056953: 45 | if (s.equals("PASSIVE")) { 46 | System.out.println("Application is running on Passive mode"); 47 | } 48 | break; 49 | case 2537357: 50 | if (s.equals("SAFE")) { 51 | System.out.println("Application is running on Safe mode"); 52 | } 53 | break; 54 | case 1925346054: 55 | if (s.equals("ACTIVE")) { 56 | System.out.println("Application is running on Active mode"); 57 | } 58 | break; 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | 包含case string的 switch 语句,在编译时会转为为嵌套代码(switch+if)。第一个switch将 case 中的string转为唯一的integer值。这个integer值就是原先string的hashcode值。在case的逻辑中,会加入if语句,这个if语句用于进一步检查string值是否跟原先的case string匹配。这样可以防止hash碰撞,确保代码的健壮。这本质上是一种语法糖,既支持了string作为case值这一特性,又能确保逻辑正确性。 65 | 66 | **Switchs in the JVM** 67 | 68 | switch的更多深层技术实现,可以参考JVM规范,[compliation of switch statements](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.10)。简单概括说,根据使用的常量的多寡,switch会对应到两种不同的JVM指令。JVM指令有所不同,归根结底都是为了代码的效率。 69 | 70 | 如果常量很多,会将case的int值去掉最低位后作为索引,放到一个指针表中——也就是所谓的`tablewitch`指令 71 | 72 | 如果常量相对较少,那么可用二分查找来找到正确的case--也就是所谓的`lookupswitch`指令 73 | 74 | 这两种指令,都要求在编译时确保case的对应值是integer常量。在运行时,虽然`tableswitch`O(1)的性能通常要好于`lookupswitch`O(log(n))的性能。但是前者需要更多的空间开销,因此需要兼顾空间及时间综合考虑性价比。Bill Venners的文章[a great article](http://www.artima.com/underthehood/flowP.html)有更多深入的分析。 75 | 76 | **Before JDK 7** 77 | 78 | 在JDK之前,可以用枚举来实现类似的需求。它和在case中使用string有异曲同工之妙。例如如下: 79 | ``` 80 | Pill p = Pill.valueOf(str); 81 | switch(p) { 82 | case RED:pop();break; 83 | case BLUE:push();break; 84 | } 85 | ``` 86 | 87 | [stackoverflow原链接:Why can`t I switch on a String](http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string) 88 | 89 | [可参考中文文章《Java中字符串switch的实现细节》](http://www.deepinmind.com/java/2014/05/08/how-string-in-switch-works-in-java-7.html) 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /contents/why-does-java-have-transient-variables.md: -------------------------------------------------------------------------------- 1 | # transient 关键字的作用? 2 | 3 | [Java 语言规范](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3)中提到,transient 关键字用来说明指定属性不进行序列化. 4 | 5 | 若要理解 transient 关键字的作用,自然需要对序列化有一定的认识. 6 | 7 | ** 序列化 ** 8 | 9 | 序列化是用来持久化对象的状态 -- 将对象转化为字节码保存到指定的文件中.类似地,可以通过反序列化,将字节码还原为对象原有的状态.序列化是 Java 中一个比较重要的概念,因为在网络编程中会经常用到序列化与反序列化机制.一个相对若想在网络中传输,就必须转化为字节的形式.而 Serializable 接口就是用来标识某个类或接口可以转化为字节码,Serializable 可以认为是一个标识符,因为它没有任何的方法. 10 | 11 | Serializable 允许我们将一个类转化为字节码,进而在网络传输.可是,一个类中可能存在某些敏感的信息,我们是不想在网络中传输的,这时候我们就需要借助 transient 关键字了.被 transient 关键字标识的 field,不会进行序列化. 12 | 13 | 下面通过一个例子说明 transient 关键字的作用.现假设我们需要在网络中传输 Person 类: 14 | ``` 15 | public class Person implements Serializable{ 16 | 17 | private static final long serialVersionUID = 1L; 18 | 19 | private String name; 20 | private String certNo; // 身份证号码 21 | private int age; 22 | 23 | public Person(String name, String certNo, int age) { 24 | this.name = name; 25 | this.certNo = certNo; 26 | this.age = age; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "Person{" + 32 | "name='" + name + '\'' + 33 | ", certNo='" + certNo + '\'' + 34 | ", age=" + age + 35 | '}'; 36 | } 37 | } 38 | ``` 39 | 40 | 若不使用 transient 关键字,反序列化时输出的信息是 : 41 | ``` 42 | Person{name='tianya', certNo='12314', age=23} 43 | ``` 44 | 45 | 我们知道,身份证号码属于敏感信息,并不想在网络中传输,这时我们就可以借助 transient 关键字,如下: 46 | ``` 47 | private transient String certNo; 48 | ``` 49 | 50 | 这个时候,通过反序列化获取的 Person 信息如下 : 51 | ``` 52 | Person{name='tianya', certNo='null', age=23} 53 | ``` 54 | 55 | stackoverflow原址:http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables -------------------------------------------------------------------------------- /contents/why-does-math-round0-49999999999999994-return-1.md: -------------------------------------------------------------------------------- 1 | # 为什么数学函数Math.round(0.49999999999999994) 返回 1 2 | 3 | tags:stackoverflow-java-top-qa 4 | 5 | --- 6 | 7 | ###问题 8 | 通过下面的程序你可以看出来,对于任意一个比0.5略小的都是舍去小数向下取整,只有0.5是例外. 9 | 10 | ```java 11 | for (int i = 10; i >= 0; i--) { 12 | long l = Double.doubleToLongBits(i + 0.5); 13 | double x; 14 | do { 15 | x = Double.longBitsToDouble(l); 16 | System.out.println(x + " rounded is " + Math.round(x)); 17 | l--; 18 | } while (Math.round(x) > i); 19 | } 20 | ``` 21 | 22 | 输出为: 23 | 24 | ``` 25 | 10.5 rounded is 11 26 | 10.499999999999998 rounded is 10 27 | 9.5 rounded is 10 28 | 9.499999999999998 rounded is 9 29 | 8.5 rounded is 9 30 | 8.499999999999998 rounded is 8 31 | 7.5 rounded is 8 32 | 7.499999999999999 rounded is 7 33 | 6.5 rounded is 7 34 | 6.499999999999999 rounded is 6 35 | 5.5 rounded is 6 36 | 5.499999999999999 rounded is 5 37 | 4.5 rounded is 5 38 | 4.499999999999999 rounded is 4 39 | 3.5 rounded is 4 40 | 3.4999999999999996 rounded is 3 41 | 2.5 rounded is 3 42 | 2.4999999999999996 rounded is 2 43 | 1.5 rounded is 2 44 | 1.4999999999999998 rounded is 1 45 | 0.5 rounded is 1 46 | 0.49999999999999994 rounded is 1 47 | 0.4999999999999999 rounded is 0 48 | 49 | ``` 50 | *_译者注:请看输出的最后两行,0.49999999999999994的输出为1,而0.49999999999999999的输出为0* 51 | 52 | 我使用的版本是 Java 6 update 31 53 | 54 | ### 回答 55 | **总结** 56 | 57 | 在 Java 6(或者之前的版本),round(x)是用floor(x+0.5)实现的.¹ 这是一个规范上的bug,恰恰是在这种病理条件下.²Java 7 不再使用这个有问题的实现了. 58 | 59 | **问题** 60 | 61 | 0.5+0.49999999999999994 在double的精度下的结果是1 62 | ```java 63 | static void print(double d) { 64 | System.out.printf("%016x\n", Double.doubleToLongBits(d)); 65 | } 66 | 67 | public static void main(String args[]) { 68 | double a = 0.5; 69 | double b = 0.49999999999999994; 70 | 71 | print(a); // 3fe0000000000000 72 | print(b); // 3fdfffffffffffff 73 | print(a+b); // 3ff0000000000000 74 | print(1.0); // 3ff0000000000000 75 | } 76 | ``` 77 | 这是因为0.49999999999999994的指数比0.5的指数小,所以当它们两个相加时,0.49999999999999994的原数就会发生移位,然后最小精度单位(unit of least precision)/最后置单位(unit of last place)相应的变大了. 78 | 79 | **解决方案** 80 | 81 | 自从Java 7以来,OpenJDK(举个栗子)实现如下⁴: 82 | 83 | ```java 84 | public static long round(double a) { 85 | if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5 86 | return (long)floor(a + 0.5d); 87 | else 88 | return 0; 89 | } 90 | ``` 91 | 1. [http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29](http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29) 92 | 2. [http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675](http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675) (credits to @SimonNickerson for finding this) 93 | 3. [http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29](http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29) 94 | 4. [http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29) 95 | 96 | ### stackoverflow原文链接:[http://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1](http://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1) -------------------------------------------------------------------------------- /contents/why-does-math.round-(0.49999999999999994)-return-1.md: -------------------------------------------------------------------------------- 1 | ##为什么Math.round(0.49999999999999994)返回1? 2 | 3 | ###问题描述: 4 | 5 | 在下面的程序中你可以看到每个稍微比.5小的值都被向下舍入了,除了`0.5`那个。 6 | 7 | ``` 8 | for (int i = 10; i >= 0; i--) { 9 | long l = Double.doubleToLongBits(i + 0.5); 10 | double x; 11 | do { 12 | x = Double.longBitsToDouble(l); 13 | System.out.println(x + " rounded is " + Math.round(x)); 14 | l--; 15 | } while (Math.round(x) > i); 16 | } 17 | ``` 18 | 输出 19 | ``` 20 | 10.5 rounded is 11 21 | 10.499999999999998 rounded is 10 22 | 9.5 rounded is 10 23 | 9.499999999999998 rounded is 9 24 | 8.5 rounded is 9 25 | 8.499999999999998 rounded is 8 26 | 7.5 rounded is 8 27 | 7.499999999999999 rounded is 7 28 | 6.5 rounded is 7 29 | 6.499999999999999 rounded is 6 30 | 5.5 rounded is 6 31 | 5.499999999999999 rounded is 5 32 | 4.5 rounded is 5 33 | 4.499999999999999 rounded is 4 34 | 3.5 rounded is 4 35 | 3.4999999999999996 rounded is 3 36 | 2.5 rounded is 3 37 | 2.4999999999999996 rounded is 2 38 | 1.5 rounded is 2 39 | 1.4999999999999998 rounded is 1 40 | 0.5 rounded is 1 41 | 0.49999999999999994 rounded is 1 42 | 0.4999999999999999 rounded is 0 43 | ``` 44 | 我使用 Java 6 update 31。 45 | 46 | ###回答: 47 | 48 | **总结** 49 | 50 | 在Java 6中(而且很可能更早),`round x`以`floor(x+0.5)`的方式执行[1]。这是一个规则上的bug,它恰好会导致这样一种不合道理的情况[2]。Java 7不再授权这种分离的执行方式[3]。 51 | 52 | **问题** 53 | 54 | 0.5+0.49999999999999994在双精度数中正好表示1: 55 | ``` 56 | static void print(double d) { 57 | System.out.printf("%016x\n", Double.doubleToLongBits(d)); 58 | } 59 | 60 | public static void main(String args[]) { 61 | double a = 0.5; 62 | double b = 0.49999999999999994; 63 | 64 | print(a); // 3fe0000000000000 65 | print(b); // 3fdfffffffffffff 66 | print(a+b); // 3ff0000000000000 67 | print(1.0); // 3ff0000000000000 68 | } 69 | ``` 70 | 这是因为0.49999999999999994有一个比0.5小的指数,所以它们相加时,它的尾数被移位了,ULP(ULP表示现有数值最后一位代表的最小数值单位,小于ULP的数值将无法累加到现有数值)变的更大。 71 | 72 | **解决方法** 73 | 74 | 从Java 7开始,OpenJDK(如下例)这样执行它(round函数)[4]。 75 | ``` 76 | public static long round(double a) { 77 | if (a != 0x1.fffffffffffffp-2) // greatest double value less than 0.5 78 | return (long)floor(a + 0.5d); 79 | else 80 | return 0; 81 | } 82 | ``` 83 | [1] http://docs.oracle.com/javase/6/docs/api/java/lang/Math.html#round%28double%29 84 | 85 | [2] http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6430675 (credits to @SimonNickerson for finding this) 86 | 87 | [3] http://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#round%28double%29 88 | 89 | [4] http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/lang/Math.java#Math.round%28double%29 90 | 91 | 92 | [stackoverflow链接:Why does Math.round(0.49999999999999994) return 1?](http://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1) 93 | -------------------------------------------------------------------------------- /contents/why-does-this-code-using-random-strings-print-hello-world.md: -------------------------------------------------------------------------------- 1 | ##为什么以下用随机生成的文字会得出 “hello world”? 2 | 3 | ###问题 4 | 为什么以下用随机生成的文字会得出"hello world". 5 | 有人能解释一下吗? 6 | 7 | ``` 8 | System.out.println(randomString(-229985452) + " " + randomString(-147909649)); 9 | 10 | public static String randomString(int i) 11 | { 12 | Random ran = new Random(i); 13 | StringBuilder sb = new StringBuilder(); 14 | while (true) 15 | { 16 | int k = ran.nextInt(27); 17 | if (k == 0) 18 | break; 19 | 20 | sb.append((char)('`' + k)); 21 | } 22 | return sb.toString(); 23 | } 24 | ``` 25 | ###回答1(最佳) 26 | 在JAVA 里面,随机类的实现不是真正的随机,是伪随机. 27 | 就是说如果随机类的种子是一样的话,他们会生成同一组的数字。 28 | 29 | 比如说这个问题: 30 | 31 | new Random(-229985452).nextInt(27) 32 | 33 | 首6个生成的数字一定是: 34 | 35 | 8 36 | 5 37 | 12 38 | 12 39 | 15 40 | 0 41 | 42 | 43 | 而 `new Random(-147909649).nextInt(27)` 首6个生成的数字一定是: 44 | 45 | 23 46 | 15 47 | 18 48 | 12 49 | 4 50 | 0 51 | 52 | 而把每一个数目字加 ` (which is 96),就会得到了相应的英文字母: 53 | 54 | 8 + 96 = 104 --> h 55 | 5 + 96 = 101 --> e 56 | 12 + 96 = 108 --> l 57 | 12 + 96 = 108 --> l 58 | 15 + 96 = 111 --> o 59 | 60 | 23 + 96 = 119 --> w 61 | 15 + 96 = 111 --> o 62 | 18 + 96 = 114 --> r 63 | 12 + 96 = 108 --> l 64 | 4 + 96 = 100 --> d 65 | 66 | stackoverflow链接:http://stackoverflow.com/questions/15182496/why-does-this-code-using-random-strings-print-hello-world -------------------------------------------------------------------------------- /contents/why-does-this-go-into-an-infinite-loop.md: -------------------------------------------------------------------------------- 1 | ## 这段代码为什么陷入了死循环 2 | 3 | ### 问题 4 | 我写了这样一段代码 5 | 6 | ```java 7 | public class Tests { 8 | public static void main(String[] args) throws Exception { 9 | int x = 0; 10 | while(x<3) { 11 | x = x++; 12 | System.out.println(x); 13 | } 14 | } 15 | } 16 | ``` 17 | 18 | 我们知道他应该只写x++或者x=x+1就行了,但是x=x++的情况下,x应该先赋值给自己,然后再增加1。为什么X的值一直是0呢? 19 | 20 | --更新 21 |
下面是字节码 22 | 23 | ```java 24 | public class Tests extends java.lang.Object{ 25 | public Tests(); 26 | Code: 27 | 0: aload_0 28 | 1: invokespecial #1; //Method java/lang/Object."":()V 29 | 4: return 30 | 31 | public static void main(java.lang.String[]) throws java.lang.Exception; 32 | Code: 33 | 0: iconst_0 34 | 1: istore_1 35 | 2: iload_1 36 | 3: iconst_3 37 | 4: if_icmpge 22 38 | 7: iload_1 39 | 8: iinc 1, 1 40 | 11: istore_1 41 | 12: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 42 | 15: iload_1 43 | 16: invokevirtual #3; //Method java/io/PrintStream.println:(I)V 44 | 19: goto 2 45 | 22: return 46 | 47 | } 48 | ``` 49 | 50 | 51 | ### 回答 52 | 53 | 一开始我用C#代码来解释,因为C#允许用"ref"关键字来实现int参数的引用传递,所以我决定用我google到的MutableInt类写的标准的java代码来模拟C#中ref关键字作用.我不知道这对我的解答是不是有帮助,我并不是一个专业的java开发者,我知道还有很多更符合语言习惯的方法去解释这个问题。 54 | 也许我写一个函数来模拟x++的作用会解释得更清楚 55 | 56 | ```java 57 | public MutableInt postIncrement(MutableInt x) { 58 | int valueBeforeIncrement = x.intValue(); 59 | x.add(1); 60 | return new MutableInt(valueBeforeIncrement); 61 | } 62 | ``` 63 | 64 | 对不对?将传递的参数值加1然后返回原值,这就是后增操作符的定义。 65 | 现在让我们看看在你的代码中是怎么执行的。 66 | 67 | ```java 68 | MutableInt x = new MutableInt(); 69 | x = postIncrement(x); 70 | ``` 71 | 72 | `postIncrement(x)`函数做了什么?x增加1,对,然后返回x加1之前的值,并且把该返回值赋给x。所以X被赋值的顺序是0,1,0。如果我们重写上面的代码会理解得更清楚。 73 | 74 | ```java 75 | MutableInt x = new MutableInt(); // x is 0. 76 | MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0. 77 | x = temp; // Now x is 0 again. 78 | ``` 79 | 80 | 你定势地认为,将左边的X换个名称为y,"你能看到x先自增1,然后赋值给了y",这一时也让我有点困惑。bi那个不是x被赋值给y,而是x(自增)之前的值被赋给y.事实上,把左边x变量名改成y结果也没什么不同。 81 | 82 | ```java 83 | MutableInt x = new MutableInt(); // x is 0. 84 | MutableInt y = new MutableInt(); // y is 0. 85 | MutableInt temp = postIncrement(x); // Now x is 1, and temp is 0. 86 | y = temp; // y is still 0. 87 | ``` 88 | 89 | 可见,x=x++并没有改变x的值,只是让x依次被赋值为x0,x0+1,x0。 90 | 91 | 更新:你可能会怀疑在自增过程中x并没有被赋值为1,下面通过一个demo来解释确实x存在赋值为1的过程。 92 | demo中调用了x=x++;同时另外开了一个单独的线程不断输出x的值. 93 | 94 | ```java 95 | public class Main { 96 | public static volatile int x = 0; 97 | 98 | public static void main(String[] args) { 99 | LoopingThread t = new LoopingThread(); 100 | System.out.println("Starting background thread..."); 101 | t.start(); 102 | 103 | while (true) { 104 | x = x++; 105 | } 106 | } 107 | } 108 | 109 | class LoopingThread extends Thread { 110 | public @Override void run() { 111 | while (true) { 112 | System.out.println(Main.x); 113 | } 114 | } 115 | } 116 | ``` 117 | 118 | 下面是程序的部分输出,0和1不规则的出现。 119 | 120 | ``` 121 | Starting background thread... 122 | 0 123 | 0 124 | 1 125 | 1 126 | 0 127 | 0 128 | 0 129 | 0 130 | 0 131 | 0 132 | 0 133 | 0 134 | 0 135 | 0 136 | 1 137 | 0 138 | 1 139 | ``` 140 | 141 | 142 | stackoverflow链接: 143 | [http://stackoverflow.com/questions/3831341/why-does-this-go-into-an-infinite-loop](http://stackoverflow.com/questions/3831341/why-does-this-go-into-an-infinite-loop) 144 | -------------------------------------------------------------------------------- /contents/why-is-char-preferred-over-string-for-passwords-in-java.md: -------------------------------------------------------------------------------- 1 | ## 为什么在java中存放密码更倾向于char[]而不是String 2 | 3 | ### 问题 4 | 5 | 在Swing中,password字段有一个getPassword()方法(返回char[]),而不是通常的getText()方法(返回String字符串)。同样的,我看到一个建议说不要使用字符串处理密码。 6 | 为什么在涉及passwords时,都说字符串会对安全构成威胁?感觉使用char[]不是那么的方便。 7 | 8 | ### 回答 9 | String是不可变的。虽然String加载密码之后可以把这个变量扔掉,但是字符串并不会马上被GC回收,一但进程在GC执行到这个字符串之前被dump,dump出的的转储中就会含有这个明文的字符串。那如果我去“修改”这个字符串,比如把它赋一个新值,那么是不是就没有这个问题了?答案是否定的,因为String本身是不可修改的,任何基于String的修改函数都是返回一个新的字符串,原有的还会在内存里。 10 | 11 | 然而对于数组,你可以在抛弃它之前直接修改掉它里面的内容或者置为乱码,密码就不会存在了。但是如果你什么也不做直接交给gc的话,也会存在上面一样的问题。 12 | 13 | 所以,这是一个安全性的问题--但是,即使使用char[]也仅仅是降低了攻击者攻击的机会,而且仅仅对这种特定的攻击有效。 14 | 15 | 16 | **stackoverflow链接**: 17 | http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords-in-java 18 | 19 | **知乎上也有相关讨论**: 20 | https://www.zhihu.com/question/36734157 21 | -------------------------------------------------------------------------------- /contents/why-is-java-vector-class-considered-obsolete-or-deprecated.md: -------------------------------------------------------------------------------- 1 | ## 为什么Java的```Vector```类被认为是过时的或者废弃的 2 | ### 问题 3 | 为什么java ```Vector```类被认为是一个遗留的,过时的或废弃的类?在并发操作时,使用它是无效的吗? 4 | 5 | 如果我不想手动对对象实现同步,只想用一个线程安全的集合而无需创建底层数组的全新副本(如```CopyOnWriteArrayList```一样)。这种情况下,我使用```Vector```合理吗? 6 | 7 | 然后就是关于栈的问题,它是Vector的一个子类,我应该用什么代替它? 8 | ### 回答 9 | Vector中对每一个独立操作都实现了同步,这通常不是我们想要的做法。对单一操作实现同步通常不是线程安全的(举个例子,比如你想遍历一个Vector实例。你仍然需要申明一个锁来防止其他线程在同一时刻修改这个Vector实例。如果不添加锁的话 10 | 11 | 通常会在遍历实例的这个线程中导致一个``` ConcurrentModificationException ```)同时这个操作也是十分慢的(在创建了一个锁就已经足够的前提下,为什么还需要重复的创建锁) 12 | 13 | 当然,即使你不需要同步,Vector也是有锁的资源开销的。 14 | 15 | 总的来说,在大多数情况下,这种同步方法是存在很大缺陷的。正如Mr Brain Henk指出,你可以通过调用```Collections.synchronizedList```来装饰一个集合 -事实上 ```Vector``` 将“可变数组”的集合实现与“同步每一个方法”结合起来的做法是另一个糟糕的设计; 16 | 17 | 各个装饰方法能够更明确的指示其关注的功能实现。 18 | 19 | 对于```Stack```这个类-我更乐于使用```Deque/ArrayDeque```来实现 20 | 21 | stackoverflow讨论地址: http://stackoverflow.com/questions/1386275/why-is-java-vector-class-considered-obsolete-or-deprecated 22 | -------------------------------------------------------------------------------- /contents/why-is-printing-b-dramatically-slower-than-printing.md: -------------------------------------------------------------------------------- 1 | ##为什么打印B要比打印#慢很多? 2 | 3 | ###问题 4 | 我生成了两个大小为 1000 * 1000 的矩阵 5 | 第一个矩阵:O和# 6 | 第二个矩阵:O和B 7 | 使用以下代码,第一个矩阵仅用时8.25s就完成了: 8 | ```java 9 | Random r = new Random(); 10 | for (int i = 0; i < 1000; i++) { 11 | for (int j = 0; j < 1000; j++) { 12 | if(r.nextInt(4) == 0) { 13 | System.out.print("O"); 14 | } else { 15 | System.out.print("#"); 16 | } 17 | } 18 | 19 | System.out.println(""); 20 | } 21 | ```` 22 | 而使用相同的代码时,第二个矩阵却执行了259.152s 23 | ````java 24 | Random r = new Random(); 25 | for (int i = 0; i < 1000; i++) { 26 | for (int j = 0; j < 1000; j++) { 27 | if(r.nextInt(4) == 0) { 28 | System.out.print("O"); 29 | } else { 30 | System.out.print("B"); 31 | } 32 | } 33 | 34 | System.out.println(""); 35 | } 36 | ```` 37 | 为什么两者的执行时间会有如此巨大的差距? 38 | - - - 39 | 评论中一些人建议仅执行`System.out.print("#");`以及`System.out.print("B");`前者用时7.8871s,而后者仍然在打印(即花费时间过多--译者注) 40 | 另外有些人指出在他们那里工作正常(即两者花费时间差不错--译者注),我在[Ideone.com](http://ideone.com/)环境中运行两段代码,执行时间也差不多。 41 | 42 | 测试条件: 43 | - Netbeans 7.2,结果输出在IDE的控制台 44 | - 使用`System.nanoTime()`度量时间 45 | 46 | ###回答 47 | 纯推测:你正在使用的终端试图进行[“自动换行”(word-wrapping)](http://en.wikipedia.org/wiki/Word_wrap),而不是“字符换行”(character-wrapping),并且将'B'视为一个单词字符(word character),而'#'视为一个非单词字符(non-word character)。因此当输出到达行尾时,控制台搜索一个位置用来换行,当遇到'#'时可以立即执行换行;然而遇到'B'时,控制台必须继续搜索,并且可能有更多的字符需要换行(这个操作在一些控制台上可能花销很大,例如,输出退格,然后输出空白字符来覆盖那些需要被换行的字符)。 48 | 但是,这仅仅是理论推测。 49 | 50 | **译者注:** 51 | 对于"word-wrapping"和"character-wrapping",我的理解是,它们的区别在于换行时是否在一个单词内部分割,例如在 charac-ter 中的-处换行,"word-wrapping"会将character全部移到下一行,而"character-wrapping"则将ter移到下一行,而charac依旧在原来的位置。 52 | **word-wrapping** 53 | ``` 54 | ******* 55 | character 56 | ``` 57 | **character-wrapping** 58 | ``` 59 | *******charac 60 | ter 61 | ``` -------------------------------------------------------------------------------- /contents/why-is-processing-a-sorted-array-faster-than-an-unsorted-array.md: -------------------------------------------------------------------------------- 1 | ## 为什么处理排序的数组要比非排序的快 2 | ### 问题 3 | 以下是**c++**的一段非常神奇的代码。由于一些奇怪原因,对数据排序后奇迹般的让这段代码快了近6倍!! 4 | ``` 5 | #include 6 | #include 7 | #include 8 | 9 | int main() 10 | { 11 | // Generate data 12 | const unsigned arraySize = 32768; 13 | int data[arraySize]; 14 | 15 | for (unsigned c = 0; c < arraySize; ++c) 16 | data[c] = std::rand() % 256; 17 | 18 | // !!! With this, the next loop runs faster 19 | std::sort(data, data + arraySize); 20 | 21 | // Test 22 | clock_t start = clock(); 23 | long long sum = 0; 24 | 25 | for (unsigned i = 0; i < 100000; ++i) 26 | { 27 | // Primary loop 28 | for (unsigned c = 0; c < arraySize; ++c) 29 | { 30 | if (data[c] >= 128) 31 | sum += data[c]; 32 | } 33 | } 34 | 35 | double elapsedTime = static_cast(clock() - start) / CLOCKS_PER_SEC; 36 | 37 | std::cout << elapsedTime << std::endl; 38 | std::cout << "sum = " << sum << std::endl; 39 | } 40 | ``` 41 | 42 | 43 | - 没有`std::sort(data, data + arraySize);`,这段代码运行了11.54秒. 44 | - 有这个排序的代码,则运行了1.93秒. 45 | 我原以为这也许只是语言或者编译器的不一样的问题,所以我又用Java试了一下。 46 | 47 | 以下是Java代码段 48 | ``` 49 | import java.util.Arrays; 50 | import java.util.Random; 51 | 52 | public class Main 53 | { 54 | public static void main(String[] args) 55 | { 56 | // Generate data 57 | int arraySize = 32768; 58 | int data[] = new int[arraySize]; 59 | 60 | Random rnd = new Random(0); 61 | for (int c = 0; c < arraySize; ++c) 62 | data[c] = rnd.nextInt() % 256; 63 | 64 | // !!! With this, the next loop runs faster 65 | Arrays.sort(data); 66 | 67 | // Test 68 | long start = System.nanoTime(); 69 | long sum = 0; 70 | 71 | for (int i = 0; i < 100000; ++i) 72 | { 73 | // Primary loop 74 | for (int c = 0; c < arraySize; ++c) 75 | { 76 | if (data[c] >= 128) 77 | sum += data[c]; 78 | } 79 | } 80 | 81 | System.out.println((System.nanoTime() - start) / 1000000000.0); 82 | System.out.println("sum = " + sum); 83 | } 84 | } 85 | ``` 86 | 87 | 结果相似,没有很大的差别。 88 | 89 | ---------------------------------------------------------- 90 | 我首先得想法是排序把数据放到了cache中,但是我下一个想法是我之前的想法是多么傻啊,因为这个数组刚刚被构造。 91 | - 到底这是为什么呢? 92 | - 为什么排序的数组会快于没有排序的数组? 93 | - 这段代码是为了求一些无关联的数据的和,排不排序应该没有关系啊。 94 | 95 | ### 回答 96 | #### 什么是分支预测? 97 | 看看这个铁路分岔口 98 | ![](http://i.stack.imgur.com/muxnt.jpg) 99 | Image by Mecanismo, via Wikimedia Commons. Used under the CC-By-SA 3.0 license. 100 | 101 | 为了理解这个问题,想象一下,如果我们回到19世纪. 102 | 103 | 你是在分岔口的操作员。当你听到列车来了,你没办法知道这两条路哪一条是正确的。然后呢,你让列车停下来,问列车员哪条路是对的,然后你才转换铁路方向。 104 | 105 | **火车很重有很大的惯性。所以他们得花费很长的时间开车和减速。** 106 | 107 | 是不是有个更好的办法呢?你猜测哪个是火车正确的行驶方向 108 | - 如果你猜对了,火车继续前行 109 | - 如果你猜错了,火车得停下来,返回去,然后你再换条路。 110 | 111 | **如果你每次都猜对了,那么火车永远不会停下来。** 112 | **如果你猜错太多次,那么火车会花费很多时间来停车,返回,然后再启动** 113 | 114 | ---------------------------------------------------------- 115 | **考虑一个if条件语句**:在处理器层面上,这是一个分支指令: 116 | ![](http://i.stack.imgur.com/pyfwC.png) 117 | 当处理器看到这个分支时,没办法知道哪个将是下一条指令。该怎么办呢?貌似只能暂停执行,直到前面的指令完成,然后再继续执行正确的下一条指令? 118 | 现代处理器很复杂,因此它需要很长的时间"热身"、"冷却" 119 | 120 | 是不是有个更好的办法呢?你猜测下一个指令在哪! 121 | - 如果你猜对了,你继续执行。 122 | - 如果你猜错了,你需要flush the pipeline,返回到那个出错的分支,然后你才能继续。 123 | 124 | **如果你每次都猜对了**,那么你永远不会停 125 | **如果你猜错了太多次**,你就要花很多时间来滚回,重启。 126 | 127 | ------------------------------------------------------- 128 | 这就是分支预测。我承认这不是一个好的类比,因为火车可以用旗帜来作为方向的标识。但是在电脑中,处理器不能知道哪一个分支将走到最后。 129 | 130 | 所以怎样能很好的预测,尽可能地使火车必须返回的次数变小?你看看火车之前的选择过程,如果这个火车往左的概率是99%。那么你猜左,反之亦然。如果每3次会有1次走这条路,那么你也按这个三分之一的规律进行。 131 | 132 | **换句话说,你试着定下一个模式,然后按照这个模式去执行**。这就差不多是分支预测是怎么工作的。 133 | 134 | 大多数的应用都有很好的分支预测。所以现代的分支预测器通常能实现大于90%的命中率。但是当面对没有模式识别、无法预测的分支,那分支预测基本就没用了。 135 | 136 | 如果你想知道更多:[Branch predictor" article on Wikipedia](https://en.wikipedia.org/wiki/Branch_predictor). 137 | 138 | ----------------------------------------------------- 139 | 140 | #### 有了前面的说明,问题的来源就是这个if条件判断语句 141 | ``` 142 | if (data[c] >= 128) 143 | sum += data[c]; 144 | ``` 145 | 注意到数据是分布在0到255之间的。当数据排好序后,基本上前一半大的的数据不会进入这个条件语句,而后一半的数据,会进入该条件语句. 146 | 147 | 连续的进入同一个执行分支很多次,这对分支预测是非常友好的。可以更准确地预测,从而带来更高的执行效率。 148 | 149 | ##### 快速理解一下 150 | ``` 151 | T = branch taken 152 | N = branch not taken 153 | 154 | data[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ... 155 | branch = N N N N N ... N N T T T ... T T T ... 156 | 157 | = NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT (easy to predict) 158 | ``` 159 | 但是当数据是完全随机的,分支预测就没什么用了。因为他无法预测随机的数据。因此就会有大概50%的概率预测出错。 160 | ``` 161 | data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118, 14, 150, 177, 182, 133, ... 162 | branch = T, T, N, T, T, T, T, N, T, N, N, T, T, T, N ... 163 | 164 | = TTNTTTTNTNNTTTN ... (completely random - hard to predict) 165 | ``` 166 | ---------------------------------------------------------- 167 | 168 | ##### 我们能做些什么呢 169 | 如果编译器无法优化带条件的分支,如果你愿意牺牲代码的可读性换来更好的性能的话,你可以用下面的一些技巧。 170 | 171 | 把 172 | ``` 173 | if (data[c] >= 128) 174 | sum += data[c]; 175 | ``` 176 | 替换成 177 | ``` 178 | int t = (data[c] - 128) >> 31; 179 | sum += ~t & data[c]; 180 | ``` 181 | 这消灭了分支,把它替换成按位操作. 182 | 183 | (说明:这个技巧不是非常严格的等同于原来的if条件语句。但是在`data[]`当前这些值下是OK的) 184 | 185 | **使用的设备参数是:Core i7 920 @ 3.5 GHz** 186 | C++ - Visual Studio 2010 - x64 Release 187 | ``` 188 | // Branch - Random 189 | seconds = 11.777 190 | 191 | // Branch - Sorted 192 | seconds = 2.352 193 | 194 | // Branchless - Random 195 | seconds = 2.564 196 | 197 | // Branchless - Sorted 198 | seconds = 2.587 199 | ``` 200 | Java - Netbeans 7.1.1 JDK 7 - x64 201 | ``` 202 | // Branch - Random 203 | seconds = 10.93293813 204 | 205 | // Branch - Sorted 206 | seconds = 5.643797077 207 | 208 | // Branchless - Random 209 | seconds = 3.113581453 210 | 211 | // Branchless - Sorted 212 | seconds = 3.186068823 213 | ``` 214 | 结论: 215 | - 用了分支(if):没有排序和排序的数据,效率有很大的区别 216 | - 用了上面提到的按位操作替换:排序与否,效率没有很大的区别 217 | - 在使用C++的情况下,按位操作还是要比排好序的分支操作要慢。 218 | 219 | 一般的建议是尽量避免在关键循环上出现对数据很依赖的分支。(就像这个例子) 220 | 221 | ------------------------------------------------ 222 | 223 | 更新: 224 | 225 | - GCC 4.6.1 用了 `-O3` or `-ftree-vectorize`,在64位机器上,数据有没有排序,都是一样快。 226 | **...** 227 | **...** 228 | **...等各种例子** 229 | 230 | 231 | 说明了现代编译器越发成熟强大,可以在这方面充分优化代码的执行效率 232 | 233 | ### 相关内容 234 | 235 | CPU的流水线指令执行 236 | 237 | 想象现在有一堆指令等待CPU去执行,那么CPU是如何执行的呢?具体的细节可以找一本计算机组成原理来看。CPU执行一堆指令时,并不是单纯地一条一条取出来执行,而是按照一种流水线的方式,在CPU真正指令前,这条指令就像工厂里流水线生产的产品一样,已经被经过一些处理。简单来说,一条指令可能经过过程:取指(Fetch)、解码(Decode)、执行(Execute)、放回(Write-back)。 238 | 239 | 假设现在有指令序列ABCDEFG。当CPU正在执行(execute)指令A时,CPU的其他处理单元(CPU是由若干部件构成的)其实已经预先处理到了指令A后面的指令,例如B可能已经被解码,C已经被取指。这就是流水线执行,这可以保证CPU高效地执行指令。 240 | 241 | 分支预测 242 | 243 | 如上所说,CPU在执行一堆顺序执行的指令时,因为对于执行指令的部件来说,其基本不需要等待,因为诸如取指、解码这些过程早就被做了。但是,当CPU面临非顺序执行的指令序列时,例如之前提到的跳转指令,情况会怎样呢? 244 | 245 | 取指、解码这些CPU单元并不知道程序流程会跳转,只有当CPU执行到跳转指令本身时,才知道该不该跳转。所以,取指解码这些单元就会继续取跳转指令之后的指令。当CPU执行到跳转指令时,如果真的发生了跳转,那么之前的预处理(取指、解码)就白做了。这个时候,CPU得从跳转目标处临时取指、解码,然后才开始执行,这意味着:CPU停了若干个时钟周期! 246 | 247 | 这其实是个问题,如果CPU的设计放任这个问题,那么其速度就很难提升起来。为此,人们发明了一种技术,称为branch prediction,也就是分支预测。分支预测的作用,就是预测某个跳转指令是否会跳转。而CPU就根据自己的预测到目标地址取指令。这样,即可从一定程度提高运行速度。当然,分支预测在实现上有很多方法。 248 | 249 | 250 | **stackoverflow链接**: 251 | 252 | 这个问题的所有回答中,最高的回答,获取了上万个vote,还有很多个回答,非常疯狂,大家觉得不过瘾可以移步到这里查看 253 | 254 | http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array 255 | -------------------------------------------------------------------------------- /contents/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result.md: -------------------------------------------------------------------------------- 1 | ##为什么这两个时间(1927年)相减会得到一个奇怪的结果? 2 | 3 | ###问题描述 4 | 如果我运行如下的程序,将两个相距一秒的日期解析成字符串并比较他们。 5 | ``` 6 | public static void main(String[] args) throws ParseException { 7 | SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 8 | String str3 = "1927-12-31 23:54:07"; 9 | String str4 = "1927-12-31 23:54:08"; 10 | Date sDt3 = sf.parse(str3); 11 | Date sDt4 = sf.parse(str4); 12 | long ld3 = sDt3.getTime() /1000; 13 | long ld4 = sDt4.getTime() /1000; 14 | System.out.println(ld4-ld3); 15 | } 16 | ``` 17 | 18 | 输出结果为: 19 | ``` 20 | 353 21 | ``` 22 | 23 | 为什么`ld4-ld3`不是`1`(正如我所期望的那样),而是`353`? 24 | 25 | 如果我把时间改变为之后的一秒: 26 | ``` 27 | String str3 = "1927-12-31 23:54:08"; 28 | String str4 = "1927-12-31 23:54:09"; 29 | ``` 30 | 31 | 这时,`ld4-ld3`的结果为`1`. 32 | 33 | java版本: 34 | ``` 35 | java version "1.6.0_22" 36 | Java(TM) SE Runtime Environment (build 1.6.0_22-b04) 37 | Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode) 38 | ``` 39 | 40 | 时区: 41 | ``` 42 | sun.util.calendar.ZoneInfo[id="Asia/Shanghai", 43 | offset=28800000,dstSavings=0, 44 | useDaylight=false, 45 | transitions=19, 46 | lastRule=null] 47 | 48 | Locale(Locale.getDefault()): zh_CN 49 | ``` 50 | 51 | ###问题回答 52 | 这是因为1927年11月31日上海的时区改变了。 53 | 观看[此页](http://www.timeanddate.com/time/change/china/shanghai?year=1927)获得更多关于上海1927年的细节。 54 | 这个问题主要是由于在1927年12月31日的午夜,时钟回调了5分钟零52秒。 55 | 所以"1927-12-31 23:54:08"这个时间实际上发生了两次,看上去java将这个时间解析为之后的那个瞬间。 56 | 因此出现了这种差别。 57 | 58 | 这只是美好但奇怪的世界时区中的一个插曲。 59 | 60 | stackoverflow链接:[Why is subtracting these two times (in 1927) giving a strange result?](http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result) 61 | -------------------------------------------------------------------------------- /contents/目录.md: -------------------------------------------------------------------------------- 1 | # [目录](http://stackoverflow.com/questions/tagged/java?page=1&sort=votes&pagesize=15) # 2 | 3 | ## [1、为什么处理排序过得数组比没有排序的数组快](http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array "Why is processing a sorted array faster than an unsorted array?") ## 4 | 5 | 6 | ## [2、为什么在1927年的两个时间相减会产生奇怪的结果](http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result) ## 7 | 8 | ## [3、Java是传引用还是传值](http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) ## 9 | 10 | ## [4、Java += 操作符](http://stackoverflow.com/questions/8710619/java-operator) ## 11 | 12 | ## [5、避免!=null 声明](http://stackoverflow.com/questions/271526/avoiding-null-statements) ## 13 | 14 | ## [6、Android适当的用例UserManager.isUserAGoat()](http://stackoverflow.com/questions/13375357/proper-use-cases-for-android-usermanager-isuseragoat) ## 15 | 16 | ## [7、HashMap和HaspTable的区别](http://stackoverflow.com/questions/40471/differences-between-hashmap-and-hashtable) ## 17 | 18 | ## [8、InputStream转换为String](http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string) ## 19 | 20 | ## [9、为什么在存密码时char[]要优于String](http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords-in-java) ## 21 | 22 | ## [10、遍历HashMap](http://stackoverflow.com/questions/1066589/iterate-through-a-hashmap) ## 23 | 24 | ## [11、用数组创建一个ArrayList](http://stackoverflow.com/questions/157944/create-arraylist-from-array) ## 25 | 26 | ## [12、产生特定范围的随机数](http://stackoverflow.com/questions/363681/generating-random-integers-in-a-specific-range) ## 27 | 28 | ## [13、创建内存泄露?](http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java) ## 29 | 30 | ## [14、LinkedList和ArrayList区别](http://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist) ## 31 | 32 | ## [15、为什么动态的答应B比答应#慢](http://stackoverflow.com/questions/21947452/why-is-printing-b-dramatically-slower-than-printing) ## 33 | 34 | ## [16、serialVersionUID是什么](http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it) ## 35 | 36 | ## [17、把字符串转换为Int](http://stackoverflow.com/questions/5585779/converting-string-to-int-in-java) ## 37 | 38 | ## [18、public、default、protected、private的区别](http://stackoverflow.com/questions/215497/difference-between-public-default-protected-and-private) ## 39 | 40 | ## [19、Android设备是否有唯一的ID](http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id) ## 41 | 42 | ## [20、怎么样测试私有方法,字段和内部类](http://stackoverflow.com/questions/34571/how-to-test-a-class-that-has-private-methods-fields-or-inner-classes) ## 43 | 44 | ## [21、怎样有效的遍历Map中的entry](http://stackoverflow.com/questions/46898/how-to-efficiently-iterate-over-each-entry-in-a-map) ## 45 | 46 | ## [22、使用java.net.URLConnection处理HTTP requests](http://stackoverflow.com/questions/2793150/using-java-net-urlconnection-to-fire-and-handle-http-requests) ## 47 | 48 | ## [23、为什么这段代码使用随机字符串答应"hello world"](http://stackoverflow.com/questions/15182496/why-does-this-code-using-random-strings-print-hello-world) ## 49 | 50 | ## [24、 如果使用Maven创建外部JAR](http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven) ## 51 | 52 | ## [25、怎样判断数组包含一个特定的值](http://stackoverflow.com/questions/1128723/how-can-i-test-if-an-array-contains-a-certain-value) ## 53 | 54 | ## [26、如何在JSP中避免java代码](http://stackoverflow.com/questions/3177733/how-to-avoid-java-code-in-jsp-files) ## 55 | 56 | ## [27、一行代码初始化ArrayList](http://stackoverflow.com/questions/1005073/initialization-of-an-arraylist-in-one-line) ## 57 | 58 | ## [28、finally在java一定会被执行吗](http://stackoverflow.com/questions/65035/does-finally-always-execute-in-java) ## 59 | 60 | ## [29、"implements Runnable"和"extends Thread"](http://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread) ## 61 | 62 | ## [30、如何在一个构造函数中调用另一个](http://stackoverflow.com/questions/285177/how-do-i-call-one-constructor-from-another-in-java) ## 63 | 64 | ## [31、String转换为Enum](http://stackoverflow.com/questions/604424/convert-a-string-to-an-enum-in-java) ## 65 | 66 | ## [32、在//后代码会被执行](http://stackoverflow.com/questions/30727515/why-is-executing-java-code-in-comments-with-certain-unicode-characters-allowed) ## 67 | 68 | ## [33、处理java.lang.OutOfMemoryError:PermGen space错误](http://stackoverflow.com/questions/88235/dealing-with-java-lang-outofmemoryerror-permgen-space-error) ## 69 | 70 | ## [34、退出嵌套循环](http://stackoverflow.com/questions/886955/breaking-out-of-nested-loops-in-java) ## 71 | 72 | ## [35、Android SDK 安装后找不到JDK](http://stackoverflow.com/questions/4382178/android-sdk-installation-doesnt-find-jdk) ## 73 | 74 | ## [36、Java内部类和嵌套类](http://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class) ## 75 | 76 | ## [37、在导入项目后出现必须覆盖超类方法的错误](http://stackoverflow.com/questions/1678122/must-override-a-superclass-method-errors-after-importing-a-project-into-eclips) ## 77 | 78 | ## [38、错误Unsupported major.minor version 51.0 error](http://stackoverflow.com/questions/10382929/how-to-fix-unsupported-major-minor-version-51-0-error) ## 79 | 80 | ## [39、声明数组](http://stackoverflow.com/questions/1200621/declare-array-java) ## 81 | 82 | ## [40、反射的作用](http://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-it-useful) ## 83 | 84 | ## [41、Map值排序](http://stackoverflow.com/questions/109383/sort-a-mapkey-value-by-values-java) ## 85 | 86 | ## [42、产生字母数字型随机数](http://stackoverflow.com/questions/41107/how-to-generate-a-random-alpha-numeric-string) ## 87 | 88 | ## [43、判断一个数的根号是整数](http://stackoverflow.com/questions/295579/fastest-way-to-determine-if-an-integers-square-root-is-an-integer) ## 89 | 90 | ## [44、用==或者equals()比较enum成员](http://stackoverflow.com/questions/1750435/comparing-java-enum-members-or-equals) ## 91 | 92 | ## [45、最简单的答应数组](http://stackoverflow.com/questions/409784/whats-the-simplest-way-to-print-a-java-array) ## 93 | 94 | ## [46、java是否支持默认参数值](http://stackoverflow.com/questions/997482/does-java-support-default-parameter-values) ## 95 | 96 | ## [47、接口和抽象类区别](http://stackoverflow.com/questions/1913098/what-is-the-difference-between-an-interface-and-abstract-class) ## 97 | 98 | ## [48、IntelliJ持久化行号](http://stackoverflow.com/questions/13751/how-can-i-permanently-have-line-numbers-in-intellij) ## 99 | 100 | ## [49、StringBuilder and StringBuffer](http://stackoverflow.com/questions/355089/stringbuilder-and-stringbuffer) ## 101 | 102 | ## [50、怎么连接两个数组](http://stackoverflow.com/questions/80476/how-can-i-concatenate-two-arrays-in-java) ## 103 | 104 | ## [51、在JUit中怎么断言异常](http://stackoverflow.com/questions/156503/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests) ## 105 | 106 | ## [52、字符串比较](http://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) ## 107 | 108 | ## [53、根据文件的内容创建字符串](http://stackoverflow.com/questions/326390/how-to-create-a-java-string-from-the-contents-of-a-file) ## 109 | 110 | ## [54、Failed to load the JNI shared Library (JDK)](http://stackoverflow.com/questions/7352493/failed-to-load-the-jni-shared-library-jdk) ## 111 | 112 | ## [55、JavaBean](http://stackoverflow.com/questions/3295496/what-is-a-javabean-exactly) ## 113 | 114 | ## [56、为什么我不能打开一个字符串](http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string) ## 115 | 116 | ## [57、stack trace转换String](http://stackoverflow.com/questions/1149703/how-can-i-convert-a-stack-trace-to-a-string) ## 117 | 118 | ## [58、java核心代码的Gof设计模式例子](http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns-in-javas-core-libraries) ## 119 | 120 | ## [59、Apache Camel](http://stackoverflow.com/questions/8845186/what-exactly-is-apache-camel) ## 121 | 122 | ## [60、java为什么会有临时变量](http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables) ## 123 | 124 | ## [61、Servlet 怎么工作](http://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-shared-variables-and-multithreading) ## 125 | 126 | ## [62、重写equals和hashCode](http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java) ## 127 | 128 | ## [63、得到当前stack trace](http://stackoverflow.com/questions/1069066/get-current-stack-trace-in-java) ## 129 | 130 | ## [64、foreach工作原理](http://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work) ## 131 | 132 | ## [65、Access restriction on class due to restriction on required library rt.jar?](http://stackoverflow.com/questions/860187/access-restriction-on-class-due-to-restriction-on-required-library-rt-jar) ## 133 | 134 | ## [66、wait()和sleep()区别](http://stackoverflow.com/questions/1036754/difference-between-wait-and-sleep) ## 135 | 136 | ## [67、产生MD5hash](http://stackoverflow.com/questions/415953/how-can-i-generate-an-md5-hash) ## 137 | 138 | ## [68、在调用instanceof之前是否要检测is null](http://stackoverflow.com/questions/2950319/is-null-check-needed-before-calling-instanceof) ## 139 | 140 | ## [69、Download a file with Android, and showing the progress in a ProgressDialog](http://stackoverflow.com/questions/3028306/download-a-file-with-android-and-showing-the-progress-in-a-progressdialog) ## 141 | 142 | ## [70、初始化静态Map](http://stackoverflow.com/questions/507602/how-can-i-initialize-a-static-map) ## 143 | 144 | ## [71、怎么发现Android应用的内存只用量](http://stackoverflow.com/questions/2298208/how-do-i-discover-memory-usage-of-my-application-in-android) ## 145 | 146 | ## [72、在遍历list的时候,remove一个值,避免出现concurrenModificationExcetiion](http://stackoverflow.com/questions/223918/iterating-through-a-list-avoiding-concurrentmodificationexception-when-removing) ## 147 | 148 | ## [73、在java classpath中设置多行jars](http://stackoverflow.com/questions/219585/setting-multiple-jars-in-java-classpath) ## 149 | 150 | ## [74、Hibernate hbm2ddl.auto possible values and what they do](http://stackoverflow.com/questions/438146/hibernate-hbm2ddl-auto-possible-values-and-what-they-do) ## 151 | 152 | ## [75、split a String](http://stackoverflow.com/questions/3481828/how-to-split-a-string-in-java) ## 153 | 154 | ## [76、LINQ for java](http://stackoverflow.com/questions/1217228/what-is-the-java-equivalent-for-linq) ## 155 | 156 | ## [77、What's the difference between @Component, @Repository & @Service annotations in Spring](http://stackoverflow.com/questions/6827752/whats-the-difference-between-component-repository-service-annotations-in) ## 157 | 158 | ## [78、Can I add jars to maven 2 build classpath without installing them](http://stackoverflow.com/questions/364114/can-i-add-jars-to-maven-2-build-classpath-without-installing-them) ## 159 | 160 | ## [79、How to round a number to n decimal places in Java](http://stackoverflow.com/questions/153724/how-to-round-a-number-to-n-decimal-places-in-java) ## 161 | 162 | ## [80、对ArrayList的对象属性排序](http://stackoverflow.com/questions/2784514/sort-arraylist-of-custom-objects-by-property) ## 163 | 164 | ## [81、创建和写文件](http://stackoverflow.com/questions/2885173/how-to-create-a-file-and-write-to-a-file-in-java) ## 165 | 166 | ## [82、Update Eclipse with Android development tools v. 23](http://stackoverflow.com/questions/24437564/update-eclipse-with-android-development-tools-v-23) ## 167 | 168 | ## [83、Uncatchable ChuckNorrisException](http://stackoverflow.com/questions/13883166/uncatchable-chucknorrisexception) ## 169 | 170 | ## [84、怎么在一个整数的左边填充0](http://stackoverflow.com/questions/473282/how-can-i-pad-an-integers-with-zeros-on-the-left) ## 171 | 172 | ## [85、创建泛型数组](http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java) ## 173 | 174 | ## [86、判断三个以上为true条件](http://stackoverflow.com/questions/3076078/check-if-at-least-two-out-of-three-booleans-are-true) ## 175 | 176 | ## [87、ThreadLocal variable](http://stackoverflow.com/questions/817856/when-and-how-should-i-use-a-threadlocal-variable) ## 177 | 178 | ## [88、JSF、Servlet、JSP区别](http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp) ## 179 | 180 | ## [89、为什么java向量类被认为是过时的和被废弃](http://stackoverflow.com/questions/1386275/why-is-java-vector-class-considered-obsolete-or-deprecated) ## 181 | 182 | ## [90、反编译DEX为源码](http://stackoverflow.com/questions/1249973/decompiling-dex-into-java-sourcecode) ## 183 | 184 | ## [91、ArrayList转换为String[]数组](http://stackoverflow.com/questions/5374311/convert-arrayliststring-to-string-array) ## 185 | 186 | ## [92、软引用和弱引用的区别](http://stackoverflow.com/questions/299659/what-is-the-difference-between-a-soft-reference-and-a-weak-reference-in-java) ## 187 | 188 | ## [93、Efficiency of Java “Double Brace Initialization”](http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization) ## 189 | 190 | ## [94、实现单例模式的最有效方法](http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java) ## 191 | 192 | ## [95、java中什么相当于C++的](http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java) ## 193 | 194 | ## [96、从纯文本文件中读](http://stackoverflow.com/questions/4716503/reading-a-plain-text-file-in-java) ## 195 | 196 | ## [97、Which @NotNull Java annotation should I use](http://stackoverflow.com/questions/4963300/which-notnull-java-annotation-should-i-use) ## 197 | 198 | ## [98、为什么这段代码陷入无限循环](http://stackoverflow.com/questions/3831341/why-does-this-go-into-an-infinite-loop) ## 199 | 200 | ## [99、Eclipse: Set maximum line length for auto formatting](http://stackoverflow.com/questions/3697287/eclipse-set-maximum-line-length-for-auto-formatting) ## 201 | 202 | ## [100、Why does Math.round(0.49999999999999994) return 1](http://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1) ## 203 | -------------------------------------------------------------------------------- /目录.md: -------------------------------------------------------------------------------- 1 | # [目录](http://stackoverflow.com/questions/tagged/java?page=1&sort=votes&pagesize=15) # 2 | 3 | ## [1、为什么处理排序过得数组比没有排序的数组快](http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array "Why is processing a sorted array faster than an unsorted array?") ## 4 | 5 | 6 | ## [2、为什么在1927年的两个时间相减会产生奇怪的结果](http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result) ## 7 | 8 | ## [3、Java是传引用还是传值](http://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) ## 9 | 10 | ## [4、Java += 操作符](http://stackoverflow.com/questions/8710619/java-operator) ## 11 | 12 | ## [5、避免!=null 声明](http://stackoverflow.com/questions/271526/avoiding-null-statements) ## 13 | 14 | ## [6、Android适当的用例UserManager.isUserAGoat()](http://stackoverflow.com/questions/13375357/proper-use-cases-for-android-usermanager-isuseragoat) ## 15 | 16 | ## [7、HashMap和HaspTable的区别](http://stackoverflow.com/questions/40471/differences-between-hashmap-and-hashtable) ## 17 | 18 | ## [8、InputStream转换为String](http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string) ## 19 | 20 | ## [9、为什么在存密码时char[]要优于String](http://stackoverflow.com/questions/8881291/why-is-char-preferred-over-string-for-passwords-in-java) ## 21 | 22 | ## [10、遍历HashMap](http://stackoverflow.com/questions/1066589/iterate-through-a-hashmap) ## 23 | 24 | ## [11、用数组创建一个ArrayList](http://stackoverflow.com/questions/157944/create-arraylist-from-array) ## 25 | 26 | ## [12、产生特定范围的随机数](http://stackoverflow.com/questions/363681/generating-random-integers-in-a-specific-range) ## 27 | 28 | ## [13、创建内存泄露?](http://stackoverflow.com/questions/6470651/creating-a-memory-leak-with-java) ## 29 | 30 | ## [14、LinkedList和ArrayList区别](http://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist) ## 31 | 32 | ## [15、为什么动态的答应B比答应#慢](http://stackoverflow.com/questions/21947452/why-is-printing-b-dramatically-slower-than-printing) ## 33 | 34 | ## [16、serialVersionUID是什么](http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it) ## 35 | 36 | ## [17、把字符串转换为Int](http://stackoverflow.com/questions/5585779/converting-string-to-int-in-java) ## 37 | 38 | ## [18、public、default、protected、private的区别](http://stackoverflow.com/questions/215497/difference-between-public-default-protected-and-private) ## 39 | 40 | ## [19、Android设备是否有唯一的ID](http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id) ## 41 | 42 | ## [20、怎么样测试私有方法,字段和内部类](http://stackoverflow.com/questions/34571/how-to-test-a-class-that-has-private-methods-fields-or-inner-classes) ## 43 | 44 | ## [21、怎样有效的遍历Map中的entry](http://stackoverflow.com/questions/46898/how-to-efficiently-iterate-over-each-entry-in-a-map) ## 45 | 46 | ## [22、使用java.net.URLConnection处理HTTP requests](http://stackoverflow.com/questions/2793150/using-java-net-urlconnection-to-fire-and-handle-http-requests) ## 47 | 48 | ## [23、为什么这段代码使用随机字符串答应"hello world"](http://stackoverflow.com/questions/15182496/why-does-this-code-using-random-strings-print-hello-world) ## 49 | 50 | ## [24、 如果使用Maven创建外部JAR](http://stackoverflow.com/questions/574594/how-can-i-create-an-executable-jar-with-dependencies-using-maven) ## 51 | 52 | ## [25、怎样判断数组包含一个特定的值](http://stackoverflow.com/questions/1128723/how-can-i-test-if-an-array-contains-a-certain-value) ## 53 | 54 | ## [26、如何在JSP中避免java代码](http://stackoverflow.com/questions/3177733/how-to-avoid-java-code-in-jsp-files) ## 55 | 56 | ## [27、一行代码初始化ArrayList](http://stackoverflow.com/questions/1005073/initialization-of-an-arraylist-in-one-line) ## 57 | 58 | ## [28、finally在java一定会被执行吗](http://stackoverflow.com/questions/65035/does-finally-always-execute-in-java) ## 59 | 60 | ## [29、"implements Runnable"和"extends Thread"](http://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread) ## 61 | 62 | ## [30、如何在一个构造函数中调用另一个](http://stackoverflow.com/questions/285177/how-do-i-call-one-constructor-from-another-in-java) ## 63 | 64 | ## [31、String转换为Enum](http://stackoverflow.com/questions/604424/convert-a-string-to-an-enum-in-java) ## 65 | 66 | ## [32、在//后代码会被执行](http://stackoverflow.com/questions/30727515/why-is-executing-java-code-in-comments-with-certain-unicode-characters-allowed) ## 67 | 68 | ## [33、处理java.lang.OutOfMemoryError:PermGen space错误](http://stackoverflow.com/questions/88235/dealing-with-java-lang-outofmemoryerror-permgen-space-error) ## 69 | 70 | ## [34、退出嵌套循环](http://stackoverflow.com/questions/886955/breaking-out-of-nested-loops-in-java) ## 71 | 72 | ## [35、Android SDK 安装后找不到JDK](http://stackoverflow.com/questions/4382178/android-sdk-installation-doesnt-find-jdk) ## 73 | 74 | ## [36、Java内部类和嵌套类](http://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class) ## 75 | 76 | ## [37、在导入项目后出现必须覆盖超类方法的错误](http://stackoverflow.com/questions/1678122/must-override-a-superclass-method-errors-after-importing-a-project-into-eclips) ## 77 | 78 | ## [38、错误Unsupported major.minor version 51.0 error](http://stackoverflow.com/questions/10382929/how-to-fix-unsupported-major-minor-version-51-0-error) ## 79 | 80 | ## [39、声明数组](http://stackoverflow.com/questions/1200621/declare-array-java) ## 81 | 82 | ## [40、反射的作用](http://stackoverflow.com/questions/37628/what-is-reflection-and-why-is-it-useful) ## 83 | 84 | ## [41、Map值排序](http://stackoverflow.com/questions/109383/sort-a-mapkey-value-by-values-java) ## 85 | 86 | ## [42、产生字母数字型随机数](http://stackoverflow.com/questions/41107/how-to-generate-a-random-alpha-numeric-string) ## 87 | 88 | ## [43、判断一个数的根号是整数](http://stackoverflow.com/questions/295579/fastest-way-to-determine-if-an-integers-square-root-is-an-integer) ## 89 | 90 | ## [44、用==或者equals()比较enum成员](http://stackoverflow.com/questions/1750435/comparing-java-enum-members-or-equals) ## 91 | 92 | ## [45、最简单的答应数组](http://stackoverflow.com/questions/409784/whats-the-simplest-way-to-print-a-java-array) ## 93 | 94 | ## [46、java是否支持默认参数值](http://stackoverflow.com/questions/997482/does-java-support-default-parameter-values) ## 95 | 96 | ## [47、接口和抽象类区别](http://stackoverflow.com/questions/1913098/what-is-the-difference-between-an-interface-and-abstract-class) ## 97 | 98 | ## [48、IntelliJ持久化行号](http://stackoverflow.com/questions/13751/how-can-i-permanently-have-line-numbers-in-intellij) ## 99 | 100 | ## [49、StringBuilder and StringBuffer](http://stackoverflow.com/questions/355089/stringbuilder-and-stringbuffer) ## 101 | 102 | ## [50、怎么连接两个数组](http://stackoverflow.com/questions/80476/how-can-i-concatenate-two-arrays-in-java) ## 103 | 104 | ## [51、在JUit中怎么断言异常](http://stackoverflow.com/questions/156503/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests) ## 105 | 106 | ## [52、字符串比较](http://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) ## 107 | 108 | ## [53、根据文件的内容创建字符串](http://stackoverflow.com/questions/326390/how-to-create-a-java-string-from-the-contents-of-a-file) ## 109 | 110 | ## [54、Failed to load the JNI shared Library (JDK)](http://stackoverflow.com/questions/7352493/failed-to-load-the-jni-shared-library-jdk) ## 111 | 112 | ## [55、JavaBean](http://stackoverflow.com/questions/3295496/what-is-a-javabean-exactly) ## 113 | 114 | ## [56、为什么我不能打开一个字符串](http://stackoverflow.com/questions/338206/why-cant-i-switch-on-a-string) ## 115 | 116 | ## [57、stack trace转换String](http://stackoverflow.com/questions/1149703/how-can-i-convert-a-stack-trace-to-a-string) ## 117 | 118 | ## [58、java核心代码的Gof设计模式例子](http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns-in-javas-core-libraries) ## 119 | 120 | ## [59、Apache Camel](http://stackoverflow.com/questions/8845186/what-exactly-is-apache-camel) ## 121 | 122 | ## [60、java为什么会有临时变量](http://stackoverflow.com/questions/910374/why-does-java-have-transient-variables) ## 123 | 124 | ## [61、Servlet 怎么工作](http://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-shared-variables-and-multithreading) ## 125 | 126 | ## [62、重写equals和hashCode](http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java) ## 127 | 128 | ## [63、得到当前stack trace](http://stackoverflow.com/questions/1069066/get-current-stack-trace-in-java) ## 129 | 130 | ## [64、foreach工作原理](http://stackoverflow.com/questions/85190/how-does-the-java-for-each-loop-work) ## 131 | 132 | ## [65、Access restriction on class due to restriction on required library rt.jar?](http://stackoverflow.com/questions/860187/access-restriction-on-class-due-to-restriction-on-required-library-rt-jar) ## 133 | 134 | ## [66、wait()和sleep()区别](http://stackoverflow.com/questions/1036754/difference-between-wait-and-sleep) ## 135 | 136 | ## [67、产生MD5hash](http://stackoverflow.com/questions/415953/how-can-i-generate-an-md5-hash) ## 137 | 138 | ## [68、在调用instanceof之前是否要检测is null](http://stackoverflow.com/questions/2950319/is-null-check-needed-before-calling-instanceof) ## 139 | 140 | ## [69、Download a file with Android, and showing the progress in a ProgressDialog](http://stackoverflow.com/questions/3028306/download-a-file-with-android-and-showing-the-progress-in-a-progressdialog) ## 141 | 142 | ## [70、初始化静态Map](http://stackoverflow.com/questions/507602/how-can-i-initialize-a-static-map) ## 143 | 144 | ## [71、怎么发现Android应用的内存只用量](http://stackoverflow.com/questions/2298208/how-do-i-discover-memory-usage-of-my-application-in-android) ## 145 | 146 | ## [72、在遍历list的时候,remove一个值,避免出现concurrenModificationExcetiion](http://stackoverflow.com/questions/223918/iterating-through-a-list-avoiding-concurrentmodificationexception-when-removing) ## 147 | 148 | ## [73、在java classpath中设置多行jars](http://stackoverflow.com/questions/219585/setting-multiple-jars-in-java-classpath) ## 149 | 150 | ## [74、Hibernate hbm2ddl.auto possible values and what they do](http://stackoverflow.com/questions/438146/hibernate-hbm2ddl-auto-possible-values-and-what-they-do) ## 151 | 152 | ## [75、split a String](http://stackoverflow.com/questions/3481828/how-to-split-a-string-in-java) ## 153 | 154 | ## [76、LINQ for java](http://stackoverflow.com/questions/1217228/what-is-the-java-equivalent-for-linq) ## 155 | 156 | ## [77、What's the difference between @Component, @Repository & @Service annotations in Spring](http://stackoverflow.com/questions/6827752/whats-the-difference-between-component-repository-service-annotations-in) ## 157 | 158 | ## [78、Can I add jars to maven 2 build classpath without installing them](http://stackoverflow.com/questions/364114/can-i-add-jars-to-maven-2-build-classpath-without-installing-them) ## 159 | 160 | ## [79、How to round a number to n decimal places in Java](http://stackoverflow.com/questions/153724/how-to-round-a-number-to-n-decimal-places-in-java) ## 161 | 162 | ## [80、对ArrayList的对象属性排序](http://stackoverflow.com/questions/2784514/sort-arraylist-of-custom-objects-by-property) ## 163 | 164 | ## [81、创建和写文件](http://stackoverflow.com/questions/2885173/how-to-create-a-file-and-write-to-a-file-in-java) ## 165 | 166 | ## [82、Update Eclipse with Android development tools v. 23](http://stackoverflow.com/questions/24437564/update-eclipse-with-android-development-tools-v-23) ## 167 | 168 | ## [83、Uncatchable ChuckNorrisException](http://stackoverflow.com/questions/13883166/uncatchable-chucknorrisexception) ## 169 | 170 | ## [84、怎么在一个整数的左边填充0](http://stackoverflow.com/questions/473282/how-can-i-pad-an-integers-with-zeros-on-the-left) ## 171 | 172 | ## [85、创建泛型数组](http://stackoverflow.com/questions/529085/how-to-create-a-generic-array-in-java) ## 173 | 174 | ## [86、判断三个以上为true条件](http://stackoverflow.com/questions/3076078/check-if-at-least-two-out-of-three-booleans-are-true) ## 175 | 176 | ## [87、ThreadLocal variable](http://stackoverflow.com/questions/817856/when-and-how-should-i-use-a-threadlocal-variable) ## 177 | 178 | ## [88、JSF、Servlet、JSP区别](http://stackoverflow.com/questions/2095397/what-is-the-difference-between-jsf-servlet-and-jsp) ## 179 | 180 | ## [89、为什么java向量类被认为是过时的和被废弃](http://stackoverflow.com/questions/1386275/why-is-java-vector-class-considered-obsolete-or-deprecated) ## 181 | 182 | ## [90、反编译DEX为源码](http://stackoverflow.com/questions/1249973/decompiling-dex-into-java-sourcecode) ## 183 | 184 | ## [91、ArrayList转换为String[]数组](http://stackoverflow.com/questions/5374311/convert-arrayliststring-to-string-array) ## 185 | 186 | ## [92、软引用和弱引用的区别](http://stackoverflow.com/questions/299659/what-is-the-difference-between-a-soft-reference-and-a-weak-reference-in-java) ## 187 | 188 | ## [93、Efficiency of Java “Double Brace Initialization”](http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization) ## 189 | 190 | ## [94、实现单例模式的最有效方法](http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java) ## 191 | 192 | ## [95、java中什么相当于C++的](http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java) ## 193 | 194 | ## [96、从纯文本文件中读](http://stackoverflow.com/questions/4716503/reading-a-plain-text-file-in-java) ## 195 | 196 | ## [97、Which @NotNull Java annotation should I use](http://stackoverflow.com/questions/4963300/which-notnull-java-annotation-should-i-use) ## 197 | 198 | ## [98、为什么这段代码陷入无限循环](http://stackoverflow.com/questions/3831341/why-does-this-go-into-an-infinite-loop) ## 199 | 200 | ## [99、Eclipse: Set maximum line length for auto formatting](http://stackoverflow.com/questions/3697287/eclipse-set-maximum-line-length-for-auto-formatting) ## 201 | 202 | ## [100、Why does Math.round(0.49999999999999994) return 1](http://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1) ## 203 | --------------------------------------------------------------------------------