文章目录
前言
因学校实训要求需接触 南京陆加壹智能科技的RFID,查询了许多资料在此记录
一、RXTX是什么?
Fork of the Java RXTX project to primarily provide a compiled native 64-bit package for Windows and Linux. RXTX is a Java native library providing serial and parallel communication for the Java Development Toolkit (JDK). RXTX is licensed under the GNU LGPL license as well as these binary distributions. RXTX is a great package, but it was lacking pre-built binaries for x64 (64-bit) versions of Windows. This project distributes binary builds of RXTX for Windows x64, x86, ia64 and Linux x86, x86_64. These builds are compiled with the latest Microsoft Visual Studio tools. The latest CVS snapshots of RXTX were much better and more stable than the versions on the official rxtx.org website. Therefore, builds for Linux are also included to be consistent with the Windows binaries.二、环境搭建
引入库
-
点击官网地址
-
找到Downloads 选择自己需要的版本(作者下载的是Windows64版本)
-
解压后文件如下
-
拷贝 rxtxSerial.dll 到 JAVA_HOME\bin目录中;
-
拷贝 rxtxParallel.dll 到 JAVA_HOME\bin目录中;
- 打开idea项目在项目下创建lib将 RXTXcomm.jar 放在lib里
- 打开模块设置
- 选择模块 ,勾选RXTX, 点击应用
- 选择 SDK ,点击“+“ 找到RXTX ,点击确定 ,点击应用
三、代码实现
SerialPortManager类
package manager;
import gnu.io.*;
import utils.ArrayUtils;
import utils.ByteUtils;
import utils.ShowUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.TooManyListenersException;
/**
* 串口管理
*
* @author yangle
*/
@SuppressWarnings("all")
public class SerialPortManager {
/**
* 查找所有可用端口
*
* @return 可用端口名称列表
*/
public static final ArrayList<String> findPorts() {
// 获得当前所有可用串口
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
ArrayList<String> portNameList = new ArrayList<String>();
// 将可用串口名添加到List并返回该List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
/**
* 打开串口
*
* @param portName
* 端口名称
* @param baudrate
* 波特率
* @return 串口对象
* @throws PortInUseException
* 串口已被占用
*/
public static final SerialPort openPort(String portName, int baudrate) throws PortInUseException {
try {
// 通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
// 打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 2000);
// 判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
try {
// 设置一下串口的波特率等参数
// 数据位:8
// 停止位:1
// 校验位:None
serialPort.setSerialPortParams(baudrate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
}
return serialPort;
}
} catch (NoSuchPortException e1) {
e1.printStackTrace();
}
return null;
}
/**
* 关闭串口
*
* @param serialport
* 待关闭的串口对象
*/
public static void closePort(SerialPort serialPort) {
if (serialPort != null) {
serialPort.close();
}
}
/**
* 往串口发送数据
*
* @param serialPort
* 串口对象
* @param order
* 待发送数据
*/
public static void sendToPort(SerialPort serialPort, byte[] order) {
OutputStream out = null;
try {
out = serialPort.getOutputStream();
out.write(order);
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
out = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 从串口读取数据
*
* @param serialPort
* 当前已建立连接的SerialPort对象
* @return 读取到的数据
*/
public static byte[] readFromPort(SerialPort serialPort) {
InputStream in = null;
byte[] bytes = {};
try {
in = serialPort.getInputStream();
// 缓冲区大小为一个字节
byte[] readBuffer = new byte[1];
int bytesNum = in.read(readBuffer);
while (bytesNum > 0) {
bytes = ArrayUtils.concat(bytes, readBuffer);
bytesNum = in.read(readBuffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
in = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
/**
* 添加监听器
*
* @param port
* 串口对象
* @param listener
* 串口存在有效数据监听
*/
public static void addListener(SerialPort serialPort, DataAvailableListener listener) {
try {
// 给串口添加监听器
serialPort.addEventListener(new SerialPortListener(listener));
// 设置当有数据到达时唤醒监听接收线程
serialPort.notifyOnDataAvailable(true);
// 设置当通信中断时唤醒中断线程
serialPort.notifyOnBreakInterrupt(true);
} catch (TooManyListenersException e) {
e.printStackTrace();
}
}
/**
* 串口监听
*/
public static class SerialPortListener implements SerialPortEventListener {
private DataAvailableListener mDataAvailableListener;
public SerialPortListener(DataAvailableListener mDataAvailableListener) {
this.mDataAvailableListener = mDataAvailableListener;
}
public void serialEvent(SerialPortEvent serialPortEvent) {
switch (serialPortEvent.getEventType()) {
case SerialPortEvent.DATA_AVAILABLE: // 1.串口存在有效数据
if (mDataAvailableListener != null) {
mDataAvailableListener.dataAvailable();
}
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2.输出缓冲区已清空
break;
case SerialPortEvent.CTS: // 3.清除待发送数据
break;
case SerialPortEvent.DSR: // 4.待发送数据准备好了
break;
case SerialPortEvent.RI: // 5.振铃指示
break;
case SerialPortEvent.CD: // 6.载波检测
break;
case SerialPortEvent.OE: // 7.溢位(溢出)错误
break;
case SerialPortEvent.PE: // 8.奇偶校验错误
break;
case SerialPortEvent.FE: // 9.帧错误
break;
case SerialPortEvent.BI: // 10.通讯中断
ShowUtils.errorMessage("与串口设备通讯中断");
break;
default:
break;
}
}
}
/**
* 串口存在有效数据监听
*/
public interface DataAvailableListener {
/**
* 串口存在有效数据
*/
void dataAvailable();
}
public static void getListener(SerialPort port){
SerialPortManager.addListener(port, new DataAvailableListener() {
@Override
public void dataAvailable() {
byte[] data = null;
try {
if (port == null) {
ShowUtils.errorMessage("串口对象为空,监听失败!");
} else {
// 读取串口数据
data = SerialPortManager.readFromPort(port);
System.out.println("=============数据"+ByteUtils.byteArrayToHexString(data));
}
} catch (Exception e) {
ShowUtils.errorMessage(e.toString());
// 发生读取错误时显示错误信息后退出系统
System.exit(0);
}
}
});
}
}
Tools类
package utils;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import manager.SerialPortManager;
/**
* @ClassName Tools
* @Description
* @Author meng
* @Date 2021/5/1817:12
*/
public class Tools {
public static SerialPort OpenPort(SerialPortManager serialPortManager) throws PortInUseException {
return SerialPortManager.openPort(SerialPortManager.findPorts().toString().substring(1,5),57600);
}
public static void ClosePort(SerialPortManager serialPortManager,SerialPort serialPort){
SerialPortManager.closePort(serialPort);
System.out.println("关闭成功");
}
public static String WRserialPort(SerialPortManager serialPortManager, SerialPort serialPort, String WString){
byte[] Wbytes = ByteUtils.hexStr2Byte(WString);
SerialPortManager.sendToPort(serialPort,Wbytes);
byte[] Rbytes = SerialPortManager.readFromPort(serialPort);
return ByteUtils.byteArrayToHexString(Rbytes);
}
public static String FindId(SerialPortManager serialPortManager, SerialPort serialPort) throws InterruptedException {
String x ="04ff0f";
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Find命令:"+WString);
return Tools.getEpc(serialPortManager,serialPort,WRserialPort(serialPortManager,serialPort,WString));
}
public static String getEpc(SerialPortManager serialPortManager, SerialPort serialPort,String x) throws InterruptedException {
if (x.substring(6,8).equals("01")){
x=x.substring(12,x.length()-4);
System.out.println("EpcId:"+x);
}else {
System.out.println("未找到卡:"+x);
x="";
}
return x;
}
public static String FindIdAll(SerialPortManager serialPortManager, SerialPort serialPort){
String x ="04ff01";
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("命令:"+WString);
return WRserialPort(serialPortManager,serialPort,WString);
}
public static String ReadCard(SerialPortManager serialPortManager, SerialPort serialPort,String s,int Number){
if(Number>120){
Number=120;
}
String RNumber = Integer.toHexString(Number).toUpperCase();
String SLength = Integer.toHexString(14+(s.length()/2)).toUpperCase();
String EpcLength = Integer.toHexString(s.length()/4).toUpperCase();
SLength =StringUtils.JLEvennumbers(SLength);
EpcLength =StringUtils.JLEvennumbers(EpcLength);
RNumber=StringUtils.JLEvennumbers(RNumber);
System.out.println("ReadCard======="+SLength + "===="+EpcLength +"==="+RNumber);
String x =SLength+"0002"+EpcLength+s+"0300"+RNumber+"000000000000";
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Read命令:"+WString);
String z = WRserialPort(serialPortManager,serialPort,WString);
// System.out.println("读取返回数据:"+z);
if (z.substring(6,8).equals("00")){
x= z.substring(8, z.length()-4);
}else {
System.out.println("数据为空或者读取错误:"+x);
x="";
}
return x ;
}
public static String WriteCard(SerialPortManager serialPortManager, SerialPort serialPort,String s,String transmitting){
String SLength = Integer.toHexString(14+(s.length()/2)+(transmitting.length()/2)).toUpperCase();
String TLength = Integer.toHexString(transmitting.length()/4).toUpperCase();
String EpcLength = Integer.toHexString(s.length()/4).toUpperCase();
SLength =StringUtils.JLEvennumbers(SLength);
TLength =StringUtils.JLEvennumbers(TLength);
EpcLength =StringUtils.JLEvennumbers(EpcLength);
// System.out.println("WriteCard======="+SLength + "===="+TLength );
String x =SLength+"0003"+TLength+EpcLength+s+"0300"+transmitting+"000000000000";
System.out.println(x.length());
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Write命令:"+WString);
return WRserialPort(serialPortManager,serialPort,WString);
}
public static String WriteEPCid(SerialPortManager serialPortManager, SerialPort serialPort,String s){
String SLength = Integer.toHexString(9+(s.length()/2)).toUpperCase();
String TLength = Integer.toHexString(s.length()/4).toUpperCase();
SLength =StringUtils.JLEvennumbers(SLength);
TLength =StringUtils.JLEvennumbers(TLength);
System.out.println("WriteCard======="+SLength + "===="+TLength );
String x =SLength+"0004"+TLength+"00000000"+s;
System.out.println(x.length());
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Write命令:"+WString);
return WRserialPort(serialPortManager,serialPort,WString);
}
public static String BlockErase(SerialPortManager serialPortManager, SerialPort serialPort,String s){
String SLength = Integer.toHexString(14+(s.length()/2)).toUpperCase();
String EpcLength = Integer.toHexString(s.length()/4).toUpperCase();
SLength =StringUtils.JLEvennumbers(SLength);
EpcLength =StringUtils.JLEvennumbers(EpcLength);
System.out.println("WriteCard======="+SLength + "===="+EpcLength );
String x =SLength+"0007"+EpcLength+s+"030014000000000000";
System.out.println(x.length());
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Write命令:"+WString);
return WRserialPort(serialPortManager,serialPort,WString);
}
public static String Alert(SerialPortManager serialPortManager, SerialPort serialPort){
String x ="070033140203";
System.out.println(x.length());
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Write命令:"+WString);
return WRserialPort(serialPortManager,serialPort,WString);
}
}
重要代码解析
读取数据
- 参数解析:
ENum:EPC号长度,以字为单位。EPC的长度在15个字以内,可以为0。超出范围,将返回参数错误信息。
EPC:要读取数据的标签的EPC号。长度根据所给的EPC号决定,EPC号以字为单位,且必须是整数个长度。高字在前,每个字的高字节在前。这里要求给出的是完整的EPC号。
Mem:一个字节。选择要读取的存储区。0x00:保留区;0x01:EPC存储区;0x02:TID存储区;0x03:用户存储区。其他值保留。若命令中出现了其它值,将返回参数出错的消息。
WordPtr:一个字节。指定要读取的字起始地址。0x00 表示从第一个字(第一个16位存储区)开始读,0x01表示从第2个字开始读,依次类推。
Num:一个字节。要读取的字的个数。不能设置为0x00,否则将返回参数错误信息。Num不能超过120,即最多读取120个字。若Num设置为0或者超过了120,将返回参数出错的消息。
Pwd:四个字节,这四个字节是访问密码。32位的访问密码的最高位在Pwd的第一字节(从左往右)的最高位,访问密码最低位在Pwd第四字节的最低位,Pwd的前两个字节放置访问密码的高字。只有当读保留区,并且相应存储区设置为密码锁、且标签的访问密码为非0的时候,才需要使用正确的访问密码。在其他情况下,Pwd为零或正确的访问密码。
MaskAdr:一个字节,掩模EPC号的起始字节地址。0x00表示从EPC号的最高字节开始掩模,0x01表示从EPC号的第二字节开始掩模,以此类推。
MaskLen:一个字节,掩模的字节数。掩模起始字节地址+掩模字节数不能大于EPC号字节长度,否则返回参数错误信息。
注:当MaskAdr、MaskLen为空时表示以完整的EPC号掩模。
- 代码实现
- 此代码在Tools类里
public static String ReadCard(SerialPortManager serialPortManager, SerialPort serialPort,String s,int Number){
if(Number>120){
Number=120;
}
String RNumber = Integer.toHexString(Number).toUpperCase();
String SLength = Integer.toHexString(14+(s.length()/2)).toUpperCase();
String EpcLength = Integer.toHexString(s.length()/4).toUpperCase();
SLength =StringUtils.JLEvennumbers(SLength);
EpcLength =StringUtils.JLEvennumbers(EpcLength);
RNumber=StringUtils.JLEvennumbers(RNumber);
System.out.println("ReadCard======="+SLength + "===="+EpcLength +"==="+RNumber);
//此处是读取用户的数据的命令
String x=SLength+"0002"+EpcLength+s+"0300"+RNumber+"000000000000";
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Read命令:"+WString);
String z = WRserialPort(serialPortManager,serialPort,WString);
// System.out.println("读取返回数据:"+z);
if (z.substring(6,8).equals("00")){
x= z.substring(8, z.length()-4);
}else {
System.out.println("数据为空或者读取错误:"+x);
x="";
}
return x ;
}
写数据
- 参数解析:
WNum:待写入的字个数,一个字为2个字节。这里字的个数必须和实际待写入的数据个数相等。WNum必须大于0,若上位机给出的WNum为0或者WNum和实际字个数不相等,将返回参数错误的消息。
ENum:EPC号长度。以字为单位。EPC的长度在15个字以内,可以为0。否则返回参数错误信息。
EPC:要写入数据的标签的EPC号。长度由所给的EPC号决定,EPC号以字为单位,且必须是整数个长度。高字在前,每个字的高字节在前。这里要求给出的是完整的EPC号。
Mem:一个字节,选择要写入的存储区。0x00:保留区;0x02:TID存储区;0x03:用户存储区。其他值保留。若命令中出现了其它值,将返回参数出错的消息。
WordPtr:一个字节,指定要写入数据的起始地址。
Wdt:待写入的字,字的个数必须与WNum指定的一致。这是要写入到存储区的数据。每个字的高字节在前。如果给出的数据不是整数个字长度,Data[]中前面的字写在标签的低地址中,后面的字写在标签的高地址中。比如,WordPtr等于0x02,则Data[]中第一个字(从左边起)写在Mem指定的存储区的地址0x02中,第二个字写在0x03中,依次类推。
Pwd:4个字节的访问密码。32位的访问密码的最高位在Pwd的第一字节(从左往右)的最高位,访问密码最低位在Pwd第四字节的最低位,Pwd的前两个字节放置访问密码的高字。在写操作时,应给出正确的访问密码,当相应存储区未设置成密码锁时Pwd可以为零。
MaskAdr:一个字节,掩模EPC号的起始字节地址。0x00表示从EPC号的最高字节开始掩模,0x01表示从EPC号的第二字节开始掩模,以此类推。
MaskLen:一个字节,掩模的字节数。掩模起始字节地址+掩模字节数不能大于EPC号字节长度,否则返回参数错误信息。
注:当MaskAdr、MaskLen为空时表示以完整的EPC号掩模。
- 代码实现
- 此代码在Tools类里
public static String WriteCard(SerialPortManager serialPortManager, SerialPort serialPort,String s,String transmitting){
String SLength = Integer.toHexString(14+(s.length()/2)+(transmitting.length()/2)).toUpperCase();
String TLength = Integer.toHexString(transmitting.length()/4).toUpperCase();
String EpcLength = Integer.toHexString(s.length()/4).toUpperCase();
SLength =StringUtils.JLEvennumbers(SLength);
TLength =StringUtils.JLEvennumbers(TLength);
EpcLength =StringUtils.JLEvennumbers(EpcLength);
// System.out.println("WriteCard======="+SLength + "===="+TLength );
//处是写命令(向用户区)
String x =SLength+"0003"+TLength+EpcLength+s+"0300"+transmitting+"000000000000";
System.out.println(x.length());
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Write命令:"+WString);
return WRserialPort(serialPortManager,serialPort,WString);
}
写EPC号
- 参数解析:
ENum:1个字节。要写入的EPC的长度,以字为单位。可以为0,但不能超过15,否则返回参数错误信息。
Pwd:4个字节的访问密码。32位的访问密码的最高位在Pwd的第一字节(从左往右)的最高位,访问密码最低位在Pwd第四字节的最低位,Pwd的前两个字节放置访问密码的高字。在本命令中,当EPC区设置为密码锁、且标签访问密码为非0的时候,才需要使用访问密码。在其他情况下,Pwd为零或正确的访问密码。
WEPC:要写入的EPC号,长度必须和ENum说明的一样。WEPC最小0个字,最多15个字,否则返回参数错误信息。
- 代码实现
- 此代码在Tools类里
public static String WriteEPCid(SerialPortManager serialPortManager, SerialPort serialPort,String s){
String SLength = Integer.toHexString(9+(s.length()/2)).toUpperCase();
String TLength = Integer.toHexString(s.length()/4).toUpperCase();
SLength =StringUtils.JLEvennumbers(SLength);
TLength =StringUtils.JLEvennumbers(TLength);
System.out.println("WriteCard======="+SLength + "===="+TLength );
//此处为写EPC号命令
String x =SLength+"0004"+TLength+"00000000"+s;
System.out.println(x.length());
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Write命令:"+WString);
return WRserialPort(serialPortManager,serialPort,WString);
}
询查单张标签
- 参数解析
Num:本条命令中包含的电子标签的EPC的个数。
EPC ID:读到的电子标签的EPC数据,EPC-1是第一张标签的EPC长度+第一张标签的EPC号。电子标签EPC号高字(EPC C1 G2中数据以字为单位)在前,每一个字的高字节在前。EPC长度以一个字节表示。
- 代码实现
- 此代码在Tools类里
public static String FindId(SerialPortManager serialPortManager, SerialPort serialPort) throws InterruptedException {
String x ="04ff0f";
int [] c =StringUtils.StringToInt(x);
String WString =x+ CRC16.getCRC2(c);
System.out.println("Find命令:"+WString);
return Tools.getEpc(serialPortManager,serialPort,WRserialPort(serialPortManager,serialPort,WString));
}
CRC校验
-通过看使用说明可知所有的命令都需要CRC(最后两位)
-
使用手册已给出CRC校验C语言版
-
此处是我写java版
public class CRC16 {
public static String getCRC2(int [] bytes) {
int CRC = 0xFFFF;
int POLYNOMIAL = 0x8408;
int i, j;
for (i = 0; i < bytes.length; i++) {
CRC ^= bytes[i];
for (j = 0; j < 8; j++) {
if ((CRC & 0x0001)!=0) {
CRC = (CRC >> 1)^POLYNOMIAL;
} else {
CRC= (CRC>>1);
}
}
}
//高低位转换
CRC = ( (CRC & 0x0000FF00) >> 8) | ( (CRC & 0x000000FF ) << 8);
return Integer.toHexString(CRC);
}
}
测试
- 展示功能将EPCID改为 201843320,向用户区写入数据 2,2021-05-20
SerialPortManager serialPortManager = new SerialPortManager();
SerialPort serialPort = Tools.OpenPort(serialPortManager);
String a= Tools.FindId(serialPortManager,serialPort);
System.out.println(a+"========"+a.length());
Thread.sleep(10);
String b =Tools.ReadCard(serialPortManager, serialPort, a,12);
System.out.println("数据:"+b+"长度"+b.length());
Thread.sleep(10);
System.out.println(Tools.BlockErase(serialPortManager, serialPort, a));
Thread.sleep(10);
String transmitting1 = StringUtils.StringToAscii("201843321");
transmitting1 =StringUtils.StrTo16(transmitting1);
System.out.println("transmitting1(16)========"+transmitting1+"====length"+transmitting1.length()/4);
String q =Tools.WriteEPCid(serialPortManager,serialPort,transmitting1);
System.out.println(q);
String transmitting2 = StringUtils.StringToAscii("2,2021-05-20");
transmitting2 =StringUtils.StrTo16(transmitting2);
System.out.println("transmitting2(16)========"+transmitting2+"====length"+transmitting2.length()/4);
String transmitting=transmitting2;
String c = Tools.WriteCard(serialPortManager,serialPort,transmitting1,transmitting);
System.out.println("写入返回"+c);
System.out.println("==================="+c.substring(6,8));
Thread.sleep(10);
String d =Tools.ReadCard(serialPortManager, serialPort, transmitting1,12);
System.out.println("数据:"+d+"长度"+d.length());
System.out.println(StringUtils.AsciiToString(d));
Tools.ClosePort(serialPortManager,serialPort);
- 结果
- 展示功能读取卡号,读取卡数据(用户区)
SerialPortManager serialPortManager = new SerialPortManager();
SerialPort serialPort = Tools.OpenPort(serialPortManager);
while (true){
String a= Tools.FindId(serialPortManager,serialPort);
Thread.sleep(10);
if (a.length()>0){
System.out.println(StringUtils.AsciiToString(a));
String b = Tools.ReadCard(serialPortManager, serialPort, a, 12);
Thread.sleep(10);
if (b.length() > 0) {
System.out.println("数据:" + b + "长度" + b.length());
System.out.println("Ascii转换后:" + StringUtils.AsciiToString(b));
break;
}
}
Thread.sleep(50);
}
Tools.ClosePort(serialPortManager,serialPort);
- 结果
四、注意
- 读取数据的字不要太长,虽然手册上说可为120个字,但个人建议20字以内(太大会出现意外)
- CRC 校验注意高低位问题
- 执行每个命令(读卡号,写数据)最好使线程休眠一段时间,太快会造成问题。
- 必须使用Java1.8(jdk1.8.0_20)及下