【Java面试】第七天

news2024/11/17 8:32:46

在这里插入图片描述

🌟个人主页:时间会证明一切.


目录

  • 有三个线程T1,T2,T3如何保证顺序执行?
    • 依次执行start方法
    • 使用join
    • 使用CountDownLatch
    • 使用线程池
    • 使用CompletableFuture
  • Spring Bean的生命周期是怎么样的?
  • Autowired和Resource的关系?
    • 相同点
    • 不同点
      • byName和byType匹配顺序不同
      • 作用域不同
      • 支持方不同

有三个线程T1,T2,T3如何保证顺序执行?

想要让三个线程依次执行,并且严格按照T1,T2,T3的顺序的话,主要就是想办法让三个线程之间可以通信、或者可以排队。

想让多个线程之间可以通信,可以通过join方法实现,还可以通过CountDownLatch、CyclicBarrier和Semaphore来实现通信。

想要让线程之间排队的话,可以通过线程池或者CompletableFuture的方式来实现。

依次执行start方法

在代码中,分别依次调用三个线程的start方法,这种方法是最容易想到的,但是也是最不靠谱的。

代码实现如下,通过执行的话可以发现,数据结果是不固定的:

    public static void main(String[] args) {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread 1 running");
            }
        });


        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread 2 running");
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread 3 running");
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
    }

以上代码的数据结果每次执行都不固定,所以,没办法满足我们的要求。

使用join

Thread类中提供了一个join方法,他的有以下代码:

public static void main(String[] args) throws InterruptedException {
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread 1 running");
        }
    });

    thread1.start();

    System.out.println("Main 1 running");
}


输出结果会是:

Main 1 running
Thread 1 running

但是,如果我们在上面的第15行,增加一行thread1.join();那么输出结果就会有变化,如下:

Thread 1 running
Main 1 running

所以,join就是把thread1这个子线程加入到当前主线程中,也就是主线程要阻塞在这里,等子线程执行完之后再继续执行。

所以,我们可以通过join来实现多个线程的顺序执行:

    public static void main(String[] args) {
        final Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " is Running.");
            }
        },"T1");


        final Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread1.join();
                } catch (InterruptedException e) {
                    System.out.println("join thread1 failed");
                }
                System.out.println(Thread.currentThread().getName() + " is Running.");
            }
        },"T2");

        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.join();
                } catch (InterruptedException e) {
                    System.out.println("join thread1 failed");
                }
                System.out.println(Thread.currentThread().getName() + " is Running.");
            }
        },"T3");

        thread3.start();
        thread2.start();
        thread1.start();
    }

我们在thread2中等待thread1执行完,然后在thread3中等待thread2执行完。那么整体的执行顺序就是:

T1 is Running.
T2 is Running.
T3 is Running.

使用CountDownLatch

CountDownLatch是Java并发库中的一个同步辅助类,它允许一个或多个线程等待其他线程完成操作。我们可以借助他来让三个线程之间相互通信,以达到顺序执行的目的。

public class CountDownLatchThreadExecute {
    public static void main(String[] args) throws InterruptedException {
                // 创建CountDownLatch对象,用来做线程通信
        CountDownLatch latch = new CountDownLatch(1);
        CountDownLatch latch2 = new CountDownLatch(1);
        CountDownLatch latch3 = new CountDownLatch(1);

        // 创建并启动线程T1
        Thread t1 = new Thread(new MyThread(latch), "T1");
        t1.start();

        // 等待线程T1执行完
        latch.await();

        // 创建并启动线程T2
        Thread t2 = new Thread(new MyThread(latch2), "T2");
        t2.start();

        // 等待线程T2执行完
        latch2.await();

        // 创建并启动线程T3
        Thread t3 = new Thread(new MyThread(latch3), "T3");
        t3.start();

        // 等待线程T3执行完
        latch3.await();
    }
}

class MyThread implements Runnable {
    private CountDownLatch latch;

    public MyThread(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 完成一个线程,计数器减1
            latch.countDown();
        }
    }
}

主要就是想办法让编排三个子线程的主线程阻塞,保证T1执行完再启动T2,T2执行完再启动T3。而这个编排的方式就是想办法知道什么时候子线程执行完,就可以通过CountDownLatch实现。

基于相同的原理, 我们还可以借助CyclicBarrier和Semaphore实现此功能:

public class CyclicBarrierThreadExecute {

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        // 创建CyclicBarrier对象,用来做线程通信
        CyclicBarrier barrier = new CyclicBarrier(2);

        // 创建并启动线程T1
        Thread t1 = new Thread(new MyThread(barrier), "T1");
        t1.start();

        // 等待线程T1执行完
        barrier.await();

        // 创建并启动线程T2
        Thread t2 = new Thread(new MyThread(barrier), "T2");
        t2.start();

        // 等待线程T2执行完
        barrier.await();

        // 创建并启动线程T3
        Thread t3 = new Thread(new MyThread(barrier), "T3");
        t3.start();

        // 等待线程T3执行完
        barrier.await();
    }
}

class MyThread implements Runnable {
    private CyclicBarrier barrier;

    public MyThread(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 等待其他线程完成
            try {
                barrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}


借助Semaphore实现此功能:

public class SemaphoreThreadExecute {
    public static void main(String[] args) throws InterruptedException {
        // 创建Semaphore对象,用来做线程通信
        Semaphore semaphore = new Semaphore(1);

        // 等待线程T1执行完
        semaphore.acquire();

        // 创建并启动线程T1
        Thread t1 = new Thread(new MyThread(semaphore), "T1");
        t1.start();

        // 等待线程T2执行完
        semaphore.acquire();

        // 创建并启动线程T2
        Thread t2 = new Thread(new MyThread(semaphore), "T2");
        t2.start();

        // 等待线程T3执行完
        semaphore.acquire();

        // 创建并启动线程T3
        Thread t3 = new Thread(new MyThread(semaphore), "T3");
        t3.start();
    }
}

class MyThread implements Runnable {
    private Semaphore semaphore;

    public MyThread(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放许可证,表示完成一个线程
            semaphore.release();
        }
    }
}

使用线程池

了解线程池的开发者都知道,线程池内部是使用了队列来存储任务的,所以线程的执行顺序会按照任务的提交顺序执行的,但是如果是多个线程同时执行的话,是保证不了先后顺序的,因为可能先提交的后执行了。但是我们可以定义一个只有一个线程的线程池,然后依次的将T1,T2,T3提交给他执行:

public class ThreadPoolThreadExecute {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 创建并启动线程T1
        executor.submit(new MyThread("T1"));

        // 创建并启动线程T2
        executor.submit(new MyThread("T2"));

        // 创建并启动线程T3
        executor.submit(new MyThread("T3"));

        // 关闭线程池
        executor.shutdown();
    }
}

class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(name + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

使用CompletableFuture

Java 8引入了CompletableFuture,它是一个用于异步编程的新的强大工具。CompletableFuture提供了一系列的方法,可以用来创建、组合、转换和管理异步任务,并且可以让你实现异步流水线,在多个任务之间轻松传递结果。

如以下实现方式:

public class CompletableFutureThreadExecute {
    public static void main(String[] args) {
        // 创建CompletableFuture对象
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(new MyThread("T1"));

        // 等待线程T1完成
        future1.join();

        // 创建CompletableFuture对象
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(new MyThread("T2"));

        // 等待线程T2完成
        future2.join();

        // 创建CompletableFuture对象
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(new MyThread("T3"));

        // 等待线程T3完成
        future3.join();
    }
}

class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(name + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

上面的代码还可以做一些优化:

public class CompletableFutureThreadExecute {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建CompletableFuture对象
        CompletableFuture<Void> future = CompletableFuture.runAsync(new MyThread("T1")).thenRun(new MyThread("T2")).thenRun(new MyThread("T3"));
        future.get();
    }
}

class MyThread implements Runnable {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            // 模拟执行任务
            Thread.sleep(1000);
            System.out.println(name + " is Running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Spring Bean的生命周期是怎么样的?

一个Spring的Bean从出生到销毁的全过程就是他的整个生命周期,那么经历以下几个阶段:
image.png

整个生命周期可以大致分为3个大的阶段,分别是:创建、使用、销毁。还可以进一步分为5个小的阶段:实例化、初始化、注册Destruction回调、Bean的正常使用以及Bean的销毁。

有人把设置属性值这一步单独拿出来了,主要是因为在源码中doCreateBean是先调了populateBean进行属性值的设置,然后再调initializeBean进行各种前置&后置处理。但是其实属性的设置其实就是初始化的一部分。要不然初始化啥呢?

有人也把注册Destruction回调放到销毁这一步了,其实是不对的,其实他不算初始化的一步,也不应该算作销毁的一个过程,他虽然和销毁有关,但是他是在创建的这个生命周期中做的。

具体到代码方面,可以参考以下这个更加详细的过程介绍,我把具体实现的代码位置列出来了。

  1. 实例化Bean
    • Spring容器首先创建Bean实例。
    • AbstractAutowireCapableBeanFactory类中的createBeanInstance方法中实现
  2. 设置属性值
    • Spring容器注入必要的属性到Bean中。
    • AbstractAutowireCapableBeanFactorypopulateBean方法中处理
  3. 检查Aware
    • 如果Bean实现了BeanNameAware、BeanClassLoaderAware等这些Aware接口,Spring容器会调用它们。
    • AbstractAutowireCapableBeanFactoryinitializeBean方法中调用
  4. 调用BeanPostProcessor的前置处理方法
    • 在Bean初始化之前,允许自定义的BeanPostProcessor对Bean实例进行处理,如修改Bean的状态。BeanPostProcessor的postProcessBeforeInitialization方法会在此时被调用。
    • AbstractAutowireCapableBeanFactoryapplyBeanPostProcessorsBeforeInitialization方法执行。
  5. 调用InitializingBean的afterPropertiesSet方法
    • 提供一个机会,在所有Bean属性设置完成后进行初始化操作。如果Bean实现了InitializingBean接口,afterPropertiesSet方法会被调用。
    • AbstractAutowireCapableBeanFactoryinvokeInitMethods方法中调用。
  6. 调用自定义init-method方法:
    • 提供一种配置方式,在XML配置中指定Bean的初始化方法。如果Bean在配置文件中定义了初始化方法,那么该方法会被调用。
    • AbstractAutowireCapableBeanFactoryinvokeInitMethods方法中调用。
  7. 调用BeanPostProcessor的后置处理方法
    • 在Bean初始化之后,再次允许BeanPostProcessor对Bean进行处理。BeanPostProcessor的postProcessAfterInitialization方法会在此时被调用。
    • AbstractAutowireCapableBeanFactoryapplyBeanPostProcessorsAfterInitialization方法执行
  8. 注册Destruction回调:
    • 如果Bean实现了DisposableBean接口或在Bean定义中指定了自定义的销毁方法,Spring容器会为这些Bean注册一个销毁回调,确保在容器关闭时能够正确地清理资源。
    • AbstractAutowireCapableBeanFactory类中的registerDisposableBeanIfNecessary方法中实现
  9. Bean准备就绪
    • 此时,Bean已完全初始化,可以开始处理应用程序的请求了。
  10. 调用DisposableBean的destroy方法
  • 当容器关闭时,如果Bean实现了DisposableBean接口,destroy方法会被调用。
  • DisposableBeanAdapterdestroy方法中实现
  1. 调用自定义的destory-method
  • 如果Bean在配置文件中定义了销毁方法,那么该方法会被调用。
  • DisposableBeanAdapterdestroy方法中实现

可以看到,整个Bean的创建的过程都依赖于AbstractAutowireCapableBeanFactory这个类,而销毁主要依赖DisposableBeanAdapter这个类。

AbstractAutowireCapableBeanFactory 的入口处,doCreateBean的核心代码如下,其中包含了实例化、设置属性值、初始化Bean以及注册销毁回调的几个核心方法。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
			throws BeanCreationException {

		// 实例化bean
		BeanWrapper instanceWrapper = null;
		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}

        // ...

		Object exposedObject = bean;
		try {
            //设置属性值
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
                //初始化Bean
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}
		
    	// ...

		// 注册Bean的销毁回调
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}

		return exposedObject;
	}

DisposableBeanAdapter的destroy方法中核心内容如下:

@Override
public void destroy() {
    if (this.invokeDisposableBean) {
            // ...
            ((DisposableBean) bean).destroy();
        }
        // ...
    }

    if (this.destroyMethod != null) {
        invokeCustomDestroyMethod(this.destroyMethod);
    }
    else if (this.destroyMethodName != null) {
        Method methodToCall = determineDestroyMethod();
        if (methodToCall != null) {
            invokeCustomDestroyMethod(methodToCall);
        }
    }
}

Autowired和Resource的关系?

相同点

对于下面的代码来说,如果是Spring容器的话,两个注解的功能基本是等价的,他们都可以将bean注入到对应的field中

@Autowired
private Bean beanA;
@Resource
private Bean beanB;

不同点

byName和byType匹配顺序不同

  1. Autowired在获取bean的时候,先是byType的方式,再是byName的方式。意思就是先在Spring容器中找以Bean为类型的Bean实例,如果找不到或者找到多个bean,则会通过fieldName来找。举个例子:
@Component("beanOne")
class BeanOne implements Bean {}
@Component("beanTwo")
class BeanTwo implements Bean {}
@Service
class Test {
    // 此时会报错,先byType找到两个bean:beanOne和beanTwo
    // 然后通过byName(bean)仍然没办法匹配
	@Autowired
    private Bean bean; 

    // 先byType找到两个bean,然后通过byName确认最后要注入的bean
    @Autowired
    private Bean beanOne;

    // 先byType找到两个bean,然后通过byName确认最后要注入的bean
    @Autowired
    @Qualifier("beanOne")
    private Bean bean;
}
  1. Resource在获取bean的时候,和Autowired恰好相反,先是byName方式,然后再是byType方式。当然,我们也可以通过注解中的参数显示指定通过哪种方式。同样举个例子:
@Component("beanOne")
class BeanOne implements Bean {}
@Component("beanTwo")
class BeanTwo implements Bean {}
@Service
class Test {
    // 此时会报错,先byName,发现没有找到bean
    // 然后通过byType找到了两个Bean:beanOne和beanTwo,仍然没办法匹配
	@Resource
    private Bean bean; 

    // 先byName直接找到了beanOne,然后注入
    @Resource
    private Bean beanOne;

    // 显示通过byType注入,能注入成功
    @Resource(type = BeanOne.class)
    private Bean bean;
}

作用域不同

  1. Autowired可以作用在构造器,字段,setter方法上
  2. Resource 只可以使用在field,setter方法上

支持方不同

  1. Autowired是Spring提供的自动注入注解,只有Spring容器会支持,如果做容器迁移,是需要修改代码的
  2. Resource是JDK官方提供的自动注入注解(JSR-250)。它等于说是一个标准或者约定,所有的IOC容器都会支持这个注解。假如系统容器从Spring迁移到其他IOC容器中,是不需要修改代码的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Python生成多种不同类型的Excel图表

目录 一、使用工具 二、生成Excel图表的基本步骤 三、使用Python创建Excel图表 柱形图饼图折线图条形图散点图面积图组合图瀑布图树形图箱线图旭日图漏斗图直方图不使用工作表数据生成图表 四、总结 Excel图表是数据可视化的重要工具&#xff0c;它通过直观的方式将数字信…

Linux环境

Linux环境 导语程序参数getoptgetopt_long 环境变量getenv/putenvenviron 时间和日期timedifftime和gmtimectime&asctimestrftime/strptime 临时文件tmpnamtmpfile 获取信息用户主机日志 资源和限制总结参考文献 导语 任何程序都是在一定的环境下运行的&#xff0c;通常这…

分布式事务学习笔记(二)Seata架构、TC服务器部署、微服务集成Seata

文章目录 前言2 Seata2.1 Seata的架构2.2 部署TC服务1&#xff09;下载安装包2&#xff09;解压3&#xff09;修改配置文件4&#xff09;在Nacos中添加TC服务配置5&#xff09;创建数据库表6&#xff09;启动TC服务7&#xff09;查看TC服务 2.3 微服务集成Seata2.3.1 引入依赖2…

通过覆写 url_for 将 flask 应用部署到子目录下

0. 缘起 最近用 flask 写了一个 web 应用&#xff0c;需要部署到服务器上。而服务器主域名已经被使用了&#xff0c;只能给主域名加个子目录进行部署&#xff0c;比如主域名 example.org &#xff0c;我需要在 example.org/flask 下部署。这时 flask 应用里的内部连接们就出现…

sqli-labs Basic Challenge Less_1 通关指南

sqli-labs Basic Challenge Less_1 通关指南 测试注入点测试注入点的数据类型测试过程&#xff1a; 测试返回点&#xff1a;测试列数&#xff1a;测试返回点&#xff1a; 收集数据&#xff1a;版本用户权限库名表名与列名 查找敏感信息 测试注入点 在进行SQL注入之前&#xff…

React js Router 路由 2, (把写过的几个 app 组合起来)

完整的项目&#xff0c;我已经上传了&#xff0c;资源链接. 起因&#xff0c; 目的: 每次都是新建一个 react 项目&#xff0c;有点繁琐。 刚刚学了路由&#xff0c;不如写一个 大一点的 app &#xff0c;把前面写过的几个 app, 都包含进去。 这部分感觉就像是&#xff0c; …

打包部署若依(RuoYi)SpringBoot后端和Vue前端图文教程

打包后端‘ 1&#xff0c;打开若依&#xff0c;点击右侧的Maven展开Maven管理&#xff0c;选择ruoyi>Lifecycle 先双击clean清除原本启动项目时生成的文件。然后点击package等待项目打包&#xff0c;切记要取消运行再打包 打包完成后会在ruoyi-admin>src>target里面…

Qt 边框border - qss样式

border属性 实际上&#xff0c;border并不是一个单独的属性&#xff0c;在Qt样式表中&#xff0c;它通常指的是一系列与边框相关的属性的组合。然而&#xff0c;你也可以在一条样式规则中一次性设置所有这些值&#xff0c;如下所示&#xff1a; QPushButton { border: 2px sol…

HTB-Unified(log4j2漏洞、MongoDb替换管理员密码)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解Unified靶机 渗透过程 信息搜集 服务器开放了SSH服务&#xff0c;HTTP服务 访问网站 验证log4j2漏洞 8443端口&#xff1a;UniFi 网络 &#xff0c;访问查询 是否有Nday漏洞利用 可以观察到UniFi的版…

汇编实现从1加到1000(《X86汇编语言 从实模式到保护模式(第2版》) 第135页第2题解答)

题目: 编写一段主引导扇区程序,计算从1加到1000的和,并在屏幕上显示结果 输出结果: 代码: jmp near start text db 123...1000 start:mov ax,0x07c0mov ds,ax ;数据段从主引导区开始mov ax,0xb800mov es,ax ;显存地址从B8000物理地址开始mov si,text ;si指向text的第…

linux系统安装miniconda3

一、下载minconda3 下载地址&#xff1a;https://docs.conda.io/en/latest/miniconda.html 一般国内访问比较困难&#xff0c;可到清华软件镜像站 Index of /anaconda/miniconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 需要特别注意自己的下载版本和自己的…

苹果CMS海洋CMS那个更容易被百度收录?苹果CMS站群

SEO优化和搜索引擎的友好性常常是网站管理员关注的重点。苹果CMS&#xff08;maccmscn&#xff09;和海洋CMS都是国内常见的CMS平台&#xff0c;但在搜索引擎优化&#xff08;SEO&#xff09;和百度收录方面&#xff0c;苹果CMS凭借其优秀的插件生态系统&#xff0c;特别是泛目…

Java 类一口气给你讲完!(✿◡‿◡)

Java 类实例 Java面向对象设计 - Java类实例 以下是创建类的实例的一般语法: new <Class Constructor>;new 运算符后面是对构造函数的调用。 new 运算符通过分配堆上的内存来创建类的实例。以下语句创建Dog类的实例: new Dog();Dog()是对Dog类的构造函数的调用。 当…

PTT:Point Tree Transformer for Point Cloud Registration 论文解读

目录 一、导言 二、相关工作 1、基于Transformer的点云配准 2、针对点云的局部注意力 三、PTT 1、KPconv提取特征 2、Tree Transformer Encoder 3、Decoder 4、估计姿态 5、损失函数 四、实验 1、对比不同Backbone 2、运行时间对比 3、对比不同PTT方法下RR指标的…

[机器学习]决策树

1 决策树简介 2 信息熵 3 ID3决策树 3.1 决策树构建流程 3.2 决策树案例 4 C4.5决策树 5 CART决策树&#xff08;分类&回归&#xff09; 6 泰坦尼克号生存预测案例 import pandas as pd from sklearn.model_selection import train_test_split from sklearn.tree import …

starUML使用说明文档[简单易懂/清晰明了]||好上手

1. 安装 StarUML 首先&#xff0c;您需要从 StarUML 官方网站&#xff08;http://staruml.io/&#xff09;下载最新版本的 StarUML 安装包。然后&#xff0c;按照提示进行安装。&#xff08;联想可以从软件管家里下载&#xff09; 2. 打开 StarUML 安装完成后&#xff0c;双击桌…

mac上Charles怎么配置,可以抓取浏览器/IDEA的接口

一、抓取浏览器接口 1、下载安装Charles后&#xff0c;按下图操作安装证书&#xff0c;mac撒好难过要把证书调整为可信任 2、打开macOS代理 方式一&#xff1a;指点开启这里 方式二&#xff1a;进入代理配置中开启&#xff0c;结果和方式一一样的 3、这时就可以抓取到浏览器…

高职院校人工智能技术和无人机技术实训室建设方案

一、方案背景与需求分析 1.1 人工智能与无人机技术发展概况 人工智能&#xff08;AI&#xff09;和无人机技术作为当今科技领域的两大热点&#xff0c;正以前所未有的速度发展和渗透到各行各业中。根据国际数据公司(IDC)的报告&#xff0c;全球人工智能市场规模预计将在2024年…

Oracle按照某一字段值排序并显示,相同的显示序号

Oracle按照某一字段值排序并显示,相同的显示序号 最近的工作遇到对于相同的字段,按照序号去显示值,并对相同的值进行排序 实验了半天,感觉满意的答案,分享给大家 第一种: ROW_NUMBER 语法: ROW_NUMBER() OVER (ORDER BY your_column) AS sequence_number 说明: 根据your_column…

Python 数学建模——ARMA 时间序列分析

文章目录 前言使用前提平稳性检验白噪声检验 用法代码实例第一步——平稳性分析方法一方法二方法三 第二步——白噪声分析第三步——确定参数第四步——模型构建与检验检验模型效果预测未来数据 前言 常见的时间序列分析方法有很多&#xff0c;之前介绍了一个稍微新颖的 Prophe…