开门见山!
***************************************************************
1:如何最有效率地计算2*8?
很老的一道题了,直接左移三位,即2<<3;利用二进制,移位操作,这是最快的方式;移位可比普通的四则运算速度快。
2:请简单介绍下,JVM加载类的机制?
这个问的不多,但大点的公司对于底层要求还是比较严的,在此参考周志明的那本书介绍下。
大家知道,我们编写的Java或者其他语言编写生成的文件,编译成Class文件,而Class文件中的各种信息,只有加载到虚拟机之中后才能运行和使用,那么,虚拟机是怎么实现这个家在的呢?
简单来说,虚拟机吧描述类的数据从Class文件加载到内存,并且对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
这里,类从被加载到内存开始,到卸载出内存为止,是类的整个生命周期,包括:加载、验证、准备、解析、初始化、使用、卸载七个阶段,其中验证、准备、解析三个部分统称为连接。
(1)加载:在加载的过程中,虚拟机需要完成以下三件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的的静态存储结构,转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
这里其实灵活性就很强了,就拿第一步来说吧,因为只说从一个Class文件中获取,并没有指定路径,所以可以从各种zip包中,可以从网络中获取,运行时的计算生成等等各种手段
(2)验证:验证是连接阶段的第一步,这一阶段的目的,是为了确保Class文件中的字节流内包含的信息,符合当前虚拟机的要求,并且不会危害虚拟机自身的安全,这个是毫无疑问的,name,验证阶段具体都会做什么检验操作呢?
- 文件格式验证:会验证字节流是否符合Class文件格式的规范,并且能够被当前版本的虚拟机处理。
- 元数据验证:这个阶段,对于字节码描述的信息进行语义分析,以保证其描述的信息,是否符合Java语言规范的要求。
- 字节码验证:这是整个验证过程中最复杂的一个阶段,主要目的是通过数据流和控制流分析,确定程序的语义是合法的、符合逻辑的,这个阶段,会对类的方法体进行校验分析,保证类的方法在运行的过程中,不会做出危害虚拟机的行为。
- 符号引用验证:这个阶段的校验,发生在虚拟机将符号引用转化为直接引用的时候。
(3)准备:是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
(4)解析:这个阶段,是虚拟机将常量池内的符号引用替换为直接引用的过程。
(5)初始化:类的初始化阶段是类过程中的最后一步,到了这个阶段,才真正开始执行类中定义的Java程序代码。
3:谈谈你对JVM类加载器的了解?
首先,从虚拟机的角度来说,只有两种不同的类加载器:
- 启动类加载器,这个类加载器是虚拟机自身的一部分。
- 另一种就是所有其他的类加载器,这些类加载器一定是是由Java语言实现的,独立于虚拟机外部,并且全部继承自抽象类:java.lang.ClassLoader。
而对于程序员来说,类加载器还可以划分地更加细致一些:
- 启动类加载器:这个类加载的目录,是<JAVA_HOME>\lib目录中的,或者是被-Xbootclasspath参数所指定的路径中的,必须是能够被虚拟机是别的类库加载到虚拟机内存中,简单说,就是虚拟机启动的时候就会加载这部分的类,或者说,启动类加载器是随着虚拟机启动就开始工作的;启动类加载器无法被Java程序直接引用。
- 扩展类加载器:这个加载器,用于加载<JAVA_HOME>\lib\ext目录中的类库,或者是为java.ext.dirs系统变量锁指定的路径中的所有类库,这是开发者可以直接使用的,换句话说,就是我们开发过程中使用到的第三方类库,基本就是用这个类加载器来加载的,所以叫做扩展类加载器。
- 应用程序类加载器:这个加载器呢?它是负责加载用户类路径所指定的类库的,开发者可以直接使用这个类加载器,如果应用程序中没有自己定义类加载器,一般情况下,就是用的这个程序中默认的类加载器。
至于类加载器的双亲委派模型这里就不多说了,大家可以自行百度;简单说就是,一个类加载器接收到类加载的请求后,不会直接自己尝试加载,而是将其为派给父类加载器去加载,一直到最顶层的加载器;只有当父加载器反馈无法加载后,子加载器才会尝试自己去加载。
这里,今天遇到了一个问题:如果我定义了一个类,全名是java.lang.String,那么,这个类能够正常加载吗?
本人亲自尝试过,是不行的,从理论上来说,就是因为父类已经加载了这个类,那么,再次加载,肯定会出问题,具体什么问题呢,参见下文:

这里报出的错是不存在主方法,因为实际加载的是JAVA_HOME目录下的String类,那个类里是没有main方法的。
3:问:给你二十五匹马,五条赛道,二十五匹马的速度各自不同,在没有什么计时工具的情况下,最短需要几场比赛能挑出其中最快的三匹马?
这个一道思考题,面一家大公司的时候碰到了,统一可以称为是跑马问题,通常会改变马匹的数量和赛道的数量,但解题的思维都是一致的。
解答:首先一场比赛,五匹马,A1,A2,A3,A4,A5,决出一匹最快的,比如说A1。
接下来四场比赛,决出每场比赛中最快的,比如说B1,C1,D1,E1。
目前进行了五场比赛,每场比赛都有一匹跑得最快的。
第六场比赛呢,让这五匹在各自队伍中最快的跑一场,决出二十五匹马中最快的,比如说是A1;这时候,最快的一匹出来了;比如说,这场比赛的名次是A1>B1>C1>D1>E1。
第七场呢,A1是不用参加了,因为其本身已经是最快的了;而我们要挑出最快的三匹马,D1和E1肯定不可能参与最后的角逐了,所以,最后一场让B1B2,C1,A2,A3跑一场,就可以决斗出剩余的两匹马了。
综上,最快七场比赛,可以决出最快的三匹马。
这个题目的关键,在于要利用第六场的赛果,去决断最后一场比赛的马匹。
4:实现一个栈,实现入栈,出栈,求栈中的最小值,时间复杂度都是O(1)。
前面两个操作,很容易实现,这就不说了。
重点与如何做到求取栈中的最小值的操作,一直保证时间复杂度是O(1)。
这里,代码不写了,简单介绍下这个问题的解决思路吧:
解答:毫无疑问,时间复杂度与空间复杂度是不能并存的,如果想要实现最小值获取的复杂度是O(1),那么,必须要创建一个空间来保存最小值,我们新建一个类,这里面有两个栈;一个主栈用于存储存入的元素,而一个副栈则是用于存储最小值。
那么,怎么利用这个副栈来达到我们的目的呢?
操作如下:
- 存入第一个元素的时候,主栈压栈;同时副栈压栈。
- 存入第二个元素的时候,主栈压栈;同时;该元素与副栈栈顶元素进行比较,如果比其要小,那么,将该元素压栈,否则,副栈不进行任何操作;综合来说,就是每存入一个元素,都要判断其是否是存入之后形成的一个新栈内所有元素的最小元素,如果是的,则压入副栈,或者称为最小值栈更好。
- 取元素的时候,普通操作不说了,这里说下如何取最小元素;因为存入的时候,最小值栈顶端元素,一定是现在主栈之中最小的元素,我们直接将该元素出栈即可。
5:一道今天刚遇到的面试题:简单介绍是这样的,给一个字符串,其中大小字母都有,而且大写字母出现的位置随机的,那么,设计一个算法,在不占用额外空间的情况下,把大写字母全部挪动到最右边,同时,保持小写字母的相对顺序不变;保证大写字母的相对顺序也不变:比如说abcdAghDefGij;操作之后的结果就是:abcdghefijADG。
解答:网上各种各样的解决方法大致相同,但写的不详细,这里给出我自己写的一个实现方式,但是,无法做到完全不占用任何额外空间,我仔细思考了,只要想做到移位和交换,实在是不可能完全不占用额外空间,我这里,占用了一个额外的空间,算法如下:
public class StringTransfer {
public static void main(String[] args) {
String text = "abcdAghDefGij";
transferBig(text);
}
public static void transferBig(String text) {
// 这里,之所以转了,是因为无法直接对字符串的某一个元素进行赋值操作
char[] array = text.toCharArray();
// 这里,索引是为了计算碰到了几个大写字母
int index = 0;
// 这里,为什么要根据index来判断呢?如果已经出现了一个大写字母,毫无疑问,就没有必要判断到最后一位的
// 所以,index要用来判断具体要交换到哪个元素的。
for (int i = array.length - 1; i >= 0; i--) {
// 如果遇到了大写字母
if (Character.isUpperCase(text.charAt(i))) {
index++;
for (int j = i; j < text.length() - index; j++) {
char temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
System.out.println(new String(array));
}
}
代码亲测是有效的。
6:简单介绍下Yarn中的几个组件呗
这个讲起来有些复杂,大家参照:Yarn
7:简单介绍下Hadoop安装过程中的重要的配置文件,以及配置文件的作用。
8:volatile关键字能够发挥什么作用?
9:Redis为什么那么快?内部使用什么机制存储的?
10:深入地讲解下HashMap的原理呗
11:Java 7的HashMap和Java 8的HashMap的区别在于哪儿?
这里先讲些简单的,Java7中的HashMap中,依靠的是数组和链表两种数据结构;而在Java8中的HashMap,扩容到一定程度的时候,会用红黑树来代替链表。