每日 Java 面试题分享【第 13 天】

欢迎来到每日 Java 面试题分享栏目!
订阅专栏,不错过每一天的练习

今日分享 3 道面试题目!

评论区复述一遍印象更深刻噢~

目录

  • 问题一:如何在 Java 中调用外部可执行程序或系统命令?
  • 问题二:如果一个线程在 Java 中被两次调用 start() 方法,会发生什么?
  • 问题三:栈和队列在 Java 中的区别是什么?

问题一:如何在 Java 中调用外部可执行程序或系统命令?

在 Java 中,可以通过 RuntimeProcessBuilder 调用外部可执行程序或系统命令。以下是两种方法的详细说明和使用示例。


方法 1:使用 Runtime

代码示例
public class RuntimeExample {public static void main(String[] args) {try {// 调用系统命令(如 Windows 的 dir 或 Linux 的 ls)Process process = Runtime.getRuntime().exec("ls"); // 替换为需要的命令// 获取命令执行的输出try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}}// 等待命令执行完成int exitCode = process.waitFor();System.out.println("退出码:" + exitCode);} catch (Exception e) {e.printStackTrace();}}
}
注意
  1. 返回的 Process 对象
    • Process 表示外部进程,可以用来获取输出流、错误流,并控制进程的生命周期。
  2. 输出流的读取
    • 如果不读取或关闭进程的输出流,可能会导致进程阻塞。
优点
  • 简单直接,代码量少。
缺点
  • 不够灵活,难以传递复杂参数或处理多个 I/O。

方法 2:使用 ProcessBuilder

代码示例
import java.io.BufferedReader;
import java.io.InputStreamReader;public class ProcessBuilderExample {public static void main(String[] args) {try {// 创建 ProcessBuilderProcessBuilder processBuilder = new ProcessBuilder();// 设置要执行的命令(可带参数)processBuilder.command("ping", "www.google.com");// 合并错误流和标准输出流(可选)processBuilder.redirectErrorStream(true);// 启动进程Process process = processBuilder.start();// 读取进程输出try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {String line;while ((line = reader.readLine()) != null) {System.out.println(line);}}// 等待进程完成并获取退出码int exitCode = process.waitFor();System.out.println("退出码:" + exitCode);} catch (Exception e) {e.printStackTrace();}}
}
优点
  • 更灵活
    • 支持设置环境变量:processBuilder.environment().put("ENV_VAR", "value");
    • 支持设置工作目录:processBuilder.directory(new File("/path/to/dir"));
  • 可读性好:链式调用清晰明了。
缺点
  • Runtime 稍微复杂一点。

两种方法的对比

特性RuntimeProcessBuilder
使用简单性更简单略复杂
灵活性较低较高
支持环境变量设置不支持支持
合并输出和错误流需要手动实现直接支持(redirectErrorStream
推荐程度适合简单命令调用更推荐,适合复杂调用场景

常见使用场景

  1. 运行系统命令

    • 例如在 Linux 上执行 ls 或在 Windows 上执行 dir
    • 使用 ProcessBuildercommand 方法可以方便地传递参数。
  2. 调用外部可执行程序

    • 例如运行 .exe 文件、Python 脚本、Shell 脚本等。
    • 确保路径正确,并且有足够权限执行外部程序。
  3. 环境变量控制

    • 使用 ProcessBuilder.environment() 可以轻松传递自定义的环境变量。
  4. 读取命令输出

    • 无论是标准输出还是错误输出,Java 都可以捕获并处理。

注意事项

  1. 路径问题

    • 确保外部命令或可执行程序的路径正确,建议使用绝对路径。
    • 如果使用相对路径,请确保工作目录正确设置(ProcessBuilder.directory())。
  2. 阻塞问题

    • 如果外部进程产生大量输出,但未被读取,会导致阻塞。
    • 建议及时读取或关闭进程的输出和错误流。
  3. 跨平台性

    • 不同操作系统的命令语法可能不同,编写代码时需注意适配性。
  4. 权限问题

    • 运行外部程序可能需要特定的权限,特别是在受限的环境(如服务器)中。

扩展

如何执行带空格的命令或参数?
  • 使用 ProcessBuilder.command() 方法,将每个参数单独传递为列表元素。

    ProcessBuilder processBuilder = new ProcessBuilder();
    processBuilder.command("cmd.exe", "/c", "echo", "Hello World!");
    
如何处理输入流(标准输入)?
  • 使用 Process 对象的 getOutputStream() 方法,向外部进程写入数据。

    Process process = new ProcessBuilder("cat").start();
    try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()))) {writer.write("Hello from Java!");writer.flush();
    }
    

总结

在 Java 中调用外部程序时:

  • 简单任务使用 Runtime.getRuntime().exec()
  • 复杂任务优先使用 ProcessBuilder,以获得更好的灵活性和控制力。

问题二:如果一个线程在 Java 中被两次调用 start() 方法,会发生什么?

问题分析

在 Java 中,Thread 类的 start() 方法被用来启动一个新线程。如果尝试对同一个线程对象调用两次 start() 方法,会发生异常。


答案

如果对同一个线程对象调用两次 start() 方法,第二次调用会抛出 IllegalThreadStateException 异常。这是因为线程一旦启动后,其状态会从 NEW(新建) 转变为其他状态(如 RUNNABLETERMINATED 等)。根据 Java 线程模型,已经启动过的线程对象不能被重新启动。


代码示例

public class ThreadStartExample {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("线程正在运行…");});// 第一次启动线程thread.start();// 再次调用 start() 方法try {thread.start(); // 这里会抛出 IllegalThreadStateException} catch (IllegalThreadStateException e) {System.out.println("异常信息:线程已经启动过,不能再次调用 start()");}}
}
运行结果
线程正在运行…
异常信息:线程已经启动过,不能再次调用 start()

原因分析

1. 线程生命周期

线程的生命周期如下:

  • NEW:线程对象被创建,但未调用 start()
  • RUNNABLE:调用 start() 后,线程处于可运行状态。
  • TERMINATED:线程运行完毕,进入终止状态。

当线程离开 NEW 状态后,不能回到 NEW,因此无法再次启动。

2. start() 方法的作用

start() 方法的核心功能是:

  • 通知 JVM 创建一个新的线程(底层通过本地方法调用操作系统线程)。
  • 将线程状态从 NEW 改为 RUNNABLE,并让线程进入可调度队列。

第二次调用 start() 时,由于线程不再是 NEW 状态,JVM 会拒绝这个操作,抛出异常。

3. 设计初衷

Java 线程模型的设计目的是让每个 Thread 对象只启动一次,避免复杂的状态管理(如重新初始化线程)。如果需要再次启动线程,应该创建一个新的 Thread 实例。


扩展讲解

如何避免这种问题?
  • 检查线程状态:如果需要对线程进行管理,可以通过 Thread.getState() 方法检查其状态。

    示例代码:

    public class ThreadStateCheck {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("线程运行中…");});System.out.println("线程状态:" + thread.getState()); // NEWthread.start();System.out.println("线程状态:" + thread.getState()); // RUNNABLE 或 TERMINATEDtry {thread.start(); // 再次调用会抛异常} catch (IllegalThreadStateException e) {System.out.println("异常:线程已经启动过");}}
    }
    
  • 重新创建线程对象:如果需要重复执行任务,可以通过新建线程实现:

    Thread thread1 = new Thread(() -> System.out.println("任务 1"));
    thread1.start();Thread thread2 = new Thread(() -> System.out.println("任务 2"));
    thread2.start();
    
线程池的使用

如果需要多次执行相同任务,推荐使用线程池(ExecutorService),而非手动管理 Thread 对象。例如:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(2);Runnable task = () -> System.out.println("任务正在运行…");executor.execute(task); // 启动任务executor.execute(task); // 再次启动任务executor.shutdown();}
}

线程池可以高效管理线程复用,避免直接操作线程带来的复杂性。


总结

  1. 对一个线程对象调用两次 start() 方法会抛出 IllegalThreadStateException
  2. 每个线程对象只能启动一次。如果需要重新运行任务,需要新建线程实例或使用线程池。
  3. 推荐使用线程池(如 ExecutorService)来管理多次任务执行,避免手动控制线程的复杂性。

问题三:栈和队列在 Java 中的区别是什么?

栈和队列的区别

栈(Stack)和队列(Queue)是两种常用的线性数据结构,它们在数据存取方式和应用场景上有显著的区别。以下从定义、操作规则、实现和应用等方面进行分析:


1. 栈 (Stack)

定义

栈是一种**后进先出(LIFO, Last In First Out)**的数据结构,即最后插入的数据最先被取出。

核心操作
  • push(E item):将元素压入栈顶。
  • pop():移除并返回栈顶元素。
  • peek():仅返回栈顶元素,但不移除。
Java 实现
  • 使用 java.util.Stack 类。

  • 示例代码:

    import java.util.Stack;public class StackExample {public static void main(String[] args) {Stack<Integer> stack = new Stack<>();stack.push(10);stack.push(20);stack.push(30);System.out.println("栈顶元素:" + stack.peek()); // 输出 30System.out.println("弹出元素:" + stack.pop()); // 输出 30System.out.println("弹出后栈顶:" + stack.peek()); // 输出 20}
    }
    

2. 队列 (Queue)

定义

队列是一种**先进先出(FIFO, First In First Out)**的数据结构,即最先插入的数据最先被取出。

核心操作
  • add(E item)offer(E item):将元素添加到队列尾部。
  • remove()poll():移除并返回队列头部元素。
  • element()peek():仅返回队列头部元素,但不移除。
Java 实现
  • 使用 java.util.Queue 接口的实现类,例如 LinkedListArrayDeque

  • 示例代码:

    import java.util.LinkedList;
    import java.util.Queue;public class QueueExample {public static void main(String[] args) {Queue<Integer> queue = new LinkedList<>();queue.offer(10);queue.offer(20);queue.offer(30);System.out.println("队列头元素:" + queue.peek()); // 输出 10System.out.println("移除元素:" + queue.poll()); // 输出 10System.out.println("移除后队列头:" + queue.peek()); // 输出 20}
    }
    

3. 栈与队列的主要区别

特性栈 (Stack)队列 (Queue)
访问规则后进先出(LIFO)先进先出(FIFO)
常用方法push()pop()peek()offer()poll()peek()
插入位置栈顶队尾
移除位置栈顶队头
实现方式使用 java.util.Stack使用 java.util.Queue 接口及实现类
常见应用场景递归、括号匹配、函数调用栈、回溯算法消息队列、任务调度、广度优先搜索

4. 特殊队列:双端队列 (Deque)

定义

双端队列(Deque, Double-Ended Queue)允许在队首和队尾同时插入和移除元素。

实现
  • 使用 java.util.ArrayDequejava.util.LinkedList

  • 示例代码:

    import java.util.Deque;
    import java.util.ArrayDeque;public class DequeExample {public static void main(String[] args) {Deque<Integer> deque = new ArrayDeque<>();deque.addFirst(10); // 插入到队首deque.addLast(20);  // 插入到队尾System.out.println("队首元素:" + deque.peekFirst()); // 输出 10System.out.println("队尾元素:" + deque.peekLast());  // 输出 20deque.removeFirst(); // 移除队首deque.removeLast();  // 移除队尾}
    }
    
应用
  • 双端队列可用于实现栈或队列的功能,也可以用作滑动窗口算法等高级场景。

5. 实际应用场景

  • 栈:
    • 函数调用栈
    • 括号匹配
    • 表达式求值
    • 深度优先搜索(DFS)
  • 队列:
    • 任务调度
    • 广度优先搜索(BFS)
    • 消息队列
    • 缓冲区管理

总结

  • 栈是后进先出的数据结构,常用于递归、回溯等场景。
  • 队列是先进先出的数据结构,适合任务调度和广度优先搜索等场景。
  • 双端队列是栈和队列的通用化版本,既可以实现栈的功能,也可以实现队列的功能。

总结

今天的 3 道 Java 面试题,您是否掌握了呢?持续关注我们的每日分享,深入学习 Java 面试的各个细节,快速提升技术能力!如果有任何疑问,欢迎在评论区留言,我们会第一时间解答!

明天见!🎉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/7936.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

粒子群算法 笔记 数学建模

引入: 如何找到全局最大值&#xff1a;如果只是贪心的话&#xff0c;容易被局部最大解锁定 方法有&#xff1a;盲目搜索&#xff0c;启发式搜索 盲目搜索&#xff1a;枚举法和蒙特卡洛模拟&#xff0c;但是样例太多花费巨量时间 所以启发式算法就来了&#xff0c;通过经验和规…

从ai产品推荐到利用cursor快速掌握一个开源项目再到langchain手搓一个Text2Sql agent

目录 0. 经验分享&#xff1a;产品推荐 1. 经验分享&#xff1a;提示词优化 2. 经验分享&#xff1a;使用cursor 阅读一篇文章 3. 经验分享&#xff1a;使用cursor 阅读一个完全陌生的开源项目 4. 经验分享&#xff1a;手搓一个text2sql agent &#xff08;使用langchain l…

14-6-1C++STL的list

(一&#xff09;list容器的基本概念 list容器简介&#xff1a; 1.list是一个双向链表容器&#xff0c;可高效地进行插入删除元素 2.list不可以随机存取元素&#xff0c;所以不支持at.(pos)函数与[ ]操作符 &#xff08;二&#xff09;list容器头部和尾部的操作 list对象的默…

Couchbase UI: Dashboard

以下是 Couchbase UI Dashboard 页面详细介绍&#xff0c;包括页面布局和功能说明&#xff0c;帮助你更好地理解和使用。 1. 首页&#xff08;Overview&#xff09; 功能&#xff1a;提供集群的整体健康状态和性能摘要 集群状态 节点健康状况&#xff1a;绿色&#xff08;正…

【WebRTC - STUN/TURN服务 - COTURN配置】

在WebRTC中&#xff0c;对于通信的两端不在同一个局域网的情况下&#xff0c;通信两端往往无法P2P直接连接&#xff0c;需要一个TURN中继服务&#xff0c;而中继服务可以选用 COTURN 构建。 注&#xff1a;COTURN 是一个开源的 TURN&#xff08;Traversal Using Relays around…

基于OSAL的嵌入式裸机事件驱动框架——整体架构调度机制

参考B站up主【架构分析】嵌入式祼机事件驱动框架 感谢大佬分享 任务ID &#xff1a; TASK_XXX TASK_XXX 在系统中每个任务的ID是唯一的&#xff0c;范围是 0 to 0xFFFE&#xff0c;0xFFFF保留为SYS_TSK_INIT。 同时任务ID的大小也充当任务调度的优先级&#xff0c;ID越大&#…

【测试】UI自动化测试

长期更新&#xff0c;建议关注收藏点赞&#xff01; 目录 概论WEB环境搭建Selenium元素定位 APPAppium 概论 user interface UI 使用工具和代码执行用例。 什么样的项目需要自动化&#xff1f; 需要回归测试、自动化的功能模块需求变更不频繁、项目周期长&#xff08;功能测试…

【随手笔记】FFT资料整理

&#xff08;一&#xff09;结果验证 函数波形示例1 #define Fs 44800 #define NPT 256 void InitBufInArray() {int i 0;float fx 0;for(i0; i<NPT; i){// fx 1500 * sin(2*PI * i * 350.0 / Fs) // 2700 * sin(2*PI * i * 8400.0 / Fs) // 4000 * sin(2*P…

Airflow:Airflow子流程应用实践

Apache Airflow是一个强大的开源平台&#xff0c;用于以编程方式编写、调度和监控工作流。它的一个强大功能是使用子DAG&#xff08;子有向无环图&#xff09;创建复杂工作流的能力&#xff0c;子DAG本质上是在父DAG中更小的嵌套DAG。在这篇博文中&#xff0c;我们将深入探讨su…

狗狗能吃萝卜吗?

各位铲屎官们&#xff0c;今天咱们来聊聊一个超实用的话题&#xff1a;狗狗能吃萝卜吗&#xff1f;很多新手铲屎官可能会觉得萝卜营养丰富&#xff0c;想给狗狗也尝尝鲜&#xff0c;但其实这里面大有讲究&#xff01; 狗狗可以吃萝卜&#xff0c;但要注意方法 首先&#xff0c…

Excel制作合同到期自动提醒!

大家好&#xff0c;我是小鱼。 今天分享一下如何利用Excel制作合同到期提醒表&#xff0c;实现Excel表格自动计算合同到期日和天数&#xff0c;根据合同状态和到期天数自动填充颜色提醒&#xff0c;超实用。先看一下效果&#xff0c;已经到期的合同会自动被填充为红色&#xf…

Python Flask教程

一、前言 Flask是一个用Python编写的轻量级的Web应用框架。 Flask基于WSGI&#xff08;Web Server Gateway Interface&#xff09;和 Jinja2 模板引擎&#xff0c;旨在帮助开发者快速、简便地创建 Web 应用。 Flask 被称为"微框架"&#xff0c;因为它使用简单的核…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.10 文本数据炼金术:从CSV到结构化数组

1.10 《文本数据炼金术&#xff1a;从CSV到结构化数组》 目录 #mermaid-svg-TNkACjzvaSXnULaB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-TNkACjzvaSXnULaB .error-icon{fill:#552222;}#mermaid-svg-TNkACjzva…

Linux进程概念:【环境变量】【程序地址空间】

目录 一.环境变量 1.1基本概念 1.2命令行参数 1.3一个例子&#xff0c;一个环境变量 1.4环境变量的组织方式 1.5查看环境变量的方法 ​编辑 1.6从存储的角度理解环境变量 1.7环境变量最开始是从系统的配置文件里来的 1.8认识更多的环境变量 HOME HISTSIZE HOSTNANE…

医疗集群系统中基于超融合数据库架构的应用与前景探析

一、引言 1.1 研究背景与意义 随着医疗信息化的飞速发展,医疗数据呈爆炸式增长。从日常诊疗记录、患者病历,到各类医疗影像、检查检验数据等,海量信息不断涌现。据统计,医疗数据的年增长率高达 30% 以上 ,2025 年,全球医疗数据量将达到 2314 艾字节(EB)。如此庞大的数…

【leetcode100】从前序与中序遍历序列构造二叉树

1、题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,nul…

【CSS入门学习】Flex布局设置div水平、垂直分布与居中

水平平均分布 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>…

【ARM】解决MDK在打开工程的时候提示CMSIS的版本不对问题

1、 文档目标 解决MDK在打开使用Compiler 6的工程的时候&#xff0c;提示CMSIS 的API版本过低的报错。 2、 问题场景 客户在Pack包中打开一个示例工程&#xff0c;打算熟悉一下对应芯片的功能和软件的功能&#xff0c;但是&#xff0c;打开软件后&#xff0c;在构建信息输出框…

PyCharm接入DeepSeek实现AI编程

目录 效果演示 创建API key 在PyCharm中下载CodeGPT插件 配置Continue DeepSeek 是一家专注于人工智能技术研发的公司&#xff0c;致力于开发高性能、低成本的 AI 模型。DeepSeek-V3 是 DeepSeek 公司推出的最新一代 AI 模型。其前身是 DeepSeek-V2.5&#xff0c;经过持续的…

【PyTorch][chapter 29][李宏毅深度学习]Fine-tuning LLM

参考&#xff1a; https://www.youtube.com/watch?veC6Hd1hFvos 目录&#xff1a; 什么是 Fine-tune 为什么需要Fine-tuning 如何进行Fine-tune Fine-tuning- Supervised Fine-tuning 流程 Fine-tuning参数训练的常用方案 LORA 简介 示例代码 一 什么是 Fine-tune …