搜索
首页Javajava教程4 中最有趣的 Java 错误

2024 年,我们分析了大量项目,并在博客上分享我们的发现。现在是除夕夜——是时候讲喜庆故事了!我们收集了在开源项目中检测到的最有趣的 Java 错误,现在将它们带给您!

Top most intriguing Java errors in 4

前言

我们长期以来一直秉承发布 PVS-Studio 检测到的最有趣的 bug 的传统,但自 2020 年以来就没有出现与 Java 相关的置顶!这一次,我尝试复兴复古风格。我希望您手边有一条舒适的毯子和一杯热茶,因为我专门为您挑选了 10 多种有趣的昆虫。以下是他们的排名:

  • 我的个人意见;
  • 该错误的有趣背景;
  • 多样性、可信度和重要性。

准备好用自己的编程智慧来迎接 10 个有趣的故事吧——除夕夜很快就要到来了:)

第10名。回到未来

第十位,第一个代码片段张开双臂欢迎我们。

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

我忍不住把它放在除夕夜的顶部,因为这段代码中的一个错误可以让我们更快地到达下一年:)猜猜这个错误是从哪里来的?

让我们看一下传递给 SimpleDateFormat 构造函数的参数。看起来有效吗?如果我们传递几乎任何日期,例如撰写本文的日期 (10/12/2024),代码将返回正确的值 20241210。

但是,如果我们传递 29/12/2024,它将返回 20251229,从而巧妙地提前进入新年。顺便说一句,时光倒流也是可行的。

发生这种情况是因为 SimpleDateFormat 参数中的 Y 字符代表基于周数的年份。简而言之,当一周至少包含新年的四天时,该一周被视为第一周。所以,如果我们的一周从周日开始,我们就可以提前三天进入新的一年。

要修复此问题,只需将大写 Y 替换为小写 y 即可。想了解更多吗?我们专门写了一整篇文章来讨论这个主题。

这是针对此错误的 PVS-Studio 警告:

V6122 检测到使用“Y”(周年)模式:它可能打算使用“y”(年)。 SkeinParameters.java 246

由于周数的具体情况,因此测试并不是发现此错误的最佳方法。那么为什么这样一个话题性的错误会出现在最后呢?原因是该警告不是来自 Bouncy Castle 的实际版本,而是来自我们的测试基地。旧的资源仍然存在,并且这个错误已经修复了很长时间。这是来自过去的致敬,又是一次时光旅行:)

第9名。 “看来不行”

第九位,我们收到来自 GeoServer 分析的警告:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // 



这是 PVS-Studio 警告:

V6027 变量“newValue”、“oldValue”通过调用同一函数进行初始化。这可能是一个错误或未优化的代码。 DataAccessRuleDAOTest.java 110、DataAccessRuleDAOTest.java 111

这样的错误有什么有趣的?让我来揭示这四个点背后隐藏的是什么:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

有评论称该代码由于某种原因无法运行。说实话,我第一次看到的时候就笑了。

不过,这个评论相当含糊。测试很可能是故意以这种方式编写的,以防止在比较失败时出现故障。然而,该代码已经处于这种状态十多年了,这引发了一些问题。这种模糊性就是我没有将其排名更高的原因。

第8名。脚中弹

如果我们不能将 JBullet 中的 bug 称为“搬起石头砸自己的脚”,我不知道哪些可以这样称呼。这是文章中的一个错误:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // 



<p>我认为我们甚至不需要 PVS-Studio 警告来发现错误所在。无论如何,以防万一,这里是:</p>

<p>V6026 该值已分配给“proxy1”变量。 HashedOverlappingPairCache.java 233</p>

<p>是的,这是一个令人尴尬的简单错误。不过,这种简单性让它变得更加搞笑。尽管如此,它还是有自己的故事。</p>

<p>JBullet 库是 C/C 子弹库的移植,那里有类似的功能:<br>
</p>

<pre class="brush:php;toolbar:false">@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}

很容易看出这段代码写得正确。从 gitblame 来看,原来写的是正确的。原来是代码从一种语言移植到另一种语言时出现了错误。

由于其惊人的朴实加上丰富的历史,我将这个警告评为第八名。我希望你喜欢这个搬起石头砸自己脚的 bug 原来是与 C 语言相关的。

第七名。即使是伟大的数学家也会犯错误

诚然,下一个警告出于多种原因温暖了我的心。以下是 GeoGebra 检查的代码片段:

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}

尝试自己找出错误!为了不让你们偷看,我把警告和解释隐藏在剧透里了。

答案
首先,我们看一下PVS-Studio的警告:

V6107 正在使用常量 0.7071067811865。结果值可能不准确。考虑使用 Math.sqrt(0.5)。 DrawAngle.java 303

事实上,0.7071067811865 并不是什么神奇的数字——它只是 0.5 平方根的四舍五入结果。但这种精度损失有多严重呢? GeoGebra 是一款为数学家量身定制的软件,额外的精度似乎并没有什么坏处。

为什么我这么喜欢这个bug?

首先,在会议上,我经常要求与会者在其他代码片段中找到类似的错误。当错误隐藏在常量中时,看着他们仔细分析代码总是很有趣。

其次,这是我为 Java 分析器实现的第一个诊断规则。这就是为什么我无法抗拒将它放在顶部的原因——即使意识到了偏见——我把它放在第七位:)

第六名。这个模式不起作用

以下警告是我从基于 DBeaver 检查的第一篇文章中获取的,可能不会立即引起我们的注意。这是一个代码片段:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

以下是 PVS-Studio 分析器检测到的内容:

V6082 不安全的双重检查锁定。该字段应声明为易失性的。 TaskImpl.java 59、TaskImpl.java317

虽然这个特定的警告没有什么特别的,但我仍然觉得它非常有趣。关键是所应用的双重检查锁定模式不起作用。有什么窍门呢?这在 20 年前是相关的:)

如果您想了解有关该主题的更多信息,我建议您阅读全文。但现在,让我给您一个快速总结。

双重检查锁定模式用于在多线程环境中实现延迟初始化。在“重量级”检查之前,会在没有同步块的情况下执行“轻量级”检查。仅当两项检查都通过时才会创建资源。

但是,在这种方法中,对象创建是非原子,并且处理器和编译器可以更改操作顺序。因此,另一个线程可能会意外收到部分创建的对象并开始使用它,这可能会导致不正确的行为。这个错误可能很少发生,因此调试对于开发人员来说将是一个巨大的挑战。

这里有一个变化:这种模式直到 JDK 5 才起作用。从 JDK 5 开始,由于 happens-before 原则,引入了 volatile 关键字来解决重新排序操作的潜在问题。分析器警告我们应该添加此关键字。

但是,无论如何,最好避免这种模式。从那时起,硬件和 JVM 性能已经取得了长足的进步,并且同步操作不再那么慢了。然而,不正确地实现 DCL 模式仍然是一个常见的陷阱,可能会产生上述的严重后果。这证实了我们的分析器在旧项目中仍然发现此类疏忽错误的事实。

第5名。微观优化

第五名是另一个 DBeaver 警告,我们专门写了一篇文章。我们来看看:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // 



这里有一个解释:

V6030 无论左侧操作数的值如何,都会调用“&”运算符右侧的方法。也许,最好使用“&&”。 ExasolTableColumnManager.java 79、DB2TableColumnManager.java 77

开发人员将逻辑 && 与按位 & 混淆了。它们具有不同的行为:表达式中的条件在按位 AND 之后不会终止。 短路求值 不适用于按位 AND。因此,即使 exasolTableBase != null 将返回 false,执行线程也会到达 exasolTableBase.getClass() 并导致 NPE。

好吧,这只是一个错字,让我们继续吧,好吗? DBeaver 有很多这样的警告。很多。许多都是相对无害的,但对于好奇的读者,我在下面留下了一些例子:

使用不会导致错误的按位运算
ExasolSecurityPolicy.java:
public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

ExasolConnectionManager.java:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // 



<p>ExasolDataSource.java:<br>
</p>

<pre class="brush:php;toolbar:false">@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}

深入挖掘后,我的团队假设开发人员可能一直在尝试对性能进行微观优化。想了解完整情况,你可以看看我们的文章——这里我总结一下。

关键点是按位运算不依赖于分支预测,与逻辑运算相比,可能允许更快的执行。

令我们惊讶的是,一个本土基准测试支持了这一说法:

Top most intriguing Java errors in 4

图表说明了每种操作类型所需的时间。如果我们相信它,按位运算似乎比逻辑运算快 40%。

我为什么要提出这个话题?强调潜在微观优化的成本。

首先,开发人员发明分支预测是有原因的——放弃它的成本太高。因此,基准测试可能运行得更快,因为值具有正态分布,而在实际情况下不太可能观察到。

第二,放弃短路评估机制会导致成本大幅上升。如果我们看一下上面剧透中的第三个示例,我们可以看到最快的 contains 操作并不是一直执行而不是立即停止。

第三,我们从本章开始就全权处理此类错误。

总体而言,我发现微优化价格的警示故事足以进入我们的前五名。

第四名。不行的话测试也不会落下

自动化测试通常被认为是防止各种错误的最终保障。然而,时不时地,我很想问:“谁自己测试这些测试?”来自 GeoServer 检查的另一个警告再次证明了这一点。这是一个代码片段:

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}

PVS-Studio 警告:

V6060 在验证“e”引用是否为 null 之前,已使用该引用。 ResourceAccessManagerWCSTest.java 186、ResourceAccessManagerWCSTest.java 193

乍一看,这个警告似乎不是分析器最令人兴奋的警告,因为 V6060 经常是针对冗余代码发出的。然而,我承诺我会根据他们的吸引力来选择提名。所以,这个案例远比看上去有趣。

最初,测试逻辑可能看起来是错误的,因为 e 变量是从 catch 运算符获取的,并且进一步保持不变,因此它永远不会为 null。我们可以进行杂散编辑,并将 if(e == nul) 条件的 then 分支删除为无法到达。然而,那是完全错误的。你找到窍门了吗?

关键在于包含异常对象的代码中多了一个变量,它是一个se。它的值会在循环体内发生变化。所以,我们很容易猜测,条件中应该有se变量,而不是e。

这个错误会导致then分支永远不会被执行,所以我们不知道有没有异常。更糟糕的是,在代码审查中很难注意到这样的错误,因为变量名称非常相似。

从这个故事中可以汲取两个智慧:

  1. 清楚地命名变量,即使在测试中也是如此。不然更容易犯这样的错误;
  2. 测试不足以保证项目质量,因为它们也可能包含错误。因此,它会在应用程序内留下漏洞。

由于提供了如此宝贵的课程,我将此警告授予第四名。

第三名。祝各位调试愉快

前三名获胜者属于 NetBeans 检查的警告。之前的代码片段比较紧凑,我们看一下比较长的代码片段:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

最后一次,尝试自己找到错误——我会等...

Top most intriguing Java errors in 4

正在寻找?

不错!那些仅在表达式 iDesc.neighbor != null || 中发现错误的人iDesc.index == iDesc.index,很遗憾,你输了:)

当然,有一个问题,但对于排名第一的问题来说还不够有趣。是的,这里有两个错误,我欺骗了你一点。但是没有一点恶作剧的假期怎么算呢? :)

分析器检测到此处的 i^i 表达式存在错误,并发出以下警告:

V6001 在“^”运算符的左侧和右侧有相同的子表达式“i”。 LayoutFeeder.java 3897

异或运算没有任何意义,因为两个相同值的异或将始终为零。为了快速回顾一下,这里是 XOR 的真值表:

a b a^b
0 0 0
0 1 1
1 0 1
1 1 0

换句话说,只有当操作数不同时,运算才为真。我们将拥有相同的所有位,因为值是相同的。

为什么我这么喜欢这个bug?有 i^1 操作,看起来与 i^i 几乎相同。因此,在代码审查中很容易错过这个错误,因为我们已经在上面看到了正确的 i^1。

我不了解你,但这让我想起了著名的:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

否则,很难解释它是如何进入代码的——除非我们用一个简单的拼写错误来忽略这个无聊的版本。如果您确实发现了该错误,请拍拍自己的背,或者在评论中分享您的侦探技巧:)

第二名。当模式失败时

我已经显示了第一篇和第三篇 DBeaver 文章中的错误,跳过第二篇文章。我纠正了——以下内容仅来自第二篇文章。

PVS-Studio 分析器不喜欢从 TextWithOpen 类的构造函数调用 isBinaryContents,该类在子类中被重写:

@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // ....
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);              // 



<p>那又怎样?它被覆盖了——不过,没什么大不了的。这看起来像是代码味道,没什么关键的。至少,我以前是这么认为的。我专门写了一篇文章来阐述我与这个错误的斗争。</p>

<p>TextWithOpen 有很多子类,其中之一就是 TextWithOpenFile。在那里,该方法实际上被重写,它返回一个超类没有的字段,而不是 false:<br>
</p>

<pre class="brush:php;toolbar:false">@Test
public void testStore() {
  Properties newProps = dao.toProperties();

  // properties equality does not seem to work...
  Assert.assertEquals(newProps.size(), props.size());
  for (Object key : newProps.keySet()) {
    Object newValue = newProps.get(key);
    Object oldValue = newProps.get(key);
    Assert.assertEquals(newValue, oldValue);
  }
}

还有疑问吗?这个类的构造函数是什么样的?

@Override
public BroadphasePair findPair(BroadphaseProxy proxy0, BroadphaseProxy proxy1) {
  BulletStats.gFindPairs++;
  if (proxy0.getUid() > proxy1.getUid()) {
    BroadphaseProxy tmp = proxy0;
    proxy0 = proxy1;
    proxy1 = proxy0;
  }
  ....
}

注意到了吗?调用超类构造函数后,将初始化二进制字段。然而,有一个对 isBinaryContents 方法的调用,它引用了子类字段!

Top most intriguing Java errors in 4

这是 PVS-Studio 警告:

V6052 在“TextWithOpen”父类构造函数中调用重写的“isBinaryContents”方法可能会导致使用未初始化的数据。检查字段:二进制。 TextWithOpenFile.java(77), TextWithOpen.java 59

这是一张相当有趣的图片。乍一看,开发人员似乎遵循了最佳实践:避免无法维护的意大利面条式代码,并尝试通过模板方法模式实现规范的 OOP。但是,即使在实现这样一个简单的模式时,我们也可能会犯错误,这就是所发生的情况。在我看来,这种(错误的)简单之美是稳居第二的。

第一名。一个错误抵消了另一个错误

高兴吧!舞台第一名!竞争很激烈,但必须做出选择。经过深思熟虑,我决定接受 NetBeans 检查中的警告。让我介绍一下最终的代码片段:

public Builder setPersonalisation(Date date, ....) {
  ....
  final OutputStreamWriter
    out = new OutputStreamWriter(bout, "UTF-8");
  final DateFormat
    format = new SimpleDateFormat("YYYYMMdd");
  out.write(format.format(date));
    ....
}

我确信不可能一眼就能发现这样的错误——当然,除非你自己犯过这个错误。我不会让您久等的——这是 PVS-Studio 警告:

V6009 缓冲区容量使用字符值设置为“47”。最有可能的是,“/”符号应该放置在缓冲区中。忽略UnignoreCommand.java 107

事实上,这个错误非常简单:StringBuilder 构造函数没有接受 char 的重载。那么调用什么构造函数呢?开发者显然认为会调用一个接受 String 的重载,然后 StringBuilder 的初始值就是这个斜​​杠。

但是,会发生隐式类型转换,并调用接受 int 的类型构造函数。在我们的例子中,它代表 StringBuilder 的初始大小。将 char 作为参数传递不会在功能上影响任何内容,因为它不会包含在最终字符串中。如果超出初始大小,它只会自行增加,不会导致异常或其他副作用。

但是等等,我提到了两个错误,不是吗?第二个在哪里,它们是如何连接的?为了发现这一点,我们必须读入方法体并了解这段代码的作用。

它生成文件或目录的绝对路径。根据代码,生成的路径应如下所示:

  • 对于文件:/folder1/file
  • 对于目录:/folder1/folder/.

代码看起来非常正确。这就是问题所在。代码确实可以正常工作:)但是如果我们通过用字符串替换字符来修复错误,我们将得到这个而不是正确的结果:

  • /文件夹1/文件/;
  • /文件夹1/文件夹//

换句话说,我们会在字符串末尾得到一个额外的斜杠。它将位于末尾,因为上面的代码每次都会将新文本添加到行的开头。

因此,第二个错误是这个斜杠根本作为参数传递给构造函数。但是,我不会低估这样的错误,因为如果有人决定在不检查的情况下用字符串替换字符,可能会出现问题。

这就是错误顶部的第一个位置转到正确工作的代码的方式。新年奇迹,你期待什么? :)

结论

我希望您喜欢阅读我的错误故事。如果您有任何特别的故事让您印象深刻,或者您有调整排名的建议,请随时在评论中分享您的想法,我会在下次记住它们:)

如果您对其他语言感兴趣,我邀请您在此处查看 2024 年最热门的 C# 错误 - 请继续关注新的热门错误!

所有这些错误都是用PVS-Studio分析器检测到的,最新版本(7.34)刚刚发布!您可以通过此链接尝试一下。

要继续关注有关代码质量的新文章,我们邀请您订阅:

  • PVS-Studio X(推特);
  • 我们的每月文章摘要;

新年快乐!

以上是4 中最有趣的 Java 错误的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的?IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的?Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本启动Spring...

如何优雅地获取实体类变量名构建数据库查询条件?如何优雅地获取实体类变量名构建数据库查询条件?Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架进行数据库操作时,经常需要根据实体类的属性名构造查询条件。如果每次都手动...

如何利用Redis缓存方案高效实现产品排行榜列表的需求?如何利用Redis缓存方案高效实现产品排行榜列表的需求?Apr 19, 2025 pm 11:36 PM

Redis缓存方案如何实现产品排行榜列表的需求?在开发过程中,我们常常需要处理排行榜的需求,例如展示一个�...

Java对象如何安全地转换为数组?Java对象如何安全地转换为数组?Apr 19, 2025 pm 11:33 PM

Java对象与数组的转换:深入探讨强制类型转换的风险与正确方法很多Java初学者会遇到将一个对象转换成数组的�...

如何将姓名转换为数字以实现排序并保持群组中的一致性?如何将姓名转换为数字以实现排序并保持群组中的一致性?Apr 19, 2025 pm 11:30 PM

将姓名转换为数字以实现排序的解决方案在许多应用场景中,用户可能需要在群组中进行排序,尤其是在一个用...

电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品?电商平台SKU和SPU数据库设计:如何兼顾用户自定义属性和无属性商品?Apr 19, 2025 pm 11:27 PM

电商平台SKU和SPU表设计详解本文将探讨电商平台中SKU和SPU的数据库设计问题,特别是如何处理用户自定义销售属...

在Idea中如何设置SpringBoot项目默认运行配置列表以便团队成员共享?在Idea中如何设置SpringBoot项目默认运行配置列表以便团队成员共享?Apr 19, 2025 pm 11:24 PM

在Idea中如何设置SpringBoot项目默认运行配置列表在使用IntelliJ...

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。