我是靠谱客的博主 怕孤单心情,最近开发中收集的这篇文章主要介绍2022年同花顺Java面试2022年同花顺Java面试,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

2022年同花顺Java面试

前言

最近疫情期间抽空面试了一家互联网公司,很多问题都很常见,但是很长时间没有看这方面的知识了,所以回答的确实很不好,于是周末抽空把涉及的问题整理一下,希望对大家有用。

正文

Java基础

1.一个java文件中有几个类,编译后有几个class文件?
在一个源文件中用class关键字定义了几个类,编译的时候就会产生几个字节码文件

2. Java是否可以同时使用abstract和fInal声明方法?
final的四种用法:

  1. final 修饰的是引用不可修改,但是引用地址指向的数据可以改变。
  2. final 可以修饰方法 方法不可被覆盖 (继承类之间的关系)
  3. final 修饰类 表示该类不能被继承
  4. final 修饰成员变量,该成员变量不可被修改

方法声明了abstract,则需要重写abstract方法,所以我们在方法不能同时使用abstractfInal声明方法.

其他相关问题:

-a.Java是否可以同时使用abstract和static声明方法?

staticabstract不能同时使用, 用static声明方法表明这个方法在不生成类的实例时可直接被类调用, 而abstract方法不能被调用,两者矛盾。

b. Java是否可以同时使用abstract和native声明方法?

nativeabstract不能同时使用,因为 native 暗示这些方法是有实现体的,只不过这些实现体是非java 的,但是abstract却显然的指明这些方法无实现体。

c.Java是否可以同时使用abstract和 synchronized声明方法?

  • synchronizeabstract不能同时使用,synchronize(经常结合static)声明方法,我们称作类锁,用来确保多线程下的线程安全。
  • synchronized的前提是该方法可以被直接调用,所以不能和abstract一起使用.

3.使用线程的方式

  1. 使用Thread,重写run()方法
  2. 实现Runnable接口,重写run()方法
  3. 实现Callable接口,相对于Runnable是有返回值,重写call()方法
  4. 使用Executor框架来创建线程池

Future

  • Future通常与Callable结合使用,Future可以对Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
  • 但是Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了FutureTask
  • JDK1.8中提供了CompletableFuture,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

4.实现线程安全方式

实现线程同步的6种方式:

  • 使用Synchronized标记临界区
  • 对变量使用volatile进行标记,可以实现原子操作的线程安全
  • 使用锁机制
  • 使用局部变量ThreadLocal实现线程同步
  • 使用阻塞队列SyschronousQueue实现线程同步
  • 使用原子变量实现线程同步

5.synchronized是不是公平锁?公平锁的性能高还是非公平锁的性能高?

  • 公平锁和非公公平锁的区别在于公平锁会保证先来的线程会先获取资源(其内部实现时AQS原理),而非公平不能保证。
  • 公平锁的实现是通过FIFO先进先出的队列来实现的,非公平锁会由JVM就近安排线程获取资源的顺序,所以非公平锁的性能是优于公平锁的。
  • sychronized是非公平锁,ReenterLock(默认也是非公平锁)可以实现公平锁。

6.ReetrantLocK的原理

ReentrantLock主要利用CAS+AQS队列来实现的:

  • 先通过CAS尝试获取锁, 如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起;
  • 当锁被释放之后, 公平锁的情况下:排在队首的线程会被唤醒CAS再次尝试获取锁,非公平锁下由JVM就近决定哪个线程获取资源。

基于AQS原理

  • AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。
  • AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。
  • CLH队列是一个虚拟的双向队列(FIFO先进先出),虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。
    在这里插入图片描述

7.线程池的核心参数

public ThreadPoolExecutor(
      int corePoolSize,       #核心线程数
      int maxinmumPoolSize,   #线程总数  非核心数=总数-核心数
      long keepAliveTime,     #当前线程数大于核心线程数时  线程的等待新任务的等待时间(核心线程也会面临死亡)
      TimeUnit unit,          #时间单位
      BlockingQueue<Runnable> workQueue #任务队列
      RejectedExecutionHandler #(选填) 拒绝处理器
)

8.当一个新的任务进来,线程池怎么处理

线程池处理线程的过程:

  1. 当前运行线程数 小于corePoolSize 任务直接交给核心线程进行执行
  2. 当前运行线程数 大于或等于 corePoolSize 任务且满足队列未满,那么任务将进入任务队列进行等待,并且任务队列都具有阻塞性,所以只有当核心线程数的任务执行完了,才会从任务队列中获取任务。
  3. 当前运行线程数 大于或等于 corePoolSize 任务且队列已满,那么 任务进入非核心线程。
  4. 当核心线程、等待队列、非核心线程都被占用的时候线程会被拒绝器处理。

9.线程池拒绝策略有哪些?
适用:那些既不能进入核心线程、等待队列,也无法使用非核心线程来处理,或者线程异常的线程:

  • CallerRunsPolicy:直接运行该任务的run方法,但不是在线程池内部,适合处理业务比较重要且数量不多的场景。
  • AbortPolicy:RejectedExecutionException异常抛出。适用对业务非常重要的完全不能不执行的场景。(默认)
  • DiscardPolicy:不会做任何处理。适合处理丢失对业务影响不大的场景。
  • DiscardOldestPolicy:检查等待队列 强行取出队列头部任务(并抛弃该头部任务)后再进行执行该任务。适合新数据比旧数据重要的场景。

10.线程A、B如何实现线程A完成后,再执行线程B.

方式一:使用join
Thread类中的join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行。
方式二:CountDownLatch(线程计数器)+Semaphore信号量

  • CountDownLatch用于主线程等待其他子线程任务都执行完毕后再执行;
  • Semaphore信号量是 java.util.concurrent包下用来:限制线程并发的数量的工具类,基于AQS实现的,在构造的时候会设置一个值,代表着资源数量。

方式三:使用CyclicBarrier(回环栅栏)
CyclicBarrier用于一组线程相互等待大家都达到某个状态后,再同时执行;

11.Java的引用类型,并且举例说明其使用场景

  • 强引用A a=new A() 只要引用a存在,垃圾回收器不会回收。
  • 软引用SoftReference类似于缓存的方式,不影响垃圾回收,可以提升速度,节省内存。若对象被回收,此时可以重新new,主要是用来缓存服务器中间计算结果以及不需要实时保存的用户行为。通常放在用在对缓存比较敏感的应用中。
  • 弱引用WeakReference用于监控对象是否被垃圾回收器回收。
  • 虚引用PhantomReference,每次垃圾回收的时候都会被回收。主要用于判断对象是否已经从内存中删除。

Spring部分

1.Spring Bean的作用域以及Spring Bean是不线程安全的

  • 单例模式(默认)Spring创建Bean的原则是不等bean创建完成就将beanFactory提早放到缓存中,如果其他bean依赖这个bean可以直接使用,这种三级缓存的机制很好地解决了循环依赖的问题。
  • 多例模式(原型模式):每次使用时都会创建新的Bean实例,Bean调用setAllowCircularReferences(false)来禁止循环依赖,否则出现循环依赖会直接抛异常。
  • Request模式:一次 request 一个实例, 当前 Http 请求结束,该 bean 实例也将会被销毁.
  • Session模式:在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。
  • global Session模式(不常使用):在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在 使用 portlet context 时有效。

xml配置文件中添加配置属性scope= prototype使用,或者使用注解@Scope("prototype")

单例模式和多例模式的线程问题

多例模式:每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。

单例模式下的线程安全问题:

  1. 对于类似于DAO这种只有查询的无状态Bean在单例情况下是可以保证线程安全

  2. @Controller/@Service等容器中,默认单例模式是线程不安全的。

  3. 即使@Controller/@Service使用多列模式下, 如果使用静态变量,它也不是线程安全的

  4. 一定要定义变量的话,用ThreadLocal来封装,这个是线程安全的

2.说一说Spring循环依赖

循环依赖指的是Spring Bean之间相互依赖的情况。

解决循环依赖的前置条件:

  • 出现循环依赖的Bean必须要是单例,都是多例模会直接报错
  • 依赖注入的方式不能全是构造器注入的方式

Spring在创建Bean的时候默认是按照自然排序来进行创建的,IOC容器先创建A,再创建B:

  • A、B均采用setter方式相互注入
  • A采用setter方式获取B,B采用构造器方式获取A

以上两种情况的循环依赖时可以解决循环依赖,其他情况都会异常抛出。

解决循环依赖的流程:

  1. 当A完成了实例化并添加进了Bean的缓存池(一级缓存)中。
  2. 为A进行属性注入了,在注入时发现A依赖了B,会去实例化B。
  3. 在创建B的时候,因为A已经放在Bean的缓存池(一级缓存)当中了,所以无论B采用的setter方式还是构造器方式都可以获取A。

这里需要注意的是:

  • 如果A采用的是构造器方式,创建A时发现依赖于B,于是会先去创建B,但是B又依赖于A,并且缓存没有A,所以会直接因为循环依赖,导致启动异常。

  • @Autowired、@Resources实际上都是setter方式注入依赖。

AOP在解决循环依赖的问题上,会使用三级缓存的方式去完成

  1. singletonObject:一级缓存,这里的bean是已经创建完成的,一旦进行getBean操作时,我们第一时间就会寻找一级缓存
  2. earlySingletonObjects:二级缓存,该缓存所获取到的bean是提前曝光出来的,是还没创建完成的。
  3. singletonFactories:三级缓存为早期曝光对象工厂,这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象。

知识点:

  • 添加单例缓存,当bean被创建完以后进行的操作。这时候说明bean已经创建完,删除二三级缓存,直接留下一级缓存,并且注册该bean
  • 二级缓存可以提前曝光被其他Bean所引用,它可以解決循环依赖。但是二级缓存无法解决AOP+循环依赖的问题,因为不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
  • 三级缓存是用来来保存产生的代理对象,但它并没有所谓性能上的“提升”。

3.DispatcherServlet前端控制器的工作流程

在这里插入图片描述

用户发送请求和返回响应的流程:

  1. 发送请求至DispatcherServlet
  2. 映射处理器获取处理器映射至 DispatcherServet
  3. HandlerAdapter进行处理器适配
  4. 调用处理器相应功能处理方法(获得 ViewmodelDispatcherServlet
  5. ViewResolver 接收View 进行视图解析
  6. Model加入到View 中进行视图渲染
  7. DispatcherServlet返回响应

其他部分

单例模式
使用场景:资源共享的情况
描述:单例模式自行实例化且实例化一次 构造函数必须是私有的

懒汉单例模式和饥汉单例模式

  • 懒汉单例模式:在调用资源时,如果没有进行实例化的话,就进行一次实例化。
  • 饥汉单例模式:在没有调用时就已经自行实例化了。

多线程下使用懒汉单例模式
使用synchronized方法(类锁)+ synchronized块(对象锁)外部加了一个判空的逻辑,确保线程安全;

synchronized两种锁:

  • 对象锁:用sychronized修饰代码块 (手动去指定锁对象)
  • 类锁:用sychronized修饰普通方法(类锁只能在同一时刻被一个对象拥有 通过方法的 ACC_SYNCHRONIZED 标志符是否被设置,这里会隐形调用monitorentermonitorexit这两个指令 )

用单例模式下都是同一个实例对象,所以类锁无法保证数据安全,需要再结合对象锁来进行双重校验。

singleton属性被volatile修饰,其作用:

  • 可见性:线程A实例化属性之后,Volatile刷入主存
  • 防止指令重排: 防止多线程下指令重排,进而实例化两次或者获取一个null
  • volatile变量规则:对volatile 修饰的变量的写操作 先行发生于后面对这个变量的读操作;
/**
* 单例模式-双重校验锁
*
* @author szekinwin
*/
public class SingleTon3 {
    //私有化构造方法
    private SingleTon3() {
    }
    
    //通过volatile,实现添加内存屏障,防止指令重排序
    private static volatile SingleTon3 singleTon = null;
    
    public synchronized static SingleTon3 getInstance() {
        //第一次校验
        if (singleTon == null) {
            synchronized (SingleTon3.class) {
                //第二次校验
                if (singleTon == null) {
                    singleTon = new SingleTon3();
                }
            }
        }
        return singleTon;
    }
}

总结

很多时候我都觉得自己挺菜的,真的有很多东西需要去学,上面很多的知识很多其实都学过,可是真的面试的时候总是不能完整清晰的回答出来,学习真是一直习惯,不能荒废。

在这里插入图片描述

最后

以上就是怕孤单心情为你收集整理的2022年同花顺Java面试2022年同花顺Java面试的全部内容,希望文章能够帮你解决2022年同花顺Java面试2022年同花顺Java面试所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部