Skip to content

4ker/stackoverflow-java-top-qa

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stackoverflow-Java-top-qa

一些基本的约定

  • 文档的文件名,和 stackoverflowhich-notnull-java-annotation-should-i-usew 上 的 url 保持一致。 例如, http://stackoverflow.com/questions/8710619/java-operator 的文件名, 就是 java-operator.md
  • 在每篇翻译文档内容的最后,要附上 stackoverflow 的原文链接

每个人可以做(但不限于)

  • 找未翻译的问题进行翻译
  • 优化已翻译的问题
  • 输出问答精简汇总版(把所有的问答集中到一个 md 文件,然后尽量精简,让别人可 以在一天内把这 100 个问题的精髓都看完)
  • 输出 gitbook 版本(现在直接在 github 上查看,体验不好)

文档优化反馈

请大家多多反馈,优化已翻译好的文章:可以到 吐槽区 吐槽, 也可以在已翻 译文章基础上进行优化,提新的 PR。文章质量的提升,需要大家一起努力!

目录

[32/38] 基础语法

  • [X] Java += 操作符实质
  • [X] 将 Inputstream 转换为 String Apache commons IOUtils
    // method 1
    StringWriter writer = new StringWriter();
    IOUtils.copy(inputStream, writer, encoding);
    String theString = writer.toString();
    
    // method 2
    String theString = IOUtils.toString(inputStream, encoding);
    
    // method 3
    static String convertStreamToString(java.io.InputStream is) {
        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }
        
  • [X] 将数组转换为 List
    // method 1
    new ArrayList<Element>(Arrays.asList(array));
    
    // 定长, 不能 add, remove (搞得跟 array 就一样了)
    // method 2
    Arrays.asList(array);
    // method 3
    Arrays.asList(new Element(1),new Element(2),new Element(3));
        
  • [X] 如何遍历 map 对象
    // method 1: entrySet
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    for(Map.Entry<Integer, Integer> entry : map.entrySet()){
        System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue())
    }
    
    // method 2: keySet(), values()
    
    // method 3: iterator
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
    while (entries.hasNext()) {
        Map.Entry<Integer, Integer> entry = entries.next();
        System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
    }
    
    // method 4: 迭代 keys 并搜索 values(低效的)
        
  • [ ] public,protected,private,不加修饰符。有什么区别呢?
  • [X] 如何测试一个数组是否包含指定的值?
    // method 1
    Arrays.asList(...).contains(...)
    
    // method 2
    String[] fieldsToInclude = { "id", "name", "location" };
    if ( ArrayUtils.contains( fieldsToInclude, "id" ) ) {
        // Do some stuff.
    }
    
    // method 3, for ordered list
    Arrays.binarySearch(arr, targetValue);
        
  • [X] 重写(Override)equlas 和 hashCode 方法时应考虑的问题
    • equals() 定义了对象的相等关系(自反性、对称性、传递性)
    • hashCode 只是尽量要求不重合
    • equals 的对象, hashCode 也要一样, hashCode 一样, 不一定 equal
  • [X] 从一个多层嵌套循环中直接跳出 break+label, 我觉得这个和 goto 就很类似了. 应当避免这样写.
  • [X] 如何将 String 转换为 Int
    • Integer.valueOf(str); ==> Integer
    • Integer.parseInt(str); ==> int (possible NumberFormatException)
  • [X] 如何分割(split)string 字符串
    • String[] parts = string.split("-");
    • 需要注意的是,该方法的参数是个正则表达式, 要注意对某些字符做转码。 例如,.在正则表达式中表示任意字符,因此,如果你要通过.号做分割,需要这样写,split(“\.”)或者split(Pattern.quote(“.”))
  • [X] 在 java 中如何对比(compare)string
  • [X] =Map<Key,Value>=基于 Value 值排序
    • 1: 使用 TreeMap,可以参考下面的代码

      The TreeMap class implements the Map interface by using a tree. A TreeMap provides an efficient means of storing key/value pairs in sorted order, and allows rapid retrieval.

    • 2: 先通过 linkedlist 排好序,再放到 LinkedHashMap 中
  • [X] HashMap 和 Hashtable 的区别
    • Hashtable是同步的,加了synchronized锁,而HashMap不是
  • [X] 如何便捷地将两个数组合到一起
    // method 1
    ArrayUtils.addAll(T[], T...)
    
    // method 2
    public Foo[] concat(Foo[] a, Foo[] b) {
       int aLen = a.length;
       int bLen = b.length;
       Foo[] c= new Foo[aLen+bLen];
       System.arraycopy(a, 0, c, 0, aLen);
       System.arraycopy(b, 0, c, aLen, bLen);
       return c;
    }
    
    // method 3
    public <T> T[] concatenate (T[] a, T[] b) {
        int aLen = a.length;
        int bLen = b.length;
    
        @SuppressWarnings("unchecked")
        T[] c = (T[]) Array.newInstance(a.getClass().getComponentType(), aLen+bLen);
        System.arraycopy(a, 0, c, 0, aLen);
        System.arraycopy(b, 0, c, aLen, bLen);
    
        return c;
    }
    // !!!! 注意,泛型的方案不适用于基本数据类型(int,boolean……)
        
  • [X] Java 是否支持默认的参数值
    • builders: 使用创建者模式,你可以设定部分参数是有默认值,部分参数是可选的。如:
    • 方法(构造函数)重载
    • null 的传递
    • 多参数方式
      void foo(String a, Integer... b) {
          Integer b1 = b.length > 0 ? b[0] : 0;
          Integer b2 = b.length > 1 ? b[1] : 0;
          //...
      }
      
      foo("a");
      foo("a", 1, 2);
              

      如果不是同一类型, 就用 Object + cast .

    • 使用 Map 作为方法中的参数
  • [X] Java 产生指定范围的随机数
    • Math.random(): [0, 1)
    • java.util.Random
    • org.apache.commons.lang3.RandomUtils
  • [X] JavaBean 到底是什么 JavaBean 只是一个标准
    • 所有的属性是私有的(通过 getters/setters 处理属性)
    • 一个公有的无参数的构造器
    • 实现了序列化(Serializable)

    另外,一个 JavaBean 类和一个普通的类没有语法区别,如果遵循上面的标准 的话,一个类可以认为成 JavaBean 类。

    之所以需要 JavaBean,是因为这样预定义了一种类的格式,一些库能依据这 个约定的格式,来做一些自动化处理。举个例子,如果一个类库需要通过流来 处理你传递的任何对象,它知道它可以正常处理,因为这个对象是可序列化的。 (假设这个类库要求你的对象是 JavaBeans)

  • [ ] wait() 和 sleep() 的区别
  • [X] 能否在一个构造器中调用另一个构造器
    public class Foo {
        private int x;
    
        public Foo() {
            this(1);
        }
    
        public Foo(int x) {
            this.x = x;
        }
    }
        

    请注意,在构造器中,你只能调用一次其他的构造器。并且调用其他构造器的 语句,必须是这个构造器的第一个语句。

  • [X] =finally= 代码块总会被执行么
    try {
        something();
        return success;
    } catch (Exception e) {
        return failure;
    } finally {
        System.out.println("i don't know if this will get printed out.");
    }
    // 只有以下情况 finally 不会被调用:
    //     +    当你使用 System.exit() 后
    //     +    其他线程干扰了现在运行的线程(通过 interrupt 方法)
    //     +    JVM 崩溃 (crash) 了
        
  • [X] 如何将 String 转换为 enum
    • Blah.valueOf(“A”) 将会得到 Blah.A
    • 静态方法 valueOf() 和 values() 不存在于源码中,而是在编译时创建
    • hack
      public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
          if( c != null && string != null ) {
              try {
                  return Enum.valueOf(c, string.trim().toUpperCase());
              } catch(IllegalArgumentException ex) {}
          }
          return null;
      }
      
      public enum MyEnum {
          ...
          public static MyEnum fromString(String name) {
              return getEnumFromString(MyEnum.class, name);
          }
          ...
      }
              
  • [X] 在 Java 中声明数组
    // primtive type
    int[] myIntArray = new int[3];
    int[] myIntArray = {1, 2, 3};
    int[] myIntArray = new int[]{1, 2, 3};
    
    // string
    String[] myStringArray = new String[3];
    String[] myStringArray = {"a", "b","c"};
    String[] myStringArray = new String[]{"a", "b", "c"};
        
  • [X] 反射是什么及其用途
    Method method = foo.getClass().getMethod("dosomething",null);
    method.invoke(foo,null);  //调用foo的dosomething方法
        

    可以看看我的这个笔记 https://github.com/4ker/JavaAOP

  • [X] 为什么不能用 string 类型进行 switch 判断 在 JDK7 中,这个特性已经实现了。在编译阶段,以 string 作为 case 值的代码,会按照特定的模式,被转换为更加复杂的代码。最终的执行代码将是一些使用了 JVM 指令的代码。

    这是在说, java 7 之前是不可以的.

  • [X] 比较 java 枚举成员使用 equal 还是 ==
    • 如果你看过枚举的源码,你会发现在源码中,equals 也仅仅非常简单的 ==
    • 其实很简单, 因为 Enum 的 valueOf 方法是从自己的备选项里面枚举, 找到那个 enum 取出来的, 所以内存位置也是一样的.
    • 还有一个区别是 null.equals 可能有风险, == 则没有
    • 总而言之,在枚举比较上使用 == , 因为:
      • 能正常工作
      • 更快
      • 运行时是安全的
      • 编译期也是安全的
  • [X] 用 java 怎么创建一个文件并向该文件写文本内容
    // TEXT FILE
    // method 1: print writer, text file
    PrintWriter writer = new PrintWriter("the-file-name.txt", "UTF-8");
    writer.println("The first line");
    writer.println("The second line");
    writer.close();
    // method 2: array of lines to file
    List<String> lines = Arrays.asList("The first line", "The second line");
    Path file = Paths.get("the-file-name.txt");
    Files.write(file, lines, Charset.forName("UTF-8"));
    //Files.write(file, lines, Charset.forName("UTF-8"), StandardOpenOption.APPEND);
    
    // BINARY
    // method 3: FileOutputStream, binary
    byte data[] = ...
    FileOutputStream out = new FileOutputStream("the-file-name");
    out.write(data);
    out.close();
    // method 4: FileOutputStream, binary
    Path file = Paths.get("the-file-name");
    Files.write(file, data);
        
  • [X] serialVersionUID 有什么作用?该如何使用?
    • 对象的序列化主要有两种用途:
      • 把对象序列化成字节码,保存到指定介质上 (如磁盘等)
      • 用于网络传输
    • 可当我们增加 email 字段后,不作向后兼容。即放弃原来序列化到磁盘的 Person 类,这时我们可以将版本标识提高
    • serialVersionUID 就是控制版本是否兼容的,若我们认为修改的 Person 是向后兼容的, 则不修改 serialVersionUID;反之,则提高 serialVersionUID 的值。再回到一开始的问题
  • [X] 为什么 Java 的 Vector 类被认为是过时的或者废弃的
    • Vector 中对每一个独立操作都实现了同步,这通常不是我们想要的做法。
    • 总的来说,在大多数情况下,这种同步方法是存在很大缺陷的。正如 Mr Brain Henk 指出, 你可以通过调用Collections.synchronizedList来装饰一个集合 - 事实上 Vector 将 “可变数组” 的集合实现 与 “同步每一个方法” 结合起来的做法是另一个糟糕的设计;
  • [X] Java 的 foreach 循环是如何工作的 用了 iterator
    // for (String item : someList) {System.out.println(item);}
    for(Iterator<String> i = someList.iterator(); i.hasNext(); ) {
        String item = i.next();
        System.out.println(item);
    }
        
  • [X] 为什么相减这两个时间(1927 年)会得到奇怪的结果 这是因为 1927 年 11 月 31 日上海的时区改变了。 观看此页获得更多关于 上海 1927 年的细节。 这个问题主要是由于在 1927 年 12 月 31 日的午夜, 时钟回调了 5 分钟零 52 秒。 所以 “1927-12-31 23:54:08” 这个时间实际 上发生了两次,看上去 java 将这个时间解析为之后的那个瞬间。 因此出现 了这种差别。

    这只是美好但奇怪的世界时区中的一个插曲。

    Shanghai Municipality (mjʊ’nɪsə’pæləti) 自治市

    Daylight Saving Time (DST) Not Observed in Year 1927 Shanghai observed China Standard Time (CST) all year.

    DST was not in use in 1927.

  • [ ] 该什么时候使用 ThreadLocal 变量,它是如何工作的 一种可能的(也是常见的)使用情形是你不想通过同步方式(synchronized)访问非线程安全的对象(说的就是 SimpleDateFormat), 而是想给每个线程一个对象实例的时候。 例如
    public class Foo
    {
        // SimpleDateFormat is not thread-safe, so give one to each thread
        private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue() {
                return new SimpleDateFormat("yyyyMMdd HHmm");
            }
        };
    
        public String formatIt(Date date) {
            return formatter.get().format(date);
        }
    }
        
  • [ ] servlets 的运行原理
  • [X] 如何计算 MD5 值 使用 MessageDigest 和 String 时,一定要显式声明你的数据编码类型。如 果你使用无参的 String.getBytes() , 它会以当前平台的默认编码来转换数 据。不同平台的默认编码可能是不同的,这可能会导致你的数据不一致。
    import java.security.*;
    
    ...
    byte[] bytesOfMessage = yourString.getBytes("UTF-8");
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] thedigest = md.digest(bytesOfMessage);
        

    如果你的要计算的数据量很大,你可以循环使用 .update(byte[]) 方法来加载数据。加载完毕后用 .digest() 方法来得到计算出的 MD5 值。

  • [ ] Java 中软引用和弱引用的区别
  • [ ] JSF, Servlet 和 JSP
  • [X] Java 内部类和嵌套静态类 因此, 要实例化一个内部类对象, 必须先实例化外部类对象. 然后用这种语法来创建内部类对象:
    OuterClass.InnerClass innerObject = outerObject.new InnerClass();
        

    这是什么鬼…

    class A {
        int t() { return 1; }
        static A a =  new A() { int t() { return 2; } };
    }
        
  • [X] =@Component=, @Repository, @Service 的区别 在 spring 集成的框架中,注解在类上的@Component,@Repository,@Service等注解能否被互换?或者说这些注解有什么区别?

    在 Spring2.0 之前的版本中,@Repository注解可以标记在任何的类上,用来 表明该类是用来执行与数据库相关的操作(即 dao 对象),并支持自动处理 数据库操作产生的异常

    在 Spring2.5 版本中,引入了更多的 Spring 类注解: @Component,@Service,@Controller。Component是一个通用的 Spring 容器管 理的单例 bean 组件。而@Repository, @Service, @Controller就是针对不同 的使用场景所采取的特定功能化的注解组件。

    因此,当你的一个类被@Component所注解,那么就意味着同样可以用 @Repository, @Service, @Controller来替代它,同时这些注解会具备有更多 的功能,而且功能各异。

    最后,如果你不知道要在项目的业务层采用@Service还是@Component注解。那 么,@Service是一个更好的选择。

    这几个注解几乎可以说是一样的:因为被这些注解修饰的类就会被 Spring 扫 描到并注入到 Spring 的 bean 容器中。

    这里,有两个注解是不能被其他注解所互换的:

    • @Controller 注解的 bean 会被 spring-mvc 框架所使用。
    • @Repository 会被作为持久层操作(数据库)的 bean 来使用
  • [X] 如何创建泛型 java 数组
    public class GenSet<E> {
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // 使用原生的反射方法,在运行时知道其数组对象类型
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    
        //...如果传入参数不为E类型,那么强制添加进数组将会抛出异常
        void add(E e) {...}
    }
        

[8/21] 编程技巧

  • [X] 去掉烦人的 =!=null=(判空语句)

    用 Java 8 的 Optional 啊! 懒得看了.

  • [X] 获取完整的堆栈信息

    捕获了异常后,如何获取完整的堆栈轨迹(stack trace)

    // method 1
    String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e);
    
    // method 2
    Thread.currentThread().getStackTrace();
        
  • [X] 如何用一行代码初始化一个 ArrayList
    // good
    ArrayList<String> places = new ArrayList<String>(
        Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));
    
    // works too. 匿名内部类
    ArrayList<String> list = new ArrayList<String>() {{
        add("A");
        add("B");
        add("C");
    }};
        
  • [X] 初始化静态 map
    // 我使用static初始化器来创建一个固定长度的静态map
    public class Test{
        private static final Map<Integer, String> myMap;
        static {
            Map<Integer, String> aMap = ...;
            aMap.put(1,"one");
            aMap.put(2,"two");
            myMap = Collections.unmodifiableMap(aMap); (ref:h1507011118)
        }
    }
    
    // 我喜欢用Guava(是 Collection 框架的增强)的方法初始化一个静态的,不可改变的map
    static fianl Map<Integer, String> myMap = ImmutablMap.of(
        1,"one",
        2, "two"
    )
    
    // build
    static fianl Map<Integer, String> myMap =
            new ImmutableMap.Builder<String, Integer>()
                    .put("one", 1)
                    .put("two", 2)
                    .put("three", 3)
                    .build();
        
  • [X] 给 3 个布尔变量,当其中有 2 个或者 2 个以上为 true 才返回 true

    这个智障答案…

    我觉得 return ((a && b) || (b && c) || (a && c)) 是最好的.

  • [X] 输出 Java 数组最简单的方式
    int[] intArray = new int[] {1, 2, 3, 4, 5};
    System.out.println(Arrays.toString(intArray));
    //输出: [1, 2, 3, 4, 5]
    
    String[] strArray = new String[] {"John", "Mary", "Bob"};
    System.out.println(Arrays.deepToString(strArray));
    //输出: [John, Mary, Bob]
    
    // Arrays.deepToString与Arrays.toString不同之处在于,Arrays.deepToString更适合打印多维数组
        
  • [X] 什么在 java 中存放密码更倾向于 char 而不是 String

    String是不可变的。虽然String加载密码之后可以把这个变量扔掉,但是字符 串并不会马上被GC回收,一但进程在GC执行到这个字符串之前被dump,dump出 的的转储中就会含有这个明文的字符串。那如果我去“修改”这个字符串,比如 把它赋一个新值,那么是不是就没有这个问题了?答案是否定的,因为String 本身是不可修改的,任何基于String的修改函数都是返回一个新的字符串,原 有的还会在内存里。

    然而对于数组,你可以在抛弃它之前直接修改掉它里面的内容或者置为乱码, 密码就不会存在了。但是如果你什么也不做直接交给gc的话,也会存在上面一 样的问题。

    所以,这是一个安全性的问题–但是,即使使用char[]也仅仅是降低了攻击者 攻击的机会,而且仅仅对这种特定的攻击有效。

  • [ ] 如何避免在 JSP 文件中使用 Java 代码
  • [ ] Java 源码里的设计模式
  • [ ] 如何产生一个随机的字母数字串作为 session 的唯一标识符

    如果允许产生的随机字符串是可猜测的(随机字符串比较都短,或者使用有缺陷 的随机数生成器),进而导致攻击者可能会劫持到会话的,可以使用一个相对简 单随机数生成代码,如下所示:

  • [ ] 如何创建单例
    // 预加载
    public class Foo {
    
        private static final Foo INSTANCE = new Foo();
    
        private Foo() {
            if (INSTANCE != null) {
                throw new IllegalStateException("Already instantiated");
            }
        }
    
        public static Foo getInstance() {
            return INSTANCE;
        }
    }
    
    // 懒加载
    class Foo {
    
        private static Foo INSTANCE = null;
    
        private Foo() {
            if (INSTANCE != null) {
                throw new IllegalStateException("Already instantiated");
            }
        }
    
        public static Foo getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
            return INSTANCE;
        }
    }
        
  • [X] 实现 Runnable 接口 VS. 继承 Thread 类

    在Java中,并发执行任务一般有两种方式:

    • 实现Runnable接口
    • 继承Thread类

    一般而言,推荐使用方式(1),主要是由于大多数情况下,人们并不会特别去 关注线程的行为,也不会去改写Thread已有的行为或方法,仅仅是期望执行任 务而已。 因此,使用接口的方式能避免引入一些并不需要的东西,同时也不 会影响继承其他类,并使程序更加灵活。

    额外的tips:

    • Runnable与Thread不是对等的概念

      在Thinking in Java中,作者吐槽过Runnable的命名,称其叫做Task更为合理。 在Java中,Runnable只是一段用于描述任务的代码段而已,是静态的概念,需要通过线程来执行。而Thread更像是一个活体,自身就具有很多行为,能够用来执行任务。

    • 仅仅当你确实想要重写(override)一些已有行为时,才使用继承,否则请使用接口。
    • 在Java 5之前,创建了Thread却没调用其start()方法,可能导致内存泄露。
  • [ ] 我应该用哪一个 @NotNull 注解
  • [ ] 怎样将堆栈追踪信息转换为字符串
  • [ ] 如何处理 java.lang.outOfMemoryError PermGen space error
  • [ ] 如何在整数左填充 0
  • [ ] 在调用 instanceof 前需要进行 null 检查吗
  • [ ] 如何从文件里读取字符串
  • [ ] 遍历集合时移除元素,怎样避免 ConcurrentModificationException 异常抛出
  • [ ] 如何让 IntelliJ 编辑器永久性显示代码行数
  • [ ] 如何使用 maven 把项目及其依赖打包为可运行 jar 包

网络

性能

测试

Android