`
huaye2007
  • 浏览: 39083 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

MINA 传送文件 文件名

 
阅读更多

网上查了堆资料,传送文件名都是写死了的。那如何同时传送文件和文件名?这就要用到Mina 的编码和解码,在刚接触mina的时候,大家可以先看下这个pdf Apache_Mina_Server_2.0中文参考手册V1.0.pdf 这个文档刚开始一般大家都会很容易传送字符串,但是到传送文件就有点难了。

直接介绍编码和解码了:

package com.blazefire.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;
import com.blazefire.util.BeanUtil;
import com.blazefire.util.FileHelper;

public class ClientHandler extends IoHandlerAdapter{
	
	/**
	 * 客户端接收到信息
	 * */
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		// TODO Auto-generated method stub
		super.messageReceived(session, message);
		
	}

	public void sessionOpened(IoSession session) { 
		BaseMessage baseMessage = new BaseMessage();
		baseMessage.setDataType(BeanUtil.UPLOAD_FILE);
		FileBean bean = new FileBean();
		File file = new File("e:\\中国.jpg");
		bean.setFileName(file.getName());
		bean.setFileSize((int)file.length());
		try {
			FileHelper helper =new FileHelper();
			bean.setFileContent(helper.getContent(file));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		baseMessage.setData(bean);
		session.write(baseMessage); 
	}

	public void sessionCreated(IoSession session) throws Exception {
		// TODO Auto-generated method stub
		super.sessionCreated(session);
	}
	
	
}

在连接成功后,调用sessionOpened方法,读取本地文件,向服务器传送文件,其中用到的一些类将在文章末尾贴出。

传送文件需要文件名,文件大小,文件byte数组: 文件名这个不用说,文件大小是为了解码的时候知道读取到哪个字节就可以停止了。文件byte数组 存储文件内容

BaseMessage baseMessage = new BaseMessage();
baseMessage.setDataType(BeanUtil.UPLOAD_FILE);//BeanUtil.UPLOAD_FILE值为1
准备好这些信息之后,调用session.write方法开始编码。

package com.blazefire.util;

import java.nio.charset.Charset;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.apache.mina.filter.codec.demux.MessageEncoder;

import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;

public class BaseMessageEncoder implements MessageEncoder<BaseMessage> {

	/**
	 * 基本信息编码
	 * */
	public void encode(IoSession session, BaseMessage message,ProtocolEncoderOutput outPut) throws Exception {
		// TODO Auto-generated method stub
		IoBuffer buffer = IoBuffer.allocate(1024*1024*50); 
		buffer.putInt(message.getDataType());
		FileBean bean = (FileBean) message.getData();
		byte[] byteStr = bean.getFileName().getBytes(BeanUtil.charset);
		buffer.putInt(byteStr.length);
		buffer.putInt(bean.getFileSize());
		buffer.put(byteStr);
		buffer.put(bean.getFileContent());
		buffer.flip();
		outPut.write(buffer);
		System.out.println("编码完成!");
	}

}
buffer.putInt(message.getDataType());首先放入了一个int值1 放入这个值,主要是因为这个项目要用到多个解码器,针对不同的信息进行不同的编码,在解码的时候会根据这个参数来判断是否要用这个解码器来解码。

byte[] byteStr = bean.getFileName().getBytes(BeanUtil.charset); 将字符串转换成byte数组,BeanUtil.charset Charset charset = Charset.forName("utf-8"); utf-8编码

buffer.putInt(byteStr.length); 记录文件名的byte数组长度
buffer.putInt(bean.getFileSize()); 记录文件的byte数组长度

buffer.put(byteStr); 存入到buffer
buffer.put(bean.getFileContent());存入到buffer

编码就到此完成了。

现在开始服务器的解码:

package com.blazefire.util;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.Charset;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.demux.MessageDecoder;
import org.apache.mina.filter.codec.demux.MessageDecoderResult;

import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;

public class BaseMessageDecoder  implements MessageDecoder {
	private AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
	/**
	 * 是否适合解码
	 * */
	public MessageDecoderResult decodable(IoSession session, IoBuffer in) {
		// TODO Auto-generated method stub
		Context context = (Context) session.getAttribute(CONTEXT);
		if(context == null){
			context = new Context();
			context.dataType = in.getInt();
			if(context.dataType == BeanUtil.UPLOAD_FILE){
				context.strLength = in.getInt();
				context.byteStr = new byte[context.strLength];
				context.fileSize = in.getInt();
				context.byteFile = new byte[context.fileSize];
				session.setAttribute(CONTEXT, context);
				return MessageDecoderResult.OK;
			}else{
				return MessageDecoderResult.NOT_OK;
			}
		}else{
			if(context.dataType == BeanUtil.UPLOAD_FILE){
				return MessageDecoderResult.OK;
			}else{
				return MessageDecoderResult.NOT_OK;
			}
		}
	}

	/**
	 * 数据解码
	 * */
	public MessageDecoderResult decode(IoSession session, IoBuffer in,
			ProtocolDecoderOutput outPut) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("开始解码:");
		Context context = (Context) session.getAttribute(CONTEXT);
		if(!context.init){
			context.init = true;
			in.getInt();
			in.getInt();
			in.getInt();
		}
		byte[] byteFile = context.byteFile;
		int count = context.count;
		while(in.hasRemaining()){
			byte b = in.get();
			if(!context.isReadName){
				context.byteStr[count] = b;
				if(count == context.strLength-1){
					context.fileName = new String(context.byteStr,BeanUtil.charset);
					System.out.println(context.fileName);
					count = -1;
					context.isReadName = true;
				}
			}
			if(context.isReadName && count != -1){
				byteFile[count] = b;
			}
		//	byteFile[count] = b;
			count++;
		}
		context.count = count;
		System.out.println("count:"+count);
		System.out.println("context.fileSize:"+context.fileSize);
		session.setAttribute(CONTEXT, context);
		if(context.count == context.fileSize){
			BaseMessage message = new BaseMessage();
			message.setDataType(context.dataType);
			FileBean bean = new FileBean();
			bean.setFileName(context.fileName);
			bean.setFileSize(context.fileSize);
			bean.setFileContent(context.byteFile);
			message.setData(bean);
			outPut.write(message);
			context.reset();
		}
		return MessageDecoderResult.OK;
	}

	/**
	 * 
	 * */
	public void finishDecode(IoSession session, ProtocolDecoderOutput outPut)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("end:::::::::::::::::");
	}
	private class Context{
		public int dataType;
		public byte[] byteFile;
		public int count;
		public int strLength;
		public boolean isReadName;
		public int fileSize;
		public byte[] byteStr;
		public String fileName;
		public boolean init = false;
		
		public void reset(){
			dataType = 0;
			byteFile = null;
			count = 0;
			strLength = 0;
			isReadName = false;
			fileSize = 0;
			byteStr = null;
			fileName = null;
			
		}
	}


}
decodable方法就是判断是否要用当前这个解码器

如果你看过上面提到的pdf中 对于这个CumulativeProtocolDecoder解码器的一些介绍和使用,特别是复杂的解码器的说明这块

你就会知道为什么这里会用到CONTEXT 这个属性,因为编码的时候,文件内容较长,分成了许多数据包,这个时候在解码的时候就需要记录解码到那个字节,下次再解码的时候就从这个字节开始。

这里需要说下注意点 当前解码器实现的是MessageDecoder这个接口。而pdf中解码器是继承CumulativeProtocolDecoder这个类。

CumulativeProtocolDecoder这个类 在doDecode方法中需要自己去判断是否需要继续调用doDecode这个方法来完成解码。如果还需要数据解码,这返回值为true,如果解码已完成则直接返回false.

而MessageDecoder这个接口不需要自己去判断

这个类会自己调用自己来将数据传送完,所以每次都会调用这个类里面的方法,每次都会将成员变量初始化.

解码这块主要是解决多个数据包的解码拼成一条完成的消息,这个问题解决了,就没有啥难题了。

if(!context.init){
			context.init = true;
			in.getInt();
			in.getInt();
			in.getInt();
		}

加这个是将那些传入的整数字节读取掉,只剩下文件名的字节和文件字节。

读取完之后,记得重置context的值,否则将影响后续文件的读取。

处理完这些之后,就是直接写文件了。

package com.blazefire.server;

import java.io.FileOutputStream;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

import com.blazefire.bean.BaseMessage;
import com.blazefire.bean.FileBean;

public class ServerHandler extends IoHandlerAdapter{


	public void sessionCreated(IoSession session) throws Exception {
		// TODO Auto-generated method stub
		super.sessionCreated(session);
	}

	public void sessionOpened(IoSession session) throws Exception {
		// TODO Auto-generated method stub
		super.sessionOpened(session);
	}
	/**
	 * 服务器接收到消息
	 * */
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		// TODO Auto-generated method stub
		super.messageReceived(session, message);
		System.out.println("==============");
		BaseMessage baseMessage = (BaseMessage) message;
		FileBean bean = (FileBean) baseMessage.getData();
		System.out.println(bean.getFileName());
		FileOutputStream os = new FileOutputStream("f:\\"+bean.getFileName());
		os.write(bean.getFileContent());
		os.close();
	}


	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		// TODO Auto-generated method stub
		super.exceptionCaught(session, cause);
	}
}

mina这块刚开始对编码解码也不是很熟悉,后来还是根据别人的代码,自己测试才最终出了这个版本。本只想让大家自己自己动手做出来,这样也可以更深入了解。为了方便大家,还是贴下源码算了。

源码下载

分享到:
评论
9 楼 fanfubao 2013-05-28  
请问线程方面如何考虑呢?还有,如果不用这种方式,有其他的方式传送附件和字符串到服务器么?
8 楼 huaye2007 2013-01-05  
iflinxin 写道
759816019@qq.com,你把你这个例子的源代码发到我邮箱来下可以吗?我试试看,目前只要启动多个客户端就会出现丢包现象

上面就有下载的连接
http://download.csdn.net/detail/huaye2007/4427694
7 楼 iflinxin 2013-01-05  
759816019@qq.com,你把你这个例子的源代码发到我邮箱来下可以吗?我试试看,目前只要启动多个客户端就会出现丢包现象
6 楼 huaye2007 2013-01-04  
iflinxin 写道
你好,我用你的例子,在客户端启动多个线程时候,传送图片,会出现图片没传送完全的情况,请问你遇到过吗?

这个我没试过,不知道你是咋样写的,应该是哪地方有问题。
5 楼 iflinxin 2013-01-04  
你好,我用你的例子,在客户端启动多个线程时候,传送图片,会出现图片没传送完全的情况,请问你遇到过吗?
4 楼 huaye2007 2012-10-16  
JavaChristmas 写道
huaye2007 写道
JavaChristmas 写道
我很想知道com.blazefire是什么jar包啊?为什么我查不到资料

最下面不是有源码下载么?

我就是想知道,那个是自己实现的,还是已有的什么jar包,因为我没有从网上查到

晕死,自己下个源码一看就知道是不是自己实现的。看包名就知道是我自己实现的。
没想到你解决问题方式咋这么死了。
3 楼 JavaChristmas 2012-10-16  
huaye2007 写道
JavaChristmas 写道
我很想知道com.blazefire是什么jar包啊?为什么我查不到资料

最下面不是有源码下载么?

我就是想知道,那个是自己实现的,还是已有的什么jar包,因为我没有从网上查到
2 楼 huaye2007 2012-10-15  
JavaChristmas 写道
我很想知道com.blazefire是什么jar包啊?为什么我查不到资料

最下面不是有源码下载么?
1 楼 JavaChristmas 2012-10-15  
我很想知道com.blazefire是什么jar包啊?为什么我查不到资料

相关推荐

    "三菱PLC与触摸屏联合开发气压传动焊条包装线技术详解",No.945 三菱PLC和触摸屏基于气压传动的焊条包装线的研发 ,核心关键词:三菱PLC; 触摸屏; 气压传动; 焊条包装线; 研

    "三菱PLC与触摸屏联合开发气压传动焊条包装线技术详解",No.945 三菱PLC和触摸屏基于气压传动的焊条包装线的研发 ,核心关键词:三菱PLC; 触摸屏; 气压传动; 焊条包装线; 研发; No.945,"三菱PLC与触摸屏在气压传动焊条包装线研发项目No.945中的应用"

    vb图书馆管理系统(源代码+论文).rar

    1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。

    [matlab系统程序]MATLAB危险区域预警系统.zip

    本项目是自己做的设计,有GUI界面,完美运行,适合小白及有能力的同学进阶学习,大家可以下载使用,整体有非常高的借鉴价值,大家一起交流学习。该资源主要针对计算机、通信、人工智能、自动化等相关专业的学生、老师或从业者下载使用,亦可作为期末课程设计、课程大作业、毕业设计等。 项目整体具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现不同的功能。

    [matlab系统程序]MATLAB图像去雾.zip

    本项目是自己做的设计,有GUI界面,完美运行,适合小白及有能力的同学进阶学习,大家可以下载使用,整体有非常高的借鉴价值,大家一起交流学习。该资源主要针对计算机、通信、人工智能、自动化等相关专业的学生、老师或从业者下载使用,亦可作为期末课程设计、课程大作业、毕业设计等。 项目整体具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现不同的功能。

    基于文献知识与知识图谱补全方法用于COVID-19药物再利用的创新算法

    内容概要:文章介绍了针对COVID-19的药物再利用的创新方法,这种方法融合了基于文献的知识(LitCovid和CORD-19数据集)及先进的知识图谱补全技术。具体采用了基于神经网络的TransE、RotatE等多种算法预测药物再利用的潜力,并通过开放和封闭的发现模式为预测结果提供合理的机制解释,包括发现模式、准确性分类及定性评估等手段,增强了方法的实用性。研究表明,TransE表现最优,并成功预测并验证了一系列药物作为COVID-19的治疗候选人选。此外,方法不仅适用于COVID-19,还具备应用于其他疾病药物再利用及其他临床问题解决的潜力。此研究为快速高效地推进药物再利用提供了一个新的计算框架。 适合人群:生物医学科研人员,从事药品再利用、人工智能药物筛选的专业研究人员,对生物信息数据分析和处理感兴趣的学者或技术人员。 使用场景及目标:① 利用计算模型预测药物能否被重新应用于新的适应症,尤其是在面对突发公共卫生事件时加快新药物的研发进程。② 对现有药物进行再评价,以发现更广泛、安全、有效的治疗用途,为临床治疗提供依据和理论指导。③ 探讨通过自动化手段发掘药物作用机理的技术路径。 其他说明:作者团队来自多个国家和地区,研究获得了多项国家级基金支持,论文详尽描述了实验细节,并附上了全部代码和数据资源供后续拓展和重复研究使用。

    "基于三菱PLC与组态王技术的智能交通灯车辆监测系统:No.808的实践与应用",No.808 基于三菱PLC和组态王的智能交通灯车辆监测 ,关键词: 基于三菱PLC; 组态王; 智能交通

    "基于三菱PLC与组态王技术的智能交通灯车辆监测系统:No.808的实践与应用",No.808 基于三菱PLC和组态王的智能交通灯车辆监测 ,关键词: 基于三菱PLC; 组态王; 智能交通灯; 车辆监测; No.808,"三菱PLC与组态王协同的智能交通灯车辆监测系统No.808"

    minecraft1.16.1生存基地 搭配了1.16.1的BSL着色器 BSL光影:https://cdn.modrinth.com/data/Q1vvjJYV/versions/oGcsNfpD/

    在湖上建造的生存基地,希望大家喜欢

    基于西门子S7-1200 PLC与Wincc组态技术的智能路口交通指挥系统解决方案 ,No.698 西门子S7-1200 和Wincc组态基于PLC的路口交通指挥系统 ,No.698; 西门子S7-1

    基于西门子S7-1200 PLC与Wincc组态技术的智能路口交通指挥系统解决方案。,No.698 西门子S7-1200 和Wincc组态基于PLC的路口交通指挥系统 ,No.698; 西门子S7-1200; Wincc组态; PLC; 路口交通指挥系统; 交通控制系统。,基于PLC与Wincc组态的西门子S7-1200交通指挥系统

    电子设计大赛+C题+FPGA+省级获奖

    本资源为无线传输信号模拟系统的完整设计报告,基于ZYNQ7020开发平台实现,包含硬件设计、FPGA算法逻辑、软件控制及详细测试方案。系统可生成直达信号、多径信号及合路信号,支持参数动态调节,适用于通信系统仿真、教学实验及科研开发。 资源内容 设计报告全文:方案论证、理论分析、电路设计、程序流程图、测试结果。 附录数据:AM调制频谱、载波有效值测量、多径时延/衰减/初相实测数据。 配套资料:系统架构图、DAC模块电路图、FPGA算法逻辑框图(PDF+高清图)。 适用场景 设计参考 FPGA数字信号处理开发 无线信道模拟与通信系统仿真 科研项目中的信号生成与测试

    毕业设计&课程设计&毕设&课设-java-ssm网络视频播放器

    项目均经过测试,可正常运行! 环境说明: 开发语言:java JDK版本:jdk1.8 框架:springboot 数据库:mysql 5.7/8 数据库工具:navicat 开发软件:eclipse/idea

    sqllite查询数据库的语句

    sqllite查询数据库的语句

    (源码)基于物联网的Buddy康复激励系统.zip

    # 基于物联网的Buddy康复激励系统 ## 项目简介 Buddy是一个旨在支持和激励个人在日常生活中的身体活动,从而促进康复和保持健康的系统。它由两部分组成可穿戴设备和名为“Wrfel”的游戏组件。通过可穿戴设备追踪用户的步数和心率等身体数据,并在显示屏上展示。名为“Motivationsbuddy”的角色会在每次活动时陪伴用户,并通过提醒和小提示激励用户保持活动。此外,用户还可以通过设备与其他人员进行网络联系。每周用户可以通过掷骰子的方式选择新的活动。收集到的可穿戴设备数据也会在骰子游戏的界面上进行展示。 ## 项目的主要特性和功能 1. 穿戴设备的步数和心率监测功能实时追踪用户的步数和心率,并在显示屏上展示数据。 2. 激励功能通过提醒和小提示激励用户保持活动。 3. 网络联系功能用户可以与其他人员进行网络联系,分享活动数据和经验。 4. 掷骰子活动选择功能每周用户可以通过掷骰子的方式选择新的活动,增加活动的多样性和趣味性。

    (源码)基于MFC框架的指纹识别系统.zip

    # 基于MFC框架的指纹识别系统 ## 项目简介 本项目是一个基于MFC(Microsoft Foundation Classes)框架的指纹识别系统,主要用于指纹的采集、预处理、特征提取、特征过滤、特征匹配和入库等操作。系统通过本地文件夹存储指纹库信息,并提供分步测试、登记和识别功能。 ## 项目的主要特性和功能 1. 指纹采集与预处理 使用指纹采集器(中控ZK4500)进行指纹图像的采集。 通过中值滤波、高斯锐化、均值化等方法对指纹图像进行预处理。 2. 特征提取与过滤 使用Sobel算法进行方向计算,提取图像梯度信息。 通过掩码计算和Gabor滤波增强指纹图像。 使用基于边界的特征过滤算法,减少特征点数量,提高识别速度。 3. 指纹识别与登记 提供指纹登记功能,用户可以通过采集指纹并输入姓名进行登记。 提供指纹识别功能,通过采集指纹并与指纹库中的信息进行匹配,识别用户身份。

    基于Unet技术的医学图像分割系统-DL00366:以皮肤病数据训练的自动分割模型,DL00366-基于Unet的医学图像分割系统 用Unet来做医学图像分割 我们将会以皮肤病的数据作为示范,训练

    基于Unet技术的医学图像分割系统——DL00366:以皮肤病数据训练的自动分割模型,DL00366-基于Unet的医学图像分割系统 用Unet来做医学图像分割。 我们将会以皮肤病的数据作为示范,训练一个皮肤病分割的模型出来,用户输入图像,模型可以自动分割去皮肤病的区域和正常的区域。 ,DL00366; 基于Unet的医学图像分割系统; 皮肤疾病数据; 模型训练; 图像自动分割。,基于Unet的皮肤病图像分割系统

    毕业设计&课程设计&毕设&课设-java-旅游景点线路网站

    项目均经过测试,可正常运行! 环境说明: 开发语言:java JDK版本:jdk1.8 框架:springboot 数据库:mysql 5.7/8 数据库工具:navicat 开发软件:eclipse/idea

    前端Node:第四章:大事件

    前端Node:第四章:大事件

    (源码)基于区块链的金融管理系统.zip

    # 基于区块链的金融管理系统 ## 项目简介 本项目是一个基于区块链技术的金融管理系统,旨在提供一个去中心化、安全可靠的平台,用于处理公司间的财务交易。通过使用智能合约和Python SDK,用户可以进行银行操作、注册公司、登录系统以及进行各种财务操作。 ## 项目的主要特性和功能 ### 主要特性 1. 去中心化利用区块链技术,实现数据的去中心化管理。 2. 安全性通过智能合约和区块链技术,保障数据的安全性和不可篡改性。 3. 可靠性确保交易的可靠性和持久性。 ### 功能 1. 银行界面展示银行相关的数据,如存款、贷款等。 2. 注册与登录允许用户注册新账户并登录系统。 3. 公司管理允许用户创建公司账户,管理公司的财务信息。 4. 财务操作包括转账、购买、融资、还款等操作。 5. 智能合约交互通过Python SDK与智能合约进行交互,实现各种功能。 ## 安装使用步骤 ### 假设用户已经下载了本项目的源码文件

    西门子S7-200 PLC与组态王联合楼宇消防系统电气控制设计解决方案 No.950,No.950 基于西门子S7-200 PLC和组态王楼宇消防系统电气控制系统设计 ,核心关键词:西门子

    西门子S7-200 PLC与组态王联合楼宇消防系统电气控制设计解决方案 No.950,No.950 基于西门子S7-200 PLC和组态王楼宇消防系统电气控制系统设计 ,核心关键词:西门子S7-200 PLC;组态王楼宇消防系统;电气控制系统设计;No.950,基于西门子S7-200 PLC的楼宇消防电气控制系统设计

    java JDK11版本安装包

    Java Development Kit (JDK) 11 版本简介 Java Development Kit (JDK) 作为Java平台的核心组件之一,是开发人员用来构建、运行和测试Java应用程序的必备工具集。JDK 11 是Oracle公司于2018年9月发布的长期支持(LTS)版本,标志着Java语言发展的一个重要里程碑。它不仅继承了之前版本的优点,还引入了一系列新特性与改进,以更好地适应现代软件开发的需求。 主要特点: 性能提升:通过优化垃圾回收机制等手段,JDK 11在性能方面取得了显著进步。 模块化系统:基于Java 9中引入的模块化系统进一步优化,使得开发者能够更高效地组织代码结构,提高安全性及可维护性。 增强的安全性:新增了多项安全功能,比如TLS 1.3的支持,以及对现有加密算法的加强。 新的APIs:增加了许多实用的新APIs,如用于处理HTTP请求的HttpClient API正式版、本地变量类型推断var关键字等。 移除过时元素:为了保持框架的简洁性和现代化,JDK 11移除了部分不推荐使用的API和选项。

    2008-2020年各省国内发明专利申请授权量数据.xlsx

    2008-2020年各省国内发明专利申请授权量数据 1、时间:2008-2020年 2、来源:国家统计J、统计nj 3、指标:行政区划代码、地区、年份、国内发明专利申请授权量(项) 4、范围:31省

Global site tag (gtag.js) - Google Analytics