您的位置 首页 java

Java SE相关面试题汇总

1 你是怎样理解面向对象的

面向对象是利用编程语言对现实事物进行抽象。面向对象具有以下四大特征:

(1)继承: 继承是从已有类得到继承信息创建新类的过程

(2)封装: 通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。

(3)多态性: 多态性是指允许不同子类型的对象对同一消息作出不同的响应。

(4)抽象: 抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。

2 int和Integer有什么区别,以及以下程序结果

(1)Integer是int的包装类,int则是 Java 的一种基本数据类型

(2)Integer变量必须实例化后才能使用,而int变量不需要

(3)Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值

(4)Integer的默认值是null,int的默认值是0

  package com.atguigu.interview.chapter03;
/**
 * @author : atguigu.com
 * @since 2019/7/28
 */ public class Test01 {
 public static void main(String[] args) {
 Integer a = 127;
 Integer b = 127;
 Integer c = 128;
 Integer d = 128;
/**
 * @author : atguigu.com
 * @since 2019/7/28
 Integer内部维护了一个IntegerCache缓冲区,范围是从-128到+127范围的
 Integer类型数组。只要Integer赋值在-128到+127范围内,统一都是用次缓
 冲区中的数据。如果不在此范围,则新创建对象。
 */ System.out.println(a == b); //true
 System.out.println(c == d); //false
}
}  

3 ==和Equals区别

(1) ==

如果比较的是基本数据类型,那么比较的是变量的值

如果比较的是引用数据类型,那么比较的是地址值(两个对象是否指向同一块内存)

(2)equals

如果没重写equals方法比较的是两个对象的地址值

如果重写了equals方法后我们往往比较的是对象中的属性的内容

equals()方法最初在Object类中定义的,默认的实现就是使用==

Java SE相关面试题汇总

4 谈谈你对反射的理解

(1) 反射机制 :

所谓的反射机制就是java语言在运行时拥有一项直观的能力。通过这种能力可以彻底地了解自身的情况为下一步的动作做准备。

Java的反射机制的实现要借助于4个类:Class,Constructor,Field,Method;

其中Class代表的是类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略地看到一个类的各个组 成部分。

(2)Java反射的作用:

在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

(3)Java 反射机制提供功能

在运行时判断任意一个对象所属的类。

在运行时构造任意一个类的对象。

在运行时判断任意一个类所具有的成员变量和方法。

在运行时调用任意一个对象的方法

5 ArrarList和LinkedList区别

(1)ArrayList是实现了基于动态数组的数据结构,LinkedList基于 链表 的数据结构。

(2)对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。

(3)对于插入和删除操作add和remove,LinkedList比较占优势,因为ArrayList每插入或删除一条数据,都要移动插入点或删除点及之后的所有数据。

6 HashMap 底层源码,数据结构

HashMap的底层结构在jdk1.7中由数组+链表实现,在jdk1.8中由数组+链表+红黑树实现,以数组+链表的结构为例。

Java SE相关面试题汇总

Java SE相关面试题汇总

JDK1.8之前put方法:

Java SE相关面试题汇总

JDK1.8之后put方法:

Java SE相关面试题汇总

Java SE相关面试题汇总

7 HashMap和 Hashtable 区别

(1) 线程 安全性不同

HashMap是线程不安全的,Hashtable是线程安全的,其中的方法是Synchronized的,在多线程并发的情况下,可以直接使用Hashtable,但是使用HashMap时必须自己增加同步处理。

(2)是否提供contains方法

HashMap只有containsValue和containsKey方法;Hashtable有contains、containsKey和containsValue三个方法,其中contains和containsValue方法功能相同。

(3)key和value是否允许null值

Hashtable中,key和value都不允许出现null值。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。

(4)数组初始化和扩容机制

Hashtable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。

Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

3.8 TreeSet和HashSet区别

HashSet是采用hash表来实现的。其中的元素没有按顺序排列,add()、remove()以及contains()等方法都是复杂度为O(1)的方法。

TreeSet是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是add()、remove()以及contains()等方法都是复杂度为O(log (n))的方法。它还提供了一些方法来处理排序的set,如first(), last(), headSet(), tailSet()等等。

9 StringBuffer和 StringBuilder 区别

(1)StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,

(2)只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

(3)在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低

10 Final、Finally、 finalize

final: 修饰符(关键字)有三种用法:修饰类、变量和方法。修饰类时,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract(abstract修饰的类通常都需要子类来继承)在使用上可以理解为互斥的。修饰变量时,该变量使用中不被改变,必须在声明时给定初值,在引用中只能读取不可修改,即为常量。修饰方法时,也同样只能使用,不能在子类中被重写。

finally: 通常放在try…catch的后面构造最终执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。

finalize: Object类中定义的方法,Java中允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize() 方法可以整理系统资源或者执行其他清理工作。

11 什么是 java 序列化 ,如何实现 java 序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

序 列 化 的 实 现 : 将 需 要 被 序 列 化 的 类 实 现 Serializable 接 口 , 该 接 口 没 有 需 要 实 现 的 方 法 , implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用 ObjectOutputStream 对象的 writeObject(Object obj)方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。

12 Object中有哪些方法

(1)protected Object clone()—>创建并返回此对象的一个副本。
(2)boolean equals(Object obj)—>指示某个其他对象是否与此对象“相等”。
(3)protected void finalize()—>当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
(4)Class<? extends Object> getClass()—>返回一个对象的运行时类。
(5)int hashCode()—>返回该对象的哈希码值。
(6)void notify()—>唤醒在此对象监视器上等待的单个线程。
(7)void notifyAll()—>唤醒在此对象监视器上等待的所有线程。
(8)String toString()—>返回该对象的 字符串 表示。
(9)void wait()—>导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
void wait(long timeout)—>导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll()方法,或者超过指定的时间量。
void wait(long timeout, int nanos)—>导致当前的线程等待,直到其他线程调用此对象的 notify()

13 线程有几种状态,产生的条件是什么

Java SE相关面试题汇总

14 产生 死锁 的基本条件

产生死锁的原因:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的解除与预防:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和
解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确
定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态
的情况下占用资源。因此,对资源的分配要给予合理的规划。

15 什么是 线程池 ,如何使用?

线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用 new 线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。

在 JDK 的 java.util.concurrent.Executors 中提供了生成多种线程池的静态方法。

ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);

ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4);

ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();

然后调用他们的 execute 方法即可。

优点:

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

16 Java自带有哪几种线程池?

一、 ThreadPoolExecutor

ThreadPoolExecutor提供了四个构造方法:

我们以最后一个构造方法(参数最多的那个),对其参数进行解释:

  public ThreadPoolExecutor(int corePoolSize, // 1
 int maximumPoolSize, // 2
 long keepAliveTime, // 3
 TimeUnit unit, // 4
 BlockingQueue < Runnable > workQueue, // 5
 ThreadFactory threadFactory, // 6
 RejectedExecutionHandler handler) { // 7
if (corePoolSize < 0 || maximumPoolSize <= 0 ||
 maximumPoolSize < corePoolSize || keepAliveTime < 0)
 throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
 throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}  

二、预定义线程池

JDK给我们预定义的几种线程池

1、FixedThreadPool

  public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}  
  • corePoolSize与maximumPoolSize相等,即其线程全为核心线程,是一个固定大小的线程池,是其优势;
  • keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
  • workQueue 为LinkedBlockingQueue(无界阻塞队列),队列最大值为Integer.MAX_VALUE。如果任务提交速度持续大于任务处理速度,会造成队列大量阻塞。因为队列很大,很有可能在拒绝策略前,内存溢出。是其劣势;
  • FixedThreadPool的任务执行是无序的;

适用场景:可用于Web服务瞬时削峰,但需注意长时间持续高峰情况造成的队列阻塞。

2、CachedThreadPool

  public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}  
  • corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即线程数量几乎无限制;
  • keepAliveTime = 60s,线程空闲60s后自动结束。
  • workQueue 为 SynchronousQueue 同步队列,这个队列类似于一个接力棒,入队出队必须同时传递,因为CachedThreadPool线程创建无限制,不会有队列等待,所以使用SynchronousQueue;

适用场景:快速处理大量耗时较短的任务,如Netty的NIO接受请求时,可使用CachedThreadPool。

3、SingleThreadExecutor

  public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}  

这里多了一层FinalizableDelegatedExecutorService包装,以下代码解释一下其作用:

  public static void main(String[] args) {
 ExecutorService fixedExecutorService = Executors.newFixedThreadPool(1);
 ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) fixedExecutorService;
 System.out.println(threadPoolExecutor.getMaximumPoolSize());
 threadPoolExecutor.setCorePoolSize(8);
 ExecutorService singleExecutorService = Executors.newSingleThreadExecutor();
// 运行时异常 java. lang .ClassCastException
//ThreadPoolExecutor threadPoolExecutor2 = (ThreadPoolExecutor) singleExecutorService;
}  

对比可以看出,FixedThreadPool可以向下转型为ThreadPoolExecutor,并对其线程池进行配置,而SingleThreadExecutor被包装后,无法成功向下转型。因此,SingleThreadExecutor被定以后,无法修改,做到了真正的Single。

4、ScheduledThreadPool

  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
 newScheduledThreadPool调用的是ScheduledThreadPoolExecutor的构造方法,而ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,构造是还是调用了其父类的构造方法。
 public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());  

}

三、自定义线程池

以下是自定义线程池,使用了有界队列,自定义ThreadFactory和拒绝策略的demo:

 public class ThreadTest {
 public static void main(String[] args) throws InterruptedException, IOException {
 int corePoolSize = 2;
 int maximumPoolSize = 4;
 long keepAliveTime = 10;
 TimeUnit unit = TimeUnit.SECONDS;
 BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
 ThreadFactory threadFactory = new NameTreadFactory();
 RejectedExecutionHandler handler = new MyIgnorePolicy();
 ThreadPoolExecutor executor
= new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
 workQueue, threadFactory, handler);
 executor.prestartAllCoreThreads(); // 预启动所有核心线程
for (int i = 1; i <= 10; i++) {
 MyTask task = new MyTask(String.valueOf(i));
 executor.execute(task);
}
 System.in.read(); //阻塞主线程
}
 static class NameTreadFactory implements ThreadFactory {
 private final AtomicInteger mThreadNum = new AtomicInteger(1);
 @Override
 public Thread newThread(Runnable r) {
 Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
 System.out.println(t.getName() + " has been created");
return t;
}
}
 public static class MyIgnorePolicy implements RejectedExecutionHandler {
 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
doLog(r, e);
}
 private void doLog(Runnable r, ThreadPoolExecutor e) {
 // 可做日志记录等
 System.err.println( r.toString() + " rejected");
// System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
}
}
 static class MyTask implements Runnable {
 private String name;
 public MyTask(String name) {
this.name = name;
}
 @Override
 public void run() {
try {
 System.out.println(this.toString() + " is running!");
 Thread.sleep(3000); //让任务执行慢点
} catch (InterruptedException e) {
 e.printStackTrace();
}
}
 public String getName() {
return name;
}
 @Override
 public String toString() {
return "MyTask [name=" + name + "]";
}
}
}  

17 Java 中有几种类型的流

18 字节流如何转为字符流

字节输入流转字符输入流通过 InputStreamReader 实现,该类的构造函数可以传入 InputStream 对象。

字符输出流转字节输出流通过OutputStreamWriter 实现,该类的构造函数可以传入 OutputStream 对象。

3.19 请写出你最常见的5个Exception

(1)java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。

(2)java.lang.ClassNotFoundException 指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常。

(3)java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。

(4)java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。

(5)java.lang.IllegalArgumentException 方法传递参数错误。

(6)java.lang.ClassCastException 数据类型转换异常。

文章来源:智云一二三科技

文章标题:Java SE相关面试题汇总

文章地址:https://www.zhihuclub.com/186258.shtml

关于作者: 智云科技

热门文章

网站地图