Java 自定义FTP连接池

2023-11-13

一、引入FTP包和连接池包

<!-- ftp连接start -->
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.5</version>
</dependency>
<!-- ftp连接start -->

<!-- 自定义连接池 start-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.5.0</version>
</dependency>
<!-- 自定义连接池 end-->

二、在项目根路径新建一个配置文件,把连接池配置属性和FTPClient属性配置在配置文件中,ftpClient.properties配置文件如下

#FTP连接池配置
#最大数
ftpClient_maxTotal=50
#最小空闲
ftpClient_minIdle=10
#最大空闲
ftpClient_maxIdle=100
#最大等待时间
ftpClient_maxWait=3000
#池对象耗尽之后是否阻塞,maxWait<0时一直等待
ftpClient_blockWhenExhausted=true
#取对象是验证
ftpClient_testOnBorrow=true
#回收验证
ftpClient_testOnReturn=true
#创建时验证
ftpClient_testOnCreate=true
#空闲验证
ftpClient_testWhileIdle=false
#后进先出
ftpClient_lifo=false

#FTP连接属性配置
#ip
ftpClient_host=192.168.158.98
#端口
ftpClient_port=21
#登录名
ftpClient_username=ftpadmin
#密码
ftpClient_pasword=eakom123456
#连接是否为主动模式
ftpClient_passiveMode=true
#编码
ftpClient_encoding=UTF-8
#超时时间
ftpClient_clientTimeout=600
#线程数
ftpClient_threaNum=1
#文件传送类型
#0=ASCII_FILE_TYPE(ASCII格式) 1=EBCDIC_FILE_TYPE 2=LOCAL_FILE_TYPE(二进制文件)  
ftpClient_transferFileType=2
#是否重命名
ftpClient_renameUploaded=true
#重新连接时间
ftpClient_retryTimes=1200
#缓存大小
ftpClient_bufferSize=1024
#默认进入的路径
ftpClient_workingDirectory=/home/ftpadmin/

三、新建一个FTP客户端属性类

package com.eakom.common.util.ftpPool;
/**
 * FTP属性相关的配置
 * @author eakom
 * @date 2018年1月11日
 */
public class FTPConfig{
    private String host;
    private int port;
    private String username;
    private String password;
    private boolean passiveMode;
    private String encoding;
    private int clientTimeout;
    private int threadNum;
    private int transferFileType;
    private boolean renameUploaded;
    private int retryTimes;
    private int bufferSize;
    private String workingDirectory;


    public String getWorkingDirectory() {
        return workingDirectory;
    }
    public void setWorkingDirectory(String workingDirectory) {
        this.workingDirectory = workingDirectory;
    }
    public int getBufferSize() {
        return bufferSize;
    }
    public void setBufferSize(int bufferSize) {
        this.bufferSize = bufferSize;
    }
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public boolean getPassiveMode() {
        return passiveMode;
    }
    public void setPassiveMode(boolean passiveMode) {
        this.passiveMode = passiveMode;
    }
    public String getEncoding() {
        return encoding;
    }
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }
    public int getClientTimeout() {
        return clientTimeout;
    }
    public void setClientTimeout(int clientTimeout) {
        this.clientTimeout = clientTimeout;
    }
    public int getThreadNum() {
        return threadNum;
    }
    public void setThreadNum(int threadNum) {
        this.threadNum = threadNum;
    }
    public int getTransferFileType() {
        return transferFileType;
    }
    public void setTransferFileType(int transferFileType) {
        this.transferFileType = transferFileType;
    }
    public boolean isRenameUploaded() {
        return renameUploaded;
    }
    public void setRenameUploaded(boolean renameUploaded) {
        this.renameUploaded = renameUploaded;
    }
    public int getRetryTimes() {
        return retryTimes;
    }
    public void setRetryTimes(int retryTimes) {
        this.retryTimes = retryTimes;
    }

}

类中的属性与配置文件ftpClient.properties中的属性相对应

四、新建一个FTP客户端工厂类,继承于commons-pool 包中的BasePooledObjectFactory类,并重写create()、 wrap(FTPClient ftpClient)、destroyObject(PooledObject p)和validateObject(PooledObject p)四个方法

package com.eakom.common.util.ftpPool;

import java.io.IOException;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FTPClientFactory extends BasePooledObjectFactory<FTPClient> {
    private static Logger logger = LoggerFactory.getLogger(FTPClientFactory.class);
    private FTPConfig ftpConfig;

    public FTPClientFactory(FTPConfig ftpConfig) {
        this.ftpConfig = ftpConfig;
    }
    /**
     * 新建对象
     */
    @Override
    public FTPClient create() throws Exception {
        FTPClient ftpClient = new FTPClient();
        ftpClient.setConnectTimeout(ftpConfig.getClientTimeout());
        try {
            ftpClient.connect(ftpConfig.getHost(), ftpConfig.getPort());
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                logger.error("FTPServer 拒绝连接");
                return null;
            }
            boolean result = ftpClient.login(ftpConfig.getUsername(),ftpConfig.getPassword());
            if (!result) {
                logger.error("ftpClient登陆失败!");
                throw new Exception("ftpClient登陆失败! userName:"+ ftpConfig.getUsername() + " ; password:"
                        + ftpConfig.getPassword());
            }
            ftpClient.setFileType(ftpConfig.getTransferFileType());
            ftpClient.setBufferSize(ftpConfig.getBufferSize());
            ftpClient.setControlEncoding(ftpConfig.getEncoding());
            if (ftpConfig.getPassiveMode()) {
                ftpClient.enterLocalPassiveMode();
            }
            ftpClient.changeWorkingDirectory(ftpConfig.getWorkingDirectory());
        } catch (IOException e) {
            logger.error("FTP连接失败:", e);
        }
        return ftpClient;
    }

    @Override
    public PooledObject<FTPClient> wrap(FTPClient ftpClient) {
        return new DefaultPooledObject<FTPClient>(ftpClient);
    }

    /**
     * 销毁对象
     */
    @Override
    public void destroyObject(PooledObject<FTPClient> p) throws Exception {
        FTPClient ftpClient = p.getObject();
        ftpClient.logout();
        super.destroyObject(p);
    }

    /**
     * 验证对象
     */
    @Override
    public boolean validateObject(PooledObject<FTPClient> p) {
        FTPClient ftpClient = p.getObject();
        boolean connect = false;
        try {
            connect = ftpClient.sendNoOp();
            if(connect){                
                ftpClient.changeWorkingDirectory(ftpConfig.getWorkingDirectory());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return connect;
    }
}

五、新建FTP连接池类,连接池中有带有一个构造方法,连接器初始化时,自动新建commons-pool包中的GenericObjectPool类,初始化连接池;

package com.eakom.common.util.ftpPool;

import java.io.InputStream;
import java.util.Properties;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;


public class FTPClientPool{
    private GenericObjectPool<FTPClient> ftpClientPool;
    public FTPClientPool(InputStream in){
        Properties pro = new Properties();
        try {
            pro.load(in);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }
        // 初始化对象池配置
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setBlockWhenExhausted(Boolean.parseBoolean(pro.getProperty("ftpClient_blockWhenExhausted")));
        poolConfig.setMaxWaitMillis(Long.parseLong(pro.getProperty("ftpClient_maxWait")));
        poolConfig.setMinIdle(Integer.parseInt(pro.getProperty("ftpClient_minIdle")));
        poolConfig.setMaxIdle(Integer.parseInt(pro.getProperty("ftpClient_maxIdle")));
        poolConfig.setMaxTotal(Integer.parseInt(pro.getProperty("ftpClient_maxTotal")));
        poolConfig.setTestOnBorrow(Boolean.parseBoolean(pro.getProperty("ftpClient_testOnBorrow")));
        poolConfig.setTestOnReturn(Boolean.parseBoolean(pro.getProperty("ftpClient_testOnReturn")));
        poolConfig.setTestOnCreate(Boolean.parseBoolean(pro.getProperty("ftpClient_testOnCreate")));
        poolConfig.setTestWhileIdle(Boolean.parseBoolean(pro.getProperty("ftpClient_testWhileIdle")));
        poolConfig.setLifo(Boolean.parseBoolean(pro.getProperty("ftpClient_lifo")));

        FTPConfig ftpConfig=new FTPConfig();
        ftpConfig.setHost(pro.getProperty("ftpClient_host"));
        ftpConfig.setPort(Integer.parseInt(pro.getProperty("ftpClient_port")));
        ftpConfig.setUsername(pro.getProperty("ftpClient_username"));
        ftpConfig.setPassword(pro.getProperty("ftpClient_pasword"));
        ftpConfig.setClientTimeout(Integer.parseInt(pro.getProperty("ftpClient_clientTimeout")));
        ftpConfig.setEncoding(pro.getProperty("ftpClient_encoding"));
        ftpConfig.setWorkingDirectory(pro.getProperty("ftpClient_workingDirectory"));
        ftpConfig.setPassiveMode(Boolean.parseBoolean(pro.getProperty("ftpClient_passiveMode")));
        ftpConfig.setRenameUploaded(Boolean.parseBoolean(pro.getProperty("ftpClient_renameUploaded")));
        ftpConfig.setRetryTimes(Integer.parseInt(pro.getProperty("ftpClient_retryTimes")));
        ftpConfig.setTransferFileType(Integer.parseInt(pro.getProperty("ftpClient_transferFileType")));
        ftpConfig.setBufferSize(Integer.parseInt(pro.getProperty("ftpClient_bufferSize")));
        // 初始化对象池
        ftpClientPool = new GenericObjectPool<FTPClient>(new FTPClientFactory(ftpConfig), poolConfig);
    }
    public FTPClient borrowObject() throws Exception {
    /*  System.out.println("获取前");
        System.out.println("活动"+ftpClientPool.getNumActive());
        System.out.println("等待"+ftpClientPool.getNumWaiters());
        System.out.println("----------");*/
        return ftpClientPool.borrowObject();
    }
    public void returnObject(FTPClient ftpClient) {

        /*System.out.println("归还前");
        System.out.println("活动"+ftpClientPool.getNumActive());
        System.out.println("等待"+ftpClientPool.getNumWaiters());
        System.out.println("----------");*/
        ftpClientPool.returnObject(ftpClient);
        System.out.println("归还后");
        System.out.println("活动"+ftpClientPool.getNumActive());
        System.out.println("等待"+ftpClientPool.getNumWaiters());
        System.out.println("----------");
    }
}

六、测试连接池
同时启动多个线程,观察连接池内,FTPClient的数量的变化

package com.eakom.common.util.ftpPool;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

import org.apache.commons.net.ftp.FTPClient;

public class Ftp {
    private static FTPClientPool ftpClientPool;
    static{
//      ftpClientPool=new FTPClientPool(Thread.currentThread().getContextClassLoader().getResourceAsStream("ftpClient.properties"));
        ftpClientPool=new FTPClientPool(Ftp.class.getClassLoader().getResourceAsStream("ftpClient.properties"));
    }
    public static void main(String[] args) {
        for(int i=0;i<50;i++){
            Thread thread=new Thread(new Runnable() {
                @Override
                public void run() {
                    sendFile();
                }
            });
            thread.start();
            try {
                thread.sleep(15);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }


    }
    public static void sendFile(){
        long start = System.currentTimeMillis();
        InputStream inputStream = null;
        FTPClient ftpClient = null;
        try {
            ftpClient = ftpClientPool.borrowObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            String path="C:/Users/Administrator/Desktop/44/中文.txt";
            File file = new File(path);
            ftpClient.changeWorkingDirectory("/home/ftpadmin/aa");
            inputStream = new FileInputStream(file);


                String fileName =new Date().getSeconds()+new Date().getSeconds()+".txt";
                boolean flag = ftpClient.storeFile(new String(fileName.getBytes("GBK"), "iso-8859-1") , inputStream);
                long end = System.currentTimeMillis();
                System.out.println("**********************************************"+flag);
                long lo=end-start;
                System.out.println("耗时:"+lo);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            ftpClientPool.returnObject(ftpClient);          
        }
    }
}

至此,FTPClient连接创建完成,如果问题,欢迎大家指正,谢谢!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 自定义FTP连接池 的相关文章

  • 了解java

    目录 一 Java是什么 二 Java语言特性 1 简单性 2 面向对象 3 健壮性 4 多线程 5 可移植性 跨平台 三 Java两种核心机制 1 Java虚拟机 Java Virtal Machine 2 垃圾收集机制 Garbage
  • Swagger3 注解使用(Open API 3.0)

    文章目录 前言 一 swagger 3 的使用 Swagger SpringFox 3 0 相关特性 SpringDoc 二 从 spring fox 迁移到 springdoc 三 使用 swagger3 注解代替 swagger2 的
  • 3、Java的If语句与For循环

    一 语句 条件语句 根据不同的条件 执行不同的语句 if if else if else if if else if else if else switch 循环语句 重复执行某些动作 for while do while 1 1 if语句
  • java基础:日志框架

    文章目录 一 日志技术的概述 二 日志技术体系 三 Logback日志框架 四 Logback快速入门 五 Logback 配置详解 5 1 输出位置 格式设置 5 2 对日志不同内容是否输出的控制 一 日志技术的概述 程序中的日志可以用来
  • JAVA: quakus程序运行

    mvnw compile quarkus dev
  • Java特训的第一天——开篇

    我是一名刚入门的Java菜鸟 我选择Java的原因是因为其语法简单 功能强大 从web 到桌面 到嵌入式 无所不能 下面我将谈一谈我对Java语言的认识 Java语言概述 关于Java的介绍网上有很详细的阐述 我在这里就不再细述了 下面只简
  • 【java基础】 方法,实参和形参,方法的重载,签名,递归

    目录 方法概念及使用 实参和形参的关系 重要 方法的重载 方法签名 简单了解 递归 简单介绍 方法概念及使用 方法就是一个代码片段 类似于 C 语言中的 函数 作用 是能够模块化的组织代码 当代码规模比较复杂的时候 做到代码被重复使用 一份
  • 【Java】Java中的String类

    文章目录 一 认识 String 类 二 String 类的常用方法 2 1 构造方法 2 2 String 类对象之间的比较 2 3 字符串查找 2 4 字符串的转换 2 5 字符串替换 2 6 字符串拆分 2 7 字符串截取 2 8 字
  • 进制及进制转换详解。原码、反码、移码,补码区别介绍。(通俗易懂)

    目录 前言 一 十进制 n进制 进制转换详解 1 先说说什么是进制 2 二进制介绍 3 十进制 n进制 进制转换详解 重点 十进制 gt n进制 2 8 16 n进制 2 8 16 gt 十进制 非十进制间的互相转化 二 原码 反码 移码
  • JAVA,异常

    异常概念 通常大家认为异常就是错误 但这个错误有很多种 1 语法错误 2 JVM虚拟机错误 3 平台错误 4 程序运行错误 平台或者资源或者逻辑 数值等错误 常见的异常 1 java lang NullPointerException 空指
  • 《编写高质量代码:改善Java程序的151个建议》读书笔记

    编写高质量代码 改善Java程序的151个建议 秦小波 67个笔记 前言 本书附带有大量的源码 下载地址见华章网站www hzbook com 建议11 养成良好习惯 显式声明UID SerialVersionUID 也叫做流标识符 Str
  • 对接百度api的工具类:Base64Util,FileUtil,HttpUtil

    对接百度api的工具类 Base64Util FileUtil HttpUtil package com baidu ai aip utils Base64 工具类 public class Base64Util private stati
  • MVC模型图

    MVC图
  • Java学习13:面向对象-多态(Polymorphism)内存分析图解

    1 概述 多态是Java面向对象三大特征之一 多态 Polymorphism 顾名思义 即对象具有多种形态 具体而言 是编译时类型 运行时类型 编译时类型 由声明时的类型决定 一般是父类 运行时类型 由实际对应的对象类型决定 具体是哪个子类
  • JAVA 8 新特性及使用

    1 前言 2019年9月19日java13已正式发布 感叹java社区强大 经久不衰 由于国内偏保守 新东西总要放一放 让其他人踩踩坑 等稳定了才会去用 并且企业目的还是赚钱 更不会因为一个新特性去重构代码 再开发一套程序出来 甚者国内大多
  • JDK介绍

    JDK JRE和JVM之间的关系 JVM是运行环境 JRE是含运行环境和相关的类库 跟node环境是一个意思 JDK目录介绍 目录名称 说明 bin 该路径下存放了JDK的各种工具命令 javac和java就放在这个目录 conf 该路径下
  • 将list集合的元素按照添加顺序的倒序进行排列取出

    将list集合的元素按照添加顺序的倒序进行排列取出 方法1 list add 0 object List
  • mysql无法连接问题及其环境变量配置

    问题 Can t connect to MySQL server on localhost 3306 10061 方案一 不推荐 第一 在环境变量 系统变量的path中添加mysql的bin目录 我的是D mysql mysql 8 0 2
  • java连接oracle出现ORA-12505错误

    问题 sqlplus可以连接 但java连接报错 ORA 12505 ORA 12505 TNS listener does not currently know of SID given in connect descr 解析 原因 数据
  • 详解toLowerCase(判断字符串相等)

    一 toLowerCase 函数简介 toLowerCase 是一个在多个编程语言中都存在的字符串方法 它的作用是将字符串中的所有大写字母转换为对应的小写字母 常用于文本处理 搜索和比较等情况 以确保字符串的一致性和非大小写敏感的操作 二

随机推荐

  • vue2 升级到 vue3 router 动态授权路由 异步加载报错 TypeError: Cannot read properties of undefined (reading ‘apply‘)

    使用 resolve gt require views item component resolve 会报错 TypeError Cannot read properties of undefined reading apply 我的解决历
  • 替换字符串中指定的字符串

    例如下面的例子 有时需要把某个字符串中的部分字符替换成另一个字符 可以使用std string 自带的函数replace replace第一个参数为起点 第二个为替换的长度 第三个为替换为的内容 for auto inventoryStat
  • 跟我学Spring Cloud(2020.0.0-M6版)-01-服务注册与服务发现-Eureka Server

    目录 1 所需要版本 2 创建基于web的Maven项目 SpringCloud 的服务注册中心 3 检查IntelliJ IDEA 的环境配置 4 检查java verison配置 5 检查Edit Configurations配置 6
  • 区块链技术基本概念

    链客 专为开发者而生 有问必答 此文章来自链客区块链技术问答社区 未经允许拒绝转载 区块链技术根本概念 了解这些名词是一个不错的开端 公钥加密系统 Alice有一把公钥和一把私钥 她可以用她的私钥创建数字签名 而Bob可以用她的公钥来验证这
  • 自定义 swap 函数

    背景 STL 中提供了 swap 算法 用于交换两个对象的值 其一般实现方法如下 namespace std template
  • c语言缩进用tab还是空格,程序员编码首行缩进使用Tab键好还是空格好?

    本文转载自CocoaChina 每个程序员都有自己喜欢的编码风格以及编码习惯 那么 问题来了 一个很常用也很简单的问题 让程序员分为两派 编程时 到时是使用Tab按键来进行首行缩进好呢还是敲空格按键好呢 少侠 别急 带老夫给你慢慢分析 Ta
  • Git入门与使用 (二) Git相关命令的介绍与使用

    文章目录 一 前言 二 理解Git的工作区 暂存区 本地仓库和远程仓库 三 Git相关命令的介绍与使用 1 初始化Git仓库 2 设置签名 2 1 设置仓库 项目级别的签名 2 2 设置系统用户级别的签名 3 查看Git仓库的状态 4 向暂
  • [转]Windows下安全权限设置详解

    一 Windows下安全权限设置详解 简 介 随着动网论坛的广泛应用和动网上传漏洞的被发现以及SQL注入式攻击越来越多的被使用 WEBSHELL让防火墙形同虚设 一台即使打了所有微软补丁 只让80端口对外开放的WEB服务器也逃不过被黑的命运
  • 刷脸庞大的交易市场从而也带来新的商机

    现在我们外出买东西付款 已经有了非常便捷的扫码支付功能 这个功能不仅是年轻人喜欢使用 就连中老年人也跟上的时代的步伐 许多超市 便利店已经菜市场 都能够看到二维码的身影 但是随着时代不断地进步 扫码付款这一新兴方式 接二连三地被曝出许多风险
  • Unity自动滚动字幕的实现

    Unity自动滚动字幕的实现 首先按照图片里的进行创建 然后用代码获取Scrollbar 在将Value值一直变为1 using System Collections using System Collections Generic usi
  • Jupyter程序安装和使用指南【操作示例】

    Jupyter Notebook 简称Jupyter 是一个交互式编辑器 它支持运行40多种编程语言 便于创建和共享文档 Jupyter本质上是一个Web应用程序 与其他编辑器相比 它具有小巧 灵活 支持实时代码 方便图表展示等优点 下面分
  • ECCV 2020 Representation Learning on Visual-Symbolic Graphs for Video Understanding

    动机 自然视频中的事件通常产生于演员和目标之间的时空交互 并且涉及多个共同发生的活动和目标类 因此 需要开发能够对时空视觉和语义上下文进行有效建模的算法 捕捉这种上下文的一种方法是使用基于图的建模 它在计算机视觉中有着丰富的历史 传统的基于
  • qt day3

  • java自动装配_Spring中自动装配的4种方式

    Spring容器可以在不使用和元素的情况下自动装配相互协作的bean之间的关系 助于减少编写一个大的基于Spring的应用程序的XML配置的数量使用元素的autowire属性为一个bean定义指定自动装配模式 在Spring中 我们有4种方
  • 计算机网络期末总结复习(全)

    文章目录 第一章 概述 1 1 计算机网络在信息时代的作用 我国互联网发展状况 1 2 因特网概述 1 网络 互连网 互联网 和因特网 2 因特网发展的三个阶段 3 因特网的标准化工作 4 因特网的组成 补充
  • 78页PPT全面揭示互联网与传统行业的融合与碰撞

    正如报告的主题 融合与碰撞 移动互联网和传统行业正在融合与碰撞之间进行行业重塑 纵观教育行业 现阶段已经有四种运营模式逐渐成熟 但仍存在自身问题和很多外部矛盾 找准发展趋势意义重大 一起来看看水深火热的TMT产业发展趋势吧
  • 【前端demo】背景渐变动画

    文章目录 效果 过程 代码 html css 其他demo 效果 效果预览 https codepen io karshey pen OJrXZwQ 过程 注意 直接在body上加height 100 可能也会出现height为0的情况 这
  • docker修改服务器参数怎么办,Docker(32)- 如何修改 docker 容器的启动参数

    如果你还想从头学起 Docker 可以看看这个系列的文章哦 前言 有时候创建容器时忘了添加 restart 参数 导致 Docker 服务重启后 容器不会自动启动 每次都需要手动启动 很不方便 那现在如何针对已创建的容器修改 restart
  • LeetCode——动态规划篇(二)

    刷题顺序及思路来源于代码随想录 网站地址 https programmercarl com 目录 343 整数拆分 力扣 LeetCode 96 不同的二叉搜索树 力扣 LeetCode 416 分割等和子集 力扣 LeetCode 104
  • Java 自定义FTP连接池

    一 引入FTP包和连接池包