【搞定Java基础】面试题整理

博主秋招提前批已拿百度、字节跳动、拼多多、顺丰等公司的offer,可加微信一起交流秋招面试经验。

面向对象的三大特性

继承:一般只能单继承,内部类可以实现多继承、接口可以多继承

封装:访问权限控制public-->protected-->package-->private

多态:编译时多态:体现在向上转型和向下转型,通过引用类型判断调用哪个方法(静态分派---->重写)

           运行时多态:体现在同名函数通过不同参数实现多种方法(动态分派---->重载)。


创建对象的5种方法

具体讲解:https://blog.csdn.net/pcwl1206/article/details/84471416

1、使用new关键字;

2、Class类的newInstance()方法;

3、Constructor类的newInstance()方法;

4、Object对象的clone方法;

5、对象的反序列化。


String、StringBuffer和StringBuilder的区别?

  • 运行速度快慢为:StringBuilder > StringBuffer > String

String最慢的原因:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。

  • String:适用于少量的字符串操作的情况;
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况(线程不安全);
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况(线程安全)。

Object的方法有哪些:比如有wait方法,为什么会有wait、notify、notifyAll?

Object的方法有:

  • protected Object clone()
  • boolean equals(Object obj)
  • protected void finalize()
  • Class< > getClass()
  • int hashCode()
  • void notify()
  • void notifyAll()
  • String toString()
  • void wait()
  • void wait(long timeout)
  • void wait(long timeout, int nanos)

wait、notify、notifyAll:

1.使用wait()、notify()和notifyAll()时需要首先对调用对象加锁;

2.调用wait()方法后,线程状态会从RUNNING变为WAITING,并将当线程加入到lock对象的等待队列中;

3.调用notify()或者notifyAll()方法后,等待在lock对象的等待队列的线程不会马上从wait()方法返回,必须要等到调用notify()或者notifyAll()方法的线程将lock锁释放,等待线程才有机会从等待队列返回。这里只是有机会,因为锁释放后,等待线程会出现竞争,只有竞争到该锁的线程才会从wait()方法返回,其他的线程只能继续等待;

4.notify()方法将等待队列中的一个线程移到lock对象的同步队列,notifyAll()方法则是将等待队列中所有线程移到lock对象的同步队列,被移动的线程的状态由WAITING变为BLOCKED;

5.wait()方法上等待锁,可以通过wait(long timeout)设置等待的超时时间。


wait和sleep的区别?

sleep方法属于线程,wait方法属于对象。

sleep休眠当前线程,不会释放对象锁,wait使当前线程进入等待状态,释放对象锁,只有针对此对象调用notify()方法(且共享对象资源释放)后本线程才会继续执行。


Java的多态表现在哪里?

多态是同一个行为具有多个不同表现形式或形态的能力。

多态就是同一个接口,使用不同的实例而执行不同操作。比如同一个打印机,可以打印黑白的纸张也可以打印彩色的,同样是人,却有黑人白人之分。


接口有什么用?

接口是一种规范,在这里举两个例子:

1.接口就比如KFC,你一听KFC就知道是卖炸鸡薯条的,他可以有不同的分店,也可以有自己的创新食品(多态),但是招牌炸鸡、鸡肉卷、全家桶什么的肯定会有。你不用进店看菜单就知道他有,但如果不叫KFC换成炸鸡店你也可以吃到炸鸡,但是你不进店看菜单你不知道他具体都卖的有哪些食品,这就是接口的好处;

2.比如电插孔,多是两孔和三孔的那种,如果没有这种规范那每家电器公司都来做一种插孔的话,试想一下插头换了怎么办?是不是只能买原装的来替换了。


集合类:List和Set比较,各自的子类比较(ArrayList、Vector、LinkedList;HashSet、TreeSet)

1、List

List:元素是有顺序的,元素可以重复因为每个元素有自己的角标(索引)

  •  |-- ArrayList:底层是数组结构,特点是:查询很快,增删稍微慢点,线程不同步:A线程将元素放在索引0位置,CPU调度线程A停止,B运行,也将元素放在索引0位置,当A和B同时运行的时候Size就编程了2;
  •  |-- LinkedList:底层使用的是链表数据结构,特点是:增删很快,查询慢。线程不安全,线程安全问题是由多个线程同时写或同时读写同一个资源造成的;
  •  |--Vector:底层是数组数据结构,线程同步,Vector的方法前面加了synchronized关键字,被ArrayList代替了,现在用的只有他的枚举。

2、Set

Set:元素是无序的,且不可以重复(存入和取出的顺序不一定一致),线程不同步。set底层是使用Map实现的,故可以通过ConcurrentHashMap的方式变通实现线程安全的Set。

  • |--HashSet:底层是哈希表数据结构。根据hashCode和equals方法来确定元素的唯一性。

   hashCode和equals:

hashCode()和equals()方法的作用一样:都是用来比较两个对象是否相等一致。equals比较的比较全面,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率高。

   equals()相等的两个对象他们的hashCode()肯定相等,也就是equal()是绝对可靠的。但是hashCode()相等的两个对象他们的equals()不一定相等,hashCode()不是绝对可靠的。

3、Map

Map:这个集合是存储键值对的,一对一对往里存,而且要确保键的唯一性(01,张三)这样的形式打印出来就是  01=张三

  • |--HashTable:底层是哈希表数据结构,不可以存入null键和null值,该集合线程是同步的,效率比较低。出现于JDK1.0。线程安全,使用synchronized锁住整张Hash表实现线程安全,即每次锁住整张表让线程独占;
  • |--HashMap:底层是哈希表数据结构,可以存入null键和null值,线程不同步,效率较高,代替了HashTable,出现于JDK 1.2;
  • |--TreeMap:底层是二叉树数据结构,线程不同步,可以用于对map集合中的键进行排序。

ConcurrentHashMap:线程安全,允许多个修改操作并发进行,其关键在于使用了锁分离技术,它使用了多个锁来控制对hash表的不同部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的Hashtable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

当两个对象需要对比的时候,首先用hashCode()去对比,如果不一样,则表示这两个对象肯定不相等(也就不用再比较equals()了),如果hashCode()相同,再比较equals(),如果equals()相同,那两个对象就是相同的。

  • |--TreeSet:可以对Set集合中的元素进行排序(自然循序),底层的数据结构是二叉树。

HashMap的底层实现,之后会问ConcurrentHashMap的底层实现

  • HashMap

HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。允许使用null值和null键。

HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。

HashMap是基于hash算法实现的,通过put(key,value)存储对象到HashMap中,也可以通过get(key)从HashMap中获取对象。

当我们使用put的时候,首先HashMap会对key的hashCode()的值进行hash计算,根据hash值得到这个元素在数组中的位置,将元素存储在该位置的链表上。

当我们使用get的时候,首先HashMap会对key的hashCode()的值进行hash计算,根据hash值得到这个元素在数组中的位置,将元素从该位置上的链表中取出

当多线程的情况下,可能产生条件竞争。当重新调整HashMap大小的时候,确实存在条件竞争,如果两个线程都发现HashMap需要重新调整大小了,

它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的数组位置的时候,

HashMap并不会将元素放在LinkedList的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了

  • ConcurrentHashMap

ConcurrentHashMap基于双数组和链表的Map接口的同步实现。 

ConcurrentHashMap中元素的key是唯一的、value值可重复。

ConcurrentHashMap不允许使用null值和null键。 

ConcurrentHashMap是无序的。

为什么使用ConcurrentHashMap:

我们都知道HashMap是非线程安全的,当我们只有一个线程在使用HashMap的时候,自然不会有问题,但如果涉及到多个线程,并且有读有写的过程中,HashMap就会fail-fast。要解决HashMap同步的问题,我们的解决方案有:Hashtable 或者Collections.synchronizedMap(hashMap) 。

这两种方式基本都是对整个hash表结构加上同步锁,这样在锁表的期间,别的线程就需要等待了,无疑性能不高,所以我们引入ConcurrentHashMap,既能同步又能多线程访问。

ConcurrentHashMap的数据结构:

ConcurrentHashMap的数据结构为一个Segment数组,Segment的数据结构为HashEntry的数组,而HashEntry存的是键值对,可以构成链表。可以简单的理解为数组里装的是HashMap。


如何实现HashMap顺序存储?

可以参考LinkedHashMap的底层实现。LinkedHashMap底层使用哈希表与双向链表来保存所有元素,它维护着一个运行于所有条目的双向链表,此链表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。

1.按插入顺序的链表:在LinkedHashMap调用get方法后,输出的顺序和输入时的相同,这就是按插入顺序的链表,默认是按插入顺序排序;

2.按访问顺序的链表:在LinkedHashMap调用get方法后,会将这次访问的元素移至链表尾部,不断访问可以形成按访问顺序排序的链表。简单的说,按最近最少访问的元素进行排序(类似LRU算法)。


 

 

 

 

 

 


参考及推荐:

1、深入浅出Java核心技术专栏:https://blog.csdn.net/a724888/column/info/21930

2、Java基础面试知识点总结:https://blog.csdn.net/a724888/article/details/70038420

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值