本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试。
记录日期:2021.12.29
大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习。
基础高频知识点
关键字
访问修饰符
分别是public、protect、default(不写)、private
final修饰符
表示最终不可变的,方法
和类
都可以用final修饰,final修饰的类是不可以被继承
的,final修饰的方法是不能被子类重写
的。
一般用final修饰常量
。
说明 final、finally、finalize 的区别?
final 如上所说。
finally,finally 是对 Java 异常处理机制的最佳补充,通常配合 try、catch 使用,用于存放那些无论是否出现异常都一定会执行的代码。在实际使用中,通常用于释放锁、数据库连接等资源,把资源释放方法放到 finally 中,可以大大降低程序出错的几率。
finalize,Object 中的方法,在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。finalize()方法仅作为了解即可,在 Java 9 中该方法已经被标记为废弃,并添加新的 java.lang.ref.Cleaner,提供了更灵活和有效的方法来释放资源。这也侧面说明了,这个方法的设计是失败的,因此更加不能去使用它。
static修饰符
修饰在属性、方法、内部类、静态代码块上。
super、this修饰符
super调用父类方法,this调用子类方法。
native修饰符
表示本地方法,调用本地JNI接口。
transient修饰符
和序列化接口一起使用,表示该字段不被序列化。
synchronized修饰符(重点)
同步关键字,后面会详细讲
。
volatile修饰符(重点)
保证变量的可见性、有序性,后面会详细讲
。
其他修饰符列举
定义类、接口、抽象类和实现接口、继承类的关键字、实例对象
class、interface、abstract、implements、extends、new
包的关键字
import、package
数据类型关键字
byte、char、boolean、short、int、float、long、double、void、null、true、false
注意点
熟知八大基本类型占用字节;
基本数据类型存储在栈上,引用数据类型存储在堆上;
条件控制、流程控制关键字
if、else、while、for、switch、case、default、do、break、continue、return、instance of
错误处理关键字
try、catch、finally、throw、throws
注意点
throw和throws的区别,前者是抛出异常对象,后者是声明异常;
String
1.String类被final修饰的原因(重点)
一般被final修饰,要从效率、安全两方面考虑问题。
安全方面,不可以被继承,就不会被其他类改变,让字符串变得更加安全。
效率方面,是为了适配字符串常量池,保证同一个字符串都是引用同一个字符串对象。
如果还要从双亲委派角度来说的话,可以看2.3。
2.String s = new String(“abc”) 与 String s = "abc"的区别(重点)
以现在java1.8来讲,两个语句都会先去字符串常量池中检查是否已经存在 “abc”,如果有则直接使用,如果没有则会在常量池中创建 “abc” 对象,另外,String s = new String(“abc”) 还会通过 new String() 在堆里创建一个内容与 “abc” 相同的对象实例。
tip :当调用new String(“abc”) 时,可能创建一个或两个对象,如果字符串常量池已经有“abc”,则是一个;否则,两个。当字符创常量池没有 “abc”,此时会创建如下两个对象:
一个是字符串字面量 “abc” 所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,此时该实例也是在堆中,字符串常量池只放引用。
另一个是通过 new String() 创建并初始化的,内容与"abc"相同的实例,也是在堆中。
3.String的intern方法
jdk1.6和jdk1.8的intern是有区别的,示例代码来说明:
String abc = new String("a") + new String("bc"); //在堆中创建"a","bc","abc"; --- 1
String abc2 = abc.intern(); // --- 2
String abc3 = "abc"; // 因为字符串常量池中有"ab",此时直接返回字符串常量池中的地址。
System.out.println(abc2 == abc3); // --- 3
System.out.println(abc== abc3); // --- 4
jdk1.6中,代码块2
先判断字符串常量池的中是否有"abc",如果有直接返回字符串常量池中的地址,如果没有则拷贝一份ab对象放入字符串常量池。
此时代码块3
返回true,代码块4
返回false。
jdk1.8中,先判断字符串常量池中是否有"abc",如果有直接返回字符串常量池中的地址,如果没有则将该对象放入字符串常量池。
此时代码块3
返回true,代码块4
返回true。
4.String、Stringbuffer、StringBuilder的区别
String,String 的值被创建后不能修改,任何对 String 的修改都会引发新的 String 对象的生成。
StringBuffer,跟 String 类似,但是值可以被修改,使用 synchronized 来保证线程安全。
StringBuilder,StringBuffer 的非线程安全版本,没有使用 synchronized,具有更高的性能,推荐优先使用。
5.字符串常量池(重点)
这块建议直接根据jvm虚拟机的内存模型来讲。
StringTable 具体存储的是 String 对象的引用,而不是 String 对象实例自身。String 对象实例在 JDK 6 及之前是在永久代里,从JDK 7 开始放在堆里。
根据 Java 虚拟机规范的定义,堆是存储 Java 对象的地方,其他地方是不会有 Java 对象实体的,如果有的话,根据规范定义,这些地方也要算堆的一部分。
StringTable 本体是存储在 native memory(本地内存)里,不是在永久代里,不是在方法区里,当然,更不是在堆里。
包装类
1.== 和 equals的使用
把这个放在包装类来说也是比较有效果的。
== 是比较两个对象地址值;equals是Object的方法,通常用来比较两个对象是否相等。
示例代码
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
Integer i3 = 1;
Integer i4 = 1;
System.out.println(i1 == i2); // false
System.out.println(i3 == i4); // true
i1 == i2
为false,是因为new Integer()时是在堆中创建了一个新的对象,两个对象的地址值不同,比较获得false,建议对于Integer的比较都使用Objects.equals(var1, var2)来比较。
i3 == i4
为true,是因为Integer内部存在缓存,当值为 -127 ~ 128时,都直接使用IntegerCache,在该范围内的值都使用用一个对象。
Integer重写了equals方法,可以比较值。
2.包装类的效率问题(延申)
因为包装类内部是调用valueOf()来进行转换的,这里存在资源消耗,所以比如说我们在项目中使用日志时,如果要打印debug日志,要先进行是否开启的判断log.isDebugEnabled()
再来进行打印日志,防止进行多余的转换,造成cpu上升。 — 参考《阿里巴巴开发手册》
运算符
- 位运算符
位与运算符 &,位或运算符 |,位非运算符 ~,位异运算符 ^,右移运算符 >>,左移运算符 <<,0填充右移运算符 >>>
- 逻辑运算符
逻辑与’&&’,逻辑或’||’,逻辑非’!’,逻辑异或’^’,逻辑与’&’,逻辑或’|'
1.& 和 && 的区别?
&&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。
&:逻辑与运算符、按位与运算符。
按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。
逻辑与运算符:& 在用于逻辑与时,和 && 的区别是不具有短路性。所在通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。
其他类推。
深拷贝和浅拷贝
博客参考链接:面试题:深拷贝和浅拷贝
知道Cloneable接口以及重写clone()方法。
浅拷贝,对于基础数据类型,直接复制数据值;对于引用数据类型,只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
深拷贝,对于基础数据类型,直接复制数据值;对于引用数据类型,开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。
简而言之,深拷贝中对于引用数据类型是重新开辟新的对象,浅拷贝中对于引用数据类型是复制引用地址。
并发和并行
并发,两个或多个事件在同一时间间隔发生。
并行,两个或者多个事件在同一时刻发生。
拿线程举例子:
并发,一个线程先干这个,再干那个,反复交替,在内核态和用户态之间反复切换。
并行,两个线程,一个干这个,一个干那个。
method
1.main方法可以被调用吗?
main方法,只是jvm虚拟机为了运行而寻找的入口,抛去这个,他只是一个简单的静态方法,谁都可以调用。
2.重载和重写的区别?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载,一个类中有多个同名的方法,但是具有有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)。
重写,发生在子类与父类之间,子类对父类的方法进行重写,参数都不能改变,返回值类型可以不相同,但是必须是父类返回值的派生类。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。
那构造器是否可以被重写?
构造器只能被重载。
3.一个参数传入方法中,方法改变对象的属性,是值传递还是引用传递?
引用对象传入方法中,是该对象的引用被传入,如果对象的属性被修改,该对象的属性就会被最终修改。
基本类型传入方法中,修改它的属性,只是在该方法内生效,因为基本类型传入时是一份拷贝副本,在栈上进行操作
。
类、抽象类和接口
1.抽象类(abstract class)和接口(interface)有什么区别?
抽象类只能单继承,接口可以多实现。
抽象类可以有构造方法,接口中不能有构造方法。
抽象类中可以有成员变量,接口中没有成员变量,只能有常量(默认就是 public static final)
抽象类中可以包含非抽象的方法,在 Java 7 之前接口中的所有方法都是抽象的,在 Java 8 之后,接口支持非抽象方法:default 方法、静态方法等。Java 9 支持私有方法、私有静态方法。
抽象类中的抽象方法类型可以是任意修饰符,Java 8 之前接口中的方法只能是 public 类型,Java 9 支持 private 类型。
设计思想的区别:
接口是自上而下的抽象过程,接口规范了某些行为,是对某一行为的抽象。我需要这个行为,我就去实现某个接口,但是具体这个行为怎么实现,完全由自己决定。
抽象类是自下而上的抽象过程,抽象类提供了通用实现,是对某一类事物的抽象。我们在写实现类的时候,发现某些实现类具有几乎相同的实现,因此我们将这些相同的实现抽取出来成为抽象类,然后如果有一些差异点,则可以提供抽象方法来支持自定义实现。
简而言之,接口是为了规范行为,抽象类是行为的抽象。
2.类和接口多继承多实现的问题
类是单继承,接口多实现;
接口是接口多继承;
3.类的初始化过程
1.静态变量只会初始化一次。
2.当有父类时,完整的初始化顺序为:父类静态变量(静态代码块)->子类静态变量(静态代码块)->父类非静态变量(非静态代码块)->父类构造器 ->子类非静态变量(非静态代码块)->子类构造器 。
具体代码说明
public class InitialTest {
public static void main(String[] args) {
A ab = new B();
ab = new B();
}
}
class A {
static {
// 父类静态代码块
System.out.print("A");
}
public A() {
// 父类构造器
System.out.print("a");
}
}
class B extends A {
static {
// 子类静态代码块
System.out.print("B");
}
public B() {
// 子类构造器
System.out.print("b");
}
}
执行结果为:ABabab
异常
1.异常的结构体系
Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。其中异常类Exception又分为运行时异常(RuntimeException)
和非运行时异常
, 这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)
和检查异常(Checked Exception)
。下面将详细讲述这些异常之间的区别与联系:
1、Error与Exception
Error
是程序无法处理的错误,它是由JVM产生和抛出的,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Exception
是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。
2、运行时异常和非运行时异常
运行时异常
都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常
是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
2.try、catch、finally的用法
参考囧辉的文章中三个代码块:
JDK各新版本特性
参考博客链接:女同事问我JDK8以后有哪些新特性?给我整不会了