Java从1.7到1.8的升级 ⭐️2

有很多,但是最重要的就是Lambda表达式以及Stream流了

ConcurrentHashMap的实现⭐️2

JDK1.7是数组+列表

JDK1.8是数组+列表+红黑树

红黑树的结构和应用

  • 红黑树是一种自平衡的二叉查找树
  • 节点要么是红色要么是黑色
  • 红黑树的根节点一定是黑色
  • 红色的叶子节点一定是黑色的
  • 从根节点出发,到每一个叶子节点的末尾,都包含相同数目的黑色节点
  • 红黑树的应用:HashMap

B+树的结构和应用

首先B树的结构:B树的每个叶子节点都存数据,但是相邻的叶子节点之间没有指针连接

B+树的结构:非叶子节点不存数据只存数据的索引,并且相邻的叶子节点有指针相互连接

这种底层是一个双向链表,双向链表的结构更适用于我们的范围查找

应用:在查询一条数据的时候很显然B树更加适合因为每个叶子节点都存数据,最好的情况就是O(1)

但是在范围查找的时候B树的确定就暴露出来了,因为如果使用B树作为Mysql的索引的话,在查询过程中会产生大量的I/O所以在综合考虑下下选择B+树作为Mysql的索引

Java中IO流的分类

  • 从数据的流向:输入流、输出流
  • 处理的字节单位:字节、字符

static的作用

  • static关键字可以修饰:属性、方法、内部类、代码块;
  • static修饰的资源属于类级别,是全体对象共享的资源
  • 使用static修饰的属性、静态属性是在类加载过程中初始化的,使用类名.属性进行访问

类加载的过程

java文件从编码到最终运行分成俩个阶段:编码期、运行期

编码期就是执行Javac命令让java文件变成,class文件

运行期就是把.class文件交给JVM去执行

而类加载的过程就是:将.class文件的元数据加载到内存,创建class并且解析、初始化类变量的过程

Java异常的基类

Throwable是 Java 语言中所有错误和异常的基类。它有两个主要的子类:Error 和 Exception,这两个类分别代表了 Java 异常处理体系中的两个分支。

深拷贝、浅拷贝的区别

浅拷贝:拷贝基本数据类型的值、引用类型的地址值、而对于引用类型变量指向的堆中的对象不会拷贝。

深拷贝:完全拷贝一个对象,拷贝被拷贝对象的成员变量的值,堆中的对象也会拷贝一份。

重载和重写的区别

重载:一个类有多个名字,但是参数的个数不相同的方法

重写:子类和父类有一样的方法(参数相同、返回值相同、方法名字相同、但是方法体可能不同)

double和float的区别

范围:float的指数位有8位,而double的指数位有11位

精度:float是单精度 double是双精度

double和float的精度损失

float的精度为7~8位有效数字,7位肯定能保证,8位的值也存在。

double的精度为16~17位有效数字

针对浮点数丢失精度的问题,我们可以通过BigDecimal来解决

java里如何精确表示小数

通过BigDecimal来解决

java里的数据结构

数组、链表、栈、队列、二叉树、哈希表

java里的数据类型

基础数据类型:byte、short、int、long、float、double、char、布尔

引用数据类型:接口、数组、类

jre、jdk、jvm的概念

JVM:Java Virtual Machine,也就是 Java 虚拟机,是 Java 实现跨平台的关键所在,针对不同的操作系统,有不同的 JVM 实现。JVM 负责将 Java 字节码转换为特定平台的机器码,并执行。

JRE:Java Runtime Environment,也就是 Java 运行时环境,包含了运行 Java 程序所必需的库,以及 Java 虚拟机(JVM)。

JDK:Java Development Kit,是一套完整的 Java SDK(软件开发工具包),包括了 JRE 以及编译器(javac)、Java 文档生成工具(Javadoc)、Java 调试器等开发工具。为开发者提供了开发、编译、调试 Java 程序的一整套环境。

HashMap的Put过程

①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容;

②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如 果table[i]不为空,转向③;

③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向 ④,这里的相同指的是hashCode以及equals;

④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值 对,否则转向⑤;

⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操 作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可;

⑥.插入成功后,判断实际存在的键值对数量size是否超多了 大容量threshold,如果超过,进行扩容。

HashMap的查找?⭐️

  1. 将key的值hash再二次hash.

  2. 拿二次hash的值跟数组相模取出桶下标。

  3. 去对应的桶下标进行调用equals比较获得一样的key,取出值。

HashMap和HashSet的原理区别

HashMap 和 HashSet 都是 Java 中的集合类,但它们有以下几点区别: HashSet 实现了 Set 接口,只存储对象;HashMap 实现了 Map 接口,用于存储键值对。 HashSet 底层是用 HashMap 存储的,HashSet 封装了一系列 HashMap 的方法,HashSet 将(自己的)值保存到 HashMap 的 Key 里面了。

HashMap中null的处理⭐️

1.先在table[0]的链表中寻找null key,如果有null key就直接覆盖原来的value,返回原来的value;

2.如果在table[0]中没有找到,就进行头插,但是要先判断是否要扩容,需要就扩容,然后进行头插,此时table[0]就是新插入的null key Entry了。

HashSet的遍历顺序

HashSet作为集合,有多种遍历方法,如普通for循环,增强for循环,迭代器,HashSet是通过HashMap来实现的,HashMap通过hash(key)来确定存储的位置,是不具备存储顺序性的,因此HashSet遍历出的元素也并非按照插入的顺序。

HashMap底层原理

JDK8前:数组+列表

JDK8后:数组+列表+红黑树

Java list的实现

Java 的 List 是非常常用的数据类型。List 是有序的 Collection。Java List 一共三个实现类:分别是 ArrayList、Vector 和 LinkedList。

Java面向对象的三大特征

继承、封装、多态

多态的实现

多态(Polymorphism)是指在面向对象程序设计中,同一个方法调用会根据对象的不同而表现出不同的行为。简而言之,多态性是指一个接口可以有多种不同的实现方式,同一个方法名可以具有不同的行为。

在Java中,多态性通过方法的重写(Override)和方法的重载(Overload)实现。重写是指子类重新定义父类中已有的方法,而重载是指在一个类中可以定义多个同名但参数列表不同的方法。

反射的机制

反射(Reflection)机制指的是在运行时检查或操作Java程序中的字段、方法、构造方法等信息的能力。通过反射,可以在运行时动态地获取类的信息、调用类的方法、操作类的属性等。这种能力使得程序可以在运行时动态加载、探索和使用类,而不需要在编译时确定。

使用:

框架和库的设计:如Spring框架、ORM(对象关系映射)工具等,通过反射来动态地管理和操作类的实例。

调试和测试:在开发过程中,可以通过反射获取类的信息和调用方法,实现灵活的调试和测试策略。

动态代理:通过动态代理技术,可以在运行时生成代理类,实现AOP(面向切面编程)等功能。

什么是动态代理?

配置文件处理:某些情况下,可以使用反射从配置文件中读取类名、方法名等信息,并动态地加载和执行。

Hashmap数组大小为2的幂次的原因⭐️

如果为奇数的话求得的索引值会重复,加大哈希碰撞的概率

死锁产生的条件

1.互斥条件 2. 请求与保持条件 3. 不可剥夺条件 4. 循环等待条件

死锁问题如何解决⭐️

1)预防

可以通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被广泛使用。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。

2)死锁避免

系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源;如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。

3)死锁检测和解除

先检测:这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区,此方法允许系统在运行过程中发生死锁。但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源。检测方法包括定时检测、效率低时检测、进程等待时检测等。

java反射的使用⭐️(需要例子)

Java的反射机制是通过java.lang.reflect包中的类和接口来实现的,主要涉及到三个核心类:Class、Constructor和Method。通过这些类,可以在运行时动态获取和操作类的成员信息。

// 获取类的Class对象
Class<?> clazz = MyClass.class;
// 获取构造函数对象
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// 创建实例对象
Object instance = constructor.newInstance("example", 123);
// 获取方法对象
Method method = clazz.getMethod("myMethod", String.class);
// 调用方法
method.invoke(instance, "parameter");

包装类的使用场景

泛型、Stream、装箱和拆箱

Stream流对原数组的影响

Stream的三个特性:不存储数据、不改变数据源、数据不可以重复使用

迭代器和增强for循环

在某些情况下,常规的遍历方式容易显得代码臃肿,增强for可以简化数组和集合的遍历,增强代码的可读性。但是操作难度比较大,所以一般使用Lambda表达式的情况居多。

ConcurrentHashMap的线程安全

简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进 行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全 的,也就实现了全局的线程安全。

synchronized的底层原理

synchronized的底层实现是完全依赖JVM虚拟机的,所以谈synchronized的底层实现,就不得不谈数据在JVM内存的存储:Java对象头,以及Monitor对象监视器。

ReentrantLock

重入锁,表示支持重新进入的锁,也就是说,如果当前线程 t1 通过调用 lock 方法获取了锁之后,再次调用 lock,是不会再阻塞去获取锁的,直接增加重试次数就行了。synchronized 和 ReentrantLock 都是可重入锁。

ConcurrentHashMap的加锁机制

在Java 1.7中,ConcurrentHashMap使用了分段锁(Segment Locking)的设计。它将整个Map划分为多个Segment(段),每个Segment继承自ReentrantLock,并且包含了一个散列桶数组。当进行写操作时,不是对整个Map加锁,而是仅仅对涉及操作的Segment加锁,这样就大大降低了锁的粒度,提高了并发性能。ConcurrentHashMap`在Java 1.8之后通过以下几个方面降低锁竞争并提高并发能力:

  • 使用细粒度的CAS操作代替传统的锁。
  • 在必要时(如修改链表头结点或扩容时)使用synchronized关键字对桶进行锁定。
  • 利用红黑树去优化冲突较多时的查找和更新性能。
  • 分布式锁的应用,即仅对涉及到的桶进行加锁,而不是整个数据结构。

sycronized和volatile的原理

  1. volatile主要应用在多个线程对实例变量更改的场合,刷新主内存共享变量的值从而使得各个 线程可 以获得最新的值,线程读取变量的值需要从主存中读取;synchronized则是锁定当前变 量,只有当前线 程可以访问该变量,其他线程被阻塞住。另外,synchronized还会创建一个内 存屏障,内存屏障指令保 证了所有CPU操作结果都会直接刷到主存中(即释放锁前),从而保证 了操作的内存可见性,同时也使 得先获得这个锁的线程的所有操作

  2. volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。 volatile不会造 成线程的阻塞;synchronized可能会造成线程的阻塞,比如多个线程争抢 synchronized锁对象时,会 出现阻塞。

  3. volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的 修改可见 性和原子性,因为线程获得锁才能进入临界区,从而保证临界区中的所有语句全部得到 执行。

  4. volatile标记的变量不会被编译器优化,可以禁止进行指令重排;synchronized标记的变量 可以被编 译器优化。

接口和抽象类的区别

  1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始 接口方法可以有默认实现),抽象类可以有非抽象的方法
  2. 接口中的实例变量默认是 final 类型的,而抽象类中则不一定
  3. 一个类可以实现多个接口,但最多只能实现一个抽象类
  4. 一个类实现接口的话要实现接口的所有方法,而抽象类不一定
  5. 接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口的对象 从设计层面来说,抽 象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

Files的常用方法

Files. exists():检测文件路径是否存在。

Files. createFile():创建文件。

Files. createDirectory():创建文件夹。

Files. delete():删除一个文件或目录。

Files. copy():复制文件。

Files. move():移动文件。

Files. size():查看文件个数。

Files. read():读取文件。

Files. write():写入文件。

== 和Equals的区别⭐️

hashcode和equals的区别.

equals() 和 hashcode() 这两个方法都是从 Object 类中继承过来的。hashCode():计算出对象实例的哈希码,并返回哈希码,又称为散列函数。equals():反映的是对象的内存地址或者对象的内容是否相等。

哪个集合接口可以实现List的并发安全⭐️

CopyOnWriteArrayList

CopyOnWriteArrayList 是线程安全的 ArrayList。CopyOnWrite 意思为写的时候会将共享变量新复制一份出来。复制的好处在于读操作是无锁的(也就是无阻塞)。

CopyOnWriteArrayList 仅适用于写操作非常少的场景,而且能够容忍读写的短暂不一致。如果读写比例均衡或者有大量写操作的话,使用 CopyOnWriteArrayList 的性能会非常糟糕。

CopyOnWriteArrayList 原理

CopyOnWriteArrayList 内部维护了一个数组,成员变量 array 就指向这个内部数组,所有的读操作都是基于 array 进行的。

栈在内存中的生长方向

栈的生长方向是由上往下的,即从高地址到低地址

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部