为什么java.net.URL是final的?

7

我想从java.net.URL继承,但是它是final的,所以我无法这样做。我遇到了以下错误:

cannot inherit from final java.net.URL

为什么它是final的?

一些关于String is final状态的问题的答案表明,JVM依赖于String,因为它是一种非常基本的数据类型。我不明白这如何适用于URL类型。

更新

许多答案和评论都给出了避免继承的原因。有些人甚至认为所有东西都应该是final的。但现实情况却不同。JDK中的大多数类都不是final的。遵循良好的实践是一个原因,为什么一个人不应该从java.net.URL继承。这甚至可能适用于JDK中的任何类。但当某个东西被定义为final时,就不能继承。这仅适用于一些非常少的类,如String或URL。

因此,在URL和所有其他非final类之间必须有一些不同之处。问题是:是什么差异使得URL必须是final的?

说我不应该关心这个问题,因为我不应该继承,这不是问题的答案。


1
我更想问你为什么想要继承它。了解你在继承背后的目的会很有趣。 - Fritz
@Gamp:我想将其封装以实现特定项目的错误处理。 - ceving
那我建议你像Matt建议的那样编写一个包装类。 - Fritz
1
它是final的,这意味着你不能从它继承。这就是final的含义,没有其他意思。 - user207421
@EJP:我知道我不能继承。问题是为什么我不能继承。而且我的意思是必须,而不是应该。 - ceving
我想创建不同的构造函数... - marcolopes
3个回答

7

它被专门设计为防止您进行扩展。 无论它在内部如何工作, 它都不是为了继承而设计的,因此作者防止您这样做。

请记住Effective Java:

  • 第17项:“为继承设计和记录文档,否则禁止继承。”
  • 第16项:“优先使用组合而非继承。”

因此,不要继承URL而是创建一个具有URL字段的类。


1
为了澄清,String是为了安全原因而设为final的。我想URL也是出于同样的原因而被设为final的。 - Wug
3
“出于安全原因”,这句话用来解释为什么String是final的范围非常广泛。不可变性还可以提高性能,并使代码更容易理解。 - Matt Ball
@MattBall:我问了一个原因。引用别人关于良好编程风格的想法并不是一个理由。它解释了为什么将其设置为final可能是个好主意,但它并没有解释为什么这样做,因为许多其他类并没有这样做。是什么使java.net.URL与java.io.BufferedInputStream不同? - ceving
1
引用《Effective Java》(尽管它有缺陷)通常是一种有效的方式,可以呈现出一个精心设计的论点,很少有堆栈溢出者能够达到这样的质量。 - Tom Hawtin - tackline
1
@MattBall 在阻止不明智的快捷方式方面很有用,例如不必要地共享可变对象。 - Tom Hawtin - tackline
显示剩余5条评论

2

我猜测这是出于安全考虑。如果一个方法需要URL对象作为参数,并且您可以扩展URL类以执行URL类不应该执行的操作,则可能会存在安全问题。


如果这是正确的,java.io.BufferedInputStream也必须是final的,因为它通常用于读取HTTPS响应的内容。如果您遭受恶意代码注入,Java的“final”无法解决您的问题。 - ceving
1
@ceving URLBufferedInputStream类在Java中的性质上是完全不同的。URL是一个值对象,而BufferedInputStream是一个引用对象。你可以存储和信任一个URL。例如,对于URL[]File,你需要复制它们——这是不可靠和糟糕的设计。InputStream对象能力。你不想小心翼翼地让特定的实例被谁拥有。 - Tom Hawtin - tackline
@ceving 我认为他们想要设计API时可以信任的东西。如果URL是安全的,他们可以编写代码来安全地获取输入流。我不会期望有人会创建一个接受BufferedInputStream的API,因为他们知道它可能被扩展以执行恶意操作。如果除了字符串以外没有类是final的话,那么所有的API都应该只接受字符串作为它们的参数。例如,File类并不是final的,因此很少见到servlet API会将File对象作为其参数之一。 - gigadot
@gigadot 实际上 InputStream 可以被覆盖以执行恶意操作 - 通常会获取传递给 read 方法的 byte[] 并在其用于其他事情时读取/修改它。 File 通过 Java SE API 传递(太多了),并且已成为漏洞的主题(TOCTOU/TOC2TOU - 在由覆盖的 File.getPath 返回的不同值之间进行检查时间和使用时间)。 - Tom Hawtin - tackline

0

据我所知,URL是IETF RFC标准的完整实现,因此不建议您进行更改或扩展。


1
RFC并非永恒不变的。如果您查看RFC 3986,您会发现以下标签:更新:1738;废弃:2732、2396、1808。 - ceving
InetAddress是RFC事物的一个例子,已经被扩展(用于IPv6),尽管您仍然无法扩展它。 - Tom Hawtin - tackline

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接