天天看點

某遊戲彩票外企Java面試題

第一輪筆試

筆試形式: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      

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?

  1. 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 = "      

運作結果:

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 = "      

運作結果:

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=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="      

運作結果:

捕獲異常:
java.lang.ArithmeticException: / by zero
    at Test.func(Test.java:6)
    at Test.main(Test.java:18)
res=2      

6 Java有哪幾類異常

答:

(1)基本概念

java的異常結構圖

某遊戲彩票外企Java面試題

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值,這将給系統帶來很大的安全漏洞。