我的校招记录:校招笔记(零)_写在前面 ,以下是校招笔记总目录。

备注
算法能力(“刷题”) 这部分就是耗时间多练习,Leetcode-Top100 是很好的选择。 补充练习:codeTop
计算机基础(上)(“八股”) 校招笔记(一)__Java_Java入门 C++后端后续更新
校招笔记(一)__Java_面对对象
校招笔记(一)__Java_集合
校招笔记(一)__Java_多线程
校招笔记(一)__Java_锁
校招笔记(一)__Java_JVM
计算机基础(下)(“八股”) 校招笔记(二)__计算机基础_Linux&Git
校招笔记(三)__计算机基础_计算机网络
校招笔记(四)__计算机基础_操作系统
校招笔记(五)__计算机基础_MySQL
校招笔记(六)__计算机基础_Redis
校招笔记(七)__计算机基础_数据结构
校招笔记(八)__计算机基础_场景&智力题
校招笔记(九)__计算机基础_相关补充
项目&实习 主要是怎么准备项目,后续更新

1.1 JAVA入门

1.1.1 JAVA基本

1.介绍一下JVM&JRE&JDK? JAVA语言有什么特点?

  • JVM&JRE&JDK
    • JVM: 即java虚拟机,针对不同操作系统,JVM把Java代码翻译成对应操作系统可以识别的内容,实现跨平台
    • JRE : JVM + 核心类库 = JRE , 即Java运行时环境。只有JVM不能运行,它还需要核心类库,才能保证Java运行
    • JDK: JRE + java开发工具(编译器等) = JDK ,Java开发工具包(JDK)是完整的Java软件开发包,包含了JRE,编译器和其他的工具。
  • Java语言特点
    1. 简单易学;

    2. 面向对象(封装,继承,多态);

    3. 平台无关性( Java 虚拟机实现平台无关性);

    4. 可靠性;

    5. 安全性;

    6. 支持多线程C++ 语⾔没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语⾔却提供了多线程支持);

2.什么是Java虚拟机?为什么Java被称为平台无关的编程语言

image-20210512104311616

  • java虚拟机,是执行字节码文件(.class)的虚拟机进程

    在 Java 中,JVM 可以理解的代码就叫做 字节码 (即扩展名为 .class 的⽂件),它不面向任 何特定的处理器,只面向虚拟机

  • java源程序(.java)被编译器编译成字节码文件(.class)。然后字节码文件,将由java虚拟机,解释成机器码(不同平台的机器码不同

3. 请你谈谈Java中是如何支持正则表达式操作的?(补充实例)

Java中的String类提供了支持正则表达式操作的方法,包括:

  • matches()、replaceAll()、replaceFirst()、split()

此外,Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作,如:

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class RegExpTest {
public static void main(String[] args) {
String str = "成都市(成华区)(武侯区)(高新区)";
Pattern p = Pattern.compile(".*?(?=\\()");
Matcher m = p.matcher(str);
if(m.find()) {
System.out.println(m.group());
}
}
}
实例示范(PCG问过)

参考:https://www.runoob.com/regexp/regexp-syntax.html

  • 特殊字符

    image-20210512110705420
  • 普通字符

    image-20210512111050519

  • 实例示范

    1. 匹配邮箱

      img

    2. 匹配电话号码

      1
      2
      3
      4
      //匹配电话号码
      String phone = "18637866964";
      String reg = "^1[3,5,7,8,9]\\d{9}$";
      System.out.println(phone.matches(reg));
    3. 匹配第一个出现的数字

      下面好像是不对的。

      1
      2
      3
      String phone = "avss1sdp22";
      String reg = "\d?";
      System.out.println(phone.matches(reg));

4.(补充例子)请你简单描述一下正则表达式及其用途

在编写处理字符串的程序时,经常会有查找 符合某些复杂规则的字符串 的需要。

  • 计算机处理的信息更多的时候不是数值而是字符串,正则表达式就是在进行字符串匹配和处理的时候最为强大的工具;
  • 绝大多数语言都提供了对正则表达式的支持。

5.&和&&区分?

  • 共同点:都要求运算符左右两端的布尔值 都是true 整个表达式的值才是true
  • 区别:&&之称为短路运算,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。 好处:
    • e.g. :右边判别式有如果有空指针NullPointerException异常判断风险,可以避免。

6.值传递和引用传递区分?

  • 值传递是该变量的一个副本, 改变副本不影响原变量;

  • 引用传递是对象地址副本,引用对象进行操作会同时改变原对象。

7.十进制与二进制?

  • 请你讲讲一个十进制的数在内存中是怎么存的?

    补码形式。

  • 为什么会出现4.0-3.6=0.40000001这种现象?

    2进制的小数无法精确的表达10进制小数,计算机在 计算10进制小数的过程中要先转换为2进制进行计算 ,这个过程中出现了误差。

8.(重要)equals与==的区别

很清晰严谨的一篇文章:https://www.cnblogs.com/skywang12345/p/3324958.html

  • ==

    1. 基本类型:比较的是值是否相同;
    2. 引用类型:比较的是引用(对象地址)是否相同;
  • equals

    要看类是否覆盖equals()方法,将它分为两种情况:

    1. 若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象(地址)是不是同一个对象。这时,等价于通过“==”去比较这两个对象

    2. 我们可以覆盖类的equals()方法,来让equals()通过其它方式比较两个对象的内容(而不是地址)是否相等。

      String 中的 equals 方法是被重写过的:

      • 因为 object 的 equals 方法是⽐教的对象的内存地址
      • String 的 equals 方法(1)先比较对象地址是否相等 ,相同则ture,否则(2)再比较值是否相等

7.请解释hashCode()和equals()方法有什么联系?

面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode方法?”

  • hashCode()介绍

    hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该 对象在哈希表中的索引位置

    我们仅在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结构中,用到该类。 其它情况下hashCode() 则根本没有任何作用,所以,不用理会hashCode()。

    • 在这种情况下对象相等,hashcode值也会不相等。
  • 为什么要有hashcode()

    hashCode() 的作用就是获取哈希码,也称为散列码;它实际上是返回⼀个 int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。 hashCode()在散列表中才有用,在其它情况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

    先判断hashcode,而不是直接遍历O(n)复杂度用equals()判断,减少判断时间

    如果hashcode一样,会调用equals()去比较

    • HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加⼊操作成功。如果不同的话,就会重新散列到其他位置。
7.1 为什么重写了equals()一定要重写hashcode()方法?

参考:为什么重写了equals(),还要重写hashCode()?

在Hashmap / Hashset中,通过计算hash = hash(key.hashcode) 然后进行取余操作,快速定位到数组中。

因为map中是不允许重复key的,所以对内部get()/add()方法:对于散列到数组同一位置的对象来说,如果hash相等 && equals()判断相等 ,是要进行覆盖的。

1
2
//调用 equals 方法判断key是否相等,若相等,该key对应的键值对已经存在,用新的value取代旧的value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

如果我们只重写了equals方法:用来判断两个对象是否相等。但是依旧可能出现:两个相同对象equals相等,但hashcode不等,被散列到不同桶上,map中依旧出现了重复键值对!

hashcode

所以,需要重写hashcode方法,保证相同对象一定是散列到同一个位置(具有同样的hash值)

7.2 两个对象值相同(x.equals(y) == true),但却可有不同的hash code,该说法是否正确,为什么

不一定正确,如果在HashSet, Hashtable, HashMap等等这些本质是散列表的数据结构中,两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。

其它情况下可能会出现题目描述的情况。

8. 自动拆箱和装箱?

8.1 介绍一下int&Integer?

Java为了编程的方便还是引入了基本数据类型,但是 为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class)

  • int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // e.g. 
    public static void main(String[] args)
    {
    Integer a = new Integer(3);
    Integer b = 3; // 将3自动装箱成Integer类型
    int c = 3;
    // false 两个引用没有引用同一对象
    System.out.println(a == b);
    // true a自动拆箱成int类型再和c比较
    System.out.println(a == c);
    }
  • Java 为每个原始类型提供了包装类型:

    • 8种基本类型)原始类型: boolean,char,byte,short,int,long,float,double
    • 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
8.2 拆箱、装箱存在的意义?

为什么要有装箱、拆箱,它们的作用是什么?

java 是 面对对象编程,而基本数据类型不是对象,所有才有封装类 引用基本数据类型进行操作。比如,下面打印出int型数据:

1
2
int i = 1
System.out.println(i); // 自动装箱

其实,查看源码,实际经过以下几个过程:

  • i 自动装箱成封装类 Integer
  • 然后调用 IntegertoString() 方法,打印出字符串输出到控制台。

自动装箱和拆箱?

  • 自动装箱:就是自动将基本数据类型转换为包装器类型
  • 自动拆箱:就是自动将包装器类型转换为基本数据类型
8.3 char和byte的区别 , 能否强制转换?

区别:

  • Char是无符号型的,可以表示一个整数,不能表示负数,大小范围 是0—65535;而byte是有符号型的,可以表示-128—127 的数
  • char可以表中英文字符,byte不可以

强制转换:

可以,但是会出现精度丢失。

9. String & StringBuffer& StringBuilder 区别?为什么String不可变?

  • 请解释String & StringBuffer区别?

    • 共同点:它们可以储存和操作字符串,即包含多个字符的字符数据;

    • 可否修改:String类提供了数值不可改变的字符,StringBuffer可以修改字符串,需要 字符数据要改变 时用。

    典型地,你可以使用StringBuffers来动态构造字符数据。

  • 请解释 StringBuilder& StringBuffer 区别?

    • 共同点AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共⽗类。都可以修改字符串,操作字符串方法丰富

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      abstract class AbstractStringBuilder implements Appendable, CharSequence
      {
      /**
      * The value is used for character storage.
      */
      char[] value;
      /**
      * The count is the number of characters used.
      */
      int count;
      AbstractStringBuilder(int capacity) {
      value = new char[capacity];
      }
    • 线程安全单线程且操作大量字符串用StringBuilder,速度快,但线程不安全,可修改;在多线程且操作大量字符串用StringBuffer,线程安全,可修改

      StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

  • 为什么String 不可变?

    String 类中使用 final 关键字修饰字符数组来保存字符串, 所以 String 对象是不可变的。

    1
    private final char value[]

10.说说深拷贝和浅拷贝

  • 浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址;

    因此,可能会出现出现浅拷贝时释放同一个内存的错误。

  • 深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。

11. 【新增】介绍一下JDK1.8的新特性?

JDK1.8新增了非常多的特性,如:

  • Lambda表达式:Lambda允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
  • 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法:默认方法就是一个在接口里面有了一个实现的方法
  • 新工具:新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
  • Stream API:新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Date Time API:加强对日期与时间的处理。
  • Optional类:Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常
  • Nashorn,JavaScript引擎:JDK1.8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

12. 【新增】java一个程序能不能有多个main方法?一个类里呢?

  • 一个程序里,多个class都有main方法

    可以,默认第一个为入口,其余为普通函数。

    1
    2
    3
    4
    5
    6
    7
    8
    class Class1 {
    public static void main(String[] args) {
    }
    }
    public class Class2 {
    public static void main(String[] args) {
    }
    }
  • 一个类有多个main方法

    可以,其余就相当是重载。但是具有以下sigature(签名)的主要方法将被视为app入口点:

    1
    public static void main(String[] args)

1.1.2 关键字

1.请你讲讲Java里面的final关键字是怎么用的

  • 修饰类:表示不能被继承,final类 成员变量 可以设为final;但final类所有方法 ,都被隐式指定为final方法;

  • 修饰方法:防任何继承类修改它的含义 ; 在早期的Java实现版本中,会将final方法转为内嵌调用,效率会更高;

  • 修饰变量、引用基本类型的话一旦初始化不能修改;引用类型,不能指定其他对象 。

2. 【重点】请你谈谈关于Synchronized和lock ?

  • Synchronized:是一个关键字,修饰类、方法 代码块 ,保证在同一时刻最多只有一个线程执行该段代码;

    作用范围:

    1. 修饰一个类/静态方法,作用的对象是这个类的所有对象
    2. 修饰一个方法/代码块,作用的对象是调用这个方法/代码块的对象
  • Lock:是一个接口,Lock能完成synchronized所实现的所有功能。

    Lock接口是不能直接实例化的,需要靠它的实现类ReentrantLock来进行实例化。

    • 区别:

      • 锁释放:synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;Lock不会主动适应 unLock() 释放,必须手动在finally释放。;
      • 线程等待: Lock可以让等待锁的线程可以响应中断,线程可以中断去干别的事务;而synchronized却不行,使用synchronized时,等待的线程会一直等待下去;
      • 成功获取锁: 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

3. instanceof关键字的作用?

instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例。

1
2
3
int i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型,不能是基本类型
System.out.println(i instanceof Object);//编译不通过
1
2
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true

4. final有哪些用法?

  • 被final修饰的类不可以被继承 ;

  • 被final修饰的方法不可以被重写而且JVM会尝试将其内联,以提高运行效率

  • 被final修饰的变量不可以被改变;

    • 被final修饰的引用,那么表示引用不可变,引用指向的内容可变

    • 被final修饰的常量,在编译阶段会存入常量池中。

5. static都有哪些用法 ?

  • 修饰静态变量和静态方法 :都属于类的静态资源,类实例所共享 ;

  • 修饰静态块:用于初始化操作。

    1
    2
    3
    4
    5
    public calss PreCache{
    static{
    //执行相关操作
    }
    }
  • 修饰静态包:在JDK 1.5之后引入的新特性,可以用来指定导入某个类中的静态资源,并且不需要使用类名,可以直接使用方法名

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import static java.lang.Math.*;

    public class Test
    {
    public static void main(String[] args)
    {
    //System.out.println(Math.sin(20));传统做法
    System.out.println(sin(20));
    }
    }

6. 谈一谈transient关键字?

参考 : Java中的关键字 transient

  • Java中序列化操作

    Java中对象的序列化指的是将对象转换成以【字节序列】的形式来表示,这些字节序列包含了对象的数据和信息

    当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象,要不然序列化后干嘛呢,所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。

    • 一个序列化后的对象可以被写到数据库或文件,也可用于网络传输,一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。
  • 关于transient关键字

    Java中transient关键字的作用,向虚拟机表明: transient变量不是对象的持久状态的一部分

    简单地说,就是让某些被修饰的成员属性变量不被序列化,例如:

    1. 类中的字段值可以根据其它字段推导出来,如一个长方形类有三个属性:长度、宽度、面积(示例而已,一般不会这样设计),那么在序列化的时候,面积这个属性就没必要被序列化了;

    2. 其它,看具体业务需求吧,哪些字段不想被序列化;

7.1 HashMap中源码modCount为什么用tranisent修饰?

modCount主要用于判断HashMap是否被修改(像put、remove操作的时候,modCount都会自增)。

对于这种变量,一开始可以为任何值,0当然也是可以(new出来、反序列化出来、或者克隆clone出来的时候都是为0的),没必要持久化其值。

1.1.3 Java异常

1. Java常见异常和分类?

Java异常的分类和类结构图1

常见分为两类,Error和Exception :

  • Error :指程序无法恢复的异常情况,对于其所有类型,都不要求程序处理。
    • 常见错误:Stackoverflow,outOfMemory
  • Exception: 程序有可能恢复的错误,又分为IOException & RuntimeException ,常见错误:
    • IOException:FileNotFoundExcepetion
    • RuntimeException : 空指针,参数不合法,类未找到等

2. OOM产生原因和分析?

OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,来源于java.lang.OutOfMemoryError 。

  1. java.lang.OutOfMemoryError: Java heap space (堆溢出)

    • 产生原因

      1. 内存泄漏
      2. 堆分配太小;
    • 解决办法

      1. 内存泄漏要手动去释放内存,比如数据库连接池,单例模式
      2. 通过虚拟机参数-Xms,-Xmx等修改,对内存大小
  2. java.lang.OutOfMemoryError: PermGen space (永久代(方法区)溢出)

    • 产生原因

      即方法区溢出了:

      1. 一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区
      2. 过多的常量尤其是字符串也会导致方法区溢出。
    • 解决办法

      1. 永久代的内存分配增大 :-XX:PermSize和-XX:MaxPermSize
  3. java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出

    线程栈相关的内存异常有两个:

    • StackOverflowError(方法调用层次太深,内存不够新建栈帧)
    • OutOfMemoryError(线程太多,内存不够新建线程)
  4. java.lang.OutOfMemoryError: Metaspace

    Java中普通I/O采用输入/输出流方式实现,输入流InputStream( 终端—>直接内存->JVM),输出流(JVM->直接内存->终端),这一过程中有kenel与JVM之间的拷贝(很多次)。

    为了使用直接内存,Java是有一块区域叫DirectBuffer,不是JavaHeap而是cHeap的一部分。

    但由于直接内存没有被java虚机完全托管,若使用不当,也容易触发溢出,导致宕机。

3. try catch finally,try里有return,finally还执行么

执行,并且finally的执行早于try里面的return :

  1. 不管有木有出现异常,finally块中代码都会执行;
  2. 当try和catch中有return时,finally仍然会执行;
  3. finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的。

4.说说你是怎么处理异常的?

try-catch-finally

image-20210505154833171

  • try 块负责监控可能出现异常的代码

  • catch 块负责捕获可能出现的异常,并进行处理

  • finally 块负责清理各种资源,不管是否出现异常都会执行

  • 其中 try 块是必须的,catch 和 finally 至少存在一个标准异常处理流程

5. web网页卡怎么排查?cpu100%怎么排查?OOM怎么排查?

  • web网页卡顿

    1. 用户端:硬件配置低、资源不足;CPU 或者内存资源不足, 比如用户是否使用了 Chrome 这种 “吃内存大户” 的浏览器并且打开了很多网页?
    2. 网络分析DNS 解析慢未设置 CDN,如果没有设置 CDN, 在跨线路访问(比如用户是铁通, 但是服务器部署在联通, 这种情况就是跨线路), 地理位置相差很远 等情况 ;用户端的带宽不足或所处环境网络不佳;
    3. 服务端:服务端响应慢,性能比较差 。
  • cpu100%

    参考:https://www.cnblogs.com/dennyzhangdd/p/11585971.html

    1. 执行top命令:查看所有进程占系统CPU的排序;

      极大可能排第一个的就是咱们的java进程(COMMAND列)。PID那一列就是进程号。

    2. 执行top -Hp 进程号命令:查看java进程下的所有线程占CPU的情况

    3. 执行printf "%x\n 10命令 :后续查看线程堆栈信息展示的都是十六进制,为了找到咱们的线程堆栈信息,咱们需要把线程号转成16进制。

    4. 执行jstack 进程号 | grep 线程ID” 查找某进程下–>线程ID(jstack堆栈信息中的nid)=0xa线程状态

      代码中有大量消耗CPU的操作,导致CPU过高,系统运行缓慢:

      1. jstack,可直接定位到代码行。例如某些复杂算法,甚至算法BUG,无限循环递归等等。

      2. 如果有死锁,会直接提示关键字:deadlock。步骤4,会打印出业务死锁的位置。

    5. 执行jstat -gcutil 进程号 统计间隔毫秒 统计次数(缺省代表一致统计)”,查看某进程GC持续变化情况如果发现返回中FGC很大且一直增大–>确认Full GC!

      也可以使用“jmap -heap 进程ID”查看一下进程的堆内从是不是要溢出了,特别是老年代内从使用情况一般是达到阈值(具体看垃圾回收器和启动时配置的阈值)就会进程Full GC。

      jstat命令监控GC情况,可以看到Full GC次数非常多,并且次数在不断增加。

    6. 执行jmap -dump:format=b,file=filename 进程ID,导出某进程下内存heap输出到文件中。

  • OOM

    参考:https://www.cnblogs.com/valjeanshaw/p/13130102.html

    先通过内存映像工具对Dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏还是内存溢出

    1
    jmap -dump:format=b,file=$java_pid.hprof     #java_pid为java进程ID

    然后看具体是报什么错:很明显下面是堆溢出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to oom.out ...
    Heap dump file created [3196858 bytes in 0.016 secs]
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:700)
    at java.lang.StringBuilder.append(StringBuilder.java:214)
    at jvm.OomDemo.main(OomDemo.java:13)