【问题标题】:final String class vs final methods of Non-final String class最终字符串类与非最终字符串类的最终方法
【发布时间】:2014-08-26 10:45:49
【问题描述】:

我知道出于安全和性能相关的原因,java.lang.String 类被声明为 final。

我不明白的是,是否可以使用所有最终变量和最终方法而不是声明最终类来实现相同的目的?

简而言之,下面两个代码 sn-ps 有什么区别.. 例如

public class final String { .. } 

v/s

// non final class
public class String {

// all final variables
private final char[] value;

// all final methods
public final String subString() { .. }
public final int length() { return value.length;}

// etc
}

编辑

简单来说,我可以通过使用任何一种方法来实现相同级别的不变性吗?它们都可以使对象不可变吗?

【问题讨论】:

  • 好吧,你不希望变量final 从那时起它们只能有一个不可变的值。但问题仍然存在于方法上。
  • 是的,完全正确。我在 Google 搜索中找不到此问题的有效答案。
  • @All Check 已编辑问题部分。
  • 阅读@shay 对this answer的第一条评论...
  • 这与询问静态类是否可以实现与静态变量相同的效果相同-它们完全不同!

标签: java string immutability final


【解决方案1】:

final 类不能扩展。

具有最终方法的非最终类,可以扩展(可以在子类中添加新方法),但不能覆盖现有的最终方法。

【讨论】:

    【解决方案2】:

    类的最终修饰符并不意味着属性也是最终的和不可变的。这只是意味着该类不再是父类。无法扩展最终类。

    Use of final class in Java

    【讨论】:

    • 查看我编辑的问题。我在问一些不同的事情。
    • @peeppeep 我不知道 Java 的 String 类的细节,但一般来说:100% final 方法和 final 方法之间没有 巨大 区别班级。然而,将一个类声明为 final 清楚地表明了意图(即不应扩展该类)。
    • @peepeep,哦,那我还是没 100% 理解你。如果您想实现不变性,那么我仍然认为您不能通过将类设置为 final 来做到这一点。
    【解决方案3】:

    Final 会根据你使用它的位置而产生不同的效果。

    如果一个类是最终的,它不能被子类化。

    如果一个方法是它的最终方法,则它不能被任何子类覆盖。

    如果一个变量是它的final,它只能被初始化一次,使其成为常量。

    【讨论】:

      【解决方案4】:

      你忘记了构造函数。构造函数不能是最终的。为了避免 Is-A 情况。

      如果你没有将类标记为 final,尽管你的所有方法都是最终的,但我们仍然可以扩展 String,并且可以欺骗线程,声称我的类也是 String 类。

      如果 String 本身不是最终的,方法是最终的,那么下面是合法的,这会导致混乱

      public class MyUnTrustedClass extends String {
      
         void MyUnTrustedClass(String virus){
              super(virus);
           }
      
      }
      

      【讨论】:

      • 为什么你假设的MyUnTrustedClass 会造成混乱(提醒:我们假设所有String 方法都是final)?
      • @JeanHominal:我认为这个例子不好;更好的问题可能是构造函数采用char[] 并直接存储引用。
      • @supercat:这不是一个更好的例子。这里的症结在于构造函数链接和方法覆盖之间有很大的区别——也就是说,只有方法覆盖对于调用者来说是静默发生的。因此,我确认,在String所有 方法都是final 的假设下,并且在不使用反射的假设下,MyUnTrustedClass 不会造成String 的任何损害不会。
      • @JeanHominal: 如果采用char[] 并直接存储它的构造函数是protected 而不是privateMyUntrustedClass 可能会通过传递一个数组来创建一个真正的混乱会暴露在外面的世界。 final 类的构造函数是 protected 还是 private 的问题当然是没有实际意义的,但如果类不是 final,那么构造函数是 protected 将同时具有实质性的可用性优势和安全性风险。
      • @supercat:我想我应该更明确一点,我正在考虑对 String 的唯一修改是:1 - 从类声明中删除 final 和 2 - 添加 @987654341 @ 到每个方法声明。我认为这是我们正在比较的两种设计。
      【解决方案5】:
      public final String { ... }
      public ExtendedString extends String { ... } // compiler error
      

      public String { ... }
      public ExtendedString extends String { ... } // fine
      

      多一点解释:

      使类最终化也隐含地使所有方法最终化,但也禁止扩展类。仅将方法设为 final 仍然允许扩展类,并且仅禁止覆盖 final 方法。在后一种情况下,您仍然可以在扩展类中声明全新的方法。

      具有所有最终方法的非最终 String 类仍然是不可变的(如果所有其他不变性方面都适用)。请注意,使类最终成为使其不可变的努力要少得多。事实上,如果设计正确,即使是非 final 类也可以是不可变的。

      【讨论】:

      • 我能理解。但是两种方法之间的逻辑原因或区别是什么?我们可以用一种方法代替另一种方法来实现字符串的不变性吗?
      • 不,public final String 并不意味着 String 是不可变的。
      • @treeno 我从来没有这么说过。其实我说的正好相反。
      • @Seelenvirtuose 我正在和 peepeep 说话(写作)。我想说的是:不,我们不能用一种方法代替另一种方法来实现字符串的不变性。
      【解决方案6】:

      我不明白的是相同目的是否可以 使用所有最终变量和最终方法而不是 声明最终类?

      将类声明为 final 的目的与将类中的所有方法声明为 final 的目的不同。

      最后的课程

      当我将一个类 A 声明为 final 时,我表示这样的类是 frozen,它不是用于扩展,也不是可以以任何其他方式使用,而不是在其结构和声明中表达。 A 类由于是最终的,因此无法细化。

      注意 1: 无论您需要在哪里使用 A 类型的东西,都有一个,并且只有一个类,一种类型,即您可以使用的 A。根据您追求的任何设计或根据您的要求,限制是故意的。

      java.lang.System 类就是一个很好的例子。在运行时有一种类型的系统。一个人可以有该系统的多个实例,(或者,如在 Java 中,系统的一种单例包装器。)但是类型,该 System 类型的功能是唯一的。

      具有最终方法的类

      另一方面,具有所有方法的类 A final 只是说无论我提供给你什么,你都不能扩展。不过,您可以为我的扩展添加更多功能或状态。

      注意 2: 无论您需要使用不是 final 的类 A,但如果不是所有方法都作为 final,您可以使用 A 的实例或它的子类。您可以在不同的上下文中使用 A 的子类,但类型 A 提供的核心功能不会改变。

      我无法想象一个所有方法都是最终方法的好例子。我通常看到的是final 方法与abstract 方法的组合。在这种情况下,final 方法实现算法,其细节通过抽象方法扩展。

      考虑一个访问监视器类的缺乏想象力且相当冗长(故意)的示例,该示例冻结主要授权逻辑(即,除非该用户被授权,否则对任何人抛出异常。)进一步的细节是留“空白”,由专业类填充。

      class AccessMonitor {
          abstract AuthorizationMechanism getUnderlyingMechanism();
      
          final void checkAccess(User user)
          {
               AuthorizationMechanism mechanism = getUnderlyingMechanism()
               if( mechanism.noAccess(user) )
               {
                   throw someSecurityException("blah");
               }
               // otherwise, happy camper
          } 
      }
      
      class LdapBasedMonitor extends AccessMonitor {
          final AuthorizationMechanism getUnderlyingMechanism()
          {
             return someLdapThingieMajingie;
          } 
      }
      
      class DbBasedAuthenticator extends Authenticator {
          final AuthorizationMechanism getUnderlyingMechanism()
          {
             //query some database and make a decision, something something.
          } 
      }
      

      在现实生活中,我们通常会看到通过继承进行纯细化的其他替代方案 - 组合和委托会浮现在脑海中。事实上,在一般情况下,人们更喜欢组合/委托而不是继承。

      然而,这完全是一个不同的主题(值得一两本书),基于继承的示例在这种情况下更适合在这种情况下来说明具有 final 的非 final 类的意义方法。

      最后一个示例说明了拥有一个所有方法都是最终方法的非最终类背后的想法(即使该示例为抽象方法留出了空间。)与同一响应中的前一个相比,我希望您能看到两者背后的意图不同。

      【讨论】:

      • “在这种情况下,最终方法实现的算法的细节通过最终方法扩展。”你不是说“抽象方法”吗?
      • 哦,是的,正确的。我会相应地修复我的帖子。感谢您的关注!
      【解决方案7】:

      简单来说,我可以通过去实现相同级别的不变性吗? 用任何一种方法?它们都可以使对象不可变吗?

      没有。如果 String 类不是最终的,那么有人可以扩展 String 并创建一个可变的子类。所有 Java 开发人员都将失去假定字符串是线程安全的能力。

      【讨论】:

        【解决方案8】:

        这取决于我们如何定义不可变。如果

        1. 对象的所有字段都是最终的,并且那些已知的引用类型指向不可变类
        2. 对象的 observable 状态不能改变,其中 observable 定义为
          1. 可通过公共方法访问,或
          2. 可通过某些特定类的公共方法访问

        如果我们使用定义 1,则类本身必须是最终的,否则子类可能会声明一个新的非最终字段。

        如果我们使用定义 2.1,类本身也必须是 final 的,否则它可以用 getter 和 setter 声明一个非 final 字段。

        如果我们使用定义 2.2,那么如果每个字段都是最终的或私有的,每个方法都是最终的或私有的,并且类的所有现有方法都保持不变性就足够了。 (如果我们知道要密封的包,并且已经验证包中的所有其他代码都保持不变性和封装性,我们可能会放宽这一点,以包括受包保护的字段和方法。

        为了 Java 平台的安全性,定义 2.2 和上面概述的实现方法就足够了。然而,与将类本身声明为 final 相比,通信、实现和验证要复杂得多。由于违反该不变量会损害平台的安全性,因此采用更简单、更可靠的方法似乎是明智的。

        【讨论】:

          【解决方案9】:

          Final 就像 C++ 中的 const 一样,因此你不能改变它的状态。 不可变是 String 类的内部功能。 您可以做的是使用 StringBuilder 来实现它。 每当您为其分配新值时,不可变对象都会在 RAM 中创建一个新实例。我从不改变它的状态,因此创建了一个新的实例,它被称为通过引用传递

          【讨论】:

          • 据我了解,这不是真的(第一句话)或仅适用于原始变量(我不知道,因为我对 C/ 的内部工作原理都不太熟悉C++ 或 Java)并且没有回答问题(通过使类最终化与使所有方法最终化可以达到相同的效果吗?)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-24
          • 1970-01-01
          相关资源
          最近更新 更多