【面经】三七互娱Java游戏研发实习(一面)

一、王者荣耀服务器实现

 

二、接口和类的区别?

1、类只能继承一个类,但可以实现多个接口。

2、对于继承性,类继承了父类的方法,子类可以选择是否覆盖父类的方法。

3、接口的方法只有声明,没有实现;而类中的方法必须有方法体。

 

三、接口可以实例化吗?为什么?那lambda表达式不就是传入一个接口的实例吗?

接口不可以实例化,因为接口可以看成是特殊的抽象类,比抽象类的程度更高,其所有方法都是public abstract类型的,因此不能实例化。

Lambda表达式的原理是:

1、编译器根据Lambda表达式生成一个私有的静态函数,这个私有函数就是执行Lambda表达式里的内容

2、生成一个内部类并调用上述所生成的私有静态函数

3、将Lambda表达式替换为该内部类的一个实例传入方法体中

 

所以本质上来讲Lambda表达式并非传入一个接口的实例,而是传入一个内部类的实例。

这里引用一下别人的代码:

public class LambdaTest {
    public static void printString(String s, Print<String> print) {
        print.print(s);
    }
    public static void main(String[] args) {
        printString("test", (x) -> System.out.println(x));
    }
}

@FunctionalInterface
interface Print<T> {
    public void print(T x);
}

反编译代码: 

public class LambdaTest {
    public static void PrintString(String s, Print<String> print) {
        print.print(s);
    }

    public static void main(String[] args) {
        PrintString("test", new LambdaTest$$Lambda$1());
    }

    private static void lambda$main$0(String x) {
        System.out.println(x);
    }

    static final class LambdaTest$$Lambda$1 implements Print {
        public void print(Object obj) {
            LambdaTest.lambda$main$0((String) obj);
        }
        private LambdaTest$$Lambda$1() {
        }
    }
}

@FunctionalInterface
interface Print<T> {
    public void print(T x);
}

 

四、可以在static方法内部调用非静态方法吗

static内部不能直接调用该类的非静态方法,但是如果通过传入一个对象的引用到静态方法,然后通过该引用就可以调用非静态方法和非静态成员了。

 

五、可以在子类重写父类的static方法吗

逻辑上可以,编译可以通过,但在父类引用指向子类对象的时候,通过父类引用调用的仍然是父类的类方法,而不是子类的。

public class StaticFather {
    public static void print() {
        System.out.println("I am father");
    }
}

public class StaticSon extends StaticFather {
    public static void print() {
        System.out.println("I am son");
    }
}

public class Main {
    public static void main(String[] args) {
        StaticFather A = new StaticFather();
        StaticFather B = new StaticSon();
        StaticSon C = new StaticSon();
        A.print();
        B.print();
        C.print();
    }
}
Result:
I am father
I am father    //通过父类引用调用静态方法时,仍然是父类的静态方法,而非子类的
I am son

 

六、Hashmap底层实现及工作过程

底层采用数组+链地址表法实现,jdk1.8后当链地址表的长度超过8时会用一棵红黑树来代替。

put方法工作过程:

1、调用hashcode()函数得到其哈希值

2、根据散列函数(n-1)&hashcode来计算数组下标

3、如果定位到的数组位置没有元素就直接插入;如果有元素就检查该元素是否重复,重复就覆盖,否则判断该数组位置是存放红黑树还是链表,红黑树就调用相应的方法插入,链表就在链表尾部插入

 

七、LinkedHashmap如何保证有序

LinkedHashMap继承了HashMap,并定义了一个继承自HashMap.Node的Entry静态内部类,这个类记录了节点的上一个节点和下一个节点,因此在插入数据的时候,就会绑定在上一个最后插入的节点后面来保证有序。

 

八、线程状态有哪些

新建(new)

就绪(Runnable)

阻塞(Blocked)

死亡(Dead)

 

九、sleep()和wait()有什么区别?调用后线程的状态一致吗? 

sleep()是Thread类的方法,可使线程暂定指定的时间,进入阻塞状态,指定时间过后会变为就绪态;sleep()可以在任何地方调用,且必须捕获异常,如果不捕获异常,当产生InterruptedException异常时该线程就会进入死亡状态。

而wait()是Object类的方法,可是线程进入阻塞状态,同时进入一个和该对象相关的等待池中,同时释放掉持有的对象锁;wait()方法只能在synchronized同步块中调用,不需要捕获异常。

两者都会进入阻塞状态。

 

九、java线程池怎么实现

线程池是指管理一组同构工作线程的资源池,线程池的实现与工作队列密切相关,在工作队列中保存了所有等待执行的任务,工作者线程的任务就是从工作队列中获取一个任务、执行任务,然后返回线程池中并等待下一个任务的分配。

当工作队列被填满后,开始执行饱和策略:

1、中止(Abort)策略:抛出未检查的RejectedExecutionException,调用者可以捕获这个异常,并根据需求编写处理代码。

2、调用者运行(Caller-Runs)策略:将某些任务回退到调用者,降低新任务的流量。

3、抛弃(Discard)策略:直接抛弃该任务。

4、抛弃最旧(Discard-Oldest)策略:抛弃下一个被执行的任务,然后尝试重新提交新的任务(如果是优先级队列则会抛弃优先级最高的)。

 

十、算法

1、打乱一个数组

public class UpsetArray {
    public static void upset(int []array, int n) {
        Random random = new Random();
        int temp;
        int randomNum;
        for (int i = 0; i < n; i++) {
            randomNum = random.nextInt(n - i) + i;
            temp = array[randomNum];
            array[randomNum] = array[i];
            array[i] = temp;
        }
    }

    public static void test() {
        int array[] = { 0, 5, 3, 2, 1, 7, 4};
        System.out.println(Arrays.toString(array));
        upset(array, array.length);
        System.out.println(Arrays.toString(array));
    }
}
Result:
[0, 5, 3, 2, 1, 7, 4]
[3, 5, 0, 2, 1, 4, 7]

 

2、归并排序(见排序算法实现博文)

 

引用:https://blog.csdn.net/jiankunking/article/details/79825928 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章