JAVA基础、集合

JAVA基础

在这里插入图片描述

概念

语法糖

Lambda表达式:依赖底层API
泛型
  • 定义接口、类时使用类型参数,使用时再用具体的类型来替换。
  • 优点:代码复用、类型安全

反射机制

  • 程序运行时能获取所有类的属性和方法。
  • 为什么慢
    • 不能执行某些java虚拟机优化
    • 包装、拆包
    • 遍历查找方法数组
    • 可见性检查、参数检查

动态代理

  • 在运行时动态创建代理对象。在不修改目标对象代码的情况下,为对象动态地添加额外的功能。
  • 实现方式
    • JDK动态代理java.lang.reflect 包的 Proxy 类和 InvocationHandler 接口。对象必须实现一个或多个接口。
    • CGLIB:运行时动态生成一个子类对象。

异常

  • 受检异常(Checked Exception):必须处理(捕获或抛出),否则编译报错。

  • 非受检异常(Unchecked Exception):运行时报错。如空指针、数组越界等。

值传递、引用传递

  • 值传递:复制出一个副本出来,传递副本。
  • Java是值传递,通过复制的方式把引用关系传递了。

浅拷贝、深拷贝

  • 浅拷贝:只复制对象的地址,而不是对象本身。共用一个地址。互相影响。
  • 深拷贝:新建一个对象,再复制。不同地址。互不影响。

数据类型

基本类型、包装类

String

  • 不可变

    • 原因
      • 缓存优势:字符串频繁使用,Java通过字符串池来缓存字符串,节省内存资源。不可变性使得多个相同内容的字符串变量可以指向同一个对象,从而实现内存节省。
      • hashCode缓存:由于字符串的不可变性,它们的哈希码(hashCode)不会改变。String类利用这一点,缓存了哈希码值,提高了性能。
      • 线程安全:不可变对象天然线程安全,因为它们的内容不会被改变。这意味着多个线程可以安全地共享不可变对象,无需额外的同步机制。
      • 安全性:存储敏感信息。
  • 如何实现

    • String类被声明为final,不能被继承,因此其内部方法不能被覆盖。
    • String类中的字符存储数组 char[] 被声明为final,一旦初始化后不能指向其他数组。
    • String类没有提供修改字符串内容的公共方法,如追加、删除或修改字符的方法。任何看似修改字符串的操作实际上都会创建一个新的String对象。

StringBuffer可变、线程安全;StringBuilder可变、不安全

面向对象

三大特征

  • 封装
  • 继承
    • 单继承:一个类只能继承一个父类。
  • 多态
    • 定义:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
    • 实现
      • 方法重载(Overloading):同一个类中可以有多个同名方法,只要它们的参数列表不同(参数类型或数量不同)。
      • 方法重写(Overriding):子类可以覆盖父类中的方法,提供具体的实现。当通过父类引用调用方法时,实际执行的是子类中的方法。
      • 接口实现:通过实现接口中的方法,不同的类可以提供各自的实现,实现多态。

序列化

  • 将对象转换为字节序列。
  • 类序列化为数据流,通过输入输出流将内存中的类持久化到硬盘或网络中。
  • 对象的类需要实现 java.io.Serializable 接口。

创建对象的方法

  • new关键字:最常见的创建对象方式,通过new关键字直接创建类的实例。
  • 通过反射机制:利用Java的反射API,在运行时动态创建对象。
  • 克隆:实现 Cloneable 接口并重写 clone() 方法,通过克隆已有对象创建新对象。
  • 反序列化:从文件、网络或其他来源读取对象的二进制流,反序列化创建对象。

抽象类和接口

  • 接口:只是定义了一些方法,没有实现。
    • 修饰符publicprotectprivate 都有;默认 public
    • 单继承多实现
    • 作用:代码复用(模板方法模式);制定规范。

集合

分类

  • List、Set、Queue、Stack、Map

List

  • ArrayList:动态数组。

  • LinkedList:双向链表。

  • Vector:类似于ArrayList,也是基于动态数组实现的。线程安全的。

  • 数组转listasList 方法;list 转数组:toArray 方法。

  • ArrayList 和 LinkedList 的区别

    1. 底层使用的数据结构
    2. 操作数据效率
    3. 内存空间占用
    4. 线程安全

Set

  • HashSet:底层使用哈希表实现,通过哈希码和哈希函数来快速定位元素。
  • TreeSet:基于红黑树实现,能够按照元素的自然顺序或定制顺序对元素进行排序。通过树的结构来保证元素的唯一性。

Map

  • hash冲突解决方法
    • 开放定址:找下一个空位置。
    • 链地址(hashmap采用这个):每个哈希桶(bucket)指向一个链表。当发生冲突时,新的元素将被添加到这个链表的末尾。当链表长度超过8时,链表会转换为红黑树,以提高搜索效率。红黑树结点数量小到6时,转回链表。
    • 再哈希:使用两个哈希函数,当发生冲突时,使用第二个哈希函数计算新的位置。
    • 建立公共溢出区:基本表+溢出表,冲突的放溢出表。
    • 一致性哈希:分布式系统中。将哈希空间视为一个环状结构,将节点和数据都映射到这个环上,并通过计算哈希值来确定数据的存储和检索位置。
HashMap
  • 结构

    • 一个数组,数组的每个成员都是一个链表。

    • 数组是 entry 类型,每个 entry 包含一对 k-v。

    • 红黑树:二叉查找树可以提高查找效率,但极端情况下会退化成链表。AVL树虽然保持平衡,但每次插入都需要旋转,消耗时间。红黑树作为二叉平衡树的一种,插入和删除操作最多需要两次旋转,更适合频繁的插入和删除操作。

      • 所有的叶子节点都是黑色的空节点,也就是叶子节点不存数据。
      • 任何相邻的节点都不能同时为红色,红色节点是被黑色节点隔开的,每个节点,从该节点到达其可达的叶子节点的所有路径,都包含相同数目的黑色节点。
      • O(logn)
    • 链表长度>8时变为红黑树;<6时退化为链表

  • 方法

    • hash():根据key计算k-v在数组中的下标。

      • 调用 ObjectHashCode() 方法,返回一个整数,用这个整数对容量取模。
      • 提高效率:
        • 位运算代替取模运算:要求容量是2^n。
        • HashCode 进行扰动计算。
    • get():计算键的哈希值,通过哈希值计算出在数组中的索引位置。如果该位置上的元素为空,说明没有找到对应的键值对,直接返回null。如果该位置上的元素不为空,遍历该位置上的元素,如果找到了与当前键相等的键值对,那么返回该键值对的值,否则返回null。

    • put():计算键的哈希值,通过哈希值计算出在数组中的索引位置。如果该位置上的元素为空,那么直接将键值对存储在该位置上。如果该位置上的元素不为空,那么遍历该位置上的元素,如果找到了与当前键相等的键值对,那么将该键值对的值更新为当前值,并返回旧值。如果该位置上的元素不为空,但没有与当前键相等的键值对,那么将键值对插入到链表或红黑树中。插入成功后,如果需要扩容,那么就进行一次扩容操作。

    • remove:计算键的哈希值,通过哈希值计算出在数组中的索引位置。如果该位置上的元素为空,说明没有找到对应的键值对,直接返回null。如果该位置上的元素不为空,检查是否与当前键相等,如果相等,那么将该键值对删除,并返回该键值对的值。如果该位置上的元素不为空,但也与当前键不相等,那么就需要在链表或红黑树中继续查找。遍历链表或者红黑树,查找与当前键相等的键值对,找到则将该键值对删除,并返回该键值对的值,否则返回null。

  • 扩容

    • 参数设置
      • 初始容量=(需要存储的元素数/负载因子)+1
      • 负载因子=0.75
      • 阈值=容量*负载因子。超过阈值则扩容。
    • 过程
      • 桶节点没有形成链表: 直接rehash到其他桶中。
      • 桶中形成链表: 将链表重新链接。
      • 桶中的链表已经形成红黑树,但是链表中的元素个数小于6: 取消树化。
  • JDK 1.8改动

    • 引入红黑树。
    • 节点 Entry 变为 Node
    • 尾插法。
    • hash 方法,位运算和异或运算少了。
    • 优化扩容机制。

ConcurrentHashMap

  • 如何保证线程安全

    • JDK 1.7:分段锁。
    • JDK1.8:节点锁,CAS+Synchronized。
      • 添加元素时,如果目标桶为空,那么使用CAS操作来添加新节点。
      • 如果目标桶不为空,对桶的第一个节点加 synchronized 锁,然后进行修改(链表或红黑树操作)。
  • 哪些地方做了并发控制

    • 初始化桶、put元素、扩容。
  • 如何保证fail-safe

    • 遍历使用弱一致性迭代器,迭代过程中反映的是某一时刻的快照,不会阻塞其他线程的修改操作。
    • CAS+Synchronized,而每个桶的第一个结点是原子更新的。
  • 多线程协同扩容

Stream

  • 并行流:利用多核处理器的并行能力来加速集合操作。基于Fork/Join框架,将大任务分解成多个小任务(Fork),并在多个线程中并行执行这些小任务(Join)。

  • 流处理

    • 流的创建
    • 中间操作
      • filter 过滤
      • map 映射
      • limit/skip 筛选出/扔掉前n个元素
      • sorted 排序
      • distinct 去重
    • 最终操作
      • for each 迭代每个数据
      • count 元素数
      • collect 汇总

集合的并发安全

  • HashMap并发问题:头插法,多线程并发扩容时,出现循环引用问题。

  • 如何将集合变成线程安全的

    • ReentrantLocksynchronized 对代码加锁。
    • ThreadLocal,将集合放到线程内访问。但是这样集合中的值就不能被其他线程访问了。
    • Collections.synchronizedXXX() 方法。可以获得一个线程安全的集合。
    • 使用不可变集合进行封装。当集合是不可变的时候,自然是线程安全的。
  • COW

    • 读时,共享同一真正的内容;修改时,把真正的内容复制出去形成一个新的内容,再修改。
    • 读写分离。读多写少。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值