后端面试笔记(二)

/ 面试 / 没有评论 / 834浏览

以下是本人在以往的后端面试中遇到的一些题目的小整理,答案和分析仅供参考,未必正确,如果发现有错误的地方,欢迎指出。

1、有8个球,其中有1个重一点,其它的球都一样重,现在只有1个天平,怎么只称2次就找出那个重一点的球? 答: 称第一次:随机取6个球出来,两边3个球放到天平上称。 称第二次:若称第一次时天平显示两边一样重,那称剩下的2个球就能找出那个重一点的球了;若天平显示一边重,那就从天平重的那边的3个球中随机取2个球放到天平上称,如果天平显示一边重,那重的那边的那个球就是那个重一点的球,如果天平显示两边一样重,那剩下的没称的那个球就是那个重一点的球。

2、输出1-100之间的素数。 答:

import java.lang.Math;

/**
 * 输出1-100之间的素数
 */
public class PrimeNumber {
    public static void main(String[] args) {
        boolean flag = true;
        for (int i = 2; i <= 100; ++i) {
            flag = true;
            for (int j = 2; j <= Math.sqrt(i); ++j) {
                // 若发现有被整除的,就是不是素数,马上标记为false,并且中断循环
                if (i % j == 0) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                System.out.println(i);
            }
        }
    }
}

运行结果: 1-100素数

3、输入1个整数,输出该数二进制表示中1的个数。 答:

import java.util.Scanner;

/**
 * 输入一个整数,输出该数二进制表示中1的个数
 */
public class StatisticsOne {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("please input an integer:");
        int n = scanner.nextInt();
        scanner.close();
        // 若是负数,就转换为正数
        if (n < 0) {
            n = n / (-1);
        }
        int m = 0;
        // 通过“除2取余,逆序排列法”算出整数对应的二进制数
        // 每求出一个位数的数字就判断是否为1
        // 求出的二进制数是倒过来的,但这里只需统计二进制数中1的个数,倒过来没影响
        do {
            // 对2求余一次就得到一个二进制位数的数字
            int rem = n % 2;
            if (rem == 1) {
                ++m;
            }
            // 不断地除2,直到除2到商为0
            n = n / 2;
        } while (n > 0);
        System.out.println("this integer contains " + m + " integer one");
    }
}

运行结果: 统计整数二进制中1的个数

用计算器验证下84516645, 验证整数二进制中1的个数

验证整数二进制中1的个数2

刚好13个1,正确。

4、写出题目中的运行结果。

public class HelloB extends HelloA {
    public HelloB() {
        System.out.println("HelloB");
    }

    {
        System.out.println("I'm B class");
    }

    static {
        System.out.println("static B");
    }

    public static void main(String[] args) {
        System.out.println("-------main start-------");
        new HelloB();
        new HelloB();
        System.out.println("-------main end-------");
    }
}

class HelloA {
    public HelloA() {
        System.out.println("HelloA");
    }

    {
        System.out.println("I'm A class");
    }

    static {
        System.out.println("static A");
    }
}

答: HelloB输出

分析: 1)首先加载两个类的静态代码块,先加载父类HelloA的静态代码块,再加载子类HelloB的静态代码块。 2)执行System.out.println("-------main start-------")。 3)执行new HelloB(),会先加载HelloB的父类HelloA,所以会先加载HelloA的代码块System.out.println("I'm A class"),然后加载HelloA的构造方法System.out.println("HelloA");之后才是加载HelloB的代码块System.out.println("I'm B class"),然后加载HelloB的构造方法System.out.println("HelloB")。 4)再次执行new HelloB(),同3)。 5)执行System.out.println("-------main end-------")。

5、写出题目中的运行结果。

public class Test {
    static {
        int x = 5;
    }

    static int x, y;

    public static void main(String[] args) {
        x--;
        myMethod();
        System.out.println(x + y++ + x);
        System.out.println(y);
    }

    public static void myMethod() {
        y = x++ + ++x;
    }
}

答: Test输出

分析: 1)首先加载Test类的静态块int x = 5,然后加载静态变量static int x, y,这时x的值被重置为0,y的值被初始化为0。 2)然后执行x--,x的值为-1。 3)再执行myMethod方法,第一个加数x++是后置自增的,自增是后执行的,先确定第一个加数的值为-1,然后x的值再自增为0;第二个加数++x是前置自增的,自增是先执行的,先自增x的值为1,然后确定第二个加数的值为1;所以经过myMethod方法后,y的值是-1+1=0,x的值是1。 4)执行System.out.println(x + y++ + x),第一个加数和第三个加数的值都为x=1;第二个加数y++是后置自增的,自增是后执行的,先确定第二个加数的值为0,然后y的值再自增为1;所以是1+0+1=2,输出2,但这时的y的值是1了。 5)执行System.out.println(y),输出1。

6、写出题目中的运行结果。 第一小题:

public class TestString {
    public static void test(String str) {
        str = "World";
    }

    public static void main(String[] args) {
        String string = "Hello";
        test(string);
        System.out.println(string);
    }
}

第二小题:

public class TestStringBuffer {
    public static void test(StringBuffer str) {
        str.append(", World!");
    }

    public static void main(String[] args) {
        StringBuffer string = new StringBuffer("Hello");
        test(string);
        System.out.println(string);
    }
}

答: 第一小题: TestString输出

分析:没有改变原来的值,因为String是final对象,值被赋予后就不能变的了,当执行String string = "Hello"时,常量池生成字符串"Hello",并且string指向字符串"Hello";当执行test(string)方法时,通过引用传递传递对象的引用给函数参数,函数参数得到一份对象引用的备份,也就是String str = string,一开始时str还是指向字符串"Hello",但执行了str = "World"后,常量池就生成新字符串"World"并且str指向了新字符串"World";当执行System.out.println(string)时,jvm会去寻找string指向的字符串对象,string指向的字符串还是原来的"Hello",所以输出的是"Hello"。

第二小题: TestStringBuilder输出

分析:改变了原来的值,因为string指向的还是同一个对象,只是这个对象的内容被添加了新的内容。这里和上一题的区别就是,上一题因为执行了str = "World"故函数里的引用指向了另一个对象,而这里执行str.append(", World!")操作的还是同一个对象。

7、用两个栈来实现队列的功能offer(入队)和poll(出队)。 答:

import java.util.Stack;

/**
 * 用两个栈实现一个队列
 */
public class StackQueue<T> {
    Stack<T> stack1 = new Stack<>();
    Stack<T> stack2 = new Stack<>();

    public static void main(String[] args) {
        StackQueue<Integer> stackQueue = new StackQueue<>();
        stackQueue.offer(1);
        stackQueue.offer(2);
        stackQueue.offer(3);
        System.out.println(stackQueue.poll());
        System.out.println(stackQueue.poll());
        stackQueue.offer(4);
        stackQueue.offer(5);
        System.out.println(stackQueue.poll());
        System.out.println(stackQueue.poll());
        System.out.println(stackQueue.poll());
    }

    /**
     * 插入元素
     */
    public boolean offer(T element) {
        stack1.push(element);
        return true;
    }

    /**
     * 取出元素
     */
    public T poll() {
        // 若stack2为空,把stack1的元素全部pop出,stack1每pop出一个就push一个到stack2,直到stack1为空
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        }
        // 若stack2不为空,直接pop出元素
        return stack2.pop();
    }
}

运行结果: StackQueue输出

分析:第一个stack存入队的元素,因为只能用栈来解决,这样顺序就反了,所以就要引入第二个stack。若要出队元素,就从第二个stack取出(pop);若发现第二个stack没有元素,就从第一个stack取元素出来放到第二个stack,每从第一个stack取出(pop)一个元素就放进(push)第二个stack,直到第一个stack的元素被取完,再从第二个stack取出(pop)元素。

8、单例模式怎么写? 答: 懒汉模式:

/**
 * 单例模式(懒汉模式),即内部对象一开始是null
 */
public class SingletonLazy {
    private static SingletonLazy instance;

    private SingletonLazy() {
    }

    public synchronized static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

饿汉模式:

/**
 * 单例模式(饿汉模式),即内部对象一开始就实例化了
 */
public class SingletonHungary {
    private static SingletonHungary instance = new SingletonHungary();

    private SingletonHungary() {
    }

    public synchronized static SingletonHungary getInstance() {
        return instance;
    }
}

运行测试: 单例模式测试

分析:懒汉模式是懒加载实例,初始化时实例引用还是null,等到需要时实例引用才指向实例;饿汉模式是预加载实例,初始化时实例引用就指向了实例。

9、请用1条sql语句通过下表查出以下结果。 game表:

  time         result
  2018-07-27   负
  2018-07-27   胜
  2018-07-27   胜
  2018-07-28   胜
  2018-07-28   负

查询结果如下:

  时间          胜   负
  2018-07-27    2    1
  2018-07-28    1    1

答: 方法一(sum函数法):

select time as '时间', sum(case result when '胜' then 1 else 0 end) as ' 胜', sum(case result when '负' then 1 else 0 end) as '负' from game group by time;

运行结果: 查game表的胜负次数(sum函数法)

分析:先以time为条件对game表分组,再用sum函数对每组结果进行一定的统计,sum(case result when '胜' then 1 else 0 end)即以result为条件,当result的值为'胜'时就是1,否则就是0,通过这样的方式就能统计出每组中胜的次数,同样地sum(case result when '负' then 1 else 0 end)统计出每组中负的次数。

方法二(子查询法):

select time as '时间', (select count(*) from game where time = t.time and result = '胜') as '胜', (select count(*) from game where time = t.time and result = '负') as '负' from game as t group by time;

运行结果: 查game表的胜负次数(子查询法)

分析:先以time为条件对game表分组,并且给game表命名为t表,'胜'这一列通过一个子查询决定,子查询再查一次game表,result的值要等于'胜',time的值要等于t表的time,当t.time=2018-07-27时,就是select count() from game where time = '2018-07-27' and result = '胜',正好是2018-07-27的胜的次数;当t.time=2018-07-28时,就是select count() from game where time = '2018-07-28' and result = '胜',正好是2018-07-28的胜的次数;'负'这一列同样这样查询出来。

方法三(联表法):

select a.time as '时间', a.胜, b.负 from (select time, count(*) as '胜' from game where result = '胜' group by time) a join (select time, count(*) as '负' from game where result = '负' group by time) b on a.time = b.time;

运行结果: 查game表的胜负次数(联表法)

分析:其实这里就是把结果拆成两块,最后再横向合并在一起。select time, count() as '胜' from game where result = '胜' group by time这句先筛选出game表中所有'胜'的结果,再以time为条件分组,统计出来就是每组中胜的次数;select time, count() as '负' from game where result = '负' group by time这句先筛选出game表中所有'负'的结果,再以time为条件分组,统计出来就是每组中负的次数;最后通过join on以time为条件把两表合并在一起,这样每组中胜的次数和每组中负的次数都有了。

以下做法是错误的:

select time as '时间', count(result = '胜') as '胜', count(result = '负') as '负' from game group by time;

运行结果: 查game表的胜负次数(错误做法)

分析:因为count只能统计某一列的个数,还不能用列的值作为条件,这里的count(result = '胜')估计会被解释成count(result)。

10、Java中List和Array是怎样相互转换的? 答:

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

/**
 * List和Array的互相转换
 */
public class ListAndArray {
    public static void main(String[] args) {
        listToArray();
        arrayToList();
        arrayToList2();
        arrayToList3();
    }

    /**
     * List转Array(利用List的toArray方法实现List转Array)
     */
    private static void listToArray() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        Integer[] array = list.toArray(new Integer[list.size()]);
        for (int i = 0; i < array.length; ++i) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }

    /**
     * Array转List方法1(直接使用Arrays的asList方法实现Array转List)
     * 该方法存在一定的弊端,返回的list是Arrays里面的一个静态内部类
     * 该类并未实现add、remove方法,因此在使用时存在局限性
     * 该ArrayList并非java.util.ArrayList
     */
    private static void arrayToList() {
        Integer[] array = {4, 5, 6};
        List<Integer> list = Arrays.asList(array);
        System.out.println(list);
    }

    /**
     * Array转List方法2(利用ArrayList的构造方法来实现Array转List)
     * (最简洁的方法)
     */
    private static void arrayToList2() {
        Integer[] array = {7, 8, 9};
        List<Integer> list = new ArrayList<>(Arrays.asList(array));
        System.out.println(list);
    }

    /**
     * Array转List方法3(利用Collections的addAll方法实现Array转List)
     */
    private static void arrayToList3() {
        Integer[] array = {10, 11, 12};
        List<Integer> list = new ArrayList<>(array.length);
        Collections.addAll(list, array);
        System.out.println(list);
    }
}

运行结果: List和Array的相互转换输出

个人微信公众号

微信公众号:纯洁的技术分享(chunjie_tech)

纯洁的技术分享