【JavaWeb|第一篇】Maven篇

Maven

一、Maven课程安排

1.1 课程安排

1.2 初识Maven

1.2.1 什么是Maven

Maven是Apache旗下的一个开源项目,是一款用于管理和构建java项目的工具

官网:https://maven.apache.org/

1.2.2 Maven的作用

使用Maven能够做什么呢?

1.依赖管理

2.统一项目结构

3.项目构建

依赖管理

  • 方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题

当使用maven进行项目依赖(jar包)管理,则很方便的可以解决这个问题。 我们只需要在maven项目的pom.xml文件中,添加一段如下图所示的配置即可实现。

**统一项目结构 : **

  • 提供标准、统一的项目结构

在项目开发中,当你使用不同的开发工具 (如:Eclipse、Idea),创建项目工程时:

若我们创建的是一个maven工程,是可以帮我们自动生成统一、标准的项目目录结构

具体的统一结构如下:

项目构建 :

  • maven提供了标准的、跨平台 (Linux、Windows、MacOS) 的自动化项目构建方式:清理、编译、测试、打包、发布

如上图所示我们开发了一套系统,代码需要进行编译、测试、打包、发布,这些操作如果需要反复进行就显得特别麻烦,而Maven提供了一套简单的命令(如下图,双击某个命令即可执行)来完成项目构建。

比如双击compile,那这个工程就会自动进行编译,并且将编译后的文件放在了新的target目录(橙色)下的classes目录中(存放编译之后的class字节码文件)

二、Maven概述

2.1 Maven介绍

Apache Maven是一个项目管理和构建工具,它基于项目对象模型 (Project Object Model , 简称: POM)的概念,通过一小段描述信息来管理项目的构建、报告和文档。

官网:https://maven.apache.org/

Maven的作用:

1.方便的依赖管理

2.统一的项目结构

3.标准的项目构建流程

2.2 Maven模型

  • 项目对象模型 (Project Object Model)
  • 依赖管理模型(Dependency)
  • 构建生命周期/阶段(Build lifecycle & phases)

1). 构建生命周期/阶段(Build lifecycle & phases)

以上图中紫色框起来的部分,就是用来完成标准化构建流程 。当我们需要编译,Maven提供了一个编译插件供我们使用;当我们需要打包,Maven就提供了一个打包插件供我们使用等。

2). 项目对象模型 (Project Object Model)

以上图中紫色框起来的部分属于项目对象模型,就是将我们自己的项目抽象成一个对象模型有自己专属的坐标 ,如下图所示是一个Maven项目:

坐标,就是资源(jar包)的唯一标识,通过坐标可以定位到所需资源(jar包)位置

MacOS下idea中Maven配置以及Maven替换国内阿里云镜像源

3). 依赖管理模型(Dependency)

以上图中紫色框起来的部分属于依赖管理模型,是使用坐标 来描述当前项目依赖哪些第三方jar包

之前我们项目中需要jar包时,直接就把jar包复制到项目下的lib目录 ,而现在书写在pom.xml文件中的坐标又是怎么能找到所要的jar包文件的呢?

答案:Maven仓库

2.3 Maven仓库

仓库:用于存储资源,管理各种jar包

仓库的本质就是一个目录(文件夹),这个目录被用来存储开发中所有依赖(就是jar包)和插件

Maven仓库分为:

  • 本地仓库:自己计算机上的一个目录(用来存储jar包)
  • 中央仓库:由Maven团队维护的全球唯一的。仓库地址:https://repo1.maven.org/maven2/
  • 远程仓库(私服):一般由公司团队搭建的私有仓库

当项目中使用坐标引入对应依赖jar包后,首先会查找本地仓库中是否有对应的jar包

  • 如果有,则在项目直接引用
  • 如果没有,则去中央仓库 中下载对应的jar包到本地仓库

如果还可以搭建远程仓库(私服),将来jar包的查找顺序则变为: 本地仓库 --> 远程仓库--> 中央仓库

2.4 Maven安装

下载地址:https://maven.apache.org/download.cgi

这里笔者使用的版本是apache-maven-3.8.6

2.4.2 安装步骤

Maven安装配置步骤:

1.解压安装

2.配置仓库

3.配置Maven环境变量

1、解压

解压缩后的目录结构如下:

  • bin目录 : 存放的是可执行命令。(mvn 命令重点关注)
  • conf目录 :存放Maven的配置文件。(settings.xml配置文件后期需要修改)
  • lib目录 :存放Maven依赖的jar包。(Maven也是使用java开发的,所以它也依赖其他的jar包)

2、配置本地仓库

2.1、在自己计算机上新一个目录(本地仓库,用来存储jar包)

2.2、进入到conf目录下修改settings.xml配置文件

1). 使用超级记事本软件,打开settings.xml 文件,定位到53行

2). 复制<localRepository>标签,粘贴到注释的外面(55行)

3). 复制之前新建的用来存储jar包的路径,替换掉<localRepository>标签体内容

3、配置阿里云私服

由于中央仓库在国外,所以下载jar包速度可能比较慢,而阿里公司提供了一个远程仓库,里面基本也都有开源项目的jar包。

进入到conf目录下修改settings.xml配置文件:

1). 使用超级记事本软件,打开settings.xml 文件,定位到160行左右

2). 在<mirrors>标签下为其添加子标签<mirror>,内容如下:

xml 复制代码
<mirror>  
    <id>alimaven</id>  
    <name>aliyun maven</name>  
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <mirrorOf>central</mirrorOf>          
</mirror>

注意配置的位置,在<mirrors> ... </mirrors> 中间添加配置。如下图所示:

注: 只可配置一个 <mirror>(另一个要注释!) ,不然两个可能发生冲突,导致jar包无法下载!!!

4、配置环境变量

Maven环境变量的配置类似于JDK环境变量配置一样

1). 在系统变量处新建一个变量MAVEN_HOME

  • MAVEN_HOME环境变量的值,设置为maven的解压安装目录

2). 在Path中进行配置

  • PATH环境变量的值,设置为:%MAVEN_HOME%\bin

3). 打开DOS命令提示符进行验证,出现如图所示表示安装成功

shell 复制代码
mvn -v

5). 配置关联的JDK版本(可选)

进入到conf目录下修改settings.xml配置文件,在 <profiles> </profiles>中增加如下配置:

xml 复制代码
<profile>
        <id>jdk-17</id>
        <activation>
                <activeByDefault>true</activeByDefault>
                <jdk>17</jdk>
        </activation>
        <properties>
                <maven.compiler.source>17</maven.compiler.source>
                <maven.compiler.target>17</maven.compiler.target>
                <maven.compiler.compilerVersion>17</maven.compiler.compilerVersion>
        </properties>
</profile>

三、IDEA集成Maven

我们要想在IDEA中使用Maven进行项目构建,就需要在IDEA中集成Maven

3.1 配置Maven环境

3.1.1 当前工程设置

1、选择 IDEA中 File => Settings => Build,Execution,Deployment => Build Tools => Maven

2、设置IDEA使用本地安装的Maven,并修改配置文件及本地仓库路径。再修改Maven => Runner 中的 JRE

Local repository :指定Maven的本地仓库的路径 (如果指定了settings.xml, 这个目录会自动读取出来, 可以不用手动指定)

3、配置工程的编译版本为17

  • Maven默认使用的编译版本为5(版本过低)

(same as language level应该就可以)

上述配置的maven环境,只是针对于当前工程的,如果我们再创建一个project,又恢复成默认的配置了。 要解决这个问题, 我们就需要配置全局的maven环境。

3.1.2 全局设置

1、进入到IDEA欢迎页面

  • 选择 IDEA中 File => close project

2、打开 All settings , 选择 Build,Execution,Deployment => Build Tools => Maven。再修改Maven => Runner 中的 JRE

3、配置工程的编译版本为17

这里所设置的maven的环境信息,并未指定任何一个project,此时设置的信息就属于全局配置信息。 以后,我们再创建project,默认就是使用我们全局配置的信息。

3.2 Maven项目

3.2.1 创建Maven项目

1、创建一个空项目

2.创建好项目之后,进入项目中,要设置JDK的版本号。选择小齿轮,选择 Project Structure

  1. 创建模块,选择Java语言,选择Maven。 填写模块的基本信息

  2. 在maven项目中,创建HelloWorld类,并运行

这个test目录下的resources默认是不会创建的,如果需要的话就在test上右键创建一个新的Directory即可,它会自动推荐resources

sql 复制代码
Maven项目的目录结构:
maven-project01
        |---  src  (源代码目录和测试代码目录)
               |---  main (源代码目录)
                        |--- java (源代码java文件目录)
                        |--- resources (源代码配置文件目录)
              |---  test (测试代码目录)
                        |--- java (测试代码java目录)
                        |--- resources (测试代码配置文件目录)
        |--- target (编译、打包生成文件存放目录)
3.2.2 POM配置详解

POM (Project Object Model) :指的是项目对象模型,用来描述当前的maven项目。

  • 使用pom.xml文件来实现

pom.xml文件:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- POM模型版本 -->
    <modelVersion>4.0.0</modelVersion>

    <!-- 当前项目坐标 -->
    <groupId>com.itheima</groupId>
    <artifactId>maven-project01</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <!-- 项目的JDK版本及编码 -->
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

pom文件详解:

  • <project> :pom文件的根标签,表示当前maven项目
  • <modelVersion> :声明项目描述遵循哪一个POM模型版本
    • 虽然模型本身的版本很少改变,但它仍然是必不可少的。目前POM模型版本是4.0.0
  • 坐标 :<groupId><artifactId><version>
    • 定位项目在本地仓库中的位置,由以上三个标签组成一个坐标
  • <maven.compiler.source> :编译JDK的版本
  • <maven.compiler.target> :运行JDK的版本
  • <project.build.sourceEncoding> : 设置项目的字符集
3.2.3 Maven坐标详解

什么是坐标?

  • Maven中的坐标是资源的唯一标识 , 通过该坐标可以唯一定位资源位置
  • 使用坐标来定义项目或引入项目中需要的依赖

Maven坐标主要组成

  • groupId:定义当前Maven项目隶属组织名称 (通常是域名反写,例如:com.itheima)
  • artifactId:定义当前Maven项目名称 (通常是模块名称,例如 order-service、goods-service)
  • version:定义当前项目版本号
    • SNAPSHOT:功能不稳定、尚处于开发中的版本,即快照版本
    • RELEASE:功能趋于稳定、当前更新停止,可以用于发行的版本

注意:

  • 上面所说的资源可以是插件、依赖、当前项目。
  • 我们的项目如果被其他的项目依赖时,也是需要坐标来引入的。

3.3 导入Maven项目

先从资料中将maven-project02、maven-project03都拷贝到web-ai-project01目录下(打开web-ai-project01所在文件夹,再把他们拷贝进来),跟maven-project01同级别。

如下图,可以看出02和03与01不同,并不被识别为maven项目。如果识别为maven项目,pom文件会变成蓝色的,且模块名字也会加粗

在IDEA中导入Maven项目,有两种方式。

  • 方式一:File -> Project Structure -> Modules -> Import Module -> 选择maven项目的pom.xml。
  • 方式二:Maven面板 -> +(Add Maven Projects) -> 选择maven项目的pom.xml。

四、依赖管理

4.1 依赖配置

4.1.1 基本配置

依赖:指当前项目运行所需要的jar包。一个项目中可以引入多个依赖:

例如:在当前工程中,我们需要用到logback来记录日志,此时就可以在maven工程的pom.xml文件中,引入logback的依赖。具体步骤如下:

1.在pom.xml中编写<dependencies>标签

  1. <dependencies>标签中使用<dependency>引入坐标

  2. 定义坐标的 groupId、artifactId、version

xml 复制代码
<dependencies>
    <!-- 依赖 : spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.1.4</version>
    </dependency>
</dependencies>

4.点击刷新按钮 ,引入最新加入的坐标

刷新依赖:保证每一次引入新的依赖,或者修改现有的依赖配置,都可以加入最新的坐标

注意事项:

1.如果引入的依赖,在本地仓库中不存在,将会连接远程仓库 / 中央仓库,然后下载依赖(这个过程会比较耗时,耐心等待)

  1. 如果不知道依赖的坐标信息,可以到mvn的中央仓库(https://mvnrepository.com/)中搜索
4.1.2 查找依赖

1.利用中央仓库搜索的依赖坐标,以常见的logback-classic为例。

2.利用IDEA工具搜索依赖,以常见的logback-classic为例。

  1. 熟练上手maven后,快速导入依赖,以常见的logback-classic为例。(先artifactId再groupId再version)
4.1.3 依赖传递

我们上面在pom.xml中配置了一项依赖,就是spring-context,但是我们通过右侧的maven面板可以看到,其实引入进来的依赖,并不是这一项,有非常多的依赖,都引入进来了。我们可以看到如下图所示:

为什么会出现这样的现象呢? 那这里呢,就涉及到maven中非常重要的一个特性,那就是Maven中的依赖传递。

所谓maven的依赖传递,指的就是如果在maven项目中,A 依赖了B,B依赖了C,C依赖了D,那么在A项目中,也会有C、D依赖,因为依赖会传递。

那如果,传递下来的依赖,在项目开发中,我们确实不需要,此时,我们可以通过Maven中的排除依赖功能,来将这个依赖排除掉。

4.1.4 排除依赖
  • 排除依赖:指主动断开依赖的资源被排除的资源无需指定版本
  • 配置形式如下:
xml 复制代码
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.1.4</version>

    <!--排除依赖, 主动断开依赖的资源-->
    <exclusions>
        <exclusion>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-observation</artifactId>
        </exclusion>
    </exclusions>
</dependency>

依赖排除示例:

1.默认通过maven的依赖传递,传递下来了 micrometer-observation 的依赖。

  1. 加入排除依赖的配置之后,该依赖就被排除掉了。

4.2 生命周期

4.2.1 介绍

Maven的生命周期就是为了对所有的构建过程进行抽象和统一。 描述了一次项目构建,经历哪些阶段。

在Maven出现之前,项目构建的生命周期就已经存在,软件开发人员每天都在对项目进行清理,编译,测试及部署。虽然大家都在不停地做构建工作,但公司和公司间、项目和项目间,往往使用不同的方式做类似的工作。

Maven从大量项目和构建工具中学习和反思,然后总结了一套高度完美的,易扩展的项目构建生命周期。这个生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点生成等几乎所有构建步骤。

Maven对项目构建的生命周期划分为3套(相互独立):

  • clean:清理工作。
  • default:核心工作。如:编译、测试、打包、安装、部署等。
  • site:生成报告、发布站点等。

三套生命周期又包含哪些具体的阶段呢, 我们来看下面这幅图:

每套生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。

我们看到这三套生命周期,里面有很多很多的阶段,这么多生命周期阶段,其实我们常用的并不多,主要关注以下几个:

  • clean:移除上一次构建生成的文件。比如构建产生的字节码文件class以及上一次项目构建产生的jar包。整个target目录都会删除
  • compile:编译项目源代码,即编译成字节码文件
  • test:使用合适的单元测试框架运行测试(junit)。所有项目中的单元测试都会执行
  • package:将编译后的文件打包,如:jar、war等
  • install:安装项目到本地仓库

注意:在同一套 生命周期中,当运行后面的阶段时,前面的阶段都会运行。如下图,当执行install时,compile也会执行、package也会,前面这些都会。但是注意!!执行install时,clean不会执行,因为前提条件是同一套 生命周期

Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际工作。在Maven的设计中,实际任务(如源代码编译)都交由插件来完成。

IDEA工具为了方便程序员使用maven生命周期,在右侧的maven工具栏中,已给出快速访问通道。

  • 生命周期的顺序是:clean --> validate --> compile --> test --> package --> verify --> install --> site --> deploy
  • 我们需要关注的就是:clean --> compile --> test --> package --> install
4.2.2 执行

在日常开发中,当我们要执行指定的生命周期时,有两种执行方式:

1.在idea工具右侧的maven工具栏中,选择对应的生命周期,双击执行

2.在DOS命令行中,通过maven命令执行

方式一:在idea中执行生命周期

  • 选择对应的生命周期,双击执行






方式二:在命令行中执行生命周期

1.打开maven项目对应的磁盘目录

  1. 在当前目录下打开CMD

类似的道理,我们也可以在命令执行:

  • mvn compile
  • mvn test
  • mvn package
  • mvn install

4.3 更新依赖索引

有时候给idea配置完maven仓库信息后,在idea中依然搜索不到仓库中的jar包。这是因为仓库中的jar包索引尚未更新到idea中。这个时候我们就需要更新idea中maven的索引了,具体做法如下:

打开设置----搜索maven----Repositories----选中本地仓库-----点击Update

4.4 清理maven仓库

  • 问题现象:Maven项目中添加的依赖,未正确下载,造成右侧Maven面板中的依赖报红,再次reload重新加载也不会再下载。
  • 产生原因:由于网络原因,依赖没有下载完整导致的,在maven仓库中生成了xxx.lastUpdated文件,该文件不删除,不会再重新下载。

初始情况下,我们的本地仓库是没有任何jar包的,此时会从私服去下载(如果没有配置,就直接从中央仓库去下载),可能由于网络的原因,jar包下载不完全,这些不完整的jar包都是以lastUpdated结尾。此时,maven不会再重新帮你下载,需要你删除这些以lastUpdated结尾的文件,然后maven才会再次自动下载这些jar包。

五、单元测试

5.1 介绍

阶段划分:单元测试、集成测试、系统测试、验收测试。

测试方法:白盒测试、黑盒测试 及 灰盒测试。

5.2 Junit入门

5.2.1 单元测试
  • 单元测试:就是针对最小的功能单元(方法),编写测试代码对其正确性进行测试。
  • JUnit:最流行的Java测试框架之一,提供了一些功能,方便程序进行单元测试(第三方公司提供)。

而如果我们使用了JUnit单元测试框架进行测试,将会有以下优势:

1.测试代码与源代码分开,便于维护。

  1. 可根据需要进行自动化测试。

  2. 可自动分析测试结果,产出测试报告。

5.2.2 入门程序

需求:使用JUnit,对UserService中的业务方法进行单元测试,测试其正确性。

1.在pom.xml中,引入JUnit的依赖。

xml 复制代码
<!--Junit单元测试依赖-->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.9.1</version>
    <scope>test</scope>
</dependency>

2.在test/java目录下,创建测试类,并编写对应的测试方法,并在方法上声明@Test注解。

java 复制代码
@Test
public void testGetAge(){
    Integer age = new UserService().getAge("110002200505091218");
    System.out.println(age);
}

3.运行单元测试 (测试通过:绿色;测试失败:红色)。

  • 测试通过显示绿色
  • 测试失败显示红色

注意:

  • 测试类的命名规范为:XxxxTest
  • 测试方法的命名规定为:public void xxx(){...}

5.3 断言

JUnit提供了一些辅助方法,用来帮我们确定被测试的方法是否按照预期的效果正常工作,这种方式称为断言。

msg代表错误的提示信息

java 复制代码
package com.itheima;

import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class UserServiceTest {
    
    @Test
    public void testGetAge2(){
        Integer age = new UserService().getAge("110002200505091218");
        Assertions.assertNotEquals(18, age, "两个值相等");
//        String s1 = new String("Hello");
//        String s2 = "Hello";
//        Assertions.assertSame(s1, s2, "不是同一个对象引用");
    }

    @Test
    public void testGetGender2(){
        String gender = new UserService().getGender("612429198904201611");
        Assertions.assertEquals("男", gender);
    }
}

测试结果输出:两个都绿

5.4 常见注解

在JUnit中还提供了一些注解,还增强其功能,常见的注解有以下几个:

演示 @BeforeEach,@AfterEach,@BeforeAll,@AfterAll 注解:

java 复制代码
public class UserServiceTest {

    @BeforeEach
    public void testBefore(){
        System.out.println("before...");
    }

    @AfterEach
    public void testAfter(){
        System.out.println("after...");
    }

    @BeforeAll //该方法必须被static修饰
    public static void testBeforeAll(){ 
        System.out.println("before all ...");
    }

    @AfterAll //该方法必须被static修饰
    public static void testAfterAll(){
        System.out.println("after all...");
    }

    @Test
    public void testGetAge(){
        Integer age = new UserService().getAge("110002200505091218");
        System.out.println(age);
    }
    
    @Test
    public void testGetGender(){
        String gender = new UserService().getGender("612429198904201611");
        System.out.println(gender);
    }
 }   

输出结果如下:

演示 @ParameterizedTest ,@ValueSource ,@DisplayName 注解:

java 复制代码
package com.itheima;

import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

@DisplayName("测试-学生业务操作")
public class UserServiceTest {

    @DisplayName("测试-获取年龄")
    @Test
    public void testGetAge(){
        Integer age = new UserService().getAge("110002200505091218");
        System.out.println(age);
    }

    @DisplayName("测试-获取性别")
    @Test
    public void testGetGender(){
        String gender = new UserService().getGender("612429198904201611");
        System.out.println(gender);
    }

    @DisplayName("测试-获取性别3")
    @ParameterizedTest
    @ValueSource(strings = {"612429198904201611","612429198904201631","612429198904201626"})
    public void testGetGender3(String idcard){
        String gender = new UserService().getGender(idcard);
         System.out.println(gender);
    }
}

输出结果如下:

思考: 在maven项目中,test目录存放单元测试的代码,是否可以在main目录中编写单元测试呢 ? 可以,但是不规范

如何设置仅能在test目录进行单元测试呢?------依赖范围scope

5.5 依赖范围

依赖的jar包,默认情况下,可以在任何地方使用,在main目录下,可以使用;在test目录下,也可以使用。

在maven中,如果希望限制依赖的使用范围,可以通过 <scope>...</scope> 设置其作用范围。

作用范围:

  • 主程序范围有效。(main文件夹范围内)
  • 测试程序范围有效。(test文件夹范围内)
  • 是否参与打包运行。(package指令范围内)

可以在pom.xml中配置 <scope></scope> 属性来控制依赖范围。

如果对Junit单元测试的依赖,设置了scope为 test,就代表,该依赖,只是在测试程序中可以使用,在主程序中是无法使用的。所以我们会看到如下现象:

如上图所示,给junit依赖通过scope标签指定依赖的作用范围。 那么这个依赖就只能作用在测试环境,其他环境下不能使用。

scope的取值常见的如下:(重点关注前两个)

(暂时不学)Maven高级

相关推荐
LayZhangStrive5 分钟前
融360 一面
java·面试·后端开发
城管不管30 分钟前
重生——第十次面试之开源中国一面挂
java·linux·开发语言·算法·面试·职场和发展·开源
Java小白笔记34 分钟前
Windows系统免软件命令激活
java·网络·人工智能·windows·ai·ai编程
爱敲键盘的猴子43 分钟前
深入理解 Java HashMap
java·hashmap
霸道流氓气质43 分钟前
Java开发者AI编程常用MCP、Skills、Rules全景汇总
java·开发语言·ai编程
天涯醉青楼1 小时前
Spring Boot 接口返回 Bean 时字段名“变形”的深度剖析与解决方案
java
步行cgn1 小时前
MyBatis 高级映射与延迟加载完全指南
java·tomcat·mybatis
thefool1122661 小时前
从中序与后序遍历序列构造二叉树
java
吴声子夜歌1 小时前
Java面试题——JVM(二)
java·开发语言·jvm
CRMEB1 小时前
商城大促活动策划全流程:从定目标到复盘四阶段
java·开发语言·人工智能·ai·开源·php