Java面试题学习(Java基础)

1.Java 基础(正在浏览)

2.Spring & SpringBoot

3.数据库

4.网络

目录

一、Java基础

1.Java语言有什么特点?

2.什么是JVM、JDK、JRE?有什么联系?

3.Java程序运行过程:

4.什么是字节码?字节码有什么优势?

5.什么是面向对象?什么是面向过程?

6.面向对象的三大特性?

7.Java 基本数据类型(7种)(String 不属于基本数据类型)

8.==和equals⽅法有什么区别?

9.hashCode() 和 equals() 之间的关系

10.String、StringBuilder、StringBuffer的区别

11.重载和重写的区别

12.接口和抽象类有什么异同点?

13.List 和 Set有什么区别?

14.ArrayList 和 LinkList 有什么区别?

15.HashMap 和 HashTable 有什么区别?底层实现原理是什么?

16.HashMap如何扩容?

17.说一下HashMap的put方法

18.浅拷贝和深拷贝

19.Java中的异常体系是怎样的?

20.Java类加载过程是怎样的?什么是双亲委派机制?有什么作用?      

21.说一下ThreadLocal底层实现原理?

22.说一下ThreadLocal造成内存泄漏的原因?应如何避免?

二、JVM(Java 虚拟机)

23.JVM 内存结构

24.请你说说一个对象从加载到JVM,再到被GC清除过程?

25.怎么确定一个对象是否是垃圾?

26.堆为什么要分新生代和老年代?

27.有哪些GC算法?

28.如何进行JVM调优?

三、并发/多线程 

29.什么是并行、并发、串行?

30.什么是同步、异步?

31.什么是进程、线程?

32.Java创建线程有哪些方式?

33.说说线程的生命周期和状态?

34.线程之间如何通信?

35.Synchronized 和 Lock/ReentrantLock 有什么区别?

36.sleep()、wait()、join()、yield()之间的区别

37.ReentrantLock中的公平锁和⾮公平锁的底层实现

38.什么是死锁?如何避免死锁?

39.产生死锁的4个必要条件:

40.线程池有哪些优势?

41.解释下线程池的7大参数?

42.线程池的底层原理

四、反射

43.什么是反射?

一、Java基础

1.Java语言有什么特点?

        (1)面向对象(封装、继承、多态)

        (2)平台无关性(JVM实现平台无关性)

        (3)可靠性

        (4)安全性

        (5)支持网络编程

        (6)编译与解释并存

2.什么是JVM、JDK、JRE?有什么联系?

        (1)JDK:Java开发工具包

                 JRE:Java运行环境

                 JVM:Java虚拟机,运行字节码文件(.class)

        (2)联系:JDK包含JRE(jre目录),JRE包含JVM(bin)、lib(类库)

3.Java程序运行过程:

        .java -> 经过编译器编译 -> .class -> 经过JVM解释器解释 -> 机器语言 -> 程序运行

4.什么是字节码?字节码有什么优势?

        (1)字节码是JVM可执行的.class文件,字节码只面向JVM。

        (2)字节码为Java提供了跨平台执行的能力,增强了语言的安全性和性能

5.什么是面向对象?什么是面向过程?

        (1)面向对象:抽象出对象,对象调用方法解决问题。(容易复用、扩展、维护)

        (2)面向过程:通过一系列方法解决问题。(高效)

6.面向对象的三大特性?

        (1)封装:封装对象的属性和方法,外界用get、set访问。

        (2)继承:子类继承父类对象所有的属性和方法,但是父类中的私有属性和方法子类是无法访问;子类还可以对父类进行扩展。

        (3)多态:允许同一个接口调用不同类的对象,从而实现不同的行为。实现多态的三个条件(继承、方法重写、父类引用子类对象)。

// Animal 类
public class Animal {
    public void call() {
        System.out.println(" 动物在叫");
    }
}
 
// 继承 Animal 类
public class Dog extends Animal {
    @Override 
    public void call() {
        System.out.println(" 狗在叫");
    }
}
 
public class Cat extends Animal {
    @Override 
    public void call() {
        System.out.println(" 猫在叫");
    }
}
 
public class Demo {
    public static void main(String[] args) {
        // 同一个接口调用不同类的对象
        Animal dog = new Dog();
        Animal cat = new Cat();
 
        // 从而实现不同的行为
        dog.call();  // 输出: 狗在叫
        cat.call();  // 输出: 猫在叫
    }
}

7.Java 基本数据类型(7种)(String 不属于基本数据类型)

        整型:byte(1个字节=8bit位)、short(2个字节)、int(4个字节)、long(8个字节)

        浮点型:float(4个字节)、double(8个字节)

        字符型:char(2个字节)

        布尔型:boolean(只有true 和 false)

8.==和equals⽅法有什么区别?

        (1)==:如果是基本数据类型,比较的是值;如果是引用类型,比较的是引用地址

        (2)equals:String类重写了equals方法,比较两个字符串内容是否相等。

9.hashCode() 和 equals() 之间的关系

        hashCode() 获取哈希码,哈希码的作用是确定该对象在哈希表的索引位置。

        (1)如果两个对象hashCode值相等,那这两个对象不一定相等(哈希碰撞)

        (2)如果两个对象hashCode值相等,并且equals()方法也返回true,那认为这两个对象相等

        (3)如果两个对象hashCode值不相等,那认为这两个对象不相等

10.String、StringBuilder、StringBuffer的区别

String

String对象用final修饰,所以不可修改,线程安全

适用于操作少量数据

StringBuilder

没加同步锁,效率高,但线程不安全

适用于单线程操作大量数据

StringBuffer

加了同步锁,线程安全

适用于多线程操作大量数据

11.重载和重写的区别

        (1)重载:同一个类中,方法名相同,参数不同(参数个数、顺序、类型不同)

        (2)重写:子类对父类方法重写(父类中private方法,子类不可重写)

12.接口和抽象类有什么异同点?

都不能被实例化

都可以包含抽象方法(抽象类还包含非抽象方法)

接口没有构造方法,抽象类有构造方法(初始化类)

接口对类的行为约束,抽象类强调的是所属关系

接口可以实现多个(implements),抽象类只能单继承(extends)

13.List 和 Set有什么区别?

List

Set

元素有序、可重复

元素无序、不可重复(唯一)

ArrayList查询效率高,插入删除效率低

LinkList查询效率低,插入删除效率高

查询、插入、删除效率高

14.ArrayList 和 LinkList 有什么区别?

        (1)ArrayList:基于数组,连续存储,数组随机访问,查询效率高,插入和删除效率低

        (2)LinkList:基于链表,散列存储,查询效率低,插入和删除效率高

15.HashMap 和 HashTable 有什么区别?底层实现原理是什么?

HashMap

HashTable

线程不安全(没加锁)、效率高

线程安全(加了Synchronized锁)、效率低

允许键和值为null

不允许键和值为null

       底层实现原理:数组+链表/红黑树

16.HashMap如何扩容?

        底层实现原理:数组+链表/红黑树

        数组扩容:默认数组大小为16,当数组元素超过16*0.75=12时,重新开辟2*16=32大小的数组(数组大小翻倍),然后二次hash重新计算每个元素的位置(耗时)。

        链表/红黑树:当其中一个链表长度达到8,如果数组长度没超过64,则通过扩容解决;如果数组长度超过64,链表转为红黑树;如果红黑树节点低于6,红黑树又转为链表。

17.说一下HashMap的put方法

        JDK7,先判断是否要扩容,如果要扩容就先扩容,在用头插法插入链表。

        JDK8,先尾插法插入链表或红黑树,再判断是否要扩容。

18.浅拷贝和深拷贝

        深拷贝和浅拷贝是指拷贝对象。一个对象存在两种属性,一种是基本数据类型,一种是引用类型

        (1)浅拷贝:创建一个新的对象,拷贝原对象的基本数据类型和引用地址。因此浅拷贝的新对象和原对象引用类型的属性指向同一个地址,共享相同的数据。

        (2)深拷贝:创建一个新的对象,拷贝原对象的基本数据类型和引用地址指向的对象。因此深拷贝的新对象和原对象引用类型的属性完全独立,不会共享数据。

19.Java中的异常体系是怎样的?

        (1)Java中所有异常都来自祖先类Throwable

        (2)Throwable有两个子类,Exception 和 Error

        (3)Exception 是程序可处理的错误,可通过 catch 来捕获异常

        (4)Error 是程序无法处理的错误,一旦出现Error,程序将被迫停止

20.Java类加载过程是怎样的?什么是双亲委派机制?有什么作用?      

(1)类加载过程:加载 -> 链接 -> 初始化

         加载:把.class文件加载到JVM

         链接:检查,检查.class文件是否符合JVM规范

                    半初始化,创建类或接口的静态变量并赋初始值

                    映射,建立映射关系

          初始化

(2)双亲委派机制:

        JVM有3个默认类加载器:AppClassLoader、ExtClassLoader、BootstrapClassLoader

        当AppClassLoader收到类加载请求时,它首先不会自己加载这个类,而是委托其父类ExtClassLoader加载;当ExtClassLoader收到类加载请求时,它首先不会自己加载这个类,而是委托其父类BootstrapClassLoader加载。

        如果父类加载器没找到所需的类,才会让其子类尝试加载

(3)作用:提高了安全性,保护Java底层类不会被应用程序覆盖

                    提高了类加载效率

21.说一下ThreadLocal底层实现原理?

(1)ThreadLocal是Java提供的线程本地存储机制,可将数据缓存在线程内部。ThreadLocal会为每个线程提供独立的存储空间,保证线程安全。ThreadLocal底层是通过ThreadLocalMap实现的。

(2)每个Thread对象都有一个ThreadLocalMap,以ThreadLocal为Key,值用户设置

(3)执行set方法时,首先获取当前线程的ThreadLocalMap,以当前ThreadLocal为Key,值用户设置

(4)执行get方法时,首先获取当前线程的ThreadLocalMap,以当前ThreadLocal为Key查找值

22.说一下ThreadLocal造成内存泄漏的原因?应如何避免?

        什么是内存泄漏?没使用的对象或变量占用内存空间,不能被回收。从而导致OOM(内存不足),从而导致系统错误。

(1)弱引用的局限性:ThreadLocalMap 中使用弱引用保存 ThreadLocal 对象。当没有强引用指向 ThreadLocal 对象时,它可以被垃圾回收。然而ThreadLocalMap 中值是强引用,无法被回收,从而造成内存泄漏。

         线程生命周期长:ThreadLocal 对象不再被使用且未调用remove方法清除,其关联的数据会一直存在于 ThreadLocalMap 中,无法被释放,导致内存泄漏。

(2)解决方法:每次使用完ThreadLocal都调用remove()方法清除数据

二、JVM(Java 虚拟机)

23.JVM 内存结构

        (1)方法区(所有线程共享):存储JVM加载的类信息、常量、静态变量

        (2)堆(所有线程共享):存储实例对象和数组(GC主要区域)

        (3)程序计数器:存储当前线程的指令地址

        (4)本地方法栈:存储本地方法局部变量、操作数栈、动态链接、出口等

        (5)虚拟机栈:存储Java方法局部变量、操作数栈、动态链接、出口等

24.请你说说一个对象从加载到JVM,再到被GC清除过程?

        (1)类加载:把.class文件加载到方法区

        (2)创建对象:根据类信息在堆区创建对象

        (3)使用对象:使用对象的方法、属性

        (4)对象可达性判断:使用可达性分析算法判断对象是否可达GC Roots,如果不可达,则会标记可回收

        (5)对象回收:回收被标记对象的内存空间

25.怎么确定一个对象是否是垃圾?

        (1)引用计数算法:每个对象维护一个引用计数器,被引用了计数器加1,引用失效或移除了计数器减1,当计数器值为0时,被视为垃圾。

        (2)可达性分析算法:对象是否可达GC Roots,不可达,则被视为垃圾。

26.堆为什么要分新生代和老年代?

        (1)根据各年代特点将对象进行分区存储,便于回收

        (2)新生代,每次回收大量对象,少量对象存活,使用“复制算法”

        (3)老年代,每次回收少量对象,大量对象存活,使用“标记-清除”或“标记-整理”算法

27.有哪些GC算法?

(1)引用计数算法

        概念:每个对象创建时绑定一个计数器,如果有引用指向该对象,则计数器加1;如果引用被删除,则计数器减1。计数器为0,表示没有引用该对象,表示该对象死亡,可以回收。

        优点:引用计数算法实现简单,判断效率高

        缺点:存在对象之间循环引用问题

(2)标记-清除算法(老年代)

        概念:为每个对象存储一个标记位(记录对象存活还是死亡)。标记阶段,检查对象是否存活,为每个对象更新标记位;清除阶段,清除死亡的对象,执行GC操作

        优点:解决了循环引用的问题

        缺点:会造成内存碎片

(3)标记-整理算法(老年代)

        概念:为每个对象存储一个标记位(记录对象存活还是死亡)。标记阶段,检查对象是否存活,为每个对象更新标记位;整理阶段,将存活的对象整理存放到内存的一端,然后清除死亡的对象,执行GC操作

        优点:解决了标记-清除算法出现的内存碎片问题

        缺点:移动了存活的对象,效率低

(4)复制算法(新生代)

        概念:将内存均分为2个区,每次使用其中1个区,当这个分区内存满了,将内存中存活的对象复制到另一个分区内存,然后将之前的内存清空。如此循环。

        优点:解决了内存碎片问题

        缺点:会造成部分内存浪费(但可调节内存块比例)

                   如果存活对象多,复制算法性能差

标记-整理算法和复制算法区别:

        标记-整理算法:将存活的对象整理到同一块内存的一端,然后GC

        复制算法:内存分区,将存活的对象复制到另一块分区,然后GC之前分区内存

28.如何进行JVM调优?

(1)监控和分析:使用性能分析工具、GC日志分析工具,监控应用程序的性能和资源使用情况。(CPU、内存使用情况、垃圾回收等数据)

(2)JVM参数调优:

        调整堆内存:通常设置-Xms(初始堆大小)、-Xmx(最大堆大小)为相同的值。

        调整老年代与新生代比例:-XX:NewRatio=2(默认为2,表示老年代:新生代=2:1)。当新生代对象比较多时,可增大新生代比例,-XX:NewRatio=1。

        调整新生代升老年代年龄阈值。

(3)选择合适的垃圾回收器(GC)

        串行回收器(Serial GC):适合小型应用或单线程环境。

        并行回收器(Parallel GC):适合高吞吐量环境。

        CMS回收器(Concurrent Mark Sweep GC):适合低延迟环境。

        G1回收器(Garbage-First GC):适合大内存、多核环境,平衡吞吐量和延迟

三、并发/多线程 

        并发:在一段时间内执行多个任务或进程

        多线程:在一个进程中同时执行多个线程

29.什么是并行、并发、串行?

        (1)并行:一般是多核CPU,在同一时刻执行多个任务

        (2)并发:在一段时间内执行多个任务或进程

        (3)串行:一个接着一个执行任务

30.什么是同步、异步?

        (1)同步:执行某个任务,需等待其完成,才能继续执行后续任务。

        (2)异步:执行某个任务,无需等待其完成,就可以继续执行后续任务。

31.什么是进程、线程?

        (1)进程:是操作系统中资源分配的基本单位,具有独立的内存空间。每个进程可以拥有一个或多个线程。

        (2)线程:是进程内的执行单元,是CPU调度的基本单位。多线程共享进程的内存资源,提高了程序的并发性和响应性。

32.Java创建线程有哪些方式?

        (1) 继承Thread类,重写run()方法,用.start()执行线程(不建议使用,Java类单继承,但是接口可以多继承)

        (2) 实现Runnable接口,重写run()方法,用.start()执行线程

        (3) 用lambda表达式创建并执行线程

        (4) 实现Callable接口

        (5) 用线程池创建线程

33.说说线程的生命周期和状态?

        (1)新建:通过 new 创建一个线程对象(此时还未分配所需资源)

        (2)就绪:当线程对象已分配所需资源,调用start()方法进入就绪状态(等待CPU时间片)

        (3)运行:当线程获得CPU时间片,进入运行状态,执行run()方法

        (4)阻塞:当线程失去所需资源,就会进入阻塞状态。如果获得了所有所需资源,就会从阻塞状态转为就绪状态。

        (5)死亡:线程执行完或因异常,从运行状态转为死亡状态

34.线程之间如何通信?

(1)wait/notify机制:

        wait():让线程进入等待状态,并释放其所持有的锁

        notify():唤醒某个正在等待的线程

        notifyAll():唤醒所有正在等待的线程

        注意:这些方法必须在synchronized块中使用,以确保线程安全。

(2)锁机制:

        synchronized:作用于方法或代码块,实现对临界区的互斥访问。

        Lock/Condition接口:创建ReentrantLock实例,通过lock()获得锁,unlock()释放锁;使用newConditon()创建Condition对象,并调用await(),signal(),signalAll()方法实现线程之间的通信。

35.Synchronized 和 Lock/ReentrantLock 有什么区别?

Synchronized

Lock/ReentrantLock

Java内置关键字

Java类

会自动加锁、释放锁

必须手动lock()加锁,unlock()释放锁

无法获取锁的状态

可通过tryLock()获取锁的状态

可重入锁,不可中断,非公平锁

可重入锁,可中断,可设置公平或非公平锁

适合锁少量代码同步问题

适合锁大量代码同步问题

36.sleep()、wait()、join()、yield()之间的区别

        (1)sleep():使当前线程休眠,不释放锁。

        (2)wait():使当前线程等待,会释放对象的锁,需要其他线程调用notify()或notifyAll()方法唤醒。依赖于synchronized。

        (3)join():阻塞当前线程,等待其他线程结束,才会继续执行当前线程。通常用于主线程等待子线程结束。

        (4)yield():让出当前CPU时间片,线程进入就绪状态,不会导致线程阻塞,也不释放锁。

37.ReentrantLock中的公平锁和⾮公平锁的底层实现

        (1)不管是公平锁和⾮公平锁,底层实现都是基于AQS同步队列

        (2)它们的区别在于:线程在lock()加锁时,如果是公平锁,直接让当前线程进入AQS同步队列排队;如果是⾮公平锁,首先竞争锁,一旦没竞争到锁,也进入AQS同步队列排队。

        (3)所以⾮公平锁只是体现在了线程加锁阶段,⽽没有体现在线程被唤醒阶段。

38.什么是死锁?如何避免死锁?

        (1)死锁:是指多个线程同时被阻塞

        (2)避免死锁:破坏产生死锁的4个必要条件

39.产生死锁的4个必要条件:

        (1)互斥条件:一个资源每次只能被一个进程使用

        (2)请求与保持条件:一个进程没有拥有全部的资源,它会继续请求所需资源,并且会保持已有资源

        (3)不剥夺条件:进程的资源未使用完,不能被强行剥夺,只能等其执行完,进程自己释放

        (4)循环等待条件:若存在一组进程{ P0,P1,...,Pn },P0等待P1所占有的资源,P1等待P2所占有的资源...,Pn等待P0所占有的资源

40.线程池有哪些优势?

        (1)降低资源消耗(创建、销毁线程耗时)

        (2)管理线程,提高系统的响应速度和吞吐量

        (3)复用线程

        (4)控制最大并发数

41.解释下线程池的7大参数?

42.线程池的底层原理

        (1)如果此时线程池中的线程数量⼩于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

        (2)如果此时线程池中的线程数量等于corePoolSize,但是阻塞队列未满,那么把任务放⼊阻塞队列。

        (3)如果此时线程池中的线程数量⼤于等于corePoolSize,阻塞队列已满,并且线程池中的数量⼩于maximumPoolSize,创建新的线程来处理被添加的任务。

        (5)如果此时线程池中的线程数量⼤于corePoolSize,阻塞队列已满,并且线程池中的数量等于maximumPoolSize,使用拒绝策略处理此任务。

        (5)当线程池中的线程数量⼤于corePoolSize,如果某线程存活时间超过keepAliveTime,就会终⽌该线程。

四、反射

43.什么是反射?

        反射使程序能够在运行时动态地加载类、创建对象、调用方法、获取和设置字段值等。

        反射提高了Java程序的灵活性和动态性,但也带来了潜在的性能和安全问题。

package com.dragon.entity;

public class Person {
    private String name;
    
    public Person() {
        
    }
    
    public void setName(String name) {
        this.name = name;
    }
    public String getName(){
        return name;
    }
}
package com.dragon.demo;

/**
*   获取类的Class对象:Class.forName()
*   创建对象:newInstance()
*   获取方法:getMethod() -> 可获取当前类和父类的公共方法
             getDeclaredMethod() -> 可获取当前类私有方法,但要开启可访问权限setAccessible(true)
*   调用方法:invoke()
*   获取字段:getField() -> 可获取当前类和父类的公共字段
             getDeclaredField() -> 可获取当前类私有字段,但要开启可访问权限setAccessible(true)
*   获取字段值:get()
*   设置字段值:set()
**/
public class Demo {
    public static void main(String[] args) throws Exception {
       // 获取Class对象
       Class<?> clazz = Class.forName("com.dragon.entity.Person"); 
       // 创建对象
       Object person = clazz.getConstructor().newInstance(); 
   
       // 获取setName方法,String.class 方法参数类型
       Method setName = clazz.getMethod("setName",String.class);
       // 调用setName方法
       setName.invoke(person,"张三");
   
       // 获取getName方法
       Method getName = clazz.getMethod("getName");
       // 调用getName方法并打印
       System.out.println(getName.invoke(person));
   
       // 获取字段
       Field name = clazz.getDeclaredField("name");
       // private 字段必须开启可访问权限
       name.setAccessible(true);
       // 获取字段值,输出:张三
       System.out.println(name.get(person));
       // 设置字段值
       name.set(person,"李四");
       // 获取字段值,输出:李四
       System.out.println(name.get(person));
    }
}
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值