Home  >  Article  >  Java  >  Analysis of several common Java interview questions for autumn recruitment

Analysis of several common Java interview questions for autumn recruitment

青灯夜游
青灯夜游forward
2018-10-23 16:44:152421browse

This article brings you an analysis of several common autumn recruitment java interview questions. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Preface

Only a bald head can become strong

Redis is still watching, today I will share my autumn recruitment Some interview questions I have seen (encountered) (relatively common ones)

0. Final keywords

Briefly talk about the final key words What can the word final be used to modify?

I encountered this question in a real interview. I didn’t answer it very well at the time. Let’s sort it out now.

Final can modify classes, methods, and member variables

  • When final modifies a class, it means that the class cannot be inherited

  • When final modifies a method, it means that the method cannot be overridden

    • In the early days, the final modified method may be used, and the compiler targets All calls to these methods are converted into inline calls, which improves efficiency (but now we generally don’t care about this, compilers and JVM are getting smarter)

  • When final modifies a member variable, there are two situations:

    • If it is a basic type, it means that the value represented by this variable can never change ( cannot be reassigned)!

    • If the modification is a reference type, the reference of the variable cannot be changed, but the content of the object represented by the reference is variable!

It is worth mentioning: Not that member variables modified by final must be compile-time constants. For example, we can write code like this: private final int java3y = new Randon().nextInt(20);

Do you have such programming experience in writing code in a compiler? When doing this, you must declare the variable as final in a certain scenario, otherwise the compilation will fail. Why is it designed this way?

This situation may occur when writing anonymous inner classes. Variables that anonymous inner classes may use:

  • External class instance variables

  • Local variables within the method or scope

  • Parameters of the method

class Outer {


    // string:外部类的实例变量
    String string = "";


    //ch:方法的参数
    void outerTest(final char ch) {

        // integer:方法内局部变量
        final Integer integer = 1;
        new Inner() {
            void innerTest() {
                System.out.println(string);
                System.out.println(ch);
                System.out.println(integer);
            }
        };

    }
    public static void main(String[] args) {
        new Outer().outerTest(' ');
    }
    class Inner {
    }
}

We can see: Local variables and method parameters within a method or scope must be modified using the final keyword (under jdk1.7)!

Analysis of several common Java interview questions for autumn recruitment

If you switch to the jdk1.8 compilation environment, you can compile it~

Analysis of several common Java interview questions for autumn recruitment

Let’s talk about it first The reason why the declaration is final is shown below: In order to maintain the consistency of internal and external data

  • Java only implements closures in the form of capture-by-value, which is anonymous The function will re-copy the free variable inside the function, and then there will be two copies of data outside the function and inside the function.

  • To achieve internal and external data consistency, we can only require two variables to remain unchanged. Before JDK8, it was required to use final modification. JDK8 is smarter and can use effectively final method.

    ##The inner class stores a
  • reference to the instance of the outer class
. The inner class accesses the member variables of the outer class through this reference.

The internal class modifies the referenced data, and the external classgets the same data again

!
  • Then when you try to change the value of an external basic type variable in an anonymous inner class, or change the pointing of an external reference variable, On the surface

    It seems like everything is successful, but
  • will not actually affect the external variables
. Therefore, in order to prevent itself from looking so strange, Java added this final restriction.

Reference materials:

#java Why are parameter references of anonymous inner classes final? https://www.zhihu.com/question/21395848

  • 1. The difference between char and varchar

char has a fixed length, and varchar has a variable length. varchar: If the original storage location cannot meet its storage needs

, some additional operations are required. Depending on the storage engine, some will use the
    split mechanism
  1. , and some will use

    Paging mechanism.

  2. char的存储方式是:英文字符占1个字节,汉字占用2个字节;varchar的存储方式是:英文和汉字都占用2个字节,两者的存储数据都非unicode的字符数据。

  3. char是固定长度,长度不够的情况下,用空格代替。varchar表示的是实际长度的数据类型

选用考量:

  • 如果字段长度较和字符间长度相近甚至是相同的长度,会采用char字符类型

二、多个线程顺序打印问题

三个线程分别打印A,B,C,要求这三个线程一起运行,打印n次,输出形如“ABCABCABC....”的字符串。

原博主给出了4种方式,我认为信号量这种方式比较简单和容易理解,我这里粘贴一下(具体的可到原博主下学习)..

public class PrintABCUsingSemaphore {
    private int times;
    private Semaphore semaphoreA = new Semaphore(1);
    private Semaphore semaphoreB = new Semaphore(0);
    private Semaphore semaphoreC = new Semaphore(0);

    public PrintABCUsingSemaphore(int times) {
        this.times = times;
    }

    public static void main(String[] args) {
        PrintABCUsingSemaphore printABC = new PrintABCUsingSemaphore(10);

        // 非静态方法引用  x::toString   和() -> x.toString() 是等价的!
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();

        /*new Thread(() -> printABC.printA()).start();
        new Thread(() -> printABC.printB()).start();
        new Thread(() -> printABC.printC()).start();
*/
    }

    public void printA() {
        try {
            print("A", semaphoreA, semaphoreB);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printB() {
        try {
            print("B", semaphoreB, semaphoreC);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printC() {
        try {
            print("C", semaphoreC, semaphoreA);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void print(String name, Semaphore current, Semaphore next)
            throws InterruptedException {
        for (int i = 0; i < times; i++) {
            current.acquire();
            System.out.print(name);
            next.release();
        }
    }
}
  • 作者:cheergoivan

  • 链接:https://www.jianshu.com/p/40078ed436b4

  • 來源:简书

2018年9月14日18:15:36 yy笔试题就出了..

三、生产者和消费者

在不少的面经都能看到它的身影哈~~~基本都是要求能够手写代码的。

其实逻辑并不难,概括起来就两句话:

  • 如果生产者的队列满了(while循环判断是否满),则等待。如果生产者的队列没满,则生产数据并唤醒消费者进行消费。

  • 如果消费者的队列空了(while循环判断是否空),则等待。如果消费者的队列没空,则消费数据并唤醒生产者进行生产。

基于原作者的代码,我修改了部分并给上我认为合适的注释(下面附上了原作者出处,感兴趣的同学可到原文学习)

生产者:

import java.util.Random;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

public class Producer implements Runnable {

    // true--->生产者一直执行,false--->停掉生产者
    private volatile boolean isRunning = true;

    // 公共资源
    private final Vector sharedQueue;

    // 公共资源的最大数量
    private final int SIZE;

    // 生产数据
    private static AtomicInteger count = new AtomicInteger();

    public Producer(Vector sharedQueue, int SIZE) {
        this.sharedQueue = sharedQueue;
        this.SIZE = SIZE;
    }

    @Override
    public void run() {
        int data;
        Random r = new Random();

        System.out.println("start producer id = " + Thread.currentThread().getId());
        try {
            while (isRunning) {
                // 模拟延迟
                Thread.sleep(r.nextInt(1000));

                // 当队列满时阻塞等待
                while (sharedQueue.size() == SIZE) {
                    synchronized (sharedQueue) {
                        System.out.println("Queue is full, producer " + Thread.currentThread().getId()
                                + " is waiting, size:" + sharedQueue.size());
                        sharedQueue.wait();
                    }
                }

                // 队列不满时持续创造新元素
                synchronized (sharedQueue) {
                    // 生产数据
                    data = count.incrementAndGet();
                    sharedQueue.add(data);

                    System.out.println("producer create data:" + data + ", size:" + sharedQueue.size());
                    sharedQueue.notifyAll();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupted();
        }
    }

    public void stop() {
        isRunning = false;
    }
}

消费者:

import java.util.Random;
import java.util.Vector;

public class Consumer implements Runnable {

    // 公共资源
    private final Vector sharedQueue;

    public Consumer(Vector sharedQueue) {
        this.sharedQueue = sharedQueue;
    }

    @Override
    public void run() {

        Random r = new Random();

        System.out.println("start consumer id = " + Thread.currentThread().getId());
        try {
            while (true) {
                // 模拟延迟
                Thread.sleep(r.nextInt(1000));

                // 当队列空时阻塞等待
                while (sharedQueue.isEmpty()) {
                    synchronized (sharedQueue) {
                        System.out.println("Queue is empty, consumer " + Thread.currentThread().getId()
                                + " is waiting, size:" + sharedQueue.size());
                        sharedQueue.wait();
                    }
                }
                // 队列不空时持续消费元素
                synchronized (sharedQueue) {
                    System.out.println("consumer consume data:" + sharedQueue.remove(0) + ", size:" + sharedQueue.size());
                    sharedQueue.notifyAll();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            Thread.currentThread().interrupt();
        }
    }
}

Main方法测试:

import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test2 {


    public static void main(String[] args) throws InterruptedException {

        // 1.构建内存缓冲区
        Vector sharedQueue = new Vector();
        int size = 4;

        // 2.建立线程池和线程
        ExecutorService service = Executors.newCachedThreadPool();
        Producer prodThread1 = new Producer(sharedQueue, size);
        Producer prodThread2 = new Producer(sharedQueue, size);
        Producer prodThread3 = new Producer(sharedQueue, size);
        Consumer consThread1 = new Consumer(sharedQueue);
        Consumer consThread2 = new Consumer(sharedQueue);
        Consumer consThread3 = new Consumer(sharedQueue);
        service.execute(prodThread1);
        service.execute(prodThread2);
        service.execute(prodThread3);
        service.execute(consThread1);
        service.execute(consThread2);
        service.execute(consThread3);

        // 3.睡一会儿然后尝试停止生产者(结束循环)
        Thread.sleep(10 * 1000);
        prodThread1.stop();
        prodThread2.stop();
        prodThread3.stop();

        // 4.再睡一会儿关闭线程池
        Thread.sleep(3000);

        // 5.shutdown()等待任务执行完才中断线程(因为消费者一直在运行的,所以会发现程序无法结束)
        service.shutdown();


    }
}
  • 作者:我没有三颗心脏

  • 链接:https://www.jianshu.com/p/3f0cd7af370d

  • 來源:简书

另外,上面原文中也说了可以使用阻塞队列来实现消费者和生产者。这就不用我们手动去写wait/notify的代码了,会简单一丢丢。可以参考:

  • 使用阻塞队列解决生产者-消费者问题:https://www.cnblogs.com/chenpi/p/5553325.html

四、算法[1]

我现在需要实现一个栈,这个栈除了可以进行普通的push、pop操作以外,还可以进行getMin的操作,getMin方法被调用后,会返回当前栈的最小值,你会怎么做呢?你可以假设栈里面存的都是int整数

解决方案:

  • 使用一个min变量来记住最小值,每次push的时候,看看是否需要更新min。

    • 如果被pop出去的是min,第二次pop的时候,只能遍历一下栈内元素,重新找到最小值。

    • 总结:pop的时间复杂度是O(n),push是O(1),空间是O(1)

  • 使用辅助栈来存储最小值。如果当前要push的值比辅助栈的min值要小,那在辅助栈push的值是最小值

    • 总结:push和pop的时间复杂度都是O(1),空间是O(n)。典型以空间换时间的例子。

import java.util.ArrayList;
import java.util.List;

public class MinStack {

    private List<Integer> data = new ArrayList<Integer>();
    private List<Integer> mins = new ArrayList<Integer>();

    public void push(int num) {
        data.add(num);
        if (mins.size() == 0) {
            // 初始化mins
            mins.add(num);
        } else {
            // 辅助栈mins每次push当时最小值
            int min = getMin();
            if (num >= min) {
                mins.add(min);
            } else {
                mins.add(num);
            }
        }
    }

    public int pop() {
        // 栈空,异常,返回-1
        if (data.size() == 0) {
            return -1;
        }
        // pop时两栈同步pop
        mins.remove(mins.size() - 1);
        return data.remove(data.size() - 1);
    }

    public int getMin() {
        // 栈空,异常,返回-1
        if (mins.size() == 0) {
            return -1;
        }
        // 返回mins栈顶元素
        return mins.get(mins.size() - 1);
    }

}

继续优化:

  • 栈为空的时候,返回-1很可能会带来歧义(万一人家push进去的值就有-1呢?),这边我们可以使用Java Exception来进行优化

  • 算法的空间优化:上面的代码我们可以发现:data栈和mins栈的元素个数总是相等的,mins栈中存储几乎都是最小的值(此部分是重复的!)

    • 所以我们可以这样做:当push的时候,如果比min栈的值要小的,才放进mins栈。同理,当pop的时候,如果pop的值是mins的最小值,mins才出栈,否则mins不出栈!

    • 上述做法可以一定避免mins辅助栈有相同的元素!

但是,如果一直push的值是最小值,那我们的mins辅助栈还是会有大量的重复元素,此时我们可以使用索引(mins辅助栈存储的是最小值索引,非具体的值)!

最终代码:

import java.util.ArrayList;
import java.util.List;


public class MinStack {

    private List<Integer> data = new ArrayList<Integer>();
    private List<Integer> mins = new ArrayList<Integer>();

    public void push(int num) throws Exception {
        data.add(num);
        if(mins.size() == 0) {
            // 初始化mins
            mins.add(0);
        } else {
            // 辅助栈mins push最小值的索引
            int min = getMin();
            if (num < min) {
                mins.add(data.size() - 1);
            }
        }
    }

    public int pop() throws Exception {
        // 栈空,抛出异常
        if(data.size() == 0) {
            throw new Exception("栈为空");
        }
        // pop时先获取索引
        int popIndex = data.size() - 1;
        // 获取mins栈顶元素,它是最小值索引
        int minIndex = mins.get(mins.size() - 1);
        // 如果pop出去的索引就是最小值索引,mins才出栈
        if(popIndex == minIndex) {
            mins.remove(mins.size() - 1);
        }
        return data.remove(data.size() - 1);
    }

    public int getMin() throws Exception {
        // 栈空,抛出异常
        if(data.size() == 0) {
            throw new Exception("栈为空");
        }
        // 获取mins栈顶元素,它是最小值索引
        int minIndex = mins.get(mins.size() - 1);
        return data.get(minIndex);
    }

}

参考资料:

  • [Interview Site] How to implement a stack that can obtain the minimum value?

  • Author: channingbreeze Source: Internet Reconnaissance

5. HashMap under multi-threading

As we all know, HashMap is not a thread-safe class. But you may be asked during the interview: What will happen if HashMap is used in a multi-threaded environment? ?

Conclusion:

  • Multi-threaded data inconsistency (loss of data) caused by put()

  • resize()The operation will result in a circular linked list

    • jdk1.8 has solved the problem of circular chain (declaring two pairs of pointers and maintaining two Linked list)

  • fail-fast mechanism, deleting/modifying the current HashMap at the same time will throw a ConcurrentModificationException exception

References :

  • Talk about the manifestation of HashMap thread insecurity: http://www.importnew.com/22011.html

  • jdk1.8 Hashmap multi-thread put will not cause an infinite loop: https://blog.csdn.net/qq_27007251/article/details/71403647

## 6. Spring and Springboot Differences

1. SpringBoot is able to create independent Spring applications
2. Simplify Spring configuration
  • Spring Due to its cumbersome configuration, it was once called "configuration hell". Various XML and Annotation configurations are dazzling, and if something goes wrong, it is difficult to find out the reason.

  • The Spring Boot project is to solve the problem of tedious configuration and maximize the implementation of convention over configuration (

    Convention is greater than configuration).

    • Provides a series of dependency packages to make other tasks available out of the box. It has a built-in 'Starter POM', which highly encapsulates the project construction.

      , to maximize and simplify the configuration of project construction.

    3. Embedded Tomcat, Jetty container, no need to deploy WAR package

7. G1 and CMSThe design goal of the G1 collector is to replace the CMS collector. Compared with CMS, it performs better in the following aspects:

    G1 is a
  • Organize the memory

    The garbage collector of the process, will not produce a lot of memory fragments.

    • CMS uses a mark-and-sweep garbage collection algorithm, which may produce a lot of memory fragments
    G1 Stop The World (STW) is more controllable. G1 adds a
  • prediction mechanism

    to the pause time. Users can specify the expected pause time.

  • Extended reading:

    G1 Garbage Collector Introduction: https://javadoop.com/post/g1

8. Massive data solutionsThe processing of massive data is also a knowledge point that is often tested, both in interviews and written tests. is relatively common. Fortunately, I read the following article and excerpted some ideas for solving massive data:

    Bloom filter Bloom filter
  • ##Applicable Scope: It can be used to implement a data dictionary, determine the weight of data, or find the intersection of a set
    • Hashing
  • Scope of application: basic data structures for quick search and deletion, usually the total amount of data required can be put into memory
    • bit-map
  • Scope of application: It can quickly search, determine and delete data. Generally speaking, the data range is less than 10 times of int
    • Heap
  • Scope of application: The first n is large for massive data, and n is relatively small, and the heap can be put into memory
    • Double-layer bucket division- ---In fact, it is essentially the idea of ​​[divide and conquer], focusing on the technique of "divide"!
  • Applicable scope: kth largest, median, non-repeating or repeating numbers
    • Database Index
  • Scope of application: large amount of data addition, deletion and modification query
    • Inverted index(Inverted index)
  • Applicable scope: search engine, keyword query
    • External sorting
  • Scope of application: Big data sorting, deduplication
    • trie tree
    • Scope of application: Large amount of data, many repetitions, but small data types can be put into memory

  • Distributed processing mapreduce

    • Scope of application: large amount of data, but small data types can be put into memory

For details, please refer to the original text:

  • Summary of ten massive data processing interview questions and ten methods: https://blog.csdn.net/v_JULY_v/article/details/6279498

9. Idempotence

9.1HTTP Idempotence

Yesterday I took a set of written test questions, the difference between get/post in classic HTTP. I came back to search today and found that it was a little different from my previous understanding.

If a person does web development from the beginning, it is very likely that
the use of HTML on the HTTP protocol will be regarded as the only reasonable use of the HTTP protocol. Therefore, we have made the mistake of overgeneralizing
Simply speaking from the HTTP protocol specification, the

GET/POST distinction we summarized before may be useless. (But after reading the entire article, I personally think: If there is a GET/POST difference in the interview, it is better to answer in the Web development scenario by default. This may be what the interviewer wants Answer)

Reference:

  • What is the difference between GET and POST? And why most answers on the Internet are wrong. http://www.cnblogs.com/nankezhishi/archive/2012/06/09/getandpost.html

I also learned the concept of idempotence, so I also tried it Take notes~~~

Methods can also have the property of “idempotence” in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request .
By definition, the idempotence of HTTP methods is

means that one and multiple requests for a resource should have the same side effects.

  • Here is a brief explanation of the meaning of "side effects": it means that after you send a request, the resource status on the

    website has not been modified, that is, the request is considered It has no side effects

HTTP

GET/POST/DELETE/PUT method idempotence:

  • GET is idempotent and has no side effects

    • For example, I want to get the order with order ID 2:

      http://localhost/order /2, use GET to obtain multiple times, the order (resource) with ID 2 is and will not change!

  • DELETE/PUT is idempotent and has side effects

    • For example, I want Delete or update the order with ID 2:

      http://localhost/order/2, use PUT/DELETE to request multiple times, this ID is 2 Order (resource) will only change once (it has side effects)! But if you continue to refresh the request multiple times, the final status of order ID 2 is the same

  • ##POST

    is non-idempotent , with side effects

    • For example, I want to create an order named 3y:
    • http://localhost/order

      , use POST Multiple requests, at this time may create multiple orders named 3y , this order (resource) will change multiple times, the resource status will change each time requested !

    Digression:

The HTTP protocol itself is a

resource-oriented application layer protocol
, but the actual use of the HTTP protocol There are two different methods on the Internet: one is RESTful, which treats HTTP as an application layer protocol and more faithfully abides by the various regulations of the HTTP protocol ( makes full use of HTTP methods ); Another is SOA, which does not completely regard HTTP as an application layer protocol, but uses HTTP protocol as a transport layer protocol, and then builds its own application on top of HTTP Layer protocolReference materials:

    Understanding HTTP idempotence http://www.cnblogs.com/weidagang2046/archive/2011/06/04/2063696. html#!comments
  • How to understand the idempotence of RESTfulhttp://blog.720ui.com/2016/restful_idempotent/
  • Shallow Talking about the difference between Get and Post in HTTP http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html
  • ## POST and GET requests in HTTP requests The difference? https://www.zhihu.com/question/27622127/answer/37676304
  • ##9.2 Interface Idempotence

When checking the information, you can find that many blogs talk about the idempotence of interfaces. From the above we can also see that the POST method is non-idempotent. But we can use some means to make the interface of the POST method idempotent.

Having said so much, what are the benefits of designing an idempotent interface? ? ? ?

Give an example of the disadvantages of non-idempotence:

  • 3y When I was a freshman, I had to take physical education classes, but the school’s class taking system was terrible. Sucks (very high latency). I wanted to grab the class, so I opened more than 10 Chrome tabs to grab it (even if one Chrome tab crashed, I still had another Chrome tab available). I want to grab a table tennis ball or a badminton ball.

  • When the time comes to grab the class, I take turns clicking on the table tennis or badminton I want to grab. If the system is not well designed, the request is non-idempotent (or the transaction is not well controlled), and my hand speed is fast enough & the network is good enough, then I may have grabbed many table tennis or badminton lessons. (This is unreasonable. One person can only choose one course, but I grabbed multiple or repeated courses)

  • The application scenarios involving the mall may be: The user has placed multiple duplicate orders

If my class grabbing interface was idempotent, this problem would not occur. Because idempotence means that multiple requests for a resource should have the same side effects.

  • There will only be one record at most in the database background, so there is no such thing as grabbing multiple courses.

To put it bluntly, the purpose of designing the idempotent interface is to

prevent repeated submissions (multiple duplicate data in the database)!

Some bloggers on the Internet also shared several common solutions to solve repeated submissions:

  1. Synchronization lock (single thread, may fail in the cluster)

  2. Distributed locks such as redis (complex implementation)

  3. Business fields plus unique constraints (simple)

  4. Token table unique constraint (simple recommendation)---->A means to implement idempotent interface

  5. ##mysql's insert ignore or on duplicate key update ( Simple)
  6. Shared lock ordinary index (simple)
  7. Use MQ or Redis expansion (queuing)
  8. Other solutions such as multi-version control MVCC optimistic locking pessimistic locking state machine, etc. .
  9. Reference material:

    Distributed system interface idempotence http://blog.brucefeng.info/post/api-idempotent
  • How to avoid placing duplicate orders https://www.jianshu.com/p/e618cc818432
  • Summary on interface idempotence https: //www.jianshu.com/p/6eba27f8fb03
  • Use database unique keys to achieve transaction idempotence http://www.caosh.me/be-tech/idempotence-using- unique-key/
  • API interface non-idempotent issues and using redis to implement simple distributed locks https://blog.csdn.net/rariki/article/details/50783819
  • Finally

If there is any misunderstanding above, or if there is a better way to understand it, I hope you will not hesitate to leave a message in the comment area. Make progress together!

The above is the detailed content of Analysis of several common Java interview questions for autumn recruitment. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete