2023Java八股文学习笔记

Java八股文

1.为什么 Java 语言不支持多重继承?

为了程序的结构能够更加清晰从而便于维护。假设 Java 语言支持多重继承,类 C 继承自类 A 和类 B,如果类 A 和 B 都有自定义的成员方法

f()

,那么当代码中调用类 C 的

f()

会产生二义性。

Java 语言通过实现多个接口间接支持多重继承,接口由于只包含方法定义,不能有方法的实现,类 C 继承接口 A 与接口 B 时即使它们都有方法

f()

,也不能直接调用方法,需实现具体的

f()

方法才能调用,不会产生二义性。

多重继承会使类型转换、构造方法的调用顺序变得复杂,会影响到性能

2.重载与覆盖(重写)的区别?

  • 覆盖是父类与子类之间的关系,是垂直关系;重载是同一类中方法之间的关系,是水平关系。

  • 覆盖只能由一个方法或一对方法产生关系;重载是多个方法之间的关系。

  • 覆盖要求参数列表相同;重载要求参数列表不同。

  • 覆盖中,调用方法体是根据对象的类型来决定的,而重载是根据调用时实参表与形参表来对应选择方法体。

  • 重载方法可以改变返回值的类型,覆盖方法不能改变返回值的类型

3.equals和==

==比较的是两个对象在栈内存中存放的堆内存的地址是否相同,即用来判断两个指针是否指向同一对象。==两边的操作数必须是同一类型。例如int a=10与long b=10L,double c=10.00都是==为true的。因为他们都指向地址为10的堆。

equals比较的是两个对象的值是否相同。

4.instanceof关键字:

instanceof是一个用来测试一个对象是不是一个类的实例的,用法为:

boolean result = obj instanceof Class

5.java的自动拆箱和自动装箱

自当拆箱和自动装箱是Java的封装数据类型和基本数据类型发生转换的时候出现的,javaSE5之后提供了自动装箱和自动拆箱机制方便的进行转换,当基本数据类型转换为封装类型,叫做自动装箱,当封装类型转换为基本类型,叫做自动拆箱。自动装箱调用方法valueOf,自动拆箱调用方法xxValue。

注意:在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,就返回指向IntegerCache.cache中以及存在的对象的引用,否则创建一个新的对象。

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False。

如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。

两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。

浮点数类型特殊的在于,每一次创建的对象都是不相等的,因为某个范围内浮点数的个数是不确定的。

以下代码输出false,fasle

public class Main { public static void main(String[] args) { Double i1 = 100.0; Double i2 = 100.0; Double i3 = 200.0; Double i4 = 200.0; System.out.println(i1==i2); System.out.println(i3==i4); } }

6.String,StringBuffer.StringBuilder

String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的 字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成 新的String对象。StringBuffer和StringBuilder他们两都继承了AbstractStringBuilder抽象类,从 AbstractStringBuilder抽象类中我们可以看到 他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和 StringBuilder来进行操作。 另外StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所 以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

7.ArrayList的底层原理和扩容机制

ArrayList是一个能够实现自动扩容的数组,可以看作一个可扩展可变的数组,它的底层依然是一个数组,初始容量是10,每次扩容扩大到原来的1.5倍。ArrayList是线程不安全的

ArrayList方便查找,查询效率高,但是增删数据的效率低。

ArrayList的扩容机制:

扩容可分为两种情况:

  第一种情况,当ArrayList的容量为0时,此时添加元素的话,需要扩容,三种构造方法创建的ArrayList在扩容时略有不同:

    1.无参构造,创建ArrayList后容量为0,添加第一个元素后,容量变为10,此后若需要扩容,则正常扩容。

    2.传容量构造,当参数为0时,创建ArrayList后容量为0,添加第一个元素后,容量为1,此时ArrayList是满的,下次添加元素时需正常扩容。

    3.传列表构造,当列表为空时,创建ArrayList后容量为0,添加第一个元素后,容量为1,此时ArrayList是满的,下次添加元素时需正常扩容。

  第二种情况,当ArrayList的容量大于0,并且ArrayList是满的时,此时添加元素的话,进行正常扩容,每次扩容到原来的1.5倍。

详细参考:浅谈 ArrayList 及其扩容机制 - 城北有个混子 - 博客园 (cnblogs.com)

8.ArrayList和LinkedList的区别

ArrayList

  • 优点:ArrayList 是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。

  • 缺点:因为地址连续,ArrayList 要移动数据,所以插入和删除操作效率比较低。

LinkedList

  • 优点:LinkedList 基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址。对于新增和删除操作,LinkedList 比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景。

  • 缺点:因为 LinkedList 要移动指针,所以查询操作性能比较低。

适用场景分析

当需要对数据进行对随机访问的时候,选用 ArrayList。

当需要对数据进行多次增加删除修改时,采用 LinkedList。

如果容量固定,并且只会添加到尾部,不会引起扩容,优先采用 ArrayList。

当然,绝大数业务的场景下,使用 ArrayList 就够了,但需要注意避免 ArrayList 的扩容,以及非顺

序的插入。

9.HashMap和Hashtable的区别:

1.两者的父类不同

HashMap继承自AbstractMap类,而Hashtable是继承自Dictionary类。不过它们都同时实现了map、Cloneable、Serializable(可序列化)这三个接口。

2.对外提供的接口不同

Hashtable比HashMap多提供了elments()和contains()两个方法。elments()方法继承自Hashtable的父类Dictionary。elments()方法用于返回此Hashtable中的value的枚举。

contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()一致。事实上,containsValue()就是调用了一下contains()方法。

3.对null的支持不同:

Hashtable:key和value都不能为null、

HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null。

所以HashMap的key和value是可以同时为null的

4.安全性不同

HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。

Hashtable是线程安全的,他的每个方法都有synchronized关键字,因此可直接用于多线程中。虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable,这样设计是合理的,因为大部分的使用场景都是单线程。需要多线程时可以使用线程安全的ConcurrenthashMap。

5.hash值的计算方式不同

hash表获取值的方式:为了得到元素的位置,首先需要根据元素的key计算出一个hash值,然后再用这个hash值来计算得到的最终的位置。

HashMap:

使用了一个新的hash方法计算了key的hash值,降低hash冲突的可能性,先调用hashCode方法计算出来一个hash值,再将hash值与右移16位之后hash值相异或,从而得到新的hash值。源码如下:

static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

Hashtable:计算key的hashCode()作为hash值。

6.利用hash值求索引的方式不同:

HashMap在求索引的时候,利用Hash值与哈希表的长度-1相与,即index = (n - 1) & hash,这也是为什么Hashmap的表长度必须是2的幂次,因为是取模得到索引值,故这样取模时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。

//取模 static int indexFor(int h, int length) { //jdk1.7的源码,jdk1.8没有这个方法,但是实现原理一样的 return h & (length-1); }

Hashtable:HashTable在求hash值位置索引时计算index的方法:

int index = (hash & 0x7FFFFFFF) % tab.length;

&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号位改变,而后面的位都不变。

7.扩容方式不同

当容量不足时要进行resize方法,而resize的两个步骤:

①扩容;

②rehash:这里HashMap和HashTable都会会重新计算hash值而这里的计算方式就不同了;

HashMap 哈希扩容必须要求为原容量的2倍,而且一定是2的幂次倍扩容结果,而且每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入;(详情后面讲)

而Hashtable扩容为原容量2倍加1;

9.HashMap底层原理和扩容机制:(建议看看源码)

10.HashMap和ConcurrentHashMap的区别

单开一帖写这两个,明天写

11.Java 和 C++ 的区别?

虽然,Java 和 C++ 都是面向对象的语言,都支持封装、继承和多态,但是,它们还是有挺多不相同的地方:

  • Java 不提供指针来直接访问内存,程序内存更加安全

  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。

  • Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存。

  • C ++同时支持方法重载和操作符重载,但是 Java 只支持方法重载(操作符重载增加了复杂性,这与 Java 最初的设计思想不符)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值