自定义包不能以java开头和为什么不能自定义java.lang.String 类

本文详细解释了Java类加载机制中的双亲委派模型,并通过实例说明了为什么自定义的包不能以java.xxx.xx开头,以及这背后的系统安全机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里说一下为什么自定义的包不能以 java.xxx.xx 开头,这是一种安全机制,如果以java开头,则直接异常

 private ProtectionDomain preDefineClass(String name,
					    ProtectionDomain protectionDomain)
    {
	if (!checkName(name))
	    throw new NoClassDefFoundError("IllegalName: " + name);

	if ((name != null) && name.startsWith("java.")) {					 // 这里是防止恶意篡改系统中的类
	    throw new SecurityException("Prohibited package name: " +
					name.substring(0, name.lastIndexOf('.')));
	}
	if (protectionDomain == null) {
	    protectionDomain = getDefaultDomain();
	}

	if (name != null)
	    checkCerts(name, protectionDomain.getCodeSource());

	return protectionDomain;
    }

如果自己定义了一个 java.lang.String 类,然后执行此类中的方法会出现什么情况 ? 

java.lang.NoSuchMethodError: main
Exception in thread "main" 

会发出上面的异常,为什么呢?

因为Java的类加载机制造成的,类的加载机制为双亲委派模型,也就是先交给其父亲加载器去加载,如果父亲加载不到,那么再由自己加载

我们自己定义的类一般都由AppClassLoader 进行加载,而它的父亲为ExtclassLoader,而 ExtClassloader的父亲则是 BoostrapClasslader,所以我们自定义的

java.lang.String 最先由 BootStrapClassLoader 加载,而BootStrapLoader 加载器由C++写,它负责加载系统中的类库,而java.lang.string 正是系统中的类,已经被 

BootStrapLoader 加载过了,所以不需要再加载 java.lang.String(虽然我们写的与系统中的不是一个,但名字是一样的),所以这样就会免去了加载而直接执行java.lang.String

中的main方法,而系统中的java.lang.string 中没有main 方法,这也就出现了上面的异常


<think>我们遇到了报错信息:"dynamic method [java.lang.String, toInstant/0] not found" 这个错误通常发生在尝试对一个字符串(String型的变量调用 `toInstant()` 方法时,因为字符串并没有这个方法。 在之前的脚本中,我们假设 `doc['Base.CreatedAt'].value` 返回的是日期型(JodaCompatibleZonedDateTime),但实际上,如果该字段的映射型是文本(text)而不是日期(date),那么返回的将是字符串。 解决方案: 1. 检查字段映射,确保相关字段是日期型。 2. 如果字段确实是日期型,但脚本中仍然报错,可能是因为脚本中混用了字符串日期型。但根据错误信息,这里明确是尝试在字符串上调用`toInstant`。 因此,我们需要: - 修正映射:将字段改为日期型(如果数据是日期格式的字符串,可以重新索引或修改映射)。 - 或者,在脚本中处理字符串到日期的转换(不推荐,因为性能较差且复杂)。 如果无法修改映射,则必须在脚本中解析字符串。但注意,这会影响性能。 修正后的脚本(假设字段是字符串,且格式已知,例如ISO8601): 注意:这种方法性能较差,仅当无法修改映射时的临时解决方案。 示例脚本(假设日期格式为"yyyy-MM-dd'T'HH:mm:ss.SSSZ"): ```plaintext import java.time.format.DateTimeFormatter; import java.time.Instant; // 获取字段的字符串值 def baseCreatedAtStr = doc['Base.CreatedAt'].value; def fixTimeStr = doc['CompleteInfo.Score.FixTime'].value; // 定义日期格式解析器 DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME; // 将字符串解析为ZonedDateTime,然后转换为Instant,再转为毫秒 long baseMillis = Instant.from(formatter.parse(baseCreatedAtStr)).toEpochMilli(); long fixMillis; if (fixTimeStr == null || fixTimeStr.length() == 0) { fixMillis = new Date().getTime(); } else { fixMillis = Instant.from(formatter.parse(fixTimeStr)).toEpochMilli(); } emit(fixMillis - baseMillis); ``` 但是,请注意: - 在Painless脚本中导入(如DateTimeFormatter)可能需要特别权限,且在某些Elasticsearch版本中可能受限。 - 更简单的方式是使用 `SimpleDateFormat`(但同样需要导入)或者使用 `Date.parse()`(已弃用,且可能不推荐)。 另一种简化方法(使用Date.parse,注意时区问题): ```plaintext // 注意:Date.parse 返回的是毫秒时间戳,但它是基于默认时区解析的,可能不准确 def baseMillis = Date.parse('yyyy-MM-dd\'T\'HH:mm:ss.SSSXXX', doc['Base.CreatedAt'].value); // 同样处理fixTime ``` 然而,最佳实践是: - 确保Elasticsearch中的字段映射为正确的日期型,这样在脚本中就可以直接使用日期对象的方法(如之前的`toInstant()`),而无需字符串解析。 因此,步骤: 1. 检查映射: ```json GET your_index/_mapping ``` 查看`Base.CreatedAt``CompleteInfo.Score.FixTime`的型。如果是`text`,则需要修改映射。 2. 修改映射(如果索引已存在,可能需要重建索引): 在创建索引时指定字段型为`date`,或者使用`PUT index/_mapping`添加新的日期字段(但已有字段不能修改,所以可能需要重建索引)。 3. 如果必须使用字符串解析,则使用上述脚本,但要注意性能。 4. 如果字段是日期型,但脚本中仍然报错,请检查字段是否是多字段(multi-field)?确保使用的是主字段(即日期型的字段,而不是其下的keyword子字段)。 如果问题仍然存在,请提供字段映射信息以便进一步分析。 §§相关问题§§ 1. 如何检查Elasticsearch索引中某个字段的映射型? 2. 如果字段已经被错误地映射为文本型,如何在不重建索引的情况下将其更改为日期型? 3. 在Painless脚本中,处理日期字符串解析时有哪些性能优化建议? 4. 除了DateTimeFormatter,还有哪些方法可以在脚本中安全地解析ISO8601日期字符串? 5. 如果日期字段的格式不一致(如混合了ISO8601普通日期格式),脚本应如何兼容处理?</think>### 解决 "dynamic method [java.lang.String, toInstant/0] not found" 错误 此错误表示您尝试在字符串(String型上调用 `toInstant()` 方法,但该方法仅适用于日期型。以下是完整解决方案: #### 根本原因分析 1. **字段型不匹配** - Elasticsearch 将字段识别为 `text` 型而非 `date` 型 - 脚本尝试在字符串上调用日期方法 `toInstant()` 2. **映射验证方法** 执行以下命令检查字段型: ```json GET your_index/_mapping ``` 正确映射应为: ```json "Base.CreatedAt": { "type": "date" } ``` #### 解决方案一:修正字段映射(推荐) 1. **创建新索引(如果可重建数据)**: ```json PUT new_index { "mappings": { "properties": { "Base.CreatedAt": {"type": "date"}, "CompleteInfo.Score.FixTime": {"type": "date"} } } } ``` 2. **使用 reindex API 迁移数据**: ```json POST _reindex { "source": {"index": "old_index"}, "dest": {"index": "new_index"} } ``` #### 解决方案二:在脚本中处理字符串转换(临时方案) ```plaintext def parseDate(String dateStr) { // 支持常见日期格式 try { return Instant.parse(dateStr); // ISO8601格式 } catch (Exception e1) { try { // 自定义格式处理 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return LocalDateTime.parse(dateStr, formatter).toInstant(ZoneOffset.UTC); } catch (Exception e2) { return null; // 解析失败处理 } } } def baseCreatedAt = doc['Base.CreatedAt'].value; def fixTimeField = doc['CompleteInfo.Score.FixTime'].value; long baseMillis = parseDate(baseCreatedAt).toEpochMilli(); long fixMillis = (fixTimeField == null) ? new Date().getTime() : parseDate(fixTimeField).toEpochMilli(); emit(fixMillis - baseMillis); ``` #### 关键注意事项 1. **日期格式统一性**: - ISO8601 格式:`2023-01-15T10:30:00Z` - 自定义格式需明确指定模式 2. **空值安全处理**: ```plaintext def baseCreatedAt = doc.containsKey('Base.CreatedAt') ? doc['Base.CreatedAt'].value : "1970-01-01T00:00:00Z"; ``` 3. **性能优化**: - 避免在循环中创建 `DateTimeFormatter` - 将格式化器声明在脚本开头
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值