面试准备:
- 丰富简历。包括自我介绍、项目经验(复盘与创新)与基础知识;
- 基础知识的学习。java后台相关(JVM、并发编程、java核心技术卷1)、Linux、算法与数据结构、计算机网络、操作系统、数据库、springboot;
- 面试的常规问题以及对岗位的理解。(相关书籍、博客、github)
——————————————————————————————————————————————————————
参考面经:美团金融安卓客户端面经【 3 轮技术面 + 1 轮 HR 面】(21 届秋招)
第一题:String、StringBuffer 和 StringBuilder 的区别
分析: 从可变性、安全性、用法入手。参考链接:String、StringBuffer与StringBuilder之间区别
答:
第一,可变性。String是字符串常量,和其他两个相比主要的区别是String是不可变的对象,每次对String类型改变的时候其实等同于生成了新的对象,然后将指针指向新的String对象。这样不仅效率低下,而且还浪费有限的内存空间。
第二,安全性。面对字符串对象经常改变的情况下,需要用到StringBuilder(jdk1.5)和StringBuffer类。两者的相同点在于,均表示可变的字符序列,不产生新的未使用对象,并且方法也一样。SBuilder和SBuffer最大的区别在于,SBuilder的方法不是线程安全的(不能同步访问),但相比较于SBuffer,有速度优势。因此大多数情况下建议使用SBuilder,在要求线程安全的情况下,必须使用SBuffer。
第三,用法。StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
第二题:String的内部实现、主要存储在哪块区域?
答:主要存储在常量池中。String是一个类,而非基本数据类型。
java底层是由字符数组实现的。
“==”:假如是基本数据类型,那么比较的是值是否相等;假如是引用数据类型,那么比较的是内存地址是否相等;
“equals”:“当且仅当该参数不为null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 true”
第三题:String st1 = new String(“abc”); 这句话在内存中创建了几个对象?
答:两个,一个在堆内存(new出来的对象都是存储在堆内存),一个在常量池,堆内存是常量池对象的一个拷贝副本(新创建的字符串是该参数字符串的副本——String(String original)这个构造方法)。
package string;
public class Demo2_String {
public static void main(String[] args) {
String st1 = new String("abc");
String st2 = "abc";
System.out.println(st1 == st2);
System.out.println(st1.equals(st2));
}
}
因此,在上面这道题可以得出答案,false和true。因为"=="比较的是两个对象的内存地址,而st1的地址在堆内存,st2的地址在常量池。
“equals”比较的是两个字符串序列是否相等。
//第二题:
package string;
public class Demo2_String {
public static void main(String[] args) {
String st1 = "ab";
String st2 = "abc";
String st3 = st1 + "c";
System.out.println(st2 == st3);
System.out.println(st2.equals(st3));
}
}
首先,“+”这个拼接是如何实现的?是通过StringBuilder或者StringBuffer的append方法实现拼接,然后调用toString()方法将拼接的对象(一个StringBuffer对象或者StringBuilder对象)转化为字符串对象,最后把字符串对象的地址赋值给变量。
PS:Java中的常量优化机制
什么是常量优化机制?给一个变量赋值,如果等于号的右边是常量的表达式并且没有一个变量,那么就会在编译阶段计算该表达式的结果,然后判断该表达式的结果是否在左边类型所表示范围内,如果在,那么就赋值成功,如果不在,那么就赋值失败。但是注意如果一旦有变量参与表达式,那么就不会有编译期间的常量优化机制。
java中的常量池常量优化机制主要是两方面:
1.对于byte/short/char三种类型的常量优化机制
2.编译器对String类型优化;
参考链接:Java中的常量优化机制
最后贴一个:String类的一些内部实现方法
【第四题】重载Overload和重写Override的区别(常见面试题)
分析:口诀:重写(两同、两小、一大),重载(方法名一致,形参列表不同)。
记忆点:重写(覆盖)——覆盖在父类上,只执行一个;重载:多个方法,可同时执行。
答:重写用于多态实现,多态分为抽象多态和接口多态,抽象多态方法解析时调用相同偏移量的方法代码,接口多态线扫一次寻找对应方法代码。重载用于相同类方法调用跳转。多个方法,有相同的名字,不同的参数。
子类返回类型小于等于父类方法返回类型,
子类抛出异常小于等于父类方法抛出异常,
子类访问权限大于等于父类方法访问权限
参考链接:两同两小一大
第一点:如果返回值是类,需要与父类相同或者是父类的子类;如果返回值是void类型,只能由同样void类型的方法重写;
如果返回值是基础数据类型,那么表示范围要比父类小。
第二点:如果用多态的方式调用,也就是用父类的引用调用子类的方法,子类抛出更多的异常,会导致无法捕获Exception异常
第三点:public > protected > private
另外:两者必须同为类方法(static修饰||但是实际上类方法无法被重写,只是单纯隐藏)或者同为实例方法;
父类被private修饰,子类无法重写父类。
也就是说,子类只能对父类进行细化,不能超出父类的范围。
重载——多个方法,有相同的名字,不同的参数。编译器通过用各个方法给出的参数类型与特定方法调用所使用的值的类型进行匹配,从而挑选出具体执行的方法。如果匹配到了多个,或者0个,就会产生编译时错误(这个过程称为重载解析)。
方法的签名 = 方法名 + 参数类型;返回类型不是方法签名的一部分,也就是说,不能有两个名字相同、参数类型也相同,但返回不同类型值的方法。
重写——子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变
——————————————————————————————————————————————————
【3.12】
【第五题】HashMap 和 HashSet 的区别
两者都是collection框架的一部分,collection有自己的接口和实现,主要分为set接口、list接口和queue接口。
总的来说,是从这几个方面:1、操作的对象和方法;2、哈希值hashcode的计算;3、速度
参考链接:HashMap和HashSet的区别
【第六题】HashSet是如何实现的?
首先,实现原理:
(1)基于HashMap实现的,(即底层使用数组存储数据),默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
(2)当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
(3)HashSet的其他操作都是基于HashMap的。
//底层使用HashMap来保存HashSet中所有元素(作为key)
private transient HashMap<E,Object> map;
//定义一个虚拟的Object对象作为HashMap的Value,将此对象定义为static final
private static final Object PRESENT = new Object();
//默认的无参构造器,构造一个空的HashSet;
//实际上底层会初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75
public HashSet(){
map = new HashMap<E,Object>();
}
//构造一个包含指定Collection中的元素的新set
public