后端面试笔记(二)

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

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的相互转换输出
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容