面试准备知识点与总结——(基础篇)

news2024/11/16 0:42:10

目录

  • Java基础
    • Java面向对象有哪些特征
    • ArrayList和LinkedList有什么区别
    • 高并发的集合有哪些问题
    • 迭代器的fail-fast和fail-safe
    • ArrayList底层扩容机制
    • HashMap面试合集解答
  • 设计模式
    • 单例设计模式
    • 哪些地方体现了单例模式

Java基础

Java面向对象有哪些特征

Java面向对象有三大特征封装,继承,多态

其中,封装是增加了代码的复用性,封装好的代码可以重复使用,体现高内聚,低耦合

封装说明的是一个类的行为和属性与其他类的关系

封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。


继承也是增加了代码的复用性,不用频繁的重新编写方法,而是直接继承父类方法

继承是父类和子类的关系

继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。


多态是增加了代码的可移植性,健壮性,灵活性

多态说的是类与类之间的关系

封装和继承最后归结于多态,多态指的是类和类的关系,两个类由继承关系,存在有方法的重写,故而可以在调用时有父类引用指向子类对象。多态必备三个要素:继承,重写,父类引用指向子类对象。


ArrayList和LinkedList有什么区别

ArrayList底层是以数组实现的,对于随机访问的时间复杂度是O(1)

但是对于头部插入删除操作效率要低于LinkedList,而ArrayList尾部的插入删除操作还要比LinkedList快许多,因为尾部操作,直接在数组后面加或删,根据索引定位,比LinkedList更改指针指向还要更加快

LinkedList底层是以双向链表实现的,它对于头部和尾部添加,删除的操作,时间复杂度为O(1),但是对于随机访问效率要低于ArrayList


重点来了
①对于查询操作,二者耗时是一样的,根据值去查找,都需要一个一个比较
②对于中间元素的插入,删除操作,LinkedList要比ArrayList耗时更多,因为LinkedList的双向链表,虽然链表对于删除插入操作效率更高,但是它需要先遍历到中间位置,链表遍历耗时长,时间浪费在遍历到对应索引位置上了
③LinkedList占用内存多于ArrayList,下图可以看的很清楚
在这里插入图片描述


总结
ArrayList

  • ①基于数组,需要连续内存
  • ②随机访问快(指根据下标访问)
  • ③尾部插入、删除性能可以,其它部分插入、删除都会移动数据,因此性能会更低
  • ④可以利用cpu缓存,局部性原理

LinkedList

  • ①基于双向链表,无需连续内存
  • ②随机访问慢(要沿着链表遍历)
  • ③头尾插入删除性能高
  • ④占用内存多

高并发的集合有哪些问题

线程非安全集合类

ArrayList、HashMap 线程不安全,但是性能好

需要线程安全怎么办呢?
使用Collections.synchronizedList(list); Collections.synchronizedMap(m);
底层使用synchronized代码块锁虽然也是锁住了所有的代码,但是锁在方法里边,比锁在方法外边性能可以理解为稍有提高吧。


线程安全集合类

ConcurrentHashMap
CopyOnWriteArrayList
CopyOnWriteArraySet

底层大都采用Lock锁(1.8的ConcurrentHashMap不使用Lock锁),保证安全的同时,性能也很高。


迭代器的fail-fast和fail-safe

对于迭代器,可以遍历list,set这些单列集合,其中有一个问题,在遍历这些集合时,是否允许其他线程对集合进行插入修改操作呢?

下面是迭代器的两种处理策略

fail-fast一旦发现遍历的同时其它人来修改,则立刻抛异常
fail-safe发现遍历的同时其它人来修改,应当能有应对策略,例如牺牲一致性来让整个遍历运行完成

对于ArrayList的迭代器就是属于fail-fast这种策略,它是不允许一边遍历一边修改的,会直接报错,尽快失败

迭代器的fail-fast源码分析,其实就是我之前讲的并发修改异常,地址:http://t.csdn.cn/C43GG;在遍历开始时,记录modCount(修改值),然后在每次的next()中调用checkForComodification()方法;检查modCount,比较两个modCount是否一致来判断
在这里插入图片描述


而对于CopyOnWriteArrayList的迭代器(迭代器其实是它里面的内部类,每个集合的迭代器都有可能不同)
具体流程见下图,简而言之就是它的遍历和添加操作的不是同一个数组,用读写分离的方式,来处理遍历时的数组修改,牺牲一致性来让整个遍历运行完成

在这里插入图片描述


总结

fail-fast 与fail-safe

  • ArrayList是 fail-fast的典型代表,遍历的同时不能修改,尽快失败
  • CopyOnWriteArrayList是 fail-safe 的典型代表,遍历的同时可以修改,原理是读写分离

ArrayList底层扩容机制

JDK7初始化一个ArrayList集合时,初始容量为10;
JDK8初始化一个ArrayList集合时,长度为0,只有在添加元素时,才会给数组赋予大小

扩容机制分两种
一种是,超过了数组容量大小,底层数组扩容为原数组的1.5倍,并把原数组中的元素拷贝到新数组中

另一种是,新增加的元素数量超过了数组扩容后的容量,简单说就是即使数组扩容1.5倍后,仍然不能满足新增元素的个数(addAll这种方法),这时,扩容长度就是需要容纳所有元素的长度

容易记的说法,下次扩容的容量跟实际元素个数中取一个较大值,来作为数组的最终大小


HashMap面试合集解答

底层数据结构,1.7和1.8有何不同

在JDK7,HashMap底层数据结构是: 数组 + 链表的形式
而在JDK8, 其底层结构式:数组 + 链表 + 红黑树的结构


int index =(数组长度-1)&哈希值;

上面就是计算存储位置的公式

哈希表最大的优点就是实现集合中元素的快速查找
因为数组下标索引是根据元素的哈希值来计算得到的,所以可以根据元素值快速定位索引,找到元素位置,判断位置是否有元素,若没有即未找到;位置若有元素,比较二者hash值是否相同,相同即找到了该元素。可以通过值来查询,这是ArrayList无法比拟的,ArrayList只能用索引来查找不能根据元素值来查找

HashMap底层的数组扩容阙值是,元素个数达到了数组容量的3/4(或数组长度*扩容因子),就会扩容为原数组的两倍


树化条件
而它的链表树化时机是当链表长度大于8而且数组长度大于等于64时,链表转化为红黑树
而当数组长度未达到64,而链表长度大于等于8时,虽然不会树化,但是会进行数组扩容(链表长度可能会大于8)


树化为红黑树后,它左侧都是比根节点小的元素,右侧都是比根节点大的元素

为什么不一上来就树化
短链表时,它的性能是比红黑树要好的,最多比较几次而已,长链表情况下,红黑树的效率更高,优化也是优化在这种情况;而且红黑树底层的结构TreeNode(成员变量比Node多)比Node占用内存更多,所以链表较短情况下, 树化是没有性能提升的


为什么树化阙值是8
红黑树用来避免DoS攻击,防止链表超长时性能下降,树化应当是偶然情况

①hash表的查找,更新的时间复杂度是O(1),而红黑树的查找,更新的时间复杂度O(log2n),TreeNode占用空间也比普通Node的大,如非必要,尽量还是使用链表。
②hash值如果足够随机,则在 hash表内按泊松分布,在负载因子0.75的情况下,长度超过8的链表出现概率是0.00000006(一亿分之六),选择8就是为了让树化几率足够小


红黑树何时会退化为链表

退化情况1:在扩容时如果拆分树时,树元素个数<=6则会退化链表

退化情况2: 在remove树节点时,若root(根节点)、root.left(左孩子节点)、root.right(右孩子节点)、root.left.left(左孙子节点)有一个为null(不存在),也会退化为链表

注:举例:当删除左孩子节点时,树不会退化,但是再次执行remove操作时,会判断,若没有左孩子,就会退化为链表;
情况1和情况2不要混为一谈,1是在数组扩容时的条件,2是在删除树的结点时的条件


如何计算索引
计算对象的hashcode(),再进行调用HashMap的 hash()方法进行二次哈希,最后和数组长度减一按位与运算
哈希值 mod 数组长度 等价于==> 哈希值 & (数组长度 - 1)
用位运算效率更高,性能更高
前提是数组长度是2的n次方

二次hash()是为了综合高位数据,让哈希分布更为均匀

计算索引时,如果是2的n次幂可以使用位与运算代替取模,效率更高;扩容时 hash & 原数组长度==0的元素留在原来位置,否则新位置=旧位置索引+原数组长度

缺点:数组容量是2的n次幂时,分布性没有选质数的好


put方法流程,1.7和1.8的区别

扩容过程,先把元素添加到旧数组,然后达到阙值数组扩容,再把旧数组的所有元素迁移到新数组

1.7和1.8的不同之处

  • ①链表插入节点时1.7是头插法(新元素存数组,老元素挂新元素下面);1.8是尾插法(新元素直接挂老元素下面)
    在这里插入图片描述

  • ②1.7是大于阙值而且没有空位时,数组才会扩容;而1.8时大于阙值就进行扩容

  • ③1.8在扩容计算Node索引时,会优化,扩容时 hash & 原数组长度==0的元素留在原来位置,否则新位置=旧位置索引+原数组长度


加载因子 / 扩容因子为何是0.75f

  1. 在空间占用与查询时间之间取得较好的权衡
  2. 大于这个值,空间节省了,但链表就会比较长影响性能
  3. 小于这个值,冲突减少了,但扩容就会更频繁,空间占用也更多,造成空间浪费

多线程下操作HashMap有啥问题

数据错乱(1.7,1.8 都会存在)
JDK7会发生死链问题,原因就识JDK7的头插法

JDK7的头插法认识
在这里插入图片描述

别眨眼,接下来是多线程情况下

在这里插入图片描述

在这里插入图片描述


Key能否为null,作为key的对象有什么要求

  1. HashMap 的 key 可以为 null,但 Map 的其他实现则不然
  2. 作为 key 的对象,必须实现 hashCode(更好的分布性) 和 equals(万一hash值相同,比较equals),并且 key 的内容不能修改(不可变);hashCode不一定equals,但一旦equals了,必然hashCode相同
  3. key 的 hashCode 应该有良好的散列性

设计模式

单例设计模式

实现方式之一

饿汉式:一开始就先把INSTANCE对象创建出来,提前创建

  • 构造方法抛出异常是防止反射破坏单例
  • readResolve() 是防止反序列化破坏单例
public class Singleton1 implements Serializable {
    private Singleton1() {
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton1()");
    }

    private static final Singleton1 INSTANCE = new Singleton1();

    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

    public Object readResolve() {
        return INSTANCE;
    }
}

在这里插入图片描述


但是以下几种方式会破坏单例
①反射
可以加个判断来防止暴力反射,创建对象,以免破坏单例
已经创建了单例对象,再用反射调用创建对象时会报错

        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }

②反序列化
拿到单例对象后通过ObjectOutStream来把对象变成一个字节流,再通过ObjectInputStream还原成一个对象序列化反序列化过程又可以造出一个新的对象,甚至不需要走构造方法

防止方法
重写readResolve()方法后,反序列化时就会用readResolve()的方法返回值作为结果返回,而不是通过字节数组反序列化生产的对象了

public Object readResolve() {
    return INSTANCE;
}

③Unsafe
unsafe是JDK内置的一个类,它其中有一个allocateInstance(clazz)方法,可以根据参数类型,创建这个类型的实例,也不会走构造方法,目前没有防止的方法


实现方式之二

枚举饿汉式

枚举饿汉式能天然防止反射、反序列化破坏单例

public enum Singleton2 {
    INSTANCE;

    private Singleton2() {
        System.out.println("private Singleton2()");
    }

    @Override
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    public static Singleton2 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

实现方式之三

懒汉式单例

用的时候再创建单例对象,什么时候调,什么时候创

但是饿汉式是线程不安全的,在多线程情况下,无法保证单例,所以只能在创建单例对象方法上加synchronized来修饰,加锁保证单例

public class Singleton4 implements Serializable {
    private Singleton4() {
        System.out.println("private Singleton4()");
    }

    private static volatile Singleton4 INSTANCE = null; // 可见性,有序性

    public static Singleton4 getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton4.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton4();
                }
            }
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

为何必须加 volatile:

  • INSTANCE = new Singleton4() 不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造
  • 如果线程1 先执行了赋值,线程2 执行到第一个 INSTANCE == null 时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象

在这里插入图片描述


这里注意一下使用的是双检锁的方式来创建单例对象,在多个线程竞争锁时,获取到锁的执行,未获取到锁的挂起,等到释放锁后,挂起的线程被唤醒,而此时单例对象已经存在了,所以需要再次进行判断,防止创建新的对象

       if (INSTANCE == null) {
            synchronized (Singleton4.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton4();
                }
            }

在这里插入图片描述


反问:为什么饿汉式不用考虑线程安全问题?

因为饿汉式创建单例对象时赋值给静态变量的,单例对象会在类加载时就创建了,由JVM保证其原子性且安全执行;JVM保证了初始化阶段的线程安全
枚举饿汉式也是在静态代码块里创建枚举变量的,它的线程安全也不需要考虑


实现方式之四

内部类懒汉式

这就运用了饿汉式的保证线程安全的方法
内部类在使用时初始化,这样创建单例对象的操作就在静态代码块中执行,避免了线程安全问题

public class Singleton5 implements Serializable {
    private Singleton5() {
        System.out.println("private Singleton5()");
    }

    private static class Holder {
        static Singleton5 INSTANCE = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return Holder.INSTANCE;//这时会触发内部类的加载链接触发
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

既兼备了懒汉式的特性又避免了双检锁的缺点


哪些地方体现了单例模式

  • Runtime 体现了饿汉式单例
  • System.java的Console 体现了双检锁懒汉式单例
  • Collections 中的 EmptyNavigableSet 内部类懒汉式单例
  • ReverseComparator.REVERSE_ORDER 内部类懒汉式单例
  • Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351738.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Win10显示dds及tga缩略图

整理之前做游戏MOD时收集的模型资源,3D游戏模型的贴图文件格式基本都是dds或tga的,毕竟无损压缩、支持嵌入MipMap、带透明通道、可以被GPU硬解balabala...道理我都懂但这俩玩意系统根本直接查看不了,就算装上专门的看图软件或插件,文件夹视图下也没有缩略图预览,只能一个个点开…

SQL查询的优化:如何调整SQL查询的性能

查询优化是在合理利用系统资源和性能指标的基础上&#xff0c;定义最有效、最优化的方式和技术&#xff0c;以提高查询性能的过程。查询调整的目的是找到一种方法来减少查询的响应时间&#xff0c;防止资源的过度消耗&#xff0c;并识别不良的查询性能。 在查询优化的背景下&a…

CF1795D Triangle Coloring (组合数学)

链接 题意&#xff1a; 给定一个 n 个点 n 条边的图 &#xff0c;n 为 6 的倍数&#xff0c;每条边都有边权。 这个图是由 n/3 个三元环构成的 [1,2,3],[4,5,6],[7,8,9],[10,11,12]...... 一个 n12 的图长成这个&#xff08;唯一&#xff09;&#xff1a; 现在你需要给图染上红…

厂商推送限制说明及极光的适配方案

背景 自2023年起&#xff0c;各个厂商逐步对营销类消息做数量管控&#xff0c;具体如下&#xff1a; 华为 自2023年1月5日起&#xff0c;华为PUSH通道将根据应用类型对资讯营销类消息的每日推送数量进行上限管理&#xff0c;服务与通讯类消息每日推送数量不受限制。详情请参考推…

使用MMDetection进行目标检测、实例和全景分割

MMDetection 是一个基于 PyTorch 的目标检测开源工具箱&#xff0c;它是 OpenMMLab 项目的一部分。包含以下主要特性&#xff1a; 支持三个任务 目标检测&#xff08;Object Detection&#xff09;是指分类并定位图片中物体的任务实例分割&#xff08;Instance Segmentation&a…

服务间调用方式 OpenFeign 的介绍和使用

文章目录前言1、 RestTemplate1.1、通用方法 exchange1.2、RestTemplate 和 OpenFeign 的区别2、RPC 和 HTTP2.1、RPC 是什么2.2、RPC 调用过程示意图2.3、HTTP 是什么2.4、HTTP 调用过程示意图2.5、对比3 、OpenFeign3.1 、OpenFeign 常用注解3.2 、案例前言 Feign 停更了&am…

空间复杂度与时间复杂度

1、时间复杂度和空间复杂度 &#xff08;1&#xff09;时间复杂度、空间复杂度是什么&#xff1f; 算法效率分析分为两种&#xff1a;第一种是时间效率&#xff0c;第二种是空间效率。时间效率被称为时间复杂度&#xff0c;空间效率被称作空间复杂度时间复杂度主要衡量的是一…

Python空间分析| 02 利用Python计算空间局部自相关(LISA)

局部空间自相关 import esda import numpy as np import pandas as pd import libpysal as lps import geopandas as gpd import contextily as ctx import matplotlib.pyplot as plt from geopandas import GeoDataFrame from shapely.geometry import Point from pylab im…

LeetCode 1139. 最大的以 1 为边界的正方形

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 2023/2/17 每日一题 题目描述 给你一个由若干 000 和 111 组成的二维网格 gridgridgrid&#xff0c;请你找出边界全部由 111 组成的最大 正方形 子网格&#xff0c;并返回该子网格中的元素数量。如果不存在&am…

Transformer论文阅读:ViT算法笔记

标题&#xff1a;An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale 会议&#xff1a;ICLR2021 论文地址&#xff1a;https://openreview.net/forum?idYicbFdNTTy 文章目录Abstract1 Introduction2 Related Work3 Method3.1 Vision Transformer3.2…

【已解决】关于 luckysheet 设置纯文本,解决日期格式回显错误的办法

目录 一、现象 二、分析 三、思考过程 五、解决 六、参考链接 一、现象 在excel里面输入内容&#xff0c;如 2023-2-17 12:00 保存后&#xff0c;传回后端的数据被转化成了 数值类型&#xff0c;这显然是一种困扰。 如图所示 二、分析 查阅了文档和一些博客发现 Lucky…

2023美赛ICM F题 详细思路

对于本次美赛F题来说&#xff0c;核心之处就是综合评价模型&#xff0c;但考察我们这个模型的角度较以往常规的制定指标&#xff0c;计算得分给出排名然后分析结果这一套常规流程不同&#xff0c;这次美赛F题出题更贴合实际&#xff0c;整体聚焦在“制定完一个新指标后会带来的…

2019蓝桥杯真题数列求值(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 给定数列 1,1,1,3,5,9,17,⋯&#xff0c;从第 4 项开始&#xff0c;每项都是前 3 项的和。 求第 20190324 项的最后 4 位数字。 运行限制 最大运行时间&#xff1a…

三分钟学会用Vim

Vim知识点 目录Vim知识点一&#xff1a;什么是vim二&#xff1a;vim常用的三种模式三&#xff1a;vim的基本操作一&#xff1a;什么是vim vim最小集 vim是一款多模式的编辑器—各种模式—每种模式的用法有差别—每种模式之间可以互相切换 但是我们最常用的就是3~5个模式 vi…

更换ssl证书

更换ssl证书常用证书查看以及转换网址阿里云判断流量以及配置证书判断接入点阿里云控制台配置证书WAFAzure判断流量以及配置证书&#xff1a;判断接入点Azure配置证书CDNAPP GateWay常用证书查看以及转换网址 https://www.chinassl.net/ssltools/convert-ssl.htmlhttps://myss…

【java 高并发编程之JUC】高阶JUC特性总结

1 线程中断机制 1.1 什么是中断&#xff1f; 首先 一个线程不应该由其他线程来强制中断或停止&#xff0c;而是应该由线程自己自行停止。所以&#xff0c;Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。 其次 在Java中没有办法立即停止一条线程&#xff0c;然…

TCP简单说明

前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基础内容。 提示&#xff1a;以下是本篇文…

基于深度学习的个性化推荐系统实时化改造与升级

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2023年1月份热门报告合集ChatGPT的发展历程、原理、技术架构及未来方向《ChatGPT&#xff1a;真格基金分享.pdf》2023年AIGC发展趋势报告&#xff1a;人工智能的下一时代2023年…

ChatGPT初体验step by step:ChatGPT解决人类提出的数理逻辑问题,Python编程实践

ChatGPT初体验step by step&#xff1a;ChatGPT解决人类提出的数理逻辑问题&#xff0c;Python编程实践 如果已有有效的open ai的api key&#xff0c;则跳过本文&#xff08;1&#xff09;&#xff08;2&#xff09;&#xff08;3&#xff09;&#xff08;4&#xff09;&#x…

蓝牙 - 如何实现安全性

蓝牙技术在加密上做了很多工作&#xff0c;来保证你的数据安全。 这些年来&#xff0c;我们的许多电子设备都转向了使用无线技术进行连接。我们的鼠标、键盘、耳机和扬声器上不再有长长的纠缠的电线&#xff0c;而使用了简单方便的无线技术&#xff0c;科技进步改善了我们的生活…