第一轮笔试
笔试形式:paper test
题目难易程度:中等
笔试时间:1个小时
笔试语言:题目和答题全部用英文
1 What's difference between a "static synchronized" method and "synchronized" method?
答:
synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”,类的两个不同实例就没有这种约束了。
static synchronized恰好就是要控制类的所有实例的访问了,static synchronized是限制线程同时访问jvm中该类的所有实例同时访问对应的代码块。
实际上,在类中某方法或某代码块中有 synchronized,那么在生成一个该类实例后,该类也就有一个监视块,放置线程并发访问该实例synchronized保护块。而static synchronized则是所有该类的实例共用一个监视块了,这也就是两个的区别了。也就是synchronized相当于this.synchronized,而static synchronized相当于classname.synchronized。
2 If you define a class which will be put into a HashMap, what methods will you overwrite? and how?
答:
import java.util.*;
class Person {
private String username;
private int age;
public Person() {
}
public Person(String username,int age) {
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int hashCode() {
return age*2;
}
}
public class EqualDemo {
public static void main(String[] args) {
Map<Person,String> map = new HashMap<>();
map.put(new Person("tree",25), "hello");
System.out.println(map.get(new Person("tree",25)));
}
}
运行结果:hello
3 Given following requirements, please provide your solution, at least interface/method need to be provideed.
a. We need provide a machanism.
b. It supports log message to file, to database and to a remote server using HTTP.
c. Log message to which target output is configuable.
d. Do not use any existing log library, please implement yours.
4 What is the output of the following:
public class Foo {
public static void main(String[] args) {
if(A()) {
System.out.println("fdsa");
}
}
public static Boolean A() {
try {
return true;
} catch (Exception x) {
//
} finally {
System.out.println("asdf");
}
return false;
}
}
运行结果:
asdf
fdsa
5 Write a Singleton pattern's implementation, match following requirements.
a.Thread-safe
b.Lazy initializing
c.(Optional) Using Java 1.5 new feature to implement it.
答:
public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
6 Given following requirements, please provide your solution, at lease interface/method need to be provided.
a. Design a cache machanism, which can cache any type of objects.
b. The cache's capacity is 1,000,000 objects.
7 How to start/commit/rollback a transaction using JDBC API?
答:
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:1521:SID","username","password");
conn.setAutoCommit(false);
conn.commit();
conn.rollback();
8 Code review. Give some comments to improve the code quality.
9 You are given 50,000 large HTTP files, some of which contain phone numbers and asked to provide a list of the unique numbers contained in the files. How would you go about doing this? Describe the algorithm and tools you would use to accomplish this?
- Design and write an implementation of Iterator that meets the following requirements:
a. It must accept an instance of an Iterator as a parameter in the constructor.
b. It must allow the user to specify a predicate that is used to filter the items in the supplied iterator.
c. The hasNext() method must only return true if and only if there is at least one remaining element in the supplied iterator that satifies the predicate.
d. The next() method must only return values that satisfy the predicate.
答:
定义一个容器Collection接口
public interface Collection {
void add(Object obj);
int size();
Iterator iterator();
}
定义一个Iterator迭代器的接口
public interface Iterator {
Object next();
boolean hasNext();
}
定义一个ArrayList,实现Collection接口,并写一个实现了Iterator接口的内部类
public class ArrayList implements Collection {
Object[] objects = new Object[10];
int index = 0;
@Override
public void add(Object obj) {
if(index == objects.length) {
Object[] newObjects = new Object[objects.length * 2];
System.arraycopy(objects, 0, newObjects, 0, objects.length);
objects = newObjects;
}
objects[index] = obj;
index++;
}
@Override
public int size() {
return index;
}
@Override
public Iterator iterator() {
return new ArrayListIterator();
}
private class ArrayListIterator implements Iterator {
private int cursor = 0;
@Override
public boolean hasNext() {
return cursor != size();
}
@Override
public Object next() {
Object obj = objects[cursor];
cursor++;
return obj;
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Collection c = new ArrayList();
for(int i = 0; i < 5; i++) {
c.add("string " + i);
}
System.out.println("The length is: " + c.size());
Iterator it = c.iterator();
while(it.hasNext()) {
Object obj = it.next();
System.out.println(obj.toString() + " ");
}
}
}
运行结果:
The length is: 5
string 0
string 1
string 2
string 3
string 4
第二轮面试
面试官有两人,技术问得比较深入
1 英文自我介绍
2 某个项目的架构及技术细节
3 synchronized同步方法调用另一个synchronized同步方法
public synchronized void methodA(int a, int b) {
};
public synchronized void methodB(int a){
methodA(a, 0);
}
答:
要明白两个问题,①锁的对象是谁;②谁持有了锁。
有三种情况:
(1)假设方法A和B是在同一个类Test中的两个方法。
Test t = new Test();
t.methodB();
这个时候,methodB方法被调用时,因为加了synchronized ,需要先获得一个锁,这个锁的对象应该是t,也就是当前的这个Test类的实例,而获得锁的东西是线程,也就是说当前线程拿到了t的锁(而不是B方法获得锁),这个时候B方法内调用methodA,因为A也加了synchronized,也需要获得一个锁,因为A和B都是Test类中的方法,所以当前线程要获得的锁的对象也是t。由于当前线程在执行B方法时已经持有了t对象的锁,因此这时候调用methodA是没有任何影响的,相当于方法A上没有加synchronized。
加在非static方法上的synchronized方法是和synchronized(this)块等价的,均为对象锁,即对this加锁。获得当前对象锁的线程,可以继续获得当前对象锁,JVM负责跟踪对象被加锁的次数。线程运行B方法,此时如果this锁可以用,线程获得该锁,线程给对象加锁,计数器变成1,然后B方法调用A方法,由于是对同一个对象同一个线程,线程可以继续获得锁,计数器变为2,表示this被加锁2次。A方法完毕后,线程释放锁,计数器变为1,此时对象锁对其他线程依然是不可获得的。B方法完毕后,线程继续释放锁,此时计数器变为0,表示锁被完全释放,其他线程可以获得对象锁。
(2)假设现在有两个Test类
Test t1 = new Test();
Test t2 = new Test();
t1.methodB();//此时当前线程持有了t1对象的锁
t2.methodB();//此时当前线程也持有了t2对象的锁
当前线程持有了两把锁,锁的对象分别是两个不同的Test类的实例t1和t2,互相没有影响。
(3)假设在多线程环境下,两个线程都可以访问Test t=new Test();
此时假设thread1里调用t.methodB();同时thread2里调用t.methodB()
这时假设thread1先抢到t对象的锁,那么thread2需要等待thread1释放t对象的锁才可以执行B方法。
结果像这样:
thread1获得t的锁->thread1执行methodB->thread1执行methodA->释放t的锁;thread2获得t的锁->thread2执行methodB->thread2执行methodA->释放t的锁。
synchronized还有很多种使用方法,但只有明白是那条线程获得哪个对象的锁,就很容易明白了。
4有一个静态变量num,初始值为0。现在开了1000个线程,每个线程内循环1000次,每循环对num自加1,问最后的值是大于、等于还是小于1000000?
答:
(1)小于1000000,因为有1000个线程,不同的线程有可能同时访问num,导致自加的次数变少。
import java.util.concurrent.TimeUnit;
public class Test implements Runnable{
private static int num = 0;
@Override
public void run() {
for(int i = 1; i <=1000; i++){
num++;
System.out.println(Thread.currentThread().getName() + ", num = " + num );
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <=1000; i++) {
Thread thread = new Thread(new Test());
thread.setName("Thread:"+i);
thread.start();
}
try {
// 等待全部子线程执行完毕
TimeUnit.SECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finally, num = " + num);
}
}
运行结果:
Thread:19, num = 999981
Thread:19, num = 999982
Thread:19, num = 999983
Thread:975, num = 999367
Thread:975, num = 999984
Thread:975, num = 999985
Thread:975, num = 999986
Thread:975, num = 999987
Thread:975, num = 999988
Thread:975, num = 999989
Thread:975, num = 999990
Finally, num = 999990
(2)若要防止此现象,要用static synchronized关键字对数据进行同步保护。
import java.util.concurrent.TimeUnit;
public class Test implements Runnable{
private static int num = 0;
static synchronized private void increaseNumber() {
num++;
}
@Override
public void run() {
for(int i = 1; i <=1000; i++){
increaseNumber();
System.out.println(Thread.currentThread().getName() + ", num = " + num );
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i <=1000; i++) {
Thread thread = new Thread(new Test());
thread.setName("Thread:"+i);
thread.start();
}
try {
// 等待全部子线程执行完毕
TimeUnit.SECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finally, num = " + num);
}
}
运行结果:
Thread:3, num = 999993
Thread:3, num = 999994
Thread:3, num = 999995
Thread:3, num = 999996
Thread:3, num = 999997
Thread:3, num = 999998
Thread:3, num = 999999
Thread:3, num = 1000000
Thread:788, num = 999985
Finally, num = 1000000
5 下面代码有什么问题?
try {
return 0;
} catch (Exception e){
return 1;
} finally {
return 2;
}
return 3;
答:
最后的return 3; 会导致编译报错:Unreachable code。
分析:
(1)不管try块、catch块中是否有return语句,finally块都会执行。
(2)finally块中的return语句会覆盖前面的return语句(try块、catch块中的return语句),所以如果finally块中有return语句,Eclipse编译器会报警告“finally block does not complete normally”。
(3)如果finally块中包含了return语句,即使前面的catch块重新抛出了异常,则调用该方法的语句也不会获得catch块重新抛出的异常,而是会得到finally块的返回值,并且不会捕获异常。
实验1:
public class MyTest {
public static int func() {
try {
return 0;
} catch (Exception e){
return 1;
} finally {
return 2;
}
}
public static void main(String[] args) {
int res = func();
System.out.println("res=" + res);
}
}
运行结果:
res=2
实验2:
public class MyTest {
public static int func() {
try {
int a = 2 / 0;
return 0;
} catch (Exception e){
System.out.println("捕获异常:");
e.printStackTrace();
return 1;
} finally {
return 2;
}
}
public static void main(String[] args) {
int res = func();
System.out.println("res=" + res);
}
}
运行结果:
捕获异常:
java.lang.ArithmeticException: / by zero
at Test.func(Test.java:6)
at Test.main(Test.java:18)
res=2
6 Java有哪几类异常
答:
(1)基本概念
java的异常结构图
![Uploading throwable_458816.png . . .]
Throwable是所有异常的根,java.lang.Throwable
Error是错误,java.lang.Error
Exception是异常,java.lang.Exception
(2)Exception
一般分为Checked异常和Runtime异常,所有RuntimeException类及其子类的实例被称为Runtime异常,不属于该范畴的异常则被称为CheckedException。
①Checked异常
只有java语言提供了Checked异常,Java认为Checked异常都是可以被处理的异常,所以Java程序必须显示处理Checked异常。如果程序没有处理Checked异常,该程序在编译时就会发生错误无法编译。这体现了Java的设计哲学:没有完善错误处理的代码根本没有机会被执行。对Checked异常处理方法有两种:
a.当前方法知道如何处理该异常,则用try...catch块来处理该异常。
b.当前方法不知道如何处理,则在定义该方法时声明抛出该异常。
我们比较熟悉的Checked异常有
Java.lang.ClassNotFoundException
Java.lang.NoSuchMetodException
java.io.IOException
②RuntimeException
Runtime如除数是0和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。
我们比较熟悉的RumtimeException类的子类有
Java.lang.ArithmeticException
Java.lang.ArrayStoreExcetpion
Java.lang.ClassCastException
Java.lang.IndexOutOfBoundsException
Java.lang.NullPointerException
(3)Error
当程序发生不可控的错误时,通常做法是通知用户并中止程序的执行。与异常不同的是Error及其子类的对象不应被抛出。
Error是throwable的子类,代表编译错误和系统错误,用于指示合理的应用程序不应该试图捕获的严重问题。
Error由Java虚拟机生成并抛出,包括动态链接失败,虚拟机错误等。程序对其不做处理。
7 堆
答:
堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
(1)堆中某个节点的值总是不大于或不小于其父节点的值;
(2)堆总是一棵完全二叉树。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
8 从哈希表的原理来分析为什么不同的对象可以有相同的hashCode
答:
(1)因为hashCode是可以重载的。
比如Teacher类重载了hashCode:
int hashCode() {
return age * 2;
}
再比如Doctor类重载了hashCode:
int hashCode() {
return age * 2;
}
按照上面的规则,只要教师和医生的年龄相同,他们的hashCode就相同。但是他们很明显是不同的对象。
(2)从原理上看:hash只要求从A到B的对应为一个映射,它并没有限定该对应关系为一一映射。因此会有这样的可能:两个不同的键值对应同一个hash值。这种情况叫做hash碰撞(hash collision)或者hash 冲突。比如网络协议中的checksum就可能出现这种状况,即所要校验的内容与原文并不同,但与原文生成的checksum(hash值)相同。再比如,MD5算法常用来计算密码的hash值。已经有实验表明,MD5算法有可能发生碰撞,也就是不同的明文密码生成相同的hash值,这将给系统带来很大的安全漏洞。
9 二叉树中序遍历
答:
可参考 建立排序二叉树并中序遍历
10 两个栈模拟一个队列
可参考 用两个栈模拟一个队列
更多内容请关注微信公众号
wechat.jpg
网友评论