一、现象

Java11环境下项目启动时报错:java.nio.file.InvalidPathException: Illegal char <:> at index 4

但项目能正常启动、运行。

二、解决办法

方法1

方法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包,部分内容截图如下:

Class-Path属性的值全是以"file:/"开头,看来报错和这个jar包有关。查看Created-By属性,值为”IntelliJ IDEA“,那么问题来了:

a). 为什么Java应用会引入IDEA动态生成的jar包?

b). 以"file:/"开头的路径是不是有效路径?

针对问题a),定位到了jar包的生成路径,查询了相关博客,发现是IDEA中一个设置问题。启动项目会提示命令过长无法启动,之前我都是修改运行配置如下图:

但博客中并没有说明导致错误的原因。我猜测这个配置用来充当桥梁,集成了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相关推荐

  1. android studio 中无法安装或打开sdk管理,或者是异常android studio Illegal char 《:》 at index 40

    之前用过一段时间安卓,现在突然要用.东西被删了,就重新安装以为很方便,结果今天卸载安装了一天,到晚上才解决. 如果你们的情况是,打开Android studio之后无法选择SDK目录,亦或者是4.0版 ...

  2. asp.net的aspx页面<% %>、<%@ %>、<%# %>、<%= %>、<%$ %>的用法

    1. <%--exegesics--%> 注释 相当于<!----> 2. <% code%> 绑定后台代码块: //相当于写在后台的可执行代码 <form ...

  3. 【解错笔记】struts2项目出错Illegal char 报错(已解决)

    Illegal char<:>atindex3:jar:file:\G:\Program Files\Apache Software Foundation\Tomcat9.0\webapp ...

  4. java resourse 报错_java.nio.file.InvalidPathException: Illegal char :

    一.报错: java.nio.file.InvalidPathException: Illegal char <:>at sun.nio.fs.WindowsPathParser.norm ...

  5. activeMQ启动失败报错illegal character in hostname at index

    我在安装activemq的时候发现启动失败,查看了日志发下打印了一堆东西,但是关键信息就是illegal character in hostname at index 突然发现应该是hostname有 ...

  6. Illegal character in authority at index 18:

    java.io.IOException: Illegal character in authority at index 18: http://10.30.53.36 :8080/ServerForJ ...

  7. Optics Bridge:Celo <-> 以太坊

    1. 引言 目前,Celo生态已上线的跨链方案主要有3种: 1)Optics Bridge:当前支持 cleo.以太坊.Polygon三者之间跨链.即将支持Avalanche. 2)AllBridge ...

  8. java下载网络文件+Illegal character in path at index 135错误解决方法

    public void downloadNetTest() throws URISyntaxException {//图片的地址String netfileurl = "http://192 ...

  9. http请求报错Illegal character in query at index 303的解决方法

    http请求报错"Illegal character in query at index 303"的解决方法 执行jmeter的http请求时,请求失败,在Sampler resu ...

最新文章

  1. Glide @GlideModule 注解使用
  2. python语言if语句-Python if else语句详解
  3. Ant Design 3.0 使用案例
  4. Flink从入门到精通100篇(二十二)-Apache Flink OLAP引擎性能优化及应用
  5. 过滤请求绝技 — 布隆过滤器与布谷鸟过滤器
  6. View的三大流程之View的测量
  7. 聊聊数仓中TPCD-DSTPC-H与查询性能的那些事儿
  8. 素数之年,IT运维其实可以很简单
  9. ThinkPhp学习01
  10. Java 使用反射 Class.forName() 报错java.lang.ClassNotFoundException 的解决办法
  11. 飞行器制导与控制及其Matlab仿真
  12. 联想Y7000安装显卡驱动
  13. 立而不破,华为云注解政企智能升级的“道与术”
  14. 解题笔记——NIT 遥远的村庄
  15. strlen函数详解
  16. 数学笔记——导数1(导数的基本概念)
  17. 【常用的办公软件】万彩办公大师教程丨文件批量压缩工具
  18. 南卡蓝牙耳机好还是漫步者好?国产半入耳式蓝牙耳机对比
  19. Vue - 每个页面单独设置 body 背景色(独立修改单个页面的背景色,不同页面设置不同的背景颜色)
  20. Solution: Cannot start Microsoft outlook. Cannot open the outlook window. Invalid xml

热门文章

  1. 国内最强的电子计算机专科学校,国内十大专科院校,被誉为专科院校中的“清华北大”...
  2. 全国计算机基础一级教学,全国一级计算机基础教学幻灯片.ppt
  3. IntelliJ IDEA的安装、配置与使用
  4. php将数组里所有元素转成字符串
  5. Java Excel转换PDF
  6. 名画122 王冕 吴镇《梅竹双清图》
  7. java 构造方法和成员方法_java中构造方法和成员方法的区别
  8. Ubuntu树莓派安装macast实现DLNA投屏
  9. Effective C++(编写new和delete时需固守常规)
  10. 蜀门linux一键端,【蜀门】网游单机版 蜀门镜像一键端