Java编程问题总结

Java编程问题总结

整理自 https://github.com/giantray/stackoverflow-java-top-qa

基础语法

将InputStream转换为String

  1. apache commons-io
String content = IOUtils.toString(new FileInputStream(file), StandardCharsets.UTF_8);
//String value = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
  1. javase
static String convertStreamToString(java.io.InputStream is) {
    java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
    return s.hasNext() ? s.next() : "";
}

将数组加入到List

  1. Arrays.asList
String[] array = {"hello", "world", "cat"};
List<String> l1 = new ArrayList<>(Arrays.asList(array));
  1. Collections

List<String> arraylist = new ArrayList<>();
Collections.addAll(arraylist, array);

如何测试一个数组是否包含指定的值

  1. Arrays.asList(…).contains(…)
  2. 使用 Apache Commons Lang 包中的 ArrayUtils.contains
String[] array = {"hello", "world", "cat"};
boolean isContain = ArrayUtils.contains(array, "hello");

从一个多层嵌套循环中直接跳出

OUTER_LOOP:
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
        if (i * j > 6) {
            break OUTER_LOOP;
        }
    }
}

如何将String转换为Int

  1. javase
Integer x = Integer.valueOf(str);
int y = Integer.parseInt(str);
  1. commons-lang3
int z = NumberUtils.toInt("100", -1);

String#split()方法的注意事项

该方法的参数是个正则表达式,要注意对某些字符做转码。
例如,. 在正则表达式中表示任意字符,因此,如果你要通过 . 号做分割,
需要这样写,split("\\.") 或者 split(Pattern.quote("."))

Map<Key,Value>基于Value值排序

    @Test
    public void test3() {
        Map<String, Double> map = new HashMap<>();
        map.put("A", 60.5);
        map.put("B", 70.4);
        map.put("C", 67.4);
        map.put("D", 44.3);
        System.out.println(map);

        ValueComparator bvc = new ValueComparator(map);
        Map<String, Double> sortedMap = new TreeMap<>(bvc);
        sortedMap.putAll(map);
        System.out.println(sortedMap);
    }

    class ValueComparator implements Comparator<String> {
        Map<String, Double> map;

        public ValueComparator(Map<String, Double> map) {
            this.map = map;
        }

        public int compare(String a, String b) {
            return map.get(a) >= map.get(b) ? 1 : -1;
        }
    }

如何便捷地将两个数组合到一起

  1. commons-lang3

String[] both = ArrayUtils.addAll(first, second);

  1. javase
    public <T> T[] concatenate(T[] a, T[] b) {
        if (Objects.isNull(a) && Objects.isNull(b)) {
            return null;
        }
        if (Objects.isNull(a)) {
            return b;
        }
        if (Objects.isNull(b)) {
            return a;
        }
        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;
    }

生成 “min <= 随机数 <= max ” 的随机数

    int min = 5;
    int max = 10;    

    // Math.random() 可以产生一个 大于等于 0 且 小于 1 的双精度伪随机数
    int num1 = min + (int) (Math.random() * (max - min + 1));
    System.out.println(num1);

    int num2 = min + new Random().nextInt((max - min) + 1);
    System.out.println(num2);

    System.out.println(RandomUtils.nextInt(min, max));

该什么时候使用 ThreadLocal变量,它是如何工作的?

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);
    }
}

因为ThreadLocal是一个既定线程内部的数据引用,你可能在使用线程池的应用服务器上因此引起类加载时候的内存泄漏。你需要使用remove()方法很小心地清理TheadLocal中get()或者set()的变量。 如果程序执行完毕没有清理的话,它持有的任何对类的引用将作为部署的Web应用程序的一部分仍保持在永久堆,永远无法得到回收。重新部署/取消部署也无法清理对应用程序类的引用,因为线程不是被你的应用程序所拥有的。 每次成功部署都会创建一个永远不会被垃圾回收类的实例。

最后将会遇到内存不足的异常-java.lang.java.lang.OutOfMemoryError: PermGen space -XX:MaxPermSize,在google了很多答案之后你可能只是增加了-XX:MaxPermSize,而不是修复这个bug。 倘若你的确遇到这种问题,可以通过Eclipse’s Memory Analyzer或根据Frank Kieviet’s guide 和 followup来判断哪些线程和类保留了那些引用。

更新:又发现了Alex Vasseurs blog entry,它帮助我查清楚了一些ThreadLocal的问题。

这是一段有问题的代码

int x = 0;
while (x < 3) {
    x = x++;
    System.out.println(x);
}

如何在整数左填充0

如何在整数左填充0 举例 1 = “0001”

int x = 1;
String a = String.format("%05d", x);

DecimalFormat decimalFormat = new DecimalFormat("00000");
String c = decimalFormat.format(x);

// ApacheCommonsLanguage
String b = StringUtils.leftPad(String.valueOf(x), 5, '0');

// guava
String d = Strings.padStart(String.valueOf(x), 5, '0');

如何创建泛型java数组

数组是不能通过泛型创建的,因为我们不能创建不可具体化的类型的数组。

public class GenSet<E> {
    private E[] a;
    private int length;
    private int index;

    public GenSet(Class<E> c, int s) {
        // 使用原生的反射方法,在运行时知道其数组对象类型
        @SuppressWarnings("unchecked") final E[] a = (E[]) Array.newInstance(c, s);
        this.a = a;
        this.length = s;
        this.index = 0;
    }

    E get(int i) {
        if (this.length <= i) {
            return null;
        }
        return a[i];
    }

    //...如果传入参数不为E类型,那么强制添加进数组将会抛出异常
    void add(E e) {
        if (this.length < 1) {
            return;
        }
        if (this.length <= this.index) {
            return;
        }
        a[this.index++] = e;
    }
}

Java内部类和嵌套静态类

public class OuterClass {

    public static class InterStaticClass {
    }

    class InterClass {
    }
}
    OuterClass.InterStaticClass interStaticClass = new OuterClass.InterStaticClass();

    OuterClass outerClass = new OuterClass();
    OuterClass.InterClass interClass = outerClass.new InterClass();

创建一个文件并向该文件写文本内容

https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/how-to-create-a-file-and-write-to-a-file-in-java.md

编程技巧

如何产生一个随机的字母数字串作为 session 的唯一标识符

    private SecureRandom random = new SecureRandom();

    public String nextSessionId() {
        return new BigInteger(130, random).toString(32);
    }

为什么在java中存放密码更倾向于char[]而不是String

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

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

怎样将堆栈追踪信息转换为字符串

org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable);
Throwable t;
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
sw.toString(); // stack trace as a string
Exception e;
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
String exceptionAsString = sw.toString();

打印数组

Arrays.toString(intArray); 
Arrays.deepToString(strArray); // 多维数组依然可用