写点什么

你曾遇到过哪些大厂的 -Java- 面试?,mysql 入门书籍哪本好

用户头像
极客good
关注
发布于: 刚刚

21.Spring 怎样解决循环依赖的问题 22.dispatchServlet 怎样分发任务的 23.mysql 给离散度低的字段建立索引会出现什么问题,具体说下原因


其它经常问的 HashMap 底层实现原理,常规的多线程问题考的太多了,没什么新意就不写了


平时不能光抱着应用 Java 的目的去学习,要深入了解每个知识点背后底层实现原理,为什么这么设计,比如问烂的 HashMap 既然有 hash 进行排位还需要 equals()作用是什么?就这个问题照样能问倒一些人,所以一定要抠细节,真的把每个知识点搞懂


一时记起来的就是这 23 个吧,其它想起来后续补充,答案我这几天写个大纲吧


--2019.1.23 更新(加上了:写在前面的话;问题解答大纲;一些新面试题;)


转载请注明出处,今天发现居然有人复制我的题目到其他网站,这本倒是没什么,关键是复制的题目当时我还没作答,看到里面存在明显错误的解答,这可能会给初学者带来困扰。


感谢各位知友的关注,尤其感谢有人在评论区还作出有心的回答!


写在前面的话:


1.面试主要分为两块:一块是考查工程师对基础知识(包括了技术广度、深度、对技术的热情度等)的掌握程度,因为基础知识决定了一个技术人员发展的上限;另一块是考察工程师的工程能力,比如:做过哪些项目?遇到最难的问题怎样解决的?说说最有成就感的一项任务?工程能力是考察工程师当下能为公司带来的利益。其它考核方面:抗压性、合作能力...暂且不说。


2.Java 只是一门语言,即使是 Java 工程师也不能局限于 Java,要从面向对象语言本身,甚至从整个计算机体系,从工程实际出发看 Java。


3.很多知识在一般公司的开发中是用不到的,常有人戏称:“面试造火箭,工作拧螺丝”,但这只是通常情况下公司对程序员的标准——迅速产出,完成任务。个人观点:工程师为了自己职业的发展不能局限于公司对自己的要求,不能停留在应用层面,要能够很好地掌握基础知识,要多看源码,自己多实践,学成记得产出,比如多为开源社区贡献代码,帮助初学者指路等。


有没有发现一个有意思的事情:“面试造火箭,工作拧螺丝”的背后其实是考察者内心深处普遍都认可基础知识的重要性(这一点仅为个人观点,不展开讲哈)。


--以下为解答大纲,部分作了扩展


  1. 这题是一道思想题目,天天会碰到 private,有没有想过这个问题?谈谈对 java 设计的认识程度,主要抓住两点:1.java 的 private 修饰符并不是为了绝对安全性设计的,更多是对用户常规使用 java 的一种约束;2.从外部对对象进行常规调用时,能够看到清晰的类结构。

  2. 先说结论: 基类静态代码块,基类静态成员字段(并列优先级,按照代码中出现的先后顺序执行,且只有第一次加载时执行)——>派生类静态代码块,派生类静态成员字段(并列优先级,按照代码中出现的先后顺序执行,且只有第一次加载时执行)——>基类普通代码块,基类普通成员字段(并列优点级,按代码中出现先后顺序执行)——>基类构造函数——>派生类普通代码块,派生类普通成员字段(并列优点级,按代码中出现先后顺序执行)——>派生类构造函数代码验证:


class Log {public static String initLog(String log) { System.out.println(log);return null; }}


/**


  • 基类*/


class Base {static { System.out.println("Base Static Block 1"); }


private static String staticValue = Log.initLog("Base Static Fiels");


static { System.out.println("Base Static Block 2"); }


{ System.out.println("Base Normal Block 1"); }


private String value = Log.initLog("Base Normal Field");


{ System.out.println("Base Normal Block 2"); }


Base() { System.out.println("Base Constructor"); }}


/**


  • 派生类*/public class Derived extends Base {static { System.out.println("Static Block 1"); }


private static String staticValue = Log.initLog("Static Fiels");


static { System.out.println("Static Block 2"); }


{ System.out.println("Normal Block 1"); }


private String value = Log.initLog("Normal Field");


{ System.out.println("Normal Block 2"); }


Derived() { System.out.println("Derived Constructor"); }


/**



【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


线程*/


public static void main(String[] args) {Derived derived = new Derived();}


控制台结果输出:


Base Static Block 1Base Static FielsBase Static Block 2Static Block 1Static FielsStatic Block 2Base Normal Block 1Base Normal FieldBase Normal Block 2Base ConstructorNormal Block 1Normal FieldNormal Block 2Derived Constructor


第 2 题之前的回答欠妥,现已改正,在此非常感谢提出质疑的知友!


  1. 方法区是 jvm 规范里要求的,永久区是 Hotspot 虚拟机对方法区的具体实现,前者是规范,后者是实现方式。jdk1.8 作了改变。本题看看对方在思想层面对 jvm 的理解程度,很基础的一个题目。

  2. 文件中有几个类编译后就有几个 class 文件。

  3. 成员变量是可以不经初始化的,在类加载过程的准备阶段即可给它赋予默认值,但局部变量使用前需要显式赋予初始值,javac 不是推断不出不可以这样做,而是没有这样做,对于成员变量而言,其赋值和取值访问的先后顺序具有不确定性,对于成员变量可以在一个方法调用前赋值,也可以在方法调用后进行,这是运行时发生的,编译器确定不了,交给 jvm 去做比较合适。而对于局部变量而言,其赋值和取值访问顺序是确定的。这样设计是一种约束,尽最大程度减少使用者犯错的可能(假使局部变量可以使用默认值,可能总会无意间忘记赋值,进而导致不可预期的情况出现)。

  4. ReadWriteRock 读写锁,使用场景可分为读/读、读/写、写/写,除了读和读之间是共享的,其它都是互斥的,接着会讨论下怎样实现互斥锁和同步锁的, 想了解对方对 AQS,CAS 的掌握程度,技术学习的深度。

  5. Semaphore 拿到执行权的线程之间是否互斥,Semaphore、CountDownLatch、CyclicBarrier、Exchanger 为 java 并发编程的 4 个辅助类,面试中常问的 CountDownLatch CyclicBarrier 之间的区别,面试者肯定是经常碰到的, 所以问起来意义不大,Semaphore 问的相对少一些,有些知识点如果没有使用过还是会忽略,Semaphore 可有多把锁,可允许多个线程同时拥有执行权,这些有执行权的线程如并发访问同一对象,会产生线程安全问题。

  6. 写一个你认为最好的单例模式, 这题面试者都可能遇到过,也算是工作中最常遇到的设计模式之一,想考察面试者对经常碰到的题目的理解深度,单例一共有几种实现方式:饿汉、懒汉、静态内部类、枚举、双检锁,要是写了简单的懒汉式可能就会问:要是多线程情况下怎样保证线程安全呢,面试者可能说双检锁,那么聊聊为什么要两次校验,接着会问光是双检锁还会有什么问题,这时候基础好的面试者就会说了:对象在定义的时候加上 volatile 关键字,接下来会继续引申讨论下原子性和可见性、java 内存模型、类的加载过程。


其实没有最好,枚举方式、静态内部类、双检锁都是可以的,就想听下对不同的单例写法认识程度,写个双检锁的方式吧:


public class Singleton {private Singleton() {}


private volatile static Singleton instance;


public static Singleton getInstance() {if (null == instance) {synchronized (Singleton.class) {if (null == instance) {instance = new Singleton();}}}return instance;}


}


  1. B 树和 B+树,这题既问 mysql 索引的实现原理,也问数据结构基础,首先从二叉树说起,因为会产生退化现象,提出了平衡二叉树,再提出怎样让每一层放的节点多一些来减少遍历高度,引申出 m 叉树,m 叉搜索树同样会有退化现象,引出 m 叉平衡树,也就是 B 树,这时候每个节点既放了 key 也放了 value,怎样使每个节点放尽可能多的 key 值,以减少遍历高度呢(访问磁盘次数),可以将每个节点只放 key 值,将 value 值放在叶子结点,在叶子结点的 value 值增加指向相邻节点指针,这就是优化后的 B+树。然后谈谈数据库索引失效的情况,为什么给离散度低的字段(如性别)建立索引是不可取的,查询数据反而更慢,如果将离散度高的字段和性别建立联合索引会怎样,有什么需要注意的?

  2. 生产者消费者模式,synchronized 锁住一个 LinkedList,一个生产者,只要队列不满,生产后往里放,一个消费者只要队列不空,向外取,两者通过 wait()和 notify()进行协调,写好了会问怎样提高效率,最后会聊一聊消息队列设计精要思想及其使用。

  3. 写一个死锁,觉得这个问题真的很不错,经常说的死锁四个条件,背都能背上,那写一个看看,思想为:定义两个 ArrayList,将他们都加上锁 A,B,线程 1,2,1 拿住了锁 A ,请求锁 B,2 拿住了锁 B 请求锁 A,在等待对方释放锁的过程中谁也不让出已获得的锁。


public class DeadLock {public static void main(String[] args) {final List<Integer> list1 = Arrays.asList(1, 2, 3);final List<Integer> list2 = Arrays.asList(4, 5, 6);new Thread(new Runnable() {@Overridepublic void run() {synchronized (list1) {for (Integer i : list1) {System.out.println(i);}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (list2) {for (Integer i : list2) {System.out.println(i);}}}}}).start();


new Thread(new Runnable() {@Overridepublic void run() {synchronized (list2) {for (Integer i : list2) {System.out.println(i);}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (list1) {for (Integer i : list1) {System.out.println(i);}}}}}).start();


}}


  1. cpu 100%怎样定位,这题是一个应用性题目,网上搜一下即可,比较常见,说实话,把这题放进来有点后悔。

  2. String a = "ab"; String b = "a" + "b"; a ,b 是相等的(各位要写代码验证一下,我看到有人写了错误答案)。常规的问法是 new 一个对象赋给变量,问:这行表达式创建了几个对象,但这样的题目太常见。

  3. int a = 1; 是原子性操作。

  4. for 循环直接删除 ArrayList 中的特定元素是错的,不同的 for 循环会发生不同的错误,泛型 for 会抛出 ConcurrentModificationException,普通的 for 想要删除集合中重复且连续的元素,只能删除第一个。


错误原因:打开 JDK 的 ArrayList 源码,看下 ArrayList 中的 remove 方法(注意 ArrayList 中的 remove 有两个同名方法,只是入参不同,这里看的是入参为 Object 的 remove 方法)是怎么实现的,一般情况下程序的执行路径会走到 else 路径下最终调用 faseRemove 方法,会执行 System.arraycopy 方法,导致删除元素时涉及到数组元素的移动。针对普通 for 循环的错误写法,在遍历第一个字符串 b 时因为符合删除条件,所以将该元素从数组中删除,并且将后一个元素移动(也就是第二个字符串 b)至当前位置,导致下一次循环遍历时后一个字符串 b 并没有遍历到,所以无法删除。针对这种情况可以倒序删除的方式来避免


解决方案:用 Iterator。


List<String> list = new ArrayList(Arrays.asList("a", "b", "b" , "c", "d"));Iterator<String> iterator = list.iterator();while(iterator.hasNext()) {String element = iterator.next();if(element.equals("b")) {iterator.remove();}


将本问题扩展一下,下面的代码可能会出现什么问题?


ArrayList<String> array = new ArrayList<String>();array.add(1,"hello world");

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
你曾遇到过哪些大厂的-Java-面试?,mysql入门书籍哪本好