Java 面试题之简答题 (一)

本贴最后更新于 2162 天前,其中的信息可能已经天翻地覆

一些面试题

题目一 高并发下的 SimpleDateFormat

题目内容

在一个有较大并发量的系统中,有人在某个公共类中书写了如下方法:

public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");

public static String formatDate(Date date) {
  return dateFormat.format(date);
}

请问这么写会出现什么问题?请说明原因.

题目解析

当高并发情况下,多个线程调用了 formatDate 方法,可能会出现 NumberFormatException 这个异常,我们使用如下代码进行测试

 private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        public static void main(String[] args) throws InterruptedException {
            ExecutorService executorService = Executors.newFixedThreadPool(500);
            for (int i = 0; i < 500; i++) {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 1000000; i++) {
                            try {
                                DATE_FORMAT.parse("2018-09-04 00:00:00");
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
            }
            Thread.sleep(3000000);
        }

报错:

java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Long.parseLong(Long.java:601)
	at java.lang.Long.parseLong(Long.java:631)
	at java.text.DigitList.getLong(DigitList.java:195)
	at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2162)
	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
	at java.text.DateFormat.parse(DateFormat.java:364)
	at cn.love.potion.test.DateTest$1.run(DateTest.java:41)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

这个问题产生一般的原因是 由于传过来的日期格式不对,所以导致解析时出现了问题, 但是我们的源代码中传递过去的日期格式是正确的.我们进入 SimpleDateFormat 类的源码中可以看到如下注释(基于 jdk1.8):
imagepng

大致翻译一下的意思就是:

date formats 并未同步。建议为每个线程创建单独的实例。如果多个线程同时访问 format 方法,则必须额外的同步它。

如果想了解具体错误的产生,可以看一下 SimpleDateFormat 类的源代码,在这里不做过多的介绍.如果看不懂的话可以在下面给我留言,我会重写一下这个解析的.常规的解决方法就是给方法加锁,使用 synchronized 就可以了,不需要过于纠结性能问题,过早的优化是万恶之源.

题目二 LinkedList 下的错误操作

题目内容

现有一个LinkedList类型的list,其中含有顺序[1....100000]个元素(10w),现在要打印后10000个元素,如下操作方式会有什么方面的问题?
for(int i = 90000; i<list.size(); i++){
    System.out.println(list.get(i));
}

题目解析

大家都知道 LinkedList 的数据结构是基于双向链表的,当我们使用 get 方法时,会首先判断查找元素的索引是否大于 1/2size.如果大于则从最后一个元素处开始向前查询,否则从第一个元素处开始向后查询

 if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }

解决方法

使用get的效率是很低下的,不建议使用 get 方法获取大量的连续的 LinkedList 数据,推荐使用迭代器或者增强 for 循环(本质就是迭代器),在这一题中,可使用 LinkedList 自带的如下方法:

ListIterator<Integer> iterator = list.listIterator(90000);
   while (iterator.hasNext()) {
       System.out.println(iterator.next());
   }

题目三 空间复杂度 o(1)下的移除 ArrayList 中的偶数项

题目内容

以下代码打印列表中的偶数项,并将其移除,是否有问题?
 public void removeElement(){
        List<Integer> list = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 1000; i++) {
            list.add(random.nextInt(10000));
        }
        for (Integer i : list) {
            if (i % 2 == 0) {
                list.remove(i);
            }
        }
    }

题目解析

ArrayList 是基于类中定义的一个变量 modCount 实现的 fail-fast 机制,每次对 ArrayList 对象进行增删改时,都会进行 modCount++ 这个操作.当我们在遍历 ArrayList 对象时,每次处理完当前对象以后,会比较对象的 modCount 是否和开始遍历时的 modCount 一致,如果不一致就报 ConcurrentModificationException,这道题目中 在遍历中执行了remove操作.所以就会报错.

解决方法

在 ArrayList 类中提供了迭代器,使用其自带的迭代器进行遍历时移除就不会出错了.代码如下:


  Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()) {
            Integer i = iterator.next();
            if (i % 2 == 0) {
                //说明为偶数,进行删除
                iterator.remove();
            }
        }

题目四 字符串的"=="和"equals"

题目内容

写出如下方法调用的输出结果

String s1 = "hello";
String s2 = "hell";
String s3 = s2 + "o";
if (s1 == s3) {
	System.out.println("A");
} else if (s1.equals(s3)) {
	System.out.println("B");
}

题目解析

本题目的关键重在于理解字符串的创建和销毁过程,当我们使用 String s1 = "hello" 这种方式创建一个字符串时,会首先去常量池中寻找是否存在是否存在此字符串,如果不存在的话就在常量池中创建一个对象,并将栈中的引用指向这个对象.在本题目中,当进行 String s3 = s2 + "o"; 时,会首先看常量池中是否存在"hello"对象,不存在则在常量池中创建一个对象,然后在堆中再创建一个对象,并将栈中的引用指向堆中的这个对象.
所以在本题目中,当使用 == 进行比较时,由于是 s1 和 s3 的内存地址不同(一个指向的是常量池中的对象,一个指向的是堆中的对象),所以 s1==s3 这个条件不满足,又因为 String 类重写了 equals 方法,其比较的是字符串的内容是否相同而不是字符串的地址是否相同,所以 s1.equals(s3) 成立,打印 B.

题目五 final 修饰变量的不可变性

题目内容

下列方法在执行时是否会出现什么问题,为什么?
public int addOne(final int x) {
  return x++;
}

题目解析

当我们对一个变量设置为 final 时,那么就以为着这个变量引用的不可变性,对于基本类型及其包装类型其实也就是相当于数值的不可变,因为对于基础类型的包装类型,每次的 ++.--操作都是重新创建一个对象,并将引用指向新创建的对象,对于基础类型可以直接理解为内存所指向的对象的不可变性.所以在这一题中,x++ 操作时无论如何也不能执行的.

题目扩展

当有如下的程序时,请问代码执行是否会出现问题?

public class FinalTest {

  public static void main(String[] args) {
  FinalTest finalTest = new FinalTest();
  Other other = new Other();
  finalTest.add(other);
  }

  private void add(final Other other) {
  other.i++;
  }

}
class Other{
  public int i = 0;
}

:不会出现问题,因为之前已经说过了,final 其实是保证着引用的不可变性,只要 other 对象地址不变,那么对 other 对象进行其他操作都是可以的,final 未能实现真正的 immutable 的.
(未完待续.....)

欢迎关注我的博客以及公众号,才刚刚开始写博客,希望以后会更好!
imagepng

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    325 引用 • 1395 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...