avatar

zian

A text-focused Halo theme

  • Java
  • 面试
  • 首页
  • C语音
  • liunx
  • 数据结构与算法
  • 控制台
Home Java 面试
文章

Java 面试

Posted 2025-02-8 Updated 2025-02- 9
By Administrator
25~32 min read

String、StringBuffer、StringBuilder 区别

String是不可变类,一旦创建无法修改,这是因为在String内部使用 final 来修饰真正存储数据的字符数组,当对字符吃创建进行修改的时候,比如字符串拼接,实际上使用创建一个新的字符串对象,原来的对象是保持不变的。字符串是存储在字符串常量池中的。

StringBuilder 和 StringBuffer 都是可变类,底层是一个可变字符数组来存储数据的,所以在调用相关方法修改字符串的时候,是不会创建新的字符串的。

StringBuilder 和 StringBuffer的区别:

  1. 使用 StringBuilder 是非线程安全的,而StringBuffer 在底层使用 synchronized 修饰的
  2. 由于StringBuffer 使用 synchronized 修饰,会带来一定的性能开销,效率不如 StringBuilder
  3. 在多线程的环境下使用 StringBuffer , 在非多线程环境下使用 StringBuilder

注意:String 也是线程安全的 , 因为 String本身就是不可变的。

Java 中多态的实现方式有哪些

多态定义:允许不同类对象对象同一种消息做出不同的响应。

方法重载(静态多态,编译阶段)

方法重载:在同一个类中,定义多个方法名相同,但是参数列表不同(参数个数、类型或者顺序不同)的方法。在编译阶段就确定了。(根据方法传入的参数类型不同,做出不一样的处理)

方法重写(动态多态,运行阶段)

方法重写:发生在子类和父类之间,子类重新定义父类中已有的方法。但是使用父类来接收子类new出来的对象,并且使用父类的变量名调用重写后的方法,实际调用的子类重写的方法。

接口实现 (动态多态,运行阶段)

接口实现:不同的实现类重写同一个方法,可以方法的逻辑可以不同。

Java 中如何处理异常

try-catch-finally

try-catch 捕获异常并且进行处理,当 try 中代码出现异常就会走到 catch 中,如果没有就不会执行 catch 中代码,finally 中的代码不论代码使用出现异常都会执行。

throw

throw 手动抛出异常,一般在代码逻辑中,当一些条件是就抛出异常来通知调用者。

throws

throws:在方法声明中声明该方法可能会抛出那些异常。在该方法内部不进行处理,有调用处进行处理或者接着抛出。

集合框架

集合主要分为单例集合和双列集合

单列集合:只有 value

双列集合:key-value 结构

单列集合

单例集合.png

双列集合

双列集合.png

Java 中线程的创建方式

线程的创建主要有三种方式:

  1. 继承 Thread 类(s red)
    • 特点:直接创建该类的子类,并且重写 run 方法,run 方法中代码就是该线程需要做的事情。在通过该子类的对象调用 start 方法就可以创建线程并且运行了。
    • 缺点:由于 Java只支持单继承,一个类继承了 Thread 就不能继承其他了,限制了类的拓展性。
    • 无返回值。
  2. 实现 Runnable 接口 (卵了煲)
    • 特点:创建类实现该接口并且重写 run 方法,这种方式不会占用类的继承,增加了该类的拓展性。
    • 缺点:需要借助 Thread 来管理线程。
    • 无返回值
  3. 实现 Callable 接口(K了煲)
    • 特点:创建类实现该接口并且重写 call 方法 ,这种方式不会占用类的继承,增加了该类的拓展性。
    • 缺点:创建和使用的时候比较复杂,需要 Thread 和 futureTask 类配合。
    • 有返回值。
  4. 使用线程池(常用)
    • 特点:可实现线程复用,不用频繁创建线程和销毁线程
    • 缺点:实现比较复杂。

Java 中的反射机制

Java 中的反射机制是指在程序运行时,能够动态地获取一个类的所有信息,包括属性、方法、构造方法等,并且可以对这些成员进行操作。

使用反射的步骤: 1. 先获取 class 对象 ,2. 获取成员

获取 class 对象的方式有

Class.forName("全类名")

  1. 比较灵活,可以通过读取配置文件中内容来进行反射

类.class

  1. 类只要类存在就可以使用这个方式

对象.getClass

  1. 需要对象已经存在才能使用这个方式获取。

获取成员方式

获取属性

若要获取公共属性,可以使用 Class对象的 getField(String name)方法。但如果要获取所有属性(包括私有属性),则需要使用 getDeclaredField(String name)方法。可通过 get和set 方法来获取属性值和修改属性值。

获取构造方法

getConstructors()方法用于获取所有公共构造方法,而 getDeclaredConstructors()方法可以获取所有构造方法(包括私有构造方法)。获取构造方法对象 Constructor后,可通过 newInstance方法创建对象。

获取方法

使用 Class对象的 getMethod(String name, Class<?>... parameterTypes)方法获取指定的公共方法,getDeclaredMethod(String name, Class<?>... parameterTypes)方法用于获取所有方法(包括私有方法)。获取 Method对象后,通过 invoke方法调用方法。

Java 中的序列化和反序列化

序列化和反序列化:主要用于数据持久化和网络上数据传输。

序列化:(使用 Java 默认的序列化)器将 Java对象转换字节串。需要序列化的类应该实现 Serializable 接口(序列化接口)并且设置序列化版本号,注意这个只是一个标识,用于告知虚拟机该类的对象可以进行序列化。

反序列化:(使用 Java 默认的序列化)将字节串转换为 Java 对象。

序列化和反序列化配合使用可以不同的环境进行数据传输。

Java 中的泛型是什么

泛型:不确定的数据类型。

泛型好处:类型检查和代码复用。

泛型使用方式:

  1. 泛型类:在该类中可以使用泛型
  2. 泛型方法:在该方法可以使用泛型
  3. 泛型接口:该接口的实现类(进行泛型传递)和接口方法都可以使用。

泛型限定范围:(以 User 类来做实例)

  1. 限定泛型必须是 User 的后代类(包括自己), < ? extends User>
  2. 限定泛型必须是 User 的祖先类 (包括自己) , < ? super User>

Java 中的泛型是伪泛型,编译成 Class 泛型会替换成 Object 类。

Java 中的注解是什么

Java 中注解是一种元数据,他可以为代码在编译、运行等阶段提供一些额外的数据,开发者可以通过这数据,来实现特定的功能:比如代码检查、代码生成等。

注解的特点:以 @ + 标识符 ,常见的注解有 @Override 等注解。

元注解:注解的注解。

注解实例:比如 @OVerride 就可以用标识一个方法是重写方法,如果方法不是重写方法使用 @Override 标识出现编译错误。

Java 中内存模型

Java中内存模型分为五部分:

  1. 栈
  2. 堆
  3. 方法区
  4. 本地方法栈
  5. 程序计数器

Java内存模型.png

Java 中的垃圾回收机制(GC机制)

GC 机制:主要负责回收堆和方法区中不在使用的内存空间。

判断一个对象是否不再使用的方法:

  1. 引用计数法:每当有一个地方引用该对象,就会+1 , 每当有个一个地方不再引用该对象,就会 -1 。(可能出现,两个对象相互引用导致引用循环,这样对象就永远都不会被回收了。)
  2. 可达性分析算法:一个 "GC Roots" 对象为开始,往下找如果没有找到(不可达)该对象就可以被回收了,一般栈中引用对象、方法区中静态属性引用的对象,方法区中常量引用的对象等。

垃圾回收算法

1.标记清除算法

image

将存活的对象进行标记,然后清理掉未被标记的对象。

不足:

  • 标记和清除过程效率都不高;
  • 会产生大量不连续的内存碎片,导致无法给大对象分配内存。

2. 标记整理算法

image

让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

3. 复制算法

image

将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。这种算法适用于对象存活率较低的场景,新生代经常采用这种算法。

线程和进程的区别?

线程:cpu 调用的基础单元

进程:一个进程中包含多个线程,可以理解为进程是容器,里面装着线程。

并行和并发有什么区别

并行:同一个时间同时执行多个任务,多个cpu 同时执行多个线程。

并发:同一个时间需要做多个任务, 一个 cpu 轮流执行多个线程。

线程有哪些状态

output.png

新建:

  • 当一个线程对象被创建出来,但是还没有调用 start 方法的时候就是新建状态。

可运行:

  • 调用的 start 方法,但是没有获取的执行资格。

终结 :

  • 线程内代码执行的完毕,就冲运行状态转换为终结状态

阻塞:

  • 当线程获取锁失败后,就会进入阻塞状态。
  • 当持有锁的线程释放了锁是,就会按照一定规则唤醒被阻塞队列中的阻塞线程,唤醒的线程重新回到了 可运行状态(没有获取 cpu 的执行权 )

等待:

  • 当获取锁成功后,但是由于条件不满足,调用了 wait 方法,等待唤醒
  • 当其他线程调用 notify() 或者 notifyAll() 方法,来唤醒执行了 wait 的线程
  • 示例:厨师和客人,厨师做饭,客人吃饭
    • 厨师没有做好饭,客人就等待厨师做好饭 (客人等待),厨师做好饭了就会告知客人可以吃饭了 (唤醒客人)。

睡眠:

  • 线程调用 sleep 方法,线程就进入等待状态,等待过了指定时间,就回到了 可运行状态。

线程池

创建线程池的参数

在线程池中一共有7个核心参数:

  1. corePoolSize 核心线程数目 - 池中会保留的最多线程数
  2. maximumPoolSize 最大线程数目 - 核心线程+救急线程的最大数目
  3. keepAliveTime 生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
  4. unit 时间单位 - 救急线程的生存时间单位,如秒、毫秒等
  5. workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
  6. threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
  7. handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略

拒绝策略有4种,当线程数过多以后,第一种是抛异常、第二种是由调用者执行任务、第三是丢弃当前的任务,第四是丢弃最早排队任务。默认是直接抛异常。

  void test() {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                5, // 核心线程数量
                10,           // 最大线程数量  (临时线程数 = 最大线程 - 核心线程数 )
                1,            // 临时线程的存活时间
                TimeUnit.MINUTES, // 临时线程的存活时间的单位
                new ArrayBlockingQueue<>(10),    // 线程池满了,会创建一个队列来保留这些超出的任务
                // r -> new Thread(r , "myThread" ),     // 线程工厂
                new ThreadPoolExecutor.AbortPolicy()  // 线程池的拒绝策略,当所有线程都在执行任务,并且等待队列中也已经满了,就会触发拒绝策略
        );

        // 执行线程任务
        poolExecutor.execute(() -> System.out.println("hello "));

    }
Java, 面试
License:  CC BY 4.0
Share

Further Reading

Feb 9, 2025

其他

问: git 如何解决分支冲突 查看分支冲突文件 删除冲突代码 重新提交代码 liunx 常用命令 1. chmod 功能:修改文件或目录的权限。 常见用法示例: chmod +x script.sh:为 script.sh 文件添加可执行权限。 chmod 755 file.txt:使用数字模式设

Feb 9, 2025

Elasticsearch 面试

问:你了解 ElasticSearch 吗? 答:了解,ElasticSearch 一个强大的搜索引擎,在失物招领项目中我就使用 ES ,给 ES 安装中文分词器,就可以根据中文词语来检索索引库。Java

Feb 9, 2025

Spring 面试

问:解释 IOC 容器和DI依赖注入 答: IOC容器:控制反转,将创建对象权利交给IOC容器来完成 DI 依赖注入:获取 IOC 中获取创建好的对象,在Spring 常用的依赖注入有: 构造器注入 set 方法注入 @Autowired 注入 使用 IOC容器和DI依赖注入的好处: 降低代码耦合

OLDER

Java 集合

NEWER

MySQL 面试

Recently Updated

  • 其他
  • Elasticsearch 面试
  • Spring 面试
  • RabbitMQ 面试
  • Redis 面试

Trending Tags

ruoyi docker java

Contents

©2025 zian. Some rights reserved.

Using the Halo theme Chirpy