本人本科毕业,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上升。 — 参考《阿里巴巴开发手册》

运算符

  1. 位运算符

位与运算符 &,位或运算符 |,位非运算符 ~,位异运算符 ^,右移运算符 >>,左移运算符 <<,0填充右移运算符 >>>

  1. 逻辑运算符

逻辑与’&&’逻辑或’||’逻辑非’!’逻辑异或’^’逻辑与’&’逻辑或’|'

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类(表示可抛出)是所有异常和错误的超类,两个直接子类为ErrorException,分别表示错误和异常。其中异常类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的用法

参考囧辉的文章中三个代码块:

Java 基础高频面试题(2021年最新版)

JDK各新版本特性

参考博客链接:女同事问我JDK8以后有哪些新特性?给我整不会了