Illegal char <:> at index 4
一、现象
Java11环境下项目启动时报错:java.nio.file.InvalidPathException: Illegal char <:> at index 4
但项目能正常启动、运行。
二、解决办法
方法1
![](/assets/blank.gif)
![](/assets/blank.gif)
方法2
项目路径\.idea\workspace.xml中的PropertiesComponent节点下新增配置:
<property name="dynamic.classpath" value="true" />
三、原因
异常在WindwosPathParser:182被抛出,提示存在非法字符冒号':'
阅读该方法源码:
private static String normalize(StringBuilder sb, String path, int off) {int len = path.length();off = nextNonSlash(path, off, len);int start = off;char lastC = 0;while (off < len) {char c = path.charAt(off);if (isSlash(c)) {if (lastC == ' ')throw new InvalidPathException(path,"Trailing char <" + lastC + ">",off - 1);sb.append(path, start, off);off = nextNonSlash(path, off, len);if (off != len) //no slash at the end of normalized pathsb.append('\\');start = off;} else {if (isInvalidPathChar(c))throw new InvalidPathException(path,"Illegal char <" + c + ">",off);lastC = c;off++;}}if (start != off) {if (lastC == ' ')throw new InvalidPathException(path,"Trailing char <" + lastC + ">",off - 1);sb.append(path, start, off);}return sb.toString();
}
上述normalize方法代码块中line19会进行一次判断,若满足条件,抛出InvalidPathException异常,其判断条件isInvalidPathChar方法源码如下:
private static final String reservedChars = "<>:\"|?*";
private static final boolean isInvalidPathChar(char ch) {return ch < '\u0020' || reservedChars.indexOf(ch) != -1;
}
isInvalidPathChar方法会判断字符是否小于Unicode空格'\u0020',并且是否是<>:\"|?*中的字符。
所以若出现该异常,无非是路径中存在特殊字符,通过debug,获取了当时引发问题的路径值,normalize方法中的path入参:“file:/D:/Code/Java/foo/web/target/test-classes/”,off入参值为0。遍历到"file:/"中':'字符时,抛出异常,导致应用启动出现异常,但被catch住不影响主流程。
路径以"file:/”开头,看上去像文件传输协议,但协议应该为“file://"是双斜杠,"file:/”是什么呢?url - What is the difference between file:/, file://, file:/// - Stack Overflow StackOverflow中高赞回答称"file:/“是无效的,但将上述路径复制到windows文件管理器中可以正常访问,可能因为Windows兼容性太好?
想了解为什么传入了这个以"file:/"开头的path,off为0,需查看该方法的调用方:
private static Result parse(String input, boolean requireToNormalize) {String root = "";WindowsPathType type = null;int len = input.length();int off = 0;if (len > 1) {char c0 = input.charAt(0);char c1 = input.charAt(1);char c = 0;int next = 2;if (isSlash(c0) && isSlash(c1)) {// UNC: We keep the first two slash, collapse all the// following, then take the hostname and share name out,// meanwhile collapsing all the redundant slashes.type = WindowsPathType.UNC;off = nextNonSlash(input, next, len);next = nextSlash(input, off, len);if (off == next)throw new InvalidPathException(input, "UNC path is missing hostname");String host = input.substring(off, next); //hostoff = nextNonSlash(input, next, len);next = nextSlash(input, off, len);if (off == next)throw new InvalidPathException(input, "UNC path is missing sharename");root = "\\\\" + host + "\\" + input.substring(off, next) + "\\";off = next;} else {if (isLetter(c0) && c1 == ':') {char c2;if (len > 2 && isSlash(c2 = input.charAt(2))) {// avoid concatenation when root is "D:\"if (c2 == '\\') {root = input.substring(0, 3);} else {root = input.substring(0, 2) + '\\';}off = 3;type = WindowsPathType.ABSOLUTE;} else {root = input.substring(0, 2);off = 2;type = WindowsPathType.DRIVE_RELATIVE;}}}}if (off == 0) {if (len > 0 && isSlash(input.charAt(0))) {type = WindowsPathType.DIRECTORY_RELATIVE;root = "\\";} else {type = WindowsPathType.RELATIVE;}}if (requireToNormalize) {StringBuilder sb = new StringBuilder(input.length());sb.append(root);return new Result(type, root, normalize(sb, input, off));} else {return new Result(type, root, input);}
}
其中line60调用了normalize方法。
parse方法入参input即上文中提到的路径,方法通过多个if语句来判断路径类型,并初始化off偏移量。(我们常见的路径无非是相对路径、绝对路径,Java在sun.nio.fs.WindowsPathType中枚举出了Windows系统下所有的路径格式,Windows文件路径格式参考微软文档Windows 系统中的文件路径格式 | Microsoft Learn)
该方法line12开始判断:若路径首字符、第二个字符都是斜杠,属于WindowsPathType.UNC类型路径,然后计算出host名和root名;若首字符、第二个字符不是斜杠且首字符是字母,第二个字符是冒号':',路径字符长度大于2且第三个字符是斜杠,属于WindowsPathType.ABSOLUTE路径,比如”C:/foo"就是绝对路径,否则就是WindowsPathType.DRIVE_RELATIVE如"C:foo1/foo2"是基于驱动器的相对路径...其他判断类似,可自行阅读代码。
就路径"file:/D:/Code/Java/foo/web/target/test-classes/"来说,它既不是UNC路径,不是绝对路径,不是驱动器相对路径、不是目录相对路径,所以Java源码认为这个路径是相对路径,相对路径的off从0开始。显然,这个路径不是相对路径,off从0开始,后续字符肯定包含冒号':'特殊字符。
继续追踪堆栈,观察这个路径创建的位置,可以找到FSInfo#getJarClassPath方法:
public List<Path> getJarClassPath(Path file) throws IOException {Path parent = file.getParent();try (JarFile jarFile = new JarFile(file.toFile())) {Manifest man = jarFile.getManifest();if (man == null)return Collections.emptyList();Attributes attr = man.getMainAttributes();if (attr == null)return Collections.emptyList();String path = attr.getValue(Attributes.Name.CLASS_PATH);if (path == null)return Collections.emptyList();List<Path> list = new ArrayList<>();for (StringTokenizer st = new StringTokenizer(path);st.hasMoreTokens(); ) {String elt = st.nextToken();Path f = FileSystems.getDefault().getPath(elt);if (!f.isAbsolute() && parent != null)f = parent.resolve(f).toAbsolutePath();list.add(f);}return list;}
}
line21中getPath方法中会调用上述parse方法,抛出异常。变量elt就是路径。
getJarClassPath方法会获取jar包的classPath。94行中使用try-with-resources方式将Path转换为File,然后传入File对象创建JarFile对象。拿到JarFile后,获取Jar包中的manifest文件,获取manifest中所有属性,获取Class-Path属性的值,可以发现以“file:/foo"开头的路径来自于jar包中manifest中的Class-Path属性。getJarClassPath入参file的路径值为“D:\Users\user\AppData\Local\Temp\classpath756628492.jar”,可能是这个jar包导致的问题。解压jar包,部分内容截图如下:
![](/assets/blank.gif)
Class-Path属性的值全是以"file:/"开头,看来报错和这个jar包有关。查看Created-By属性,值为”IntelliJ IDEA“,那么问题来了:
a). 为什么Java应用会引入IDEA动态生成的jar包?
b). 以"file:/"开头的路径是不是有效路径?
针对问题a),定位到了jar包的生成路径,查询了相关博客,发现是IDEA中一个设置问题。启动项目会提示命令过长无法启动,之前我都是修改运行配置如下图:
![](/assets/blank.gif)
但博客中并没有说明导致错误的原因。我猜测这个配置用来充当桥梁,集成了Java应用所包含包的classPath,然后IDEA启动Java应用时引入了IDEA动态生成的Jar包。
针对问题b),暂未查找到文献表明"file:/"是有效协议,但Windows文件管理器确实能处理该格式路径。上文介绍了Java11处理路径的相关代码,表明Java11不支持该格式路径,会抛出异常,所以对Java11来说,"file:/"是无效路径。
但为什么JDK8没这个问题?
于是我研究了下为什么运行时无法Debug到JavacFileManager类中,或者为什么直接编写JavacFileManager这个类爆红,但全网找了找都没找到答案。难道是CompileTime没有,RunTime才有?JavacFileManager实现了StandardJavaFileManager接口,JDK8代码中可以直接跳转到StandardJavaFileManager接口,但找不到这个接口的任何实现类,说明compileTime中只有接口但没有实现类,而运行时能获取到实现类JavacFileManager,说明实现类在RunTime是存在的。联想到门面模式,应用应该依赖接口而不是依赖实现,比如Java的数据库驱动,利用SPI的特性,各个数据库厂商提供底层实现,但开发者不用关心底层实现,只依赖JDBC中提供的接口。为了避免开发者拿到数据库厂商的底层实现,可以将数据库驱动的maven依赖scope定义为runtime,这样在代码编写的过程中,开发者没法拿到接口的实现类,只能使用JDBC接口,代码运行中自动获取接口的实现类,从而做到解耦。上述内容都是我个人的猜测,也可能是其他原因导致,真实原因就不得而知了。
如果我的猜想正确,那我maven引入这个类所在的jar包就行,于是我翻了翻Java文档,看这个类在哪儿。文档显示这个类在com.sun.tools.javac.file包中,这个包在JDK8路径的/lib/tools.jar包中有,maven依赖如下:
<dependency><groupId>com.sun</groupId><artifactId>tools</artifactId><version>1.8.0</version><scope>system</scope><systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
我们回归问题本质,来看看JDK8下为什么不会报错。JDK11报错是在getJarClassPath中调用getPath抛出异常,我们直接来看JDK8的getJarClassPath方法:
public List<File> getJarClassPath(File var1) throws IOException {String var2 = var1.getParent();JarFile var3 = new JarFile(var1);List var6;try {Manifest var4 = var3.getManifest();if (var4 == null) {List var14 = Collections.emptyList();return var14;}Attributes var5 = var4.getMainAttributes();if (var5 != null) {String var15 = var5.getValue(Name.CLASS_PATH);if (var15 == null) {List var16 = Collections.emptyList();return var16;}ArrayList var7 = new ArrayList();StringTokenizer var8 = new StringTokenizer(var15);while(var8.hasMoreTokens()) {String var9 = var8.nextToken();File var10 = var2 == null ? new File(var9) : new File(var2, var9);var7.add(var10);}ArrayList var17 = var7;return var17;}var6 = Collections.emptyList();} finally {var3.close();}return var6;
}
由于引入的是jar包,都是class字节码文件,所以源码不太好理解。总的来说,JDK8中代码整体流程和JDK11类似,也是通过jar包的文件路径获取manifest中的Class-Path属性,遍历Class-Path属性中的classPath生成classPath路径对应的File对象,我们直接定位到24行开始看,在24行中的while循环中,利用classPath路径生成File对象,将对象加入列表中,列表中存放是File,看到这里是不是感觉有什么不对劲?查看该方法的签名,JDK8的方法返回List<File>,我们再看JDK11中的实现,JDK11中getJarClassPath方法返回List<Path>,差异出在这里。将String类型路径传入File类的构造器创建File对象,别说传"file:/foo",传个"FUCK"都没问题,只不过拿不到真实的文件,所以JDK8中不会出现异常。
Illegal char <:> at index 4相关推荐
- android studio 中无法安装或打开sdk管理,或者是异常android studio Illegal char 《:》 at index 40
之前用过一段时间安卓,现在突然要用.东西被删了,就重新安装以为很方便,结果今天卸载安装了一天,到晚上才解决. 如果你们的情况是,打开Android studio之后无法选择SDK目录,亦或者是4.0版 ...
- asp.net的aspx页面<% %>、<%@ %>、<%# %>、<%= %>、<%$ %>的用法
1. <%--exegesics--%> 注释 相当于<!----> 2. <% code%> 绑定后台代码块: //相当于写在后台的可执行代码 <form ...
- 【解错笔记】struts2项目出错Illegal char 报错(已解决)
Illegal char<:>atindex3:jar:file:\G:\Program Files\Apache Software Foundation\Tomcat9.0\webapp ...
- java resourse 报错_java.nio.file.InvalidPathException: Illegal char :
一.报错: java.nio.file.InvalidPathException: Illegal char <:>at sun.nio.fs.WindowsPathParser.norm ...
- activeMQ启动失败报错illegal character in hostname at index
我在安装activemq的时候发现启动失败,查看了日志发下打印了一堆东西,但是关键信息就是illegal character in hostname at index 突然发现应该是hostname有 ...
- Illegal character in authority at index 18:
java.io.IOException: Illegal character in authority at index 18: http://10.30.53.36 :8080/ServerForJ ...
- Optics Bridge:Celo <-> 以太坊
1. 引言 目前,Celo生态已上线的跨链方案主要有3种: 1)Optics Bridge:当前支持 cleo.以太坊.Polygon三者之间跨链.即将支持Avalanche. 2)AllBridge ...
- java下载网络文件+Illegal character in path at index 135错误解决方法
public void downloadNetTest() throws URISyntaxException {//图片的地址String netfileurl = "http://192 ...
- http请求报错Illegal character in query at index 303的解决方法
http请求报错"Illegal character in query at index 303"的解决方法 执行jmeter的http请求时,请求失败,在Sampler resu ...
最新文章
- Glide @GlideModule 注解使用
- python语言if语句-Python if else语句详解
- Ant Design 3.0 使用案例
- Flink从入门到精通100篇(二十二)-Apache Flink OLAP引擎性能优化及应用
- 过滤请求绝技 — 布隆过滤器与布谷鸟过滤器
- View的三大流程之View的测量
- 聊聊数仓中TPCD-DSTPC-H与查询性能的那些事儿
- 素数之年,IT运维其实可以很简单
- ThinkPhp学习01
- Java 使用反射 Class.forName() 报错java.lang.ClassNotFoundException 的解决办法
- 飞行器制导与控制及其Matlab仿真
- 联想Y7000安装显卡驱动
- 立而不破,华为云注解政企智能升级的“道与术”
- 解题笔记——NIT 遥远的村庄
- strlen函数详解
- 数学笔记——导数1(导数的基本概念)
- 【常用的办公软件】万彩办公大师教程丨文件批量压缩工具
- 南卡蓝牙耳机好还是漫步者好?国产半入耳式蓝牙耳机对比
- Vue - 每个页面单独设置 body 背景色(独立修改单个页面的背景色,不同页面设置不同的背景颜色)
- Solution: Cannot start Microsoft outlook. Cannot open the outlook window. Invalid xml
热门文章
- 国内最强的电子计算机专科学校,国内十大专科院校,被誉为专科院校中的“清华北大”...
- 全国计算机基础一级教学,全国一级计算机基础教学幻灯片.ppt
- IntelliJ IDEA的安装、配置与使用
- php将数组里所有元素转成字符串
- Java Excel转换PDF
- 名画122 王冕 吴镇《梅竹双清图》
- java 构造方法和成员方法_java中构造方法和成员方法的区别
- Ubuntu树莓派安装macast实现DLNA投屏
- Effective C++(编写new和delete时需固守常规)
- 蜀门linux一键端,【蜀门】网游单机版 蜀门镜像一键端