最后
分享一套我整理的面试干货,这份文档结合了我多年的面试官经验,站在面试官的角度来告诉你,面试官提的那些问题他最想听到你给他的回答是什么,分享出来帮助那些对前途感到迷茫的朋友。
面试经验技巧篇
- 经验技巧1 如何巧妙地回答面试官的问题
- 经验技巧2 如何回答技术性的问题
- 经验技巧3 如何回答非技术性问题
- 经验技巧4 如何回答快速估算类问题
- 经验技巧5 如何回答算法设计问题
- 经验技巧6 如何回答系统设计题
- 经验技巧7 如何解决求职中的时间冲突问题
- 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
- 经验技巧9 在被企业拒绝后是否可以再申请
- 经验技巧10 如何应对自己不会回答的问题
- 经验技巧11 如何应对面试官的“激将法”语言
- 经验技巧12 如何处理与面试官持不同观点这个问题
- 经验技巧13 什么是职场暗语
面试真题篇
- 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
- 真题详解2 某知名社交平台软件工程师笔试题
- 真题详解3 某知名安全软件服务提供商软件工程师笔试题
- 真题详解4 某知名互联网金融企业软件工程师笔试题
- 真题详解5 某知名搜索引擎提供商软件工程师笔试题
- 真题详解6 某初创公司软件工程师笔试题
- 真题详解7 某知名游戏软件开发公司软件工程师笔试题
- 真题详解8 某知名电子商务公司软件工程师笔试题
- 真题详解9 某顶级生活消费类网站软件工程师笔试题
- 真题详解10 某知名门户网站软件工程师笔试题
- 真题详解11 某知名互联网金融企业软件工程师笔试题
- 真题详解12 国内某知名网络设备提供商软件工程师笔试题
- 真题详解13 国内某顶级手机制造商软件工程师笔试题
- 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
- 真题详解15 某著名社交类上市公司软件工程师笔试题
- 真题详解16 某知名互联网公司软件工程师笔试题
- 真题详解17 某知名网络安全公司校园招聘技术类笔试题
- 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题
资料整理不易,点个关注再走吧
public Person(int age, int height) {
this.age = age;
this.height = height;
}
@Override
public String toString() {
return “Person{” +
“age=” + age +
“, height=” + height +
‘}’;
}
}
}
当我们对final修饰的引用数据类型变量person的属性改成22,是可以成功操作的。通过这个实验我们就可以看出来当final修饰基本数据类型变量时,不能对基本数据类型变量重新赋值,因此基本数据类型变量不能被改变。而对于引用类型变量而言,它仅仅保存的是一个引用,final只保证这个引用类型变量所引用的地址不会发生改变,即一直引用这个对象,但这个对象属性是可以改变的。
宏变量
利用final变量的不可更改性,在满足一下三个条件时,该变量就会成为一个“宏变量”,即是一个常量。
-
使用final修饰符修饰;
-
在定义该final变量时就指定了初始值;
-
该初始值在编译时就能够唯一指定。
注意:当程序中其他地方使用该宏变量的地方,编译器会直接替换成该变量的值
方法
重写?
当父类的方法被final修饰的时候,子类不能重写父类的该方法,比如在Object中,getClass()方法就是final的,我们就不能重写该方法,但是hashCode()方法就不是被final所修饰的,我们就可以重写hashCode()方法。我们还是来写一个例子来加深一下理解:
先定义一个父类,里面有final修饰的方法test();
public class FinalExampleParent {
public final void test() {
}
}
然后FinalExample继承该父类FinalExampleParent,当重写test()方法时出现报错
通过这个现象我们就可以看出来被final修饰的方法不能够被子类所重写。
重载?
public class FinalExampleParent {
public final void test() {
}
public final void test(String str) {
}
}
可以看出被final修饰的方法是可以重载的。经过我们的分析可以得出如下结论:
1. 父类的final方法是不能够被子类重写的
2. final方法是可以被重载的
类
当一个类被final修饰时,表名该类是不能被子类继承的。子类继承往往可以重写父类的方法和改变父类属性,会带来一定的安全隐患,因此,当一个类不希望被继承时就可以使用final修饰。还是来写一个小例子:
public final class FinalExampleParent {
public final void test() {
}
}
父类会被final修饰,当子类继承该父类的时候,就会报错,如下图:
final经常会被用作不变类上,利用final的不可更改性。我们先来看看什么是不变类。
不变类
不变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。满足以下条件则可以成为不可变类:
-
使用private和final修饰符来修饰该类的成员变量;
-
提供带参的构造器用于初始化类的成员变量;
-
仅为该类的成员变量提供getter方法,不提供setter方法,因为普通方法无法修改fina修饰的成员变量;
-
如果有必要就重写Object类 的hashCode()和equals()方法,应该保证用equals()判断相同的两个对象其Hashcode值也是相等的。
JDK中提供的八个包装类和String类都是不可变类,我们来看看String的实现。
/** The value is used for character storage. */
private final char value[];
可以看出String的value就是final修饰的,上述其他几条性质也是吻合的。
上面我们聊的final使用,应该属于Java基础层面的,当理解这些后我们就真的算是掌握了final吗?有考虑过final在多线程并发的情况吗?在Java内存模型中我们知道Java内存模型为了能让处理器和编译器底层发挥他们的最大优势,对底层的约束就很少,也就是说针对底层来说Java内存模型就是一弱内存数据模型。同时,处理器和编译为了性能优化会对指令序列有编译器和处理器重排序。那么,在多线程情况下,final会进行怎样的重排序?会导致线程安全的问题吗?下面,就来看看final的重排序。
final域重排序规则
final域为基本类型
先看一段示例性的代码:
public class FinalDemo {
private int a; //普通域
private final int b; //final域
private static FinalDemo finalDemo;
public FinalDemo() {
a = 1; // 1. 写普通域
b = 2; // 2. 写final域
}
public static void writer() {
finalDemo = new FinalDemo();
}
public static void reader() {
FinalDemo demo = finalDemo; // 3.读对象引用
int a = demo.a; //4.读普通域
int b = demo.b; //5.读final域
}
}
假设线程A在执行writer()方法,线程B执行reader()方法。
写final域重排序规则
写final域的重排序规则禁止对final域的写重排序到构造函数之外,这个规则的实现主要包含了两个方面:
-
JMM禁止编译器把final域的写重排序到构造函数之外;
-
编译器会在final域写之后,构造函数return之前,插入一个storestore屏障(关于内存屏障可以看这篇文章)。这个屏障可以禁止处理器把final域的写重排序到构造函数之外。
我们再来分析writer方法,虽然只有一行代码,但实际上做了两件事情:
-
构造了一个FinalDemo对象;
-
把这个对象赋值给成员变量finalDemo。
我们来画下存在的一种可能执行时序图,如下:
由于a,b之间没有数据依赖性,普通域(普通变量)a可能会被重排序到构造函数之外,线程B就有可能读到的是普通变量a初始化之前的值(零值),这样就可能出现错误。而final域变量b,根据重排序规则,会禁止final修饰的变量b重排序到构造函数之外,从而b能够正确赋值,线程B就能够读到final变量初始化后的值。
因此,写final域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确初始化过了,而普通域就不具有这个保障。比如在上例,线程B有可能就是一个未正确初始化的对象finalDemo。
读final域重排序规则
读final域重排序规则为:在一个线程中,初次读对象引用和初次读该对象包含的final域,JMM会禁止这两个操作的重排序。(注意,这个规则仅仅是针对处理器),处理器会在读final域操作的前面插入一个LoadLoad屏障。实际上,读对象的引用和读该对象的final域存在间接依赖性,一般处理器不会重排序这两个操作。但是有一些处理器会重排序,因此,这条禁止重排序规则就是针对这些处理器而设定的。
read()方法主要包含了三个操作:
-
初次读引用变量finalDemo;
-
初次读引用变量finalDemo的普通域a;
-
初次读引用变量finalDemo的final与b;
假设线程A写过程没有重排序,那么线程A和线程B有一种的可能执行时序为下图:
读对象的普通域被重排序到了读对象引用的前面就会出现线程B还未读到对象引用就在读取该对象的普通域变量,这显然是错误的操作。而final域的读操作就“限定”了在读final域变量前已经读到了该对象的引用,从而就可以避免这种情况。
读final域的重排序规则可以确保:在读一个对象的final域之前,一定会先读这个包含这个final域的对象的引用。
final域为引用类型
我们已经知道了final域是基本数据类型的时候重排序规则是怎么的了?如果是引用数据类型了?我们接着继续来探讨。
对final修饰的对象的成员域写操作
针对引用数据类型,final域写针对编译器和处理器重排序增加了这样的约束:在构造函数内对一个final修饰的对象的成员域的写入,与随后在构造函数之外把这个被构造的对象的引用赋给一个引用变量,这两个操作是不能被重排序的。注意这里的是“增加”也就说前面对final基本数据类型的重排序规则在这里还是使用。这句话是比较拗口的,下面结合实例来看。
public class FinalReferenceDemo {
final int[] arrays;
private FinalReferenceDemo finalReferenceDemo;
public FinalReferenceDemo() {
arrays = new int[1]; //1
arrays[0] = 1; //2
}
public void writerOne() {
finalReferenceDemo = new FinalReferenceDemo(); //3
}
public void writerTwo() {
arrays[0] = 2; //4
}
public void reader() {
总结
机会是留给有准备的人,大家在求职之前应该要明确自己的态度,熟悉求职流程,做好充分的准备,把一些可预见的事情做好。
对于应届毕业生来说,校招更适合你们,因为绝大部分都不会有工作经验,企业也不会有工作经验的需求。同时,你也不需要伪造高大上的实战经验,以此让自己的简历能够脱颖而出,反倒会让面试官有所怀疑。
你在大学时期应该明确自己的发展方向,如果你在大一就确定你以后想成为Java工程师,那就不要花太多的时间去学习其他的技术语言,高数之类的,不如好好想着如何夯实Java基础。下图涵盖了应届生乃至转行过来的小白要学习的Java内容:
请转发本文支持一下
时,你也不需要伪造高大上的实战经验,以此让自己的简历能够脱颖而出,反倒会让面试官有所怀疑。
你在大学时期应该明确自己的发展方向,如果你在大一就确定你以后想成为Java工程师,那就不要花太多的时间去学习其他的技术语言,高数之类的,不如好好想着如何夯实Java基础。下图涵盖了应届生乃至转行过来的小白要学习的Java内容:
请转发本文支持一下
[外链图片转存中…(img-kNYDZFjg-1715313157893)]
[外链图片转存中…(img-dCT9O51q-1715313157894)]