部分面试题整理【part1-JavaSE】

Object类自带哪些方法?

  1. registerNatives() //私有方法
  2. getClass() //返回此 Object 的运行类。
  3. hashCode() //用于获取对象的哈希值。
  4. equals(Object obj) //用于确认两个对象是否“相同”。
  5. clone() //创建并返回此对象的一个副本。
  6. toString() //返回该对象的字符串表示。
  7. notify() //唤醒在此对象监视器上等待的单个线程。
  8. notifyAll() //唤醒在此对象监视器上等待的所有线程。
  9. wait(long timeout) //在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
  10. wait(long timeout, int nanos) //在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
  11. wait() //用于让当前线程失去操作权限,当前线程进入等待序列
  12. finalize() //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

String、StringBuffer、StringBuilde的区别

Java语言有关字符串的类有三种

  • java.lang.String:一般作为简单字符串类型
  • java.lang.StringBuffer:字符串缓冲区
  • java.lang.StringBuilder:字符串缓冲区

对于三者的使用:

  1. 如果要操作少量的数据用 : String
  2. 单线程操作字符串缓冲区 下操作大量数据 : StringBuilder
  3. 多线程操作字符串缓冲区 下操作大量数据 : StringBuffer

区别在两个方面:

1.三者在执行速度方面的区别:
StringBuilde > StringBuffer > String

String最慢的原因是:

  • String为字符串常量
  • StringBuilder和StringBuffer是字符串变量
  • String一旦创建之后是不可更改的,而StringBuilder和StringBuffer是变量是可以更改的

Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢

2.线程安全

在线程安全上
StringBuilder是线程不安全的,StringBuffer线程安全

如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的
但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。


collection和collections的区别(集合架构)

Collection是集合类的上级接口,有子接口List、Set、Queue
collections是针对集合类的一个帮助类,不能实例化,提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作


对String的理解

  • 通过构造方法创建字符串对象是在堆内存。
    直接赋值方式创建对象是在方法区的常量池。
  • String类是被final修饰,因此是不可以继承的,是不可变的;
  • String类的本质是字符数组char[];
  • Java运行时会维护一个String 池,“字符串缓冲区”。String池用来存放运行时中产生的各种字符串,并且创建的对象仅仅存在于方法的堆栈区。

String对象的特点:不变性,常量池优化和String类的final定义。

A、不变性
String对象的状态在其被创建之后就不在发生变化。其设计者使用了 java 模式中不变模式。
作用:在一个对象被多线程共享,而且被频繁的访问时,可以省略同步和锁的时间,从而提高性能。

B、 常量池优化
如同我上面的解释,即当两个 String 对象拥有同一个值的时候,它们都只是引用了常量池中的同一个地址。

C、final 定义
String类以final进行了修饰,主要是为了“效率” 和 “安全性” 的缘故。若 String允许被继承, 由于它的高度
被使用率, 可能会降低程序的性能,所以String被定义成final。


Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?

Set 里的元素是不能重复的,元素重复与否是使用 **equals()**方法进行判断的。

  • 注意:如果一个类没有自己定义 equals方法,它默认的 equals 方法(从 Object 类继承的)就是使用 “==”操作符,也是在比较两个变量指向的对象是否是同一对象,这时候使用equals 和使用 == 会得到同样的结果,如果比较的是两个独立的对象则总返回 false

从底层区分下ArrayList和LinkedList,Arraylist、LinkedList、HashMap的初始大小以及如何扩容

  • ArrayList: 基于动态数组线程不同步。可通过数组下标的形式进行查找,查询效率高。
    扩容点规则是,新增的时候发现容量不够用了,就去扩容
    扩容大小规则是,扩容后的大小= 原始大小+原始大小/2 + 1。

  • LinkedList: 基于链表,线程不同步。插入/删除速度快,查询效率极低。没有扩容的机制

  • Arraylist初始大小:10
    将原数组的内容复制到新数组当中,并且后续增加的内容都会放到这个新的数组当中去。

  • LinkedList没有初始化大小

  • HashMap初始容量:16
    扩容是创建一个新的数组,并将原来的数组元素迁移到新数组中,根据hash值重新分配


HashMap、Hashtable的区别

区别:
  • 继承的父类不同: Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但二者都实现了Map接口
  • 线程安全性不同:Hashtable 中的方法是Synchronize,而HashMap中的方法在缺省情况下是非Synchronize的。
  • hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法;
  • HashMap可以存储null键和null值,而HashTable不允许;
  • 哈希值的使用不同:HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

HashMap的底层

  • HashMap 是一个用于存储Key-Value 键值对的集合,每一个键值对也叫做Entry。这些个Entry 分散存储在一个数组当中,这个数组就是HashMap 的主干。HashMap 数组每一个元素的初始值都是Null。
  • HashMap的实现原理:
    首先有一个每个元素都是链表的数组,当添加一个元素(key-value)时,就首先计算元素key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这时就添加到同一hash值的元素的后面,他们在数组的同一位置,但是形成了链表,同一各链表上的Hash值是相同的,所以说数组存放的是链表。而当链表长度太长时,链表就转换为红黑树,这样大大提高了查找的效率。

HashMap、LinkedHashMap、ConcurrentHashMap的异同

  • ConcurrentHashMap是使用了锁分段技术来保证线程安全的

锁分段技术:首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问

  • ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对Hash表的不同Segment进行的修改。
  • ConcurrentHashMap 是在每个段(segment)中线程安全的
  • LinkedHashMap维护一个双链表,可以将里面的数据按写入的顺序读出
  • ConcurrentHashMap的应用场景是高并发,但是并不能保证线程安全,而同步的HashMap和HashTable的是锁住整个容器,而加锁之后ConcurrentHashMap不需要锁住整个容器,只需要锁住对应的segment就好了,所以可以保证高并发同步访问,提升了效率。
  • ConcurrentHashMap能够保证每一次调用都是原子操作,但是并不保证多次调用之间也是原子操作。

Java中HashMap的key值要是为类对象,则该类需要满足什么条件?

由于是自定义类型,所以HashMap中的equals()方法和hashCode()方法都需要自定义覆盖。

不然内容相同的对象对应的hashCode会不同,无法发挥算法的正常功能,覆盖equals方法,应该就相当于c++重载==运算符来保证能判断是否相等。只不过java没有自定义重载运算符这个功能的,需要进行方法覆盖。


Comparable和Comparator接口是干什么的?列出它们的区别。

  • Comparable定义在Person的内部

      public class Persion implements Comparable {..比较Person的大小..}
    

因为已经实现了比较器,那么Person现在是一个可以比较大小的对象了,它的比较功能和String完全一样,可以随时随地的拿来比较大小,因为Person现在自身就是有大小之分的。
Collections.sort(personList) 可以得到正确的结果。

  • Comparator是定义在Person的外部的,此时Person类的结构不需要有任何变化,如public class Person{ String name; int age },然后另外定义一个比较器:public PersonComparator implements Comparator() {…比较Person的大小…},在PersonComparator里面实现了怎么比较两个Person的大小. 所以用这种方法,要对一个 personList进行排序的时候除了要传递personList过去,还需要把 PersonComparator传递过去,因为怎么比较Person的大小是在PersonComparator里面实现的,如

      Collections.sort( personList , new PersonComparator() )。
    

什么是流?按照传输的单位,分成哪两种流?他们的父类叫什么?

JAVA程序中对数据的输入输出称为流,分为字节流,字符流。

  • 字节流的父类:InputStream OutputStream;
  • 字符流的父类:Reader Writer

ThreadLocal是什么

ThreadLocal一般称为线程局部变量,是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

void set(Object value)设置当前线程的线程局部变量的值。

public Object get()该方法返回当前线程所对应的线程局部变量。

public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

ThreadLocal是如何为每个线程创建变量的副本的:

首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

总结:

a、实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的

b、为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;

c、在进行get之前,必须先set,否则会报空指针异常;如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法


什么叫对象序列化,什么是反序列化,如何实现对象序列化

  • 序列化:将对象转换成字节序列的过程
    序列化实质:将对象转换成二进制
  • 反序列化:将字节序列恢复成对象的过程
  • 实现序列化只需实现Serilazable接口即可

java中的序列化机制能够将一个实例对象(只序列化对象的属性值,而不会去序列化什么所谓的方法。)的状态信息写入到一个字节流中使其可以通过socket进行传输、或者持久化到存储数据库或文件系统中;然后在需要的时候通过字节流中的信息来重构一个相同的对象。


开启线程的三种方式

  • 继承Thread,重写run方法。Thread t = new MyThread1();
  • 实现Runnable接口,实现run方法。Thread t1 = new Thread(new MyThread3())
  • 实现Callable,实现call方法

Runnable / Callable 区别
Runnable执行方法是run(),Callable是call()
实现Runnable接口的任务线程无返回值;实现Callable接口的任务线程能返回执行结果
call方法可以抛出异常,run方法若有异常只能在内部消化


进程,线程,协程之间的区别

  1. 进程是系统资源分配的最小单位,由于进程间是隔离的,各自拥有自己的内存资源, 因此相对于线程比较安全,进程内至少有一个线程;
  2. 线程属于进程,是进程内的一个执行单元,共享进程的内存地址空间,线程是CPU调度的最小单位;
  3. 协程是属于线程的,协程的调度切换是用户手动切换的,因此更加灵活。一个线程可以多个协程。线程进程都是同步机制,而协程则是异步。协程能保留上一次调用时的状态,每次重入时,就相当于进入上一次调用的状态。

线程之间是如何通信的

线程之间通信方式


在Java中wait和seelp方法的不同

  • sleep()方法属于Thread类
  • wait()方法属于Object类中的。

run()和start()方法区别

什么是线程池,为什么要用线程池,说出几种常见的线程池

描述一下线程的生命周期(描述5个状态)

为什么会发生死锁,如何避免死锁

多线程中的锁有哪些种类,说下区别

Synchronized和Lock锁的区别

Synchronized有什么缺陷

并行与并发的异同

Java如何实现并发

什么是深克隆浅克隆?描述两个的区别

什么是双亲委派模型?

什么是类加载?类加载的过程是怎样的?

常用开发模式

https://blog.csdn.net/wdvceafvcsrgfv/article/details/78848697


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值