java基础知识整理(一)

java基础知识整理

1.为什么在重写 equals 方法的时候需要重写 hashCode 方法?

这是因为不同对象的hashCode 可能相同;但hashCode 不同的对象一定不相等,所以使用hashCode 可以起到快速判断对象是否相等的作用。

2.两个相同的对象会有不同的 hash code 吗?

不能,根据 hash code 的规定,这是不可能的

3.final、finalize 和 finally 的不同之处?
  • final 是一个修饰符,可以修饰变量、方法和类。如果 final 修饰变量,意味着该变量的值在初始化后不能被改变,方法不可重写,类不可以继承
  • finalize是Object类中的一个方法,这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,但是什么时候调用 finalize 没有保证。
  • finally 是一个关键字,与 try 和 catch 一起用于异常的处理。finally 块一定会被执行,无论在 try 块中是否有发生异常。
4.String、StringBuffer与StringBuilder的区别?

第一点: 可变和适用范围。String对象是不可变的,而StringBuffer和StringBuilder是可变字符序列。每次对String的操作相当于生成一个新的String对象,而对StringBuffer和StringBuilder的操作是对对象本身的操作,而不会生成新的对象,所以对于频繁改变内容的字符串避免使用String,因为频繁的生成对象将会对系统性能产生影响。

第二点: 线程安全。String由于有final修饰,是不可变的。StringBuilder和StringBuffer的区别在于StringBuilder不保证线程同步,也就是说如果需要线程安全需要使用StringBuffer,不需要同步的StringBuilder效率更高。

5.接口与抽象类的区别?
  • 一个子类只能继承一个抽象类, 但能实现多个接口
  • 抽象类可以有构造方法, 接口没有构造方法
  • 抽象类可以有普通成员变量, 接口没有普通成员变量
  • 抽象类和接口都可有静态成员变量, 抽象类中静态成员变量访问类型任意,接口只能public static final(默认)
  • 抽象类可以没有抽象方法, 抽象类可以有普通方法;接口在JDK8之前都是抽象方法,在JDK8可以有default方法,在JDK9中允许有私有普通方法
  • 抽象类可以有静态方法;接口在JDK8之前不能有静态方法,在JDK8中可以有静态方法,且只能被接口类直接调用(不能被实现类的对象调用)
  • 抽象类中的方法可以是public、protected; 接口方法在JDK8之前只有public abstract,在JDK8可以有default方法,在JDK9中允许有private方法
6.this() & super()在构造方法中的区别?

super() 和this() 类似,区别是,super() 从子类中调用父类的构造方法,this() 在同一类内调用其它方法。 super() 和this() 均需放在构造方法内第一行。 尽管可以用this调用一个构造器,但却不能调用两个。

this和super不能出现在同一个构造器中, 否则编译不通过

this()、super()都指的对象,不可以在static环境中使用

本质上this指向本对象的指针。super是一个关键字

7.Java移位运算符?

java中有三种移位运算符

  • << :左移运算符,x << 1,相当于x乘以2(不溢出的情况下),低位补0
  • >> :带符号右移,x >> 1,相当于x除以2,正数高位补0,负数高位补1
  • >>> :无符号右移,忽略符号位,空位都以0补齐
8.为什么要使用泛型方法呢

因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活

9.泛型的上限和下限?

在使用泛型的时候,我们可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。形如(上限)或者<? super String>(下限)

10.如何理解Java中的泛型是伪泛型?

泛型中类型擦除 Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样

11.注解的作用?

注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它主要的作用有以下四方面:

  • 生成文档,通过代码里标识的元数据生成javadoc文档。
  • 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
  • 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
  • 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。
12.注解的常见分类?

Java自带的标准注解,包括@Override@Deprecated@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。****

元注解

,元注解是用于定义注解的注解,包括

@Retention
@Target
@Inherited
@Documented
  • @Retention用于标明注解被保留的阶段
  • @Target用于标明注解使用的范围
  • @Inherited用于标明注解可继承
  • @Documented用于标明是否生成javadoc文档

自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。

13.什么是反射?

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

14.反射的使用?

在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private)

15.集合有哪些类?
  • Set

    • TreeSet 基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
    • HashSet 基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
    • LinkedHashSet 具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。
  • List

    • ArrayList 基于数组实现的动态数组,可以自动调整大小。它提供了快速的随机访问和动态增加元素的功能。
    • Vector 和 ArrayList 类似,但它是线程安全的。
    • LinkedList 基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
  • Map

    • HashMap:基于哈希表实现的键值对映射,用于快速查找和存储键值对。
    • TreeMap:基于红黑树实现的有序键值对映射,按照键的自然顺序或者指定的比较器排序。
    • LinkedHashMap:基于哈希表和双向链表实现的有序键值对映射,维护插入顺序或者访问顺序。
    • HashTableHashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高(1.7 ConcurrentHashMap 引入了分段锁, 1.8 引入了红黑树)。
  • Queue

    • QueuePriorityQueue:队列数据结构,用于存储元素并支持特定的入队和出队操作。PriorityQueue 是一个优先级队列,可以按照元素的优先级进行排序。

    • Deque:双端队列,支持在队列两端进行插入和删除操作。

16.ArrayList自动扩容?

每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过ensureCapacity(int minCapacity)方法来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。

数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

17.ArrayList的Fail-Fast机制?

ArrayList也采用了快速失败的机制,通过记录modCount参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

18.Map有哪些类?
  • TreeMap 基于红黑树实现。
  • HashMap 1.7基于哈希表实现,1.8基于数组+链表+红黑树。
  • HashTable 和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高(1.7 ConcurrentHashMap 引入了分段锁, 1.8 引入了红黑树)。
  • LinkedHashMap 使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
19.JDK8 HashMap如何实现?

当我们使用HashMap时,它是一种数据结构,用于存储键值对。它的核心思想是通过哈希函数将键转化成唯一的数字(称为哈希码),然后使用这个数字来快速查找对应的值。HashMap内部有一个数组,称为存储桶,每个存储桶可以容纳一个或多个键值对。通过哈希函数,我们决定了键应该被存储在哪个存储桶中,然后可以通过哈希函数再次找到键对应的存储桶,以查找值。当不同的键具有相同的哈希码时,发生冲突,HashMap使用不同的方法来解决这些冲突,例如将它们存储在同一个存储桶中,并使用链表或其他数据结构来组织。如果存储桶变满,HashMap会自动扩展以保持高效性能。最终,HashMap允许我们通过键快速查找、插入和删除值,使其成为编程中常用的数据结构之一。

20.HashMap 是如何扩容的?

当 HashMap 中元素个数超过临界值时会自动触发扩容,这个临界值有一个计算公式。

threashold=loadFactor*capacity(屏幕显示)。

loadFactor (扩容因子)的默认值是 0.75,capacity (初始空间)的默认值是 16,也就是元素个数达到 12 的时候触发扩容。扩容后的大小是原来的 2 倍。由于动态扩容机制的存在,所以我们在实际应用中,需要注意在集合初始化的时候明确指定集合的大小。避免频繁扩容带来性能上的影响。

HashMap 的最大容量是Integer.MAX_VALUE,也就是 2 的 31 次方-1。

部分内容从@pdai(https://pdai.tech/md/interview/x-interview.html)和网上整理所得:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值