当前位置: 首页 > news >正文

2025年 Java 面试八股文

第一章-Java基础篇

1. Java中的基本数据类型有哪些?⭐

Java中有8种基本数据类型(Primitive Types),分别是:

  • byte:8位,-128 ~ 127
  • short:16位,-32,768 ~ 32,767
  • int:32位,-2^31 ~ 2^31-1
  • long:64位,-2^63 ~ 2^63-1
  • float:32位,单精度浮点数
  • double:64位,双精度浮点数
  • char:16位,表示单一字符,范围0~65535(Unicode字符)
  • boolean:只有两个值,truefalse

2. 什么是Java中的多态?

多态是面向对象编程中的一个重要概念,它允许方法有多种表现形式。在Java中,多态主要通过**方法重载(Overloading)方法重写(Overriding)**来实现。

  • 方法重载:在同一个类中,方法名相同,但参数不同。方法重载是在编译时发生的,属于编译时多态。
  • 方法重写:子类重写父类的方法,方法签名必须相同。方法重写是在运行时发生的,属于运行时多态。

3. Java中的内存模型及垃圾回收(GC)机制

  • JVM内存模型:JVM内存分为堆(Heap)栈(Stack)。堆用于存储对象实例,栈用于存储局部变量和方法调用。
  • 垃圾回收机制:垃圾回收(GC)是JVM自动管理内存的过程,它会回收不再使用的对象。GC采用分代收集的策略,将内存划分为年轻代老年代永久代(Metaspace),使用不同的回收算法(如标记-清除、复制算法)回收不同代的对象。

4. 什么是finalfinallyfinalize

  • final:用于修饰类、方法和变量:

    • final类不能被继承。
    • final方法不能被重写。
    • final变量的值不能改变。
  • finally:用于异常处理中的代码块,无论是否发生异常,finally块中的代码都会执行。通常用于资源的释放。

  • finalize:是Object类中的一个方法,用于在对象被垃圾回收前进行清理。这个方法并不常用,且并不是立即调用,因此不推荐使用。

5. 什么是线程安全?

线程安全是指多个线程同时访问某个类或对象时,不会出现数据不一致的情况。在Java中,可以通过以下方式实现线程安全:

  • 使用同步(synchronized):通过synchronized关键字对方法或代码块加锁,确保同一时间只有一个线程能够执行被同步的代码。
  • 使用显式锁(Lock):如ReentrantLock,提供比synchronized更灵活的锁机制。
  • 使用线程安全的集合类:如VectorConcurrentHashMap等。
  • 使用volatile关键字:确保共享变量的可见性。

6. Java中的异常处理机制(Checked Exception与Unchecked Exception)

  • Checked Exception:继承自Exception类,但不继承RuntimeException类,必须通过try-catch块处理或通过throws声明抛出。
    • 例:IOExceptionSQLException
  • Unchecked Exception:继承自RuntimeException类,通常是程序中的逻辑错误,不需要强制捕获。
    • 例:NullPointerExceptionArrayIndexOutOfBoundsException

7. HashMapConcurrentHashMap的区别

  • HashMap:非线程安全的集合类,允许null键和值,通常用于单线程环境或需要手动同步的多线程环境。
  • ConcurrentHashMap:线程安全的集合类,通过分段锁技术实现高效的并发访问,适用于多线程环境。它允许并发读取,同时保证写操作的线程安全。

8. Java中的深拷贝与浅拷贝

  • 浅拷贝(Shallow Copy):复制对象时,只复制对象的引用类型的成员变量的引用,而不是引用对象本身。常用Object.clone()方法进行浅拷贝。
  • 深拷贝(Deep Copy):复制对象时,不仅复制对象本身,还会递归地复制引用对象,确保对象完全独立。

9. 什么是volatile关键字?

volatile是一个Java关键字,用于标记一个变量为易变的,确保线程对该变量的写入操作对其他线程可见。volatile的使用可以确保变量在多个线程之间的同步。

  • volatile保证了变量的可见性,即当一个线程修改了该变量的值,其他线程立即能看到这个改变。
  • volatile不保证原子性,多个线程对volatile变量进行操作时仍需考虑线程安全问题。

10. 线程池的作用和常见实现类

线程池通过复用已有的线程来避免频繁创建和销毁线程,提高程序的性能和资源的利用率。常用的线程池类有:

  • ExecutorService:接口,提供执行任务的方法。
  • ThreadPoolExecutorExecutorService的实现类,提供了更加详细的配置参数,适用于大多数场景。
  • ScheduledThreadPoolExecutor:用于定时执行任务。

11. synchronizedReentrantLock的区别

  • synchronized
    • 内置锁,语法简洁。
    • 自动释放锁。
    • 不支持尝试锁(tryLock())和定时锁。
  • ReentrantLock
    • 显式锁,功能更强大,支持尝试锁、定时锁等高级功能。
    • 需要手动释放锁。
    • 适用于需要更细粒度控制的场景。

12、你是怎样理解OOP面向对象    难度系数:⭐
面向对象是利于语言对现实事物进行抽象。面向对象具有以下特征:

  • 1、继承:继承是从已有类得到继承信息创建新类的过程
  • 2、封装:封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口
  • 3、多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应


13、重载与重写区别    难度系数:⭐ 

  • 重载发生在本类,重写发生在父类与子类之间
  • 重载的方法名必须相同,重写的方法名相同且返回值类型必须相同
  • 重载的参数列表不同,重写的参数列表必须相同
  • 重写的访问权限不能比父类中被重写的方法的访问权限更低
  • 构造方法不能被重写


14、接口与抽象类的区别    难度系数:⭐

  • 抽象类要被子类继承,接口要被类实现
  • 接口可多继承接口,但类只能单继承
  • 抽象类可以有构造器、接口不能有构造器
  • 抽象类:除了不能实例化抽象类之外,它和普通Java类没有任何区别
  • 抽象类:抽象方法可以有public、protected和default这些修饰符、接口:只能是public
  • 抽象类:可以有成员变量;接口:只能声明常量


15、sleep和wait区别    难度系数:⭐

  • sleep方法
  • 属于Thread类中的方法
  • 释放cpu给其它线程 不释放锁资源
  • sleep(1000) 等待超过1s被唤醒
  • wait方法
  • 属于Object类中的方法
  • 释放cpu给其它线程,同时释放锁资源
  • wait(1000) 等待超过1s被唤醒
  • wait() 一直等待需要通过notify或者notifyAll进行唤醒
  • wait 方法必须配合 synchronized 一起使用,不然在运行时就会抛出IllegalMonitorStateException异常
#### 锁释放时机代码演示
public static void main(String[] args) {Object o = new Object();Thread thread = new Thread(() -> {synchronized (o) {System.out.println("新线程获取锁时间:" + LocalDateTime.now() + " 新线程名称:" + Thread.currentThread().getName());try {//wait 释放cpu同时释放锁o.wait(1000);//sleep 释放cpu不释放锁//Thread.sleep(1000);System.out.println("新线程获取释放锁锁时间:" + LocalDateTime.now() + " 新线程名称:" + Thread.currentThread().getName());} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("主线程获取锁时间:" + LocalDateTime.now() + " 主线程名称:" + Thread.currentThread().getName());synchronized (o){System.out.println("主线程获取释放锁锁时间:" + LocalDateTime.now() + " 主线程名称:" + Thread.currentThread().getName());}
}

16、什么是自动拆装箱  int和Integer有什么区别    难度系数:⭐

基本数据类型,如int,float,double,boolean,char,byte,不具备对象的特征,不能调用方法。

  • 装箱:将基本类型转换成包装类对象
  • 拆箱:将包装类对象转换成基本类型的值

java为什么要引入自动装箱和拆箱的功能?主要是用于java集合中,List<Inteter> list=new ArrayList<Integer>();

list集合如果要放整数的话,只能放对象,不能放基本类型,因此需要将整数自动装箱成对象。

实现原理:javac编译器的语法糖,底层是通过Integer.valueOf()和Integer.intValue()方法实现。

区别:

  • Integer是int的包装类,int则是java的一种基本数据类型
  • Integer变量必须实例化后才能使用,而int变量不需要
  • Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
  • Integer的默认值是null,int的默认值是0

17、==和equals区别    难度系数:⭐

  • ==

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

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

  • equals

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

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

equals方法是从Object类中继承的,默认的实现就是使用==

18、String能被继承吗 为什么用final修饰    难度系数:⭐

  • 不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。
  • String 类是最常用的类之一,为了效率,禁止被继承和重写。
  • 为了安全。String 类中有native关键字修饰的调用系统级别的本地方法,调用了操作系统的 API,如果方法可以重写,可能被植入恶意代码,破坏程序。Java 的安全性也体现在这里。

19、String buffer和String builder区别    难度系数:⭐

  • StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,
  • 只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。
  • 在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低

20、Object中有哪些方法    难度系数:⭐

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

      void wait(long timeout)--->导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll()方法,或者超过指定的时间量。
      void wait(long timeout, int nanos)--->导致当前的线程等待,直到其他线程调用此对象的 notify()

21、说一下集合体系    难度系数:⭐


22、ArrarList和LinkedList区别    难度系数:⭐    

  • ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
  • 对于随机访问get和set,ArrayList效率优于LinkedList,因为LinkedList要移动指针。
  • 对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

23、HashMap底层是 数组+链表+红黑树,为什么要用这几类结构    难度系数:⭐⭐

  • 数组 Node<K,V>[] table ,哈希表,根据对象的key的hash值进行在数组里面是哪个节点
  • 链表的作用是解决hash冲突,将hash值取模之后的对象存在一个链表放在hash值对应的槽位
  • 红黑树 JDK8使用红黑树来替代超过8个节点的链表,主要是查询性能的提升,从原来的O(n)到O(logn),
  • 通过hash碰撞,让HashMap不断产生碰撞,那么相同的key的位置的链表就会不断增长,当对这个Hashmap的相应位置进行查询的时候,就会循环遍历这个超级大的链表,性能就会下降,所以改用红黑树

24、 什么是JVM,JRE,JDK?

  • JVM(Java Virtual Machine):Java虚拟机,是Java程序运行时的解释执行环境。JVM负责加载、解释和执行Java字节码。
  • JRE(Java Runtime Environment):Java运行时环境,是JVM与类库的结合。JRE包含了JVM以及Java程序运行所需的库和文件,但不包含编译工具。
  • JDK(Java Development Kit):Java开发工具包,包含了JRE和开发Java程序所需的工具,如编译器(javac)、调试器(jdb)、文档工具(javadoc)等。

25、HashMap和HashTable区别    难度系数:⭐

  • 线程安全性不同
  • HashMap是线程不安全的,HashTable是线程安全的,其中的方法是Synchronized,在多线程并发的情况下,可以直接使用HashTable,但是使用HashMap时必须自己增加同步处理。
  • 是否提供contains方法
  • HashMap只有containsValue和containsKey方法;HashTable有contains、containsKey和containsValue三个方法,其中contains和containsValue方法功能相同。
  • key和value是否允许null值
  • Hashtable中,key和value都不允许出现null值。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
  • 数组初始化和扩容机制
  • HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。
  • Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

26、线程的创建方式    难度系数:⭐

继承Thread类创建线程
实现Runnable接口创建线程
使用Callable和Future创建线程   有返回值
使用线程池创建线程

# 代码演示
import java.util.concurrent.*;
public class threadTest{public static void main(String[] args) throws ExecutionException, InterruptedException {//继承threadThreadClass thread = new ThreadClass();thread.start();Thread.sleep(100);System.out.println("#####################");//实现runnableRunnableClass runnable = new RunnableClass();new Thread(runnable).start();Thread.sleep(100);System.out.println("#####################");//实现callableFutureTask futureTask = new FutureTask(new CallableClass());futureTask.run();System.out.println("callable返回值:" + futureTask.get());Thread.sleep(100);System.out.println("#####################");//线程池ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));threadPoolExecutor.execute(thread);threadPoolExecutor.shutdown();Thread.sleep(100);System.out.println("#####################");//使用并发包ExecutorsExecutorService executorService = Executors.newFixedThreadPool(5);executorService.execute(thread);executorService.shutdown();}
}class ThreadClass extends Thread{@Overridepublic void run() {System.out.println("我是继承thread形式:" + Thread.currentThread().getName());}
}class RunnableClass implements Runnable{@Overridepublic void run(){System.out.println("我是实现runnable接口:" + Thread.currentThread().getName());}
}class CallableClass  implements Callable<String> {@Overridepublic String call(){System.out.println("我是实现callable接口:");return "我是返回值,可以通过get方法获取";}
}

27、线程的状态转换有什么(生命周期)   难度系数:⭐

  • 新建状态(New) :线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。
  • 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
  • 运行状态(Running):线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
  • 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    • 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
    • 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
    • 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  • 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

28、请写出你最常见的5个RuntimeException    难度系数:⭐

  • java.lang.NullPointerException
  • 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。
  • java.lang.ClassNotFoundException
  • 指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常。
  • java.lang.NumberFormatException
  • 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。
  • java.lang.IndexOutOfBoundsException
  • 数组角标越界异常,常见于操作数组对象时发生。
  • java.lang.IllegalArgumentException
  • 方法传递参数错误。
  • java.lang.ClassCastException
  • 数据类型转换异常。

29、谈谈你对反射的理解    难度系数:⭐

  • 反射机制

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

反射是Java的一项强大功能,允许在运行时查询类的信息并操作类的属性、方法、构造函数等。通过反射,可以动态创建对象、调用方法或修改字段,常见的类有ClassMethodField等。

  • Java反射的作用

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

  • Java 反射机制提供功能

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

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

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

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

30、什么是 java 序列化,如何实现 java 序列化    难度系数:⭐

  • 序列化是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
  • 序 列 化 的 实 现 : 将 需 要 被 序 列 化 的 类 实 现 Serializable 接 口 , 该 接 口 没 有 需 要 实 现 的 方 法 , implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用 ObjectOutputStream 对象的 writeObject(Object obj)方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。

31. 什么是垃圾回收(Garbage Collection)?

垃圾回收(GC)是JVM自动管理内存的一部分,它会在程序运行时自动清理不再使用的对象,释放内存。JVM会定期执行垃圾回收操作,主要包括:

  • 标记清除:标记所有需要回收的对象,并清除它们。
  • 复制算法:将存活的对象从一个内存区域复制到另一个区域。
  • 分代收集:将堆内存分为年轻代、老年代,分别采用不同的回收策略。

32、Http 常见的状态码    难度系数:⭐

  • 200 OK      //客户端请求成功
  • 301      Permanently Moved (永久移除),请求的 URL 已移走。Response 中应该包含一个 Location URL, 说明资源现在所处的位置
  • 302      Temporarily Moved  临时重定向
  • 400      Bad Request //客户端请求有语法错误,不能被服务器所理解
  • 401      Unauthorized //请求未经授权,这个状态代码必须和 WWW-Authenticate 报头域一起使用
  • 403      Forbidden //服务器收到请求,但是拒绝提供服务
  • 404      Not Found //请求资源不存在,eg:输入了错误的 URL
  • 500      Internal Server Error //服务器发生不可预期的错误
  • 503      Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常

33、GET 和POST 的区别    难度系数:⭐

  • GET 请求的数据会附在URL 之后(就是把数据放置在 HTTP 协议头中),以?分割URL 和传输数据,参数之间以&相连,如:login.action?name=zhagnsan&password=123456。POST 把提交的数据则放置在是 HTTP 包的包体中。
  • GET 方式提交的数据最多只能是 1024 字节,理论上POST 没有限制,可传较大量的数据。其实这样说是错误的,不准确的:“GET 方式提交的数据最多只能是 1024 字节",因为 GET 是通过 URL 提交数据,那么 GET 可提交的数据量就跟URL 的长度有直接关系了。而实际上,URL 不存在参数上限的问题,HTTP 协议规范没有对 URL 长度进行限制。这个限制是特定的浏览器及服务器对它的限制。IE 对URL 长度的限制是2083 字节(2K+35)。对于其他浏览器,如Netscape、FireFox 等,理论上没有长度限制,其限制取决于操作系统的支持。
  • POST 的安全性要比GET 的安全性高。注意:这里所说的安全性和上面 GET 提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的 Security 的含义,比如:通过 GET 提交数据,用户名和密码将明文出现在 URL 上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用 GET 提交数据还可能会造成 Cross-site request forgery 攻击。
  • Get 是向服务器发索取数据的一种请求,而 Post 是向服务器提交数据的一种请求,在 FORM(表单)中,Method
  • 默认为"GET",实质上,GET 和 POST 只是发送机制不同,并不是一个取一个发!

34、Cookie 和Session 的区别    难度系数:⭐

  • Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个 web 服务器存储 cookie。以后浏览器再给特定的 web 服务器发送请求时,同时会发送所有为该服务器存储的 cookie
  • Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去
  • Cookie 和session 的不同点
  • 无论客户端做怎样的设置,session 都能够正常工作。当客户端禁用 cookie 时将无法使用 cookie
  • 在存储的数据量方面:session 能够存储任意的java 对象,cookie 只能存储 String 类型的对象

35. String 类的常用方法都有那些?

  • indexOf():返回指定字符的索引。

  • charAt():返回指定索引处的字符。

  • replace():字符串替换。

  • trim():去除字符串两端空白。

  • split():分割字符串,返回一个分割后的字符串数组。

  • getBytes():返回字符串的 byte 类型数组。

  • length():返回字符串长度。

  • toLowerCase():将字符串转成小写字母。

  • toUpperCase():将字符串转成大写字符。

  • substring():截取字符串。

  • equals():字符串比较。

第二章-Java高级篇

1. Collection 和 Collections 有什么区别?⭐

  • java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。

  • Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

2、如何判断一个对象是否存活(或者GC对象的判定方法)    难度系数:⭐

  • 引用计数法
  • 所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收.
  • 引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象A引用对象B,对象B又引用者对象A,那么此时A,B对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。
  • 可达性算法(引用链法)
  • 该算法的基本思路就是通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到GC Roots没有任何引用链相连时(即从GC Roots节点到该节点不可达),则证明该对象是不可用的。
  • 在java中可以作为GC Roots的对象有以下几种:虚拟机栈中引用的对象、方法区类静态属性引用的对象、方法区常量池引用的对象、本地方法栈JNI引用的对象。

3、什么情况下会产生StackOverflowError(栈溢出)和OutOfMemoryError(堆溢出)怎么排查    难度系数:⭐⭐


引发 StackOverFlowError 的常见原因有以下几种

  • 无限递归循环调用(最常见)
  • 执行了大量方法,导致线程栈空间耗尽
  • 方法内声明了海量的局部变量
  • native 代码有栈上分配的逻辑,并且要求的内存还不小,比如 java.net.SocketInputStream.read0 会在栈上要求分配一个 64KB 的缓存(64位 Linux)。

引发 OutOfMemoryError的常见原因有以下几种

  • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据
  • 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收
  • 代码中存在死循环或循环产生过多重复的对象实体
  • 启动参数内存值设定的过小
  • 排查:可以通过jvisualvm进行内存快照分析

参考https://www.cnblogs.com/boboooo/p/13164071.html

栈溢出、堆溢出案例演示

public class StackOverFlowTest {private static int count = 1;public static void main(String[] args) {//模拟栈溢出//getDieCircle();//模拟堆溢出getOutOfMem();}public static void getDieCircle(){System.out.println(count++);getDieCircle();}public static void getOutOfMem(){while (true) {Object o = new Object();System.out.println(o);}}
}

4、什么是线程池,线程池有哪些(创建)    难度系数:⭐

线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用 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 方法即可。

这4种线程池底层 全部是ThreadPoolExecutor对象的实现,阿里规范手册中规定线程池采用ThreadPoolExecutor自定义的,实际开发也是。

  • newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。这种类型的线程池特点是:

工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。

如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

  • newFixedThreadPool

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

  • newSingleThreadExecutor

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

  • newScheduleThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行。例如延迟3秒执行。

5、为什么要使用线程池    难度系数:⭐

  • 线程池做的工作主要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最 大数量,超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。
  • 主要特点:线程复用;控制最大并发数:管理线程。

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

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

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

6、线程池底层工作原理    难度系数:⭐

第一步:线程池刚创建的时候,里面没有任何线程,等到有任务过来的时候才会创建线程。当然也可以调用 prestartAllCoreThreads() 或者 prestartCoreThread() 方法预创建corePoolSize个线程
第二步:调用execute()提交一个任务时,如果当前的工作线程数<corePoolSize,直接创建新的线程执行这个任务
第三步:如果当时工作线程数量>=corePoolSize,会将任务放入任务队列中缓存
第四步:如果队列已满,并且线程池中工作线程的数量<maximumPoolSize,还是会创建线程执行这个任务
第五步:如果队列已满,并且线程池中的线程已达到maximumPoolSize,这个时候会执行拒绝策略,JAVA线程池默认的策略是AbortPolicy,即抛出RejectedExecutionException异常

7、ThreadPoolExecutor对象有哪些参数 怎么设定核心线程数和最大线程数 拒绝策略有哪些    难度系数:⭐⭐

参数与作用:共7个参数

corePoolSize:核心线程数,
在ThreadPoolExecutor中有一个与它相关的配置:allowCoreThreadTimeOut(默认为false),当allowCoreThreadTimeOut为false时,核心线程会一直存活,哪怕是一直空闲着。而当allowCoreThreadTimeOut为true时核心线程空闲时间超过keepAliveTime时会被回收。

maximumPoolSize:最大线程数
线程池能容纳的最大线程数,当线程池中的线程达到最大时,此时添加任务将会采用拒绝策略,默认的拒绝策略是抛出一个运行时错误(RejectedExecutionException)。值得一提的是,当初始化时用的工作队列为LinkedBlockingDeque时,这个值将无效。

keepAliveTime:存活时间
当非核心空闲超过这个时间将被回收,同时空闲核心线程是否回收受allowCoreThreadTimeOut影响。

unit:keepAliveTime的单位。
workQueue:任务队列

常用有三种队列,即SynchronousQueue,LinkedBlockingDeque(无界队列),ArrayBlockingQueue(有界队列)。

threadFactory:线程工厂,
ThreadFactory是一个接口,用来创建worker。通过线程工厂可以对线程的一些属性进行定制。默认直接新建线程。

RejectedExecutionHandler:拒绝策略
也是一个接口,只有一个方法,当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution法。默认是抛出一个运行时异常。

线程池大小设置:

  • 需要分析线程池执行的任务的特性: CPU 密集型还是 IO 密集型
  • 每个任务执行的平均时长大概是多少,这个任务的执行时长可能还跟任务处理逻辑是否涉及到网络传输以及底层系统资源依赖有关系

如果是 CPU 密集型,主要是执行计算任务,响应时间很快,cpu 一直在运行,这种任务 cpu的利用率很高,那么线程数的配置应该根据 CPU 核心数来决定,CPU 核心数=最大同时执行线程数,加入 CPU 核心数为 4,那么服务器最多能同时执行 4 个线程。过多的线程会导致上下文切换反而使得效率降低。那线程池的最大线程数可以配置为 cpu 核心数+1 如果是 IO 密集型,主要是进行 IO 操作,执行 IO 操作的时间较长,这是 cpu 出于空闲状态,导致 cpu 的利用率不高,这种情况下可以增加线程池的大小。这种情况下可以结合线程的等待时长来做判断,等待时间越高,那么线程数也相对越多。一般可以配置 cpu 核心数的 2 倍。

一个公式:线程池设定最佳线程数目 = ((线程池设定的线程等待时间+线程 CPU 时间)/
线程 CPU 时间 )* CPU 数目

这个公式的线程 cpu 时间是预估的程序单个线程在 cpu 上运行的时间(通常使用 loadrunner测试大量运行次数求出平均值)

拒绝策略:

  • AbortPolicy:直接抛出异常,默认策略;
  • CallerRunsPolicy:用调用者所在的线程来执行任务;
  • DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
  • DiscardPolicy:直接丢弃任务;当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务

8、常见线程安全的并发容器有哪些    难度系数:⭐

  • CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap
  • CopyOnWriteArrayList、CopyOnWriteArraySet采用写时复制实现线程安全
  • ConcurrentHashMap采用分段锁的方式实现线程安全

9、多线程的价值?⭐

  •  发挥多核 CPU 的优势

多线程,可以真正发挥出多核 CPU 的优势来,达到充分利用 CPU 的目的,采用多线程的方式去同时完成几件事情而不互相干扰。

  •  防止阻塞

从程序运行效率的角度来看,单核 CPU 不但不会发挥出多线程的优势,反而会因为在单核CPU 上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核 CPU 我们还是要应用多线程,就是为了防止阻塞。试想,如果单核 CPU 使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行, 哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。

  •  便于建模

这是另外一个没有这么明显的优点了。假设有一个大的任务 A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务 A 分解成几个小任务,任务 B、任务 C、任务 D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。

10、线程池的优点?⭐
  •  重用存在的线程,减少对象创建销毁的开销。
  •  可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  •  提供定时执行、定期执行、单线程、并发数控制等功能。

11、Atomic原子类了解多少 原理是什么    难度系数:⭐

基本类型

  • 使用原子的方式更新基本类型
  • AtomicInteger:整型原子类
  • AtomicLong:长整型原子类
  • AtomicBoolean:布尔型原子类

数组类型

  • 使用原子的方式更新数组里的某个元素
  • AtomicIntegerArray:整形数组原子类
  • AtomicLongArray:长整形数组原子类
  • AtomicReferenceArray:引用类型数组原子类

引用类型

  • AtomicReference:引用类型原子类
  • AtomicStampedReference:原子更新引用类型里的字段原子类
  • AtomicMarkableReference :原子更新带有标记位的引用类型
  • AtomicIntegerFieldUpdater:原子更新整形字段的更新器
  • AtomicLongFieldUpdater:原子更新长整形字段的更新器
  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,以及解决使用 CAS 进行原子更新时可能出现的 ABA 问题
  • AtomicInteger 类利用 CAS (Compare and Swap) + volatile + native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。
  • CAS 的原理,是拿期望值和原本的值作比较,如果相同,则更新成新的值。UnSafe 类的 objectFieldOffset() 方法是个本地方法,这个方法是用来拿“原值”的内存地址,返回值是 valueOffset;另外,value 是一个 volatile 变量,因此 JVM 总是可以保证任意时刻的任何线程总能拿到该变量的最新值。

12、synchronized底层实现是什么 lock底层是什么 有什么区别    难度系数:⭐⭐⭐
Synchronized原理:

方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词),然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。

代码块的同步是利用monitorenter和monitorexit这两个字节码指令。它们分别位于同步代码块的开始和结束位置。当jvm执行到monitorenter指令时,当前线程试图获取monitor对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。

参考:一篇文章讲透synchronized底层实现原理_忘了带罗盘的船夫的博客-CSDN博客

Lock原理:

  • Lock的存储结构:一个int类型状态值(用于锁的状态变更),一个双向链表(用于存储等待中的线程)
  • Lock获取锁的过程:本质上是通过CAS来获取状态值修改,如果当场没获取到,会将该线程放在线程等待链表中。
  • Lock释放锁的过程:修改状态值,调整等待链表。
  • Lock大量使用CAS+自旋。因此根据CAS特性,lock建议使用在低锁冲突的情况下。

Lock与synchronized的区别:

  • Lock的加锁和解锁都是由java代码配合native方法(调用操作系统的相关方法)实现的,而synchronize的加锁和解锁的过程是由JVM管理的
  • 当一个线程使用synchronize获取锁时,若锁被其他线程占用着,那么当前只能被阻塞,直到成功获取锁。而Lock则提供超时锁和可中断等更加灵活的方式,在未能获取锁的     条件下提供一种退出的机制。
  • 一个锁内部可以有多个Condition实例,即有多路条件队列,而synchronize只有一路条件队列;同样Condition也提供灵活的阻塞方式,在未获得通知之前可以通过中断线程以    及设置等待时限等方式退出条件队列。
  • synchronize对线程的同步仅提供独占模式,而Lock即可以提供独占模式,也可以提供共享模式

13、了解ConcurrentHashMap吗 为什么性能比HashTable高,说下原理    难度系数:⭐⭐

ConcurrentHashMap是线程安全的Map容器,JDK8之前,ConcurrentHashMap使用锁分段技术,将数据分成一段段存储,每个数据段配置一把锁,即segment类,这个类继承ReentrantLock来保证线程安全,JKD8的版本取消Segment这个分段锁数据结构,底层也是使用Node数组+链表+红黑树,从而实现对每一段数据就行加锁,也减少了并发冲突的概率。

hashtable类基本上所有的方法都是采用synchronized进行线程安全控制,高并发情况下效率就降低 ,ConcurrentHashMap是采用了分段锁的思想提高性能,锁粒度更细化

14、ConcurrentHashMap底层原理    难度系数:⭐⭐⭐

Java7 中 ConcurrentHashMap 使用的分段锁,也就是每一个 Segment 上同时只有一个线程可以操作,每一个 Segment 都是一个类似 HashMap 数组的结构,它可以扩容,它的冲突会转化为链表。但是 Segment 的个数一但初始化就不能改变。

 

public V put(K key, V value) {Segment<K,V> s;if (value == null)throw new NullPointerException();int hash = hash(key);// hash 值无符号右移 28位(初始化时获得),然后与 segmentMask=15 做与运算// 其实也就是把高4位与segmentMask(1111)做与运算// this.segmentMask = ssize - 1;//对hash值进行右移segmentShift位,计算元素对应segment中数组下表的位置//把hash右移segmentShift,相当于只要hash值的高32-segmentShift位,右移的目的是保留了hash值的高位。然后和segmentMask与操作计算元素在segment数组中的下表int j = (hash >>> segmentShift) & segmentMask;//使用unsafe对象获取数组中第j个位置的值,后面加上的是偏移量if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck(segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment// 如果查找到的 Segment 为空,初始化s = ensureSegment(j);//插入segment对象return s.put(key, hash, value, false);
}/*** Returns the segment for the given index, creating it and* recording in segment table (via CAS) if not already present.** @param k the index* @return the segment*/
@SuppressWarnings("unchecked")
private Segment<K,V> ensureSegment(int k) {final Segment<K,V>[] ss = this.segments;long u = (k << SSHIFT) + SBASE; // raw offsetSegment<K,V> seg;// 判断 u 位置的 Segment 是否为nullif ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {Segment<K,V> proto = ss[0]; // use segment 0 as prototype// 获取0号 segment 里的 HashEntry<K,V> 初始化长度int cap = proto.table.length;// 获取0号 segment 里的 hash 表里的扩容负载因子,所有的 segment 的 loadFactor 是相同的float lf = proto.loadFactor;// 计算扩容阀值int threshold = (int)(cap * lf);// 创建一个 cap 容量的 HashEntry 数组HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap];if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) { // recheck// 再次检查 u 位置的 Segment 是否为null,因为这时可能有其他线程进行了操作Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);// 自旋检查 u 位置的 Segment 是否为nullwhile ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))== null) {// 使用CAS 赋值,只会成功一次if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))break;}}}return seg;
}final V put(K key, int hash, V value, boolean onlyIfAbsent) {// 获取 ReentrantLock 独占锁,获取不到,scanAndLockForPut 获取。HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value);V oldValue;try {HashEntry<K,V>[] tab = table;// 计算要put的数据位置int index = (tab.length - 1) & hash;// CAS 获取 index 坐标的值HashEntry<K,V> first = entryAt(tab, index);for (HashEntry<K,V> e = first;;) {if (e != null) {// 检查是否 key 已经存在,如果存在,则遍历链表寻找位置,找到后替换 valueK k;if ((k = e.key) == key ||(e.hash == hash && key.equals(k))) {oldValue = e.value;if (!onlyIfAbsent) {e.value = value;++modCount;}break;}e = e.next;}else {// first 有值没说明 index 位置已经有值了,有冲突,链表头插法。if (node != null)node.setNext(first);elsenode = new HashEntry<K,V>(hash, key, value, first);int c = count + 1;// 容量大于扩容阀值,小于最大容量,进行扩容if (c > threshold && tab.length < MAXIMUM_CAPACITY)rehash(node);else// index 位置赋值 node,node 可能是一个元素,也可能是一个链表的表头setEntryAt(tab, index, node);++modCount;count = c;oldValue = null;break;}}} finally {unlock();}return oldValue;
}
  1. Java8 中的 ConcurrentHashMap 使用的 Synchronized 锁加 CAS 的机制。结构Node 数组 + 链表 / 红黑树,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表。
  2. public V put(K key, V value) {return putVal(key, value, false);
    }/** Implementation for put and putIfAbsent */
    final V putVal(K key, V value, boolean onlyIfAbsent) {// key 和 value 不能为空if (key == null || value == null) throw new NullPointerException();int hash = spread(key.hashCode());int binCount = 0;for (Node<K,V>[] tab = table;;) {// f = 目标位置元素Node<K,V> f; int n, i, fh;// fh 后面存放目标位置的元素 hash 值if (tab == null || (n = tab.length) == 0)// 数组桶为空,初始化数组桶(自旋+CAS)tab = initTable();else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {// 桶内为空,CAS 放入,不加锁,成功了就直接 break 跳出if (casTabAt(tab, i, null,new Node<K,V>(hash, key, value, null)))break;  // no lock when adding to empty bin}else if ((fh = f.hash) == MOVED)tab = helpTransfer(tab, f);else {V oldVal = null;// 使用 synchronized 加锁加入节点synchronized (f) {if (tabAt(tab, i) == f) {// 说明是链表if (fh >= 0) {binCount = 1;// 循环加入新的或者覆盖节点for (Node<K,V> e = f;; ++binCount) {K ek;if (e.hash == hash &&((ek = e.key) == key ||(ek != null && key.equals(ek)))) {oldVal = e.val;if (!onlyIfAbsent)e.val = value;break;}Node<K,V> pred = e;if ((e = e.next) == null) {pred.next = new Node<K,V>(hash, key,value, null);break;}}}else if (f instanceof TreeBin) {// 红黑树Node<K,V> p;binCount = 2;if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,value)) != null) {oldVal = p.val;if (!onlyIfAbsent)p.val = value;}}}}if (binCount != 0) {if (binCount >= TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal != null)return oldVal;break;}}}addCount(1L, binCount);return null;
    }

15、了解volatile关键字不    难度系数:⭐

  • volatile是Java提供的最轻量级的同步机制,保证了共享变量的可见性,被volatile关键字修饰的变量,如果值发生了变化,其他线程立刻可见,避免出现脏读现象。
  • volatile禁止了指令重排,可以保证程序执行的有序性,但是由于禁止了指令重排,所以JVM相关的优化没了,效率会偏弱

16、synchronized和volatile有什么区别    难度系数:⭐⭐

  • volatile本质是告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
  • volatile仅能用在变量级别,而synchronized可以使用在变量、方法、类级别。
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
  • volatile不会造成线程阻塞,synchronized可能会造成线程阻塞。
  • volatile标记的变量不会被编译器优化,synchronized标记的变量可以被编译器优化。

17、Java类加载过程    难度系数:⭐

加载 加载时类加载的第一个过程,在这个阶段,将完成一下三件事情:
通过一个类的全限定名获取该类的二进制流。

将该二进制流中的静态存储结构转化为方法去运行时数据结构。 

在内存中生成该类的Class对象,作为该类的数据访问入口。

  • 验证 验证的目的是为了确保Class文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四钟验证:

文件格式验证:验证字节流是否符合Class文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被支持的类型.

元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不被继承的类等。

字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。

符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执行。

准备

  • 准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。

解析

  • 该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。

初始化

  • 初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。

18、什么是类加载器,类加载器有哪些  难度系数:⭐

类加载器就是把类文件加载到虚拟机中,也就是说通过一个类的全限定名来获取描述该类的二进制字节流。

  • 主要有以下四种类加载器

启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用

扩展类加载器(extension class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类

系统类加载器(system class loader)也叫应用类加载器:它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它

用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现

  • 什么时候会使用到加载器?java中的加载器是按需加载,什么时候用到,什么时候加载

new对象的时候
访问某个类或者接口的静态变量,或者对该静态变量赋值时
调用类的静态方法时
反射
初始化一个类的子类时,其父类首先会被加载
JVM启动时标明的启动类,也就是文件名和类名相同的那个类


19、简述java内存分配与回收策略以及Minor GC和Major GC(full GC)     难度系数:⭐⭐

内存分配

  • 栈区:栈分为java虚拟机栈和本地方法栈
  • 堆区:堆被所有线程共享区域,在虚拟机启动时创建,唯一目的存放对象实例。堆区是gc的主要区域,通常情况下分为两个区块年轻代和年老代。更细一点年轻代又分为Eden区,主要放新创建对象,From survivor 和 To survivor 保存gc后幸存下的对象,默认情况下各自占比 8:1:1。
  • 方法区:被所有线程共享区域,用于存放已被虚拟机加载的类信息,常量,静态变量等数据。被Java虚拟机描述为堆的一个逻辑部分。习惯是也叫它永久代(permanment generation)
  • 程序计数器:当前线程所执行的行号指示器。通过改变计数器的值来确定下一条指令,比如循环,分支,跳转,异常处理,线程恢复等都是依赖计数器来完成。线程私有的。

回收策略以及Minor GC和Major GC

  • 对象优先在堆的Eden区分配
  • 大对象直接进入老年代
  • 长期存活的对象将直接进入老年代
  • 当Eden区没有足够的空间进行分配时,虚拟机会执行一次Minor GC.Minor GC通常发生在新生代的Eden区,在这个区的对象生存期短,往往发生GC的频率较高,回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代GC的时候不会触发Minor GC,但是通过配置,可以在Full GC之前进行一次Minor GC这样可以加快老年代的回收速度。

20、如何查看java死锁     难度系数:⭐

####演示死锁
package com.ssg.mst;
public class 死锁 {private static final String lock1 = "lock1";private static final String lock2 = "lock2";public static void main(String[] args) {Thread thread1 = new Thread(() -> {while (true) {synchronized (lock1) {try {System.out.println(Thread.currentThread().getName() + lock1);Thread.sleep(1000);synchronized (lock2){System.out.println(Thread.currentThread().getName() + lock2);}} catch (InterruptedException e) {throw new RuntimeException(e);}}}});Thread thread2 = new Thread(() -> {while (true) {synchronized (lock2) {try {System.out.println(Thread.currentThread().getName() + lock2);Thread.sleep(1000);synchronized (lock1){System.out.println(Thread.currentThread().getName() + lock1);}} catch (InterruptedException e) {throw new RuntimeException(e);}}}});thread1.start();thread2.start();}
}

死锁代码演示

程序运行,进程没有停止。


通过jps查看java进程,找到没有停止的进程


通过jstack 9060 查看进程具体执行信息


21、Java死锁如何避免     难度系数:⭐

  • 造成死锁的几个原因

1.一个资源每次只能被一个线程使用

2.一个线程在阻塞等待某个资源时,不释放已占有资源

3.一个线程已经获得的资源,在未使用完之前,不能被强行剥夺

4.若干线程形成头尾相接的循环等待资源关系

这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满足其中某一个条件即可。而其中前3个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。

  • 在开发过程中

1.要注意加锁顺序,保证每个线程按同样的顺序进行加锁

2.要注意加锁时限,可以针对锁设置一个超时时间

3.要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并进行解决

22、单例模式的线程安全性

老生常谈的问题了,首先要说的是单例模式的线程安全意味着:某个类的实例在多线程环境 下只会被创建一次出来。单例模式有很多种的写法,我总结一下:

(1) 饿汉式单例模式的写法:线程安全

(2) 懒汉式单例模式的写法:非线程安全

(3) 双检锁单例模式的写法:线程安全

第三章-java框架篇

1、简单的谈一下SpringMVC的工作流程    难度系数:⭐

  • 用户发送请求至前端控制器DispatcherServlet
  • DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  • 处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  • DispatcherServlet调用HandlerAdapter处理器适配器
  • HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
  • Controller执行完成返回ModelAndView
  • HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
  • DispatcherServlet将ModelAndView传给ViewReslover视图解析器
  • ViewReslover解析后返回具体View
  • DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
  • DispatcherServlet响应用户

2、说出Spring或者SpringMVC中常用的5个注解    难度系数:⭐

  • @Component  基本注解,标识一个受Spring管理的组件
  • @Controller    标识为一个表示层的组件
  • @Service       标识为一个业务层的组件
  • @Repository    标识为一个持久层的组件
  • @Autowired     自动装配
  • @Qualifier("")    具体指定要装配的组件的id值
  • @RequestMapping()  完成请求映射
  • @PathVariable    映射请求URL中占位符到请求处理方法的形参

3、简述SpringMVC中如何返回JSON数据    难度系数:⭐

Step1:在项目中加入json转换的依赖,例如jackson,fastjson,gson等

Step2:在请求处理方法中将返回值改为具体返回的数据的类型, 例如数据的集合类List<Employee>等

Step3:在请求处理方法上使用@ResponseBody注解

4、谈谈你对Spring的理解    难度系数:⭐

Spring 是一个开源框架,为简化企业级应用开发而生。Spring 可以是使简单的JavaBean 实现以前只有EJB 才能实现的功能。Spring 是一个 IOC 和 AOP 容器框架。

Spring 容器的主要核心是:

控制反转(IOC),传统的 java 开发模式中,当需要一个对象时,我们会自己使用 new 或者 getInstance 等直接或者间接调用构造方法创建一个对象。而在 spring 开发模式中,spring 容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用spring 提供的对象就可以了,这是控制反转的思想。

依赖注入(DI),spring 使用 javaBean 对象的 set 方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。

 控制反转和依赖注入的理解(通俗易懂)-CSDN博客参考: 控制反转和依赖注入的理解(通俗易懂)-CSDN博客

面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP 底层是动态代理,如果是接口采用 JDK 动态代理,如果是类采用CGLIB 方式实现动态代理。

参考:Spring AOP的理解(通俗易懂)_根据自己的理解,解释spring的aop-CSDN博客

5、Spring中常用的设计模式    难度系数:⭐

  • 代理模式——spring 中两种代理方式,若目标对象实现了若干接口,spring 使用jdk 的java.lang.reflect.Proxy类代理。若目标兑现没有实现任何接口,spring 使用 CGLIB 库生成目标类的子类。
  • 单例模式——在 spring 的配置文件中设置 bean 默认为单例模式。
  • 模板方式模式——用来解决代码重复的问题。

比如:RestTemplate、JmsTemplate、JpaTemplate

  • 工厂模式——在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象。Spring 中使用 beanFactory 来创建对象的实例。

6. 为什么要使用 spring?

  • 1.简介
  • 目的:解决企业应用开发的复杂性
  • 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
  • 范围:任何Java应用

简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

  • 2.轻量

从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。

  • 3.控制反转

Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

  • 4.面向切面

Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

  • 5.容器

Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。

  • 6.框架

Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。

所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。

7. 解释一下什么是 aop?

AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理。Aop 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。正如Avanade公司的高级方案构架师Adam Magee所说,AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”

8. 解释一下什么是 ioc?

IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。

1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。

IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦。如下图:
在这里插入图片描述
大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:
在这里插入图片描述
我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:

软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

9. spring 有哪些主要模块?

Spring框架至今已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成,、Web、AOP(面向切面编程)、工具、消息和测试模块。

在这里插入图片描述在这里插入图片描述
10. spring 常用的注入方式有哪些?

Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:

  • 构造方法注入
  • setter注入
  • 基于注解的注入

11. spring 中的 bean 是线程安全的吗?

Spring容器中的Bean是否线程安全,容器本身并没有提供Bean的线程安全策略,因此可以说spring容器中的Bean本身不具备线程安全的特性,但是具体还是要结合具体scope的Bean去研究。

12. spring 支持几种 bean 的作用域?

当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:

  • singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例
  • prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例
  • equest:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效
  • session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效
  • globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。

如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。

13. spring 自动装配 bean 有哪些方式?

Spring容器负责创建应用程序中的bean同时通过ID来协调这些对象之间的关系。作为开发人员,我们需要告诉Spring要创建哪些bean并且如何将其装配到一起。

spring中bean装配有两种方式:

  • 隐式的bean发现机制和自动装配
  • 在java代码或者XML中进行显示配置

当然这些方式也可以配合使用。

14. spring 事务实现方式有哪些?

  • 编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。
  • 基于 TransactionProxyFactoryBean 的声明式事务管理
  • 基于 @Transactional 的声明式事务管理
  • 基于 Aspectj AOP 配置事务

15. 说一下 spring 的事务隔离?

事务隔离级别指的是一个事务对数据的修改与另一个并行的事务的隔离程度,当多个事务同时访问相同数据时,如果没有采取必要的隔离机制,就可能发生以下问题:

  • 脏读:一个事务读到另一个事务未提交的更新数据。
  • 幻读:例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样。
  • 不可重复读:比方说在同一个事务中先后执行两条一模一样的select语句,期间在此次事务中没有执行过任何DDL语句,但先后得到的结果不一致,这就是不可重复读。

16. 说一下 spring mvc 运行流程?

Spring MVC运行流程图:

在这里插入图片描述Spring运行流程描述:

  1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

  3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter;(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…)方法)

  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

1、HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
2、数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
3、数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
4、数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

  1. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

  2. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

  3. ViewResolver 结合Model和View,来渲染视图;

  4. 将渲染结果返回给客户端。

17. spring mvc 有哪些组件?

Spring MVC的核心组件:

  • DispatcherServlet:中央控制器,把请求给转发到具体的控制类
  • Controller:具体处理请求的控制器
  • HandlerMapping:映射处理器,负责映射中央处理器转发给controller时的映射策略
  • ModelAndView:服务层返回的数据和视图层的封装类
  • ViewResolver:视图解析器,解析具体的视图
  • Interceptors :拦截器,负责拦截我们定义的请求然后做处理工作

18. @RequestMapping 的作用是什么?

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

RequestMapping注解有六个属性,下面我们把她分成三类进行说明。

value, method:

  • value:指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);
  • method:指定请求的method类型, GET、POST、PUT、DELETE等;
    consumes,produces
  • consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
  • produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

params,headers

  • arams: 指定request中必须包含某些参数值是,才让该方法处理。
  • headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。

19. @Autowired 的作用是什么?

《@Autowired用法详解》@Autowired 与@Resource的区别(通俗易懂)-CSDN博客

Java综合常见面试题 + 答案汇总(问答模式,答案在文章下面链接)Java综合常见面试题及答案_java面试题-CSDN博客

相关文章:

2025年 Java 面试八股文

第一章-Java基础篇 1. Java中的基本数据类型有哪些&#xff1f;⭐ Java中有8种基本数据类型&#xff08;Primitive Types&#xff09;&#xff0c;分别是&#xff1a; byte&#xff1a;8位&#xff0c;-128 ~ 127short&#xff1a;16位&#xff0c;-32,768 ~ 32,767int&…...

Linux 内核 IPoIB 驱动中 sysfs 属性冲突问题的分析与解决

一、引言 在 Linux 内核的设备驱动开发中,sysfs 文件系统是一种重要的机制,用于向用户空间暴露内核对象的属性信息。通过 sysfs,用户空间程序可以读取或修改设备的属性,从而实现对硬件设备的监控和配置。然而,在实际开发中,可能会遇到 sysfs 属性冲突的问题,特别是在复…...

深度学习框架探秘|TensorFlow vs PyTorch:AI 框架的巅峰对决

在深度学习框架中&#xff0c;TensorFlow 和 PyTorch 无疑是两大明星框架。前面两篇文章我们分别介绍了 TensorFlow&#xff08;点击查看&#xff09; 和 PyTorch&#xff08;点击查看&#xff09;。它们引领着 AI 开发的潮流&#xff0c;吸引着无数开发者投身其中。但这两大框…...

鸿蒙Harmony-UIAbility内状态-LocalStorage详细介绍

鸿蒙Harmony-UIAbility内状态-LocalStorage详细介绍 1.1 Localstorage的概念 LocalStorage是页面级的UI状态存储&#xff0c;通过Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例&#xff0c;LocalStorage也可以在UIAbility内&#xff0c;页面间共享状态 1.2 Lo…...

Mysql中使用sql语句生成雪花算法Id

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…...

android的第一个app项目(java版)

一.学习java重要概念 java的基本类型的语言方法和C语言很像&#xff0c;这都是我们要学的东西和学过的东西。那些基础东西&#xff0c;就不和大家讨论了&#xff0c;一起看一下java的一些知识架构。 1.封装 封装是面向对象编程中的一个核心概念&#xff0c;它涉及到将数据和操…...

第6章 6.2使用ASP.NET Core 开发WebAPI ASP.NET Core Web API

6.2.1 Web API项目的搭建 进入VS&#xff0c;【创建新项目】&#xff0c;选择【ASP.NET Core Web API】模板&#xff0c;【下一步】&#xff0c;编辑项目名称及项目位置&#xff0c;【下一步】&#xff0c;选择框架&#xff0c;其他选项默认即可&#xff0c;【创建】。 进入项…...

常见的网络协议汇总(涵盖了不同的网络层次)

网络层协议 IP协议&#xff1a;IP指网际互连协议&#xff08;Internet Protocol&#xff09;&#xff0c;是TCP/IP体系中的网络层协议。IP协议包括IPv4和IPv6&#xff0c;用于为数据包提供源地址和目标地址&#xff0c;从而实现网络通信。ICMP协议&#xff1a;ICMP&#xff08…...

【Java 面试 八股文】Redis篇

Redis 1. 什么是缓存穿透&#xff1f;怎么解决&#xff1f;2. 你能介绍一下布隆过滤器吗&#xff1f;3. 什么是缓存击穿&#xff1f;怎么解决&#xff1f;4. 什么是缓存雪崩&#xff1f;怎么解决&#xff1f;5. redis做为缓存&#xff0c;mysql的数据如何与redis进行同步呢&…...

嵌入式EasyRTC实时通话支持海思hi3516cv610,编译器arm-v01c02-linux-musleabi-gcc

EasyRTC已经完美支持海思hi3516cv610&#xff0c;编译器arm-v01c02-linux-musleabi-gcc&#xff0c;总体SDK大小控制在680K以内&#xff08;预计还能压缩100K上下&#xff09;&#xff1a; EasyRTC在hi3516cv610芯片上能双向通话、发送文字以及二进制指令&#xff0c;总体运行…...

自然语言处理NLP入门 -- 第四节文本分类

目标 本章的目标是帮助你理解文本分类的基本概念&#xff0c;并通过具体示例学习如何使用 scikit-learn 训练文本分类模型&#xff0c;以及如何利用 OpenAI API 进行文本分类。 5.1 什么是文本分类&#xff1f; 文本分类&#xff08;Text Classification&#xff09;是自然语…...

深入解析:如何在C#和C/C++之间安全高效地通过P/Invoke传递多维数组

在工业控制、机器人编程和物联网等领域&#xff0c;我们经常需要让C#这样的托管语言与C/C编写的底层库进行交互。在这个过程中&#xff0c;遇到需要传递多维数组的场景时&#xff0c;许多开发者会意外遭遇System.Runtime.InteropServices.MarshalDirectiveException异常。本文将…...

Spring Boot全局异常处理终极指南:从青铜到王者的实战演进

一、为什么需要全局异常处理&#xff1f; 在用户中心这类核心服务中&#xff0c;优雅的异常处理是系统健壮性的生命线。未处理的异常会导致&#xff1a; 服务雪崩&#xff1a;单点异常扩散到整个系统&#xff08;✖️&#xff09;信息泄露&#xff1a;暴露敏感堆栈信息&#…...

【Elasticsearch】match_bool_prefix查询

match_bool_prefix查询是 Elasticsearch 中一种用于全文搜索的查询方式&#xff0c;适用于需要同时匹配多个词汇&#xff0c;但词汇顺序不固定的情况&#xff0c;它结合了布尔查询&#xff08;bool&#xff09;和前缀查询&#xff08;prefix&#xff09;的功能&#xff0c;适用…...

Unity-Mirror网络框架-从入门到精通之Multiple Additive Scenes示例

文章目录 前言Multiple Additive Scenes示例Additive Scenes示例MultiSceneNetManagerPhysicsCollisionRewardSpawner总结前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mirror网络框架的深入了解,涵盖从基础到高级的多个主题…...

[开源]MaxKb+Ollama 构建RAG私有化知识库

MaxKbOllama&#xff0c;基于RAG方案构专属私有知识库 关于RAG工作原理实现方案 一、什么是MaxKb&#xff1f;二、MaxKb的核心功能三、MaxKb的安装与使用四、MaxKb的适用场景五、安装方案、 docker版Docker Desktop安装配置MaxKb安装和配置 总结和问题 MaxKB 是一款基于 LLM 大…...

Ubuntu 下 nginx-1.24.0 源码分析 - NGX_HAVE_GETTIMEZONE 宏

表示当前平台支持通过 gettimezone() 直接获取时区偏移值&#xff08;以分钟为单位&#xff09; 该宏用于适配不同操作系统对时区信息获取方式的差异。 当 NGX_HAVE_GETTIMEZONE 被定义时&#xff0c;Nginx 会调用 ngx_gettimezone() 获取时区偏移 在 Ubuntu 环境下&#xff0c…...

数据结构之队列,哈希表

一 队列(先进先出) 1.定义&#xff1a;从一端进行数据插入&#xff0c;另一端进行删除的线性存储结构 队列类型 常见操作 - 入队&#xff08;Enqueue&#xff09;&#xff1a;将新元素添加到队列的尾部。若队列有空间&#xff0c;新元素会成为队列的新尾部元素&#xff1b;若…...

缓存的介绍

相关面试题 &#xff1a; ● 为什么要用缓存&#xff1f; ● 本地缓存应该怎么做&#xff1f; ● 为什么要有分布式缓存?/为什么不直接用本地缓存? ● 为什么要用多级缓存&#xff1f; ● 多级缓存适合哪些业务场景&#xff1f; 缓存思想 空间换时间(索引&#xff0c;集群&a…...

372_C++_当有多个通道,开启不同告警的同一种的开关时,限制该开关的打开数量(比如视频上传开关)

GetCloudUploadNum函数 GetCloudUploadNum 函数主要用于统计和控制云端视频上传的通道数量,其主要功能如下: 功能目的// 检查每个通道的云端视频上传配置,并统计启用云端上传的通道总数 int CloudUploadNum = 0; bool InValidCloudUploadChn[MAX_CHN_NUMPARA] = {};...

Jenkins 配置 Git Parameter 四

Jenkins 配置 Git Parameter 四 一、开启 项目参数设置 勾选 This project is parameterised 二、添加 Git Parameter 如果此处不显示 Git Parameter 说明 Jenkins 还没有安装 Git Parameter plugin 插件&#xff0c;请先安装插件 Jenkins 安装插件 三、设置基本参数 点击…...

Unity崩溃后信息结合符号表来查看问题

目录 SO文件符号表对调试和分析的重要性调试方面分析方面 错误数据安装Logcat解释符号表设置符号文件路径生成解析 相关参考 SO文件 so 文件&#xff08;Shared Object File&#xff0c;共享目标文件&#xff09;和符号表紧密相关&#xff0c;它们在程序的运行、调试和分析过程…...

【云安全】云原生- K8S API Server 未授权访问

API Server 是 Kubernetes 集群的核心管理接口&#xff0c;所有资源请求和操作都通过 kube-apiserver 提供的 API 进行处理。默认情况下&#xff0c;API Server 会监听两个端口&#xff1a;8080 和 6443。如果配置不当&#xff0c;可能会导致未授权访问的安全风险。 8080 端口…...

【Matlab算法】基于人工势场的多机器人协同运动与避障算法研究(附MATLAB完整代码)

📚基于人工势场的多机器人协同运动与避障算法研究 摘要1. 引言2. 方法说明2.1 人工势场模型2.2 运动控制流程3. 核心函数解释3.1 主循环结构3.2 力计算函数4. 实验设计4.1 参数配置4.2 测试场景5. 结果分析5.1 典型运动轨迹5.2 性能指标6. 总结与建议成果总结改进方向附录:完…...

Django项目中创建app并快速上手(pycharm Windows)

1.打开终端 我选择的是第二个 2.运行命令 python manage.py startapp 名称 例如&#xff1a; python manage.py startapp app01 回车&#xff0c;等待一下&#xff0c;出现app01的文件夹说明创建成功 3.快速上手 1.app注册 增加一行 "app01.apps.App01Config"&#…...

前端骨架怎样实现

前端骨架屏&#xff08;Skeleton Screen&#xff09;是一种优化页面加载体验的技术&#xff0c;通常在内容加载时展示一个简易的占位符&#xff0c;避免用户看到空白页面。骨架屏通过展示页面结构的骨架样式&#xff0c;让用户有页面正在加载的感觉&#xff0c;而不是等待内容加…...

服务器使用centos7.9操作系统前需要做的准备工作

文章目录 前言1.操作记录 总结 前言 记录一下centos7.9操作系统的服务器在部署业务服务之前需要做的准备工作。 大家可以复制到自己的编辑器里面&#xff0c;有需求的注释一些步骤。 备注&#xff1a;有条件的项目推荐使用有长期支持的操作系统版本。 1.操作记录 # 更换阿里云…...

【git-hub项目:YOLOs-CPP】本地实现01:项目构建

目录 写在前面 项目介绍 最新发布说明 Segmentation示例 功能特点 依赖项 安装 克隆代码仓库 配置 构建项目 写在前面 前面刚刚实现的系列文章: 【Windows/C++/yolo开发部署01】 【Windows/C++/yolo开发部署02】 【Windows/C++/yolo开发部署03】 【Windows/C++/yolo…...

oracle表分区--范围分区

文章目录 oracle表分区分区的原因分区的优势oracle表分区的作用oracle表分区类型一、范围分区二、 创建分区表和使用&#xff1a;1、按照数值范围划分2、按照时间范围3、MAXVALUE2. 向现有表添加新的分区3、 分区维护和重新组织&#xff08;合并/删除&#xff09; oracle表分区…...

第12周:LSTM(火灾温度)

1.库以及数据的导入 1.1库的导入 import torch.nn.functional as F import numpy as np import pandas as pd import torch from torch import nn1.2数据集的导入 data pd.read_csv("woodpine2.csv")dataTimeTem1CO 1Soot 100.00025.00.0000000.00000010.22825.…...

Xlua中C#引用Lua变量,导致Lua侧的GC无法回收的原因及解决方法

1. 引用关系导致&#xff1a; 在 XLua 中&#xff0c;当 C# 端引用了 Lua 变量时&#xff0c;Lua 的垃圾回收器&#xff08;GC&#xff09;不会回收这些被引用的变量。这是因为 Lua 的 GC 机制是基于引用计数和标记 - 清除算法的。当 C# 端持有对 Lua 变量的引用时&#xff0c;…...

Win10系统IP地址修改_出现了一个意外的情况不能完成所有你在设置中所要求的更改---Windows工作笔记001

今天在修改win10系统中的ip地址的时候报错了 来看看如何解决吧,挺简单,简单记录一下 这个时候就需要使用cmd命令来修改 一定要使用,管理员权限,运行cmd才可以 然后首先: 输入 netsh 然后输入 ip 然后输入: set address "以太网" 172.19.126.199 255.255.255.0…...

Generate html

"Generate HTML"&#xff08;生成 HTML&#xff09;指的是通过程序或工具自动创建 HTML 代码的过程。HTML&#xff08;超文本标记语言&#xff09;是用于创建网页内容和结构的标准语言。生成 HTML 通常意味着通过某些方式自动化地构建或生成网页的结构和元素&#xf…...

使用DeepSeek和Kimi快速自动生成PPT

目录 步骤1&#xff1a;在DeepSeek中生成要制作的PPT主要大纲内容。 &#xff08;1&#xff09;在DeepSeek网页端生成 &#xff08;2&#xff09;在本地部署DeepSeek后&#xff0c;使用chatBox生成PPT内容 步骤2&#xff1a;将DeepSeek成的PPT内容复制到Kimi中 步骤3&…...

全面了解HTTP(一)

全面了解HTTP&#xff08;二&#xff09;-CSDN博客 web及网络基础 使用HTTP协议访问web&#xff1a; HTTP: 网络基础TCP/IP 与HTTP关系密切的协议&#xff1a;IP,TCP,DNS 负责域名解析的DNS服务 各种协议与HTTP协议的关系 URI和URL 简单的HTTP协议 HTTP协议用于客户端和服…...

PCDN“无盘业务”

PCDN&#xff08;Proactive Content Delivery Network&#xff09;的无盘业务&#xff0c;相对于有盘业务而言&#xff0c;主要指的是在不依赖本地磁盘存储的情况下进行内容分发。在PCDN网络中&#xff0c;无盘业务通常依赖于内存或其他形式的非持久性存储来缓存和分发内容。以…...

【LLM】13:大模型算法面试题库

一、Transformer篇 1. Transformer的结构是什么 Transformer 由 编码器&#xff08;Encoder&#xff09; 和 解码器&#xff08;Decoder&#xff09; 两部分组成&#xff0c;两部分都包含目标嵌入层、位置编码、多头注意力层、前馈网络&#xff0c;且有残差连接和层归一化&am…...

redis集群

文章目录 节点启动节点集群数据结构CLUSTER MEET命令的实现 槽指派记录节点的槽指派信息专播节点的槽指派信息17.2.3记录集群所有槽的指派信息CLUSTER ADDSLOTS命令的实现 在集群中执行命令计算键属于哪个槽判断槽是否由当前节点负责处理MOVED错误节点数据库的实现 重新分片ASK…...

Spring Boot整合DeepSeek实现AI对话(API调用和本地部署)

本篇文章会分基于DeepSeek开放平台上的API&#xff0c;以及本地私有化部署DeepSeek R1模型两种方式来整合使用。 本地化私有部署可以参考这篇博文 全面认识了解DeepSeek利用ollama在本地部署、使用和体验deepseek-r1大模型 Spring版本选择 根据Spring官网的描述 Spring AI是一…...

git如何把多个commit合成一个

在 Git 中&#xff0c;如果你想把多个提交&#xff08;commit&#xff09;合并成一个&#xff0c;可以使用 git rebase 或 git reset 来完成。下面是两种常用方法&#xff1a; 方法一&#xff1a;使用 git rebase&#xff08;推荐&#xff09; git rebase 是合并多个提交为一…...

flutter启动后不显示文字,中文字体不显示

Flutter 3.29.0-1.0.pre.114运行报错&#xff1a; Failed to load font Roboto at https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Me4GZLCzYlKw.woff2 解决&#xff1a; 下载roboto字体&#xff0c;并将字体&#xff08;Roboto-Regular.ttf&#xff09;放入app代码下…...

跟着李沐老师学习深度学习(十一)

经典的卷积神经网络 在本次笔记中主要介绍一些经典的卷积神经网络模型&#xff0c;主要包含以下&#xff1a; LeNet&#xff1a;最早发布的卷积神经网络之一&#xff0c;目的是识别图像中的手写数字&#xff1b;AlexNet&#xff1a; 是第一个在大规模视觉竞赛中击败传统计算机…...

小结:OSPF的网络类型,LSA

OSPF&#xff08;Open Shortest Path First&#xff09;是一个基于链路状态的内部网关协议&#xff08;IGP&#xff09;。以下是对OSPF网络类型、LSA类型、序列号与Age作用&#xff0c;以及相关配置指令的详细讲解。 一、OSPF的网络类型 OSPF支持多种网络类型&#xff0c;不同…...

用vue3写一个好看的wiki前端页面

以下是一个使用 Vue 3 Element Plus 实现的 Wiki 风格前端页面示例&#xff0c;包含现代设计、响应式布局和常用功能&#xff1a; <template><div class"wiki-container"><!-- 头部导航 --><el-header class"wiki-header"><d…...

Redis常见数据结构

目录 基本介绍 特点&#xff1a; 全局命令 数据类型&#xff1a; String Hash List Set Zset 基本介绍 Redis是一个在内存中存储数据的中间件,可作为数据库&#xff0c;缓存&#xff0c;消息队列等。 特点&#xff1a; 持久化&#xff1a;Redis会把数据存储在内存中…...

JavaScript中Map和Set数据结构详解

目录 一、Map 1. 定义与特点 2. 创建Map对象 3. 常用方法 4. 遍历Map 5. Map与对象的区别 二、Set 1. 定义与特点 2. 创建Set对象 3. 常用方法 4. 遍历Set 5. 应用场景 数据去重 集合运算 遍历集合 一、Map 1. 定义与特点 Map 是 JavaScript ES6 中新增的一种数…...

【devops】 Git仓库如何fork一个私有仓库到自己的私有仓库 | git fork 私有仓库

一、场景说明 场景&#xff1a; 比如我们Codeup的私有仓库下载代码 放入我们的Github私有仓库 且保持2个仓库是可以实现fork的状态&#xff0c;即&#xff1a;Github会可以更新到Codeup的最新代码 二、解决方案 1、先从Codeup下载私有仓库代码 下载代码使用 git clone 命令…...

deepseek:三个月备考高级系统架构师

一、备考总体规划&#xff08;2025年2月11日 - 2025年5月&#xff09; 1. 第一阶段&#xff1a;基础夯实&#xff08;2025年2月11日 - 2025年3月10日&#xff09; 目标&#xff1a;快速掌握系统架构师考试的核心知识点。 重点内容&#xff1a; 计算机组成原理、操作系统、数据…...

设计模式Python版 命令模式(上)

文章目录 前言一、命令模式二、命令模式示例 前言 GOF设计模式分三大类&#xff1a; 创建型模式&#xff1a;关注对象的创建过程&#xff0c;包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式&#xff1a;关注类和对象之间的组合&…...

部署 DeepSeek R1各个版本所需硬件配置清单

DeepSeek-R1 通过其卓越的推理性能和灵活的训练机制&#xff0c;在 2025 年的春节期间受到了广泛关注。 DeepSeek-R1 是一款高性能的 AI 推理模型&#xff0c;主要通过强化学习技术来增强模型在复杂任务场景下的推理能力。 在本地部署 DeepSeek-R1 时&#xff0c;尤其是完整的…...