2020年Java面试题总结,这里总结一下前段时间面视过程中问到的一些问题,主要是Java的一些基础知识。有的是网络上收集的,也有的是自己遇到总结的,整体来说都还在这个范围内,这里记一下,说不定后面还会看一下,有也许对大家也有所帮助,校招面视一般都是基础考察,对于框架一般问的比较少。下面部分可能描述的不是很准确,主要为了更加容易理解。

1.java面向对象主要的几个特征是什么?

  • ①封装:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
  • ②继承:继承是类与类的一种关系,是一种“is a”的关系。子类通过extends关键字可以继承父类的所有非私有属性和方法
  • ③多态:指的就是父类引用指向子类对象,调用方法时会调用子类的实现而不是父类的实现。
  • ④抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象不打算了解全部问题,而是选择其中的一部分,暂时不用部分细节。抽象包括两个方面:过程抽象;数据抽象。

2.简述一下面向对象的”六原则一法则”

  • 单一职责原则:一个类只做它该做的事情。
  • 开闭原则:软件实体应当对扩展开放,对修改关闭。
  • 依赖倒转原则:面向接口编程。
  • 里氏替换原则:任何时候都可以用子类型替换掉父类型。
  • 接口隔离原则:接口要小而专,绝不能大而全。
  • 合成聚合复用原则:优先使用聚合或合成关系复用代码。在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。
  •  迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。

3.访问修饰符相关

4.抽象类和接口

  • ①抽象类可以有构造方法,接口中不能有构造方法。
  • ②抽象类中可以有普通成员变量,接口中没有普通成员变量
  • ③抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,JDK1.8之后可以有默认方法。
  • ④ 抽象类中的抽象方法的访问类型可以是public,protected和,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
  • ⑤抽象类中可以包含静态方法,接口中不能包含静态方法
  • ⑥ 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final,需要显式初始化。
  • ⑧ 一个类可以实现多个接口,但只能继承一个抽象类。

5.为什么要重写hashcode( )和equals( )?

  • ①equals方法:默认的情况下,比较的是对象的引用是否指向同一块内存地址,如果是两个对象,但想判断两个对象的属性是否相同,则重写equals()方法。
  • ②hashcode方法:hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSet、HashMap以及HashTable。

一致性:假设两个对象,重写了其equals方法,其相等条件是属性相等,就返回true。如果不重写hashcode方法,其返回的依然是两个对象的内存地址值的hash值,必然不相等。这就出现了equals方法相等,但是hashcode不相等的情况。,这不符合hashcode的规则。

6.为什么一般不直接equals比较值是否相同,而先要计算hashCode?

hash算法是二进制算法,计算式本质是二进制,所以hash算法速度很快。如若hashCode不同则可直接存储不用equlas比较。所以先计算hashCode大大加快了存储速率。

7.HashMap、HashTable、HashSet区别

  • HashTable:key和value都不允许出现null值,Hashtable是同步的,Hashtable继承自Dictionary类,实现了Map接口,保留了contains,containsValue和containsKey,其中contains和containsValue功能相同,HashTable初始大小是11,扩容因子:0.75,2*old+1,
  •  ②HashMap:null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。HashMap保留了containsValue和containsKey因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。 HashMap初始大小是16,扩容因子:0.75,扩容机制2*old,put()添加新元素。
  • ③HashSet:实现了Set接口,它不允许集合中出现重复元素,可以有且仅有一个null,HashSet先计算hashCode,如果有hashCode相同的值,则用equals比较值是否相同,值相同则不存储。如果hashCode不同,则直接存储。当我们提到HashSet时,第一件事就是在将对象存储在HashSet之前,要确保重写hashCode()方法和equals()方法,这样才能比较对象的值是否相等,确保集合中没有储存相同的对象。add()添加新元素。

8.ConcurrentHashMap和HashMap和HashTable的区别?

  • ①Hashtable的任何操作都会把整个表锁住,是阻塞的。好处是总能获取最实时的更新,比如说线程A调用putAll写入大量数据,期间线程B调用get,线程B就会被阻塞,直到线程A完成putAll,因此线程B肯定能获取到线程A写入的完整数据。坏处是所有调用都要排队,效率较低。
  • ②ConcurrentHashMap 是设计为非阻塞的。在更新时会局部锁住某部分数据,但不会把整个表都锁住。同步读取操作则是完全非阻塞的。好处是在保证合理的同步前提下,效率很高。坏处 是严格来说读取操作不能保证反映最近的更新。例如线程A调用putAll写入大量数据,期间线程B调用get,则只能get到目前为止已经顺利插入的部分数据。
  • ③HashMap线程不安全。HashMap根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快 的访问速度,但遍历顺序却是不确定的。 HashMap最多只允许一条记录的键为null,允许多条记 录的值为 null。

9.HashMap和TreeMap的区别?

  • HashMap底层是用数组做的,TreeMap是基于树做的这么做的结果就是HashMap的数据在不停的添加的时候效率会比较低,而对于查找的效率是比较快的,TreeMap对于添加的效率是比较高的但是对于查找的效率要相对比较低一些
  • TreeMap是排序的而HashMap不是。①往treeMap里添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序特性进行排序储存。②往treeMap里添加元素的时候,如果元素的键本身 不具备 自然顺序的特性,那么该键所属的类必须要实现Comparable接口,把键的比较规则定义在comparTo(T o)方法上。

10.ArrayList和LinkedList区别?

①HashMap底层是用数组做的,TreeMap是基于树做的这么做的结果就是HashMap的数据在不停的添加的时候效率会比较低,而对于查找的效率是比较快的,TreeMap对于添加的效率是比较高的但是对于查找的效率要相对比较低一些

1、数据结构不同

ArrayList是Array(动态数组)的数据结构,LinkedList是Link(链表)的数据结构。

2、效率不同

当随机访问List(get和set操作)时,ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找,ArrayList可以通过数组下标随机访问。

当对数据进行增加和删除的操作(add和remove操作)时,LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。LinkedList只需要改变相关的指针即可

11.StingBuilder和StringBuffer的区别?

区别

  • ①运行速度:StringBuilder>StringBuffer>String
  • ②线程安全:StringBuffer线程安全,StringBuilder线程不安全。

适用场景:

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

12.描述 Java 中的重载和重写?

重载和重写都允许你用相同的名称来实现不同的功能,但是重载是编译时活动,而重写是运行时活动。你可以在同一个类中重载方法,但是只能在子类中重写方法。重写必须要有继承。

  • 重写:1、在子类中可以根据需要对从基类中继承来的方法进行重写。2、重写的方法和被重写的方法必须具有相同方法名称、参数列表和返回类型。3、重写方法不能使用比被重写的方法更严格的访问权限。
  • 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。

13.Java 中,Comparator 与Comparable 有什么不同?

Comparable 接口用于定义对象的自然顺序,是排序接口,而 comparator 通常用于定义用户定制的顺序,是比较接口。我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。

14.Collection与Collections的区别是什么?

  • Collection是Java集合框架中的基本接口;
  • Collections是Java集合框架提供的一个工具类,其中包含了大量用于操作或返回集合的静态方法。

15.Java常见的异常

16.Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?

sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

17.线程的sleep()方法和yield()方法有什么区别?

  • sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
  • ② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
  • ③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
  • ④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

18.请说出与线程同步以及线程调度相关的方法。

  • wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
  • sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
  • notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
  • notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

19.实现线程的几种方法

  • (1)继承Thread类,重写run函数
  • (2)实现Runnable接口,重写run函数
  • (3)实现Callable接口,重写call函数

20.线程池

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);}
  • 1、corePoolSize(线程池基本大小):当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
  • 2、maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。
  • 3、keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
  • 4、workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。
  • 5、threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)。
  • 5、handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略。

21.线程池拒绝策略

  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  2. ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
  4. ThreadPoolExecutor.CallerRunsPolicy:如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务

22.简述synchronized 和java.util.concurrent.locks.Lock的异同?

Lock是Java 5以后引入的新的API,和关键字synchronized相比主要相同点:Lock 能完成synchronized所实现的所有功能;主要不同点:Lock有比synchronized更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且最好在finally 块中释放(这是释放外部资源的最好的地方)

23.事务的ACID是指什么

  • 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作的失败都会导致整个事务的失败;
  • 一致性(Consistent):事务结束后系统状态是一致的;
  • 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
  • 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。

24.获得一个类的类对象有哪些方式?

  • 方法1:类型.class,例如:String.class
  • 方法2:对象.getClass(),例如:”hello”.getClass()
  • 方法3:Class.forName(),例如:Class.forName(“java.lang.String”)

关于Java的基础知识大概就是这些,还有JVM、分布式的一些但是问的都不太深入或者仔细,这里不做赘述。另外一般还会问到计算机网络和操作系统的一些知识TCP/IP网络传输模型、TCP三次握手、四次挥别,UDP的无连接等等。

2020年Java面试题总结
2020年Java面试题总结
本文最后更新于2020年12月6日,已超过 1 个月没更新!