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)!
If you switch to the jdk1.8 compilation environment, you can compile it~
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 aThe 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, butReference materials:
#java Why are parameter references of anonymous inner classes final? https://www.zhihu.com/question/21395848char 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 thePaging mechanism.
char的存储方式是:英文字符占1个字节,汉字占用2个字节;varchar的存储方式是:英文和汉字都占用2个字节,两者的存储数据都非unicode的字符数据。
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 applications2. Simplify Spring configurationConvention is greater than configuration).
, to maximize and simplify the configuration of project construction.
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:
The garbage collector of the process, will not produce a lot of memory fragments.
to the pause time. Users can specify the expected pause time.
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:
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.
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 overgeneralizingSimply 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)
means that one and multiple requests for a resource should have the same side effects.
website has not been modified, that is, the request is considered It has no side effects
GET/POST/DELETE/PUT method idempotence:
GET is idempotent and has no side effects
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
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
is non-idempotent , with side effects
, 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 !
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:
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 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. prevent repeated submissions (multiple duplicate data in the database)! Token table unique constraint (simple recommendation)---->A means to implement idempotent interface 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! POST
method is non-idempotent. But we can use some means to make the interface of the POST
method idempotent.
To put it bluntly, the purpose of designing the idempotent interface is to
Distributed system interface idempotence http://blog.brucefeng.info/post/api-idempotent
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!