• 谜题73 : 你的隐私正在公开


    私有成员,即私有方法、域和类型这些概念的幕后思想是它们只是实现细节:一个类的实现
    者可以随意地添加一个新的私有成员,或者修改和移除一个旧的私有成员,而不需要担心对
    该类的客户造成任何损害。换句话说,私有成员被包含它们的类完全封装了。
    遗憾的是,在这种严密的盔甲保护中仍然存在细小的裂缝。例如,序列化就可以
    打破这种封装。如果使一个类成为可序列化的,并且接受缺省的序列化形式,那

    么该类的私有实例域将成为其导出 API 的一部分[EJ Item 54,55]。当客户正在
    使用现有的被序列化对象时,对私有表示的修改将会导致异常或者是错误的行
    为。
    但是编译期的错误又会怎么样呢?你能否写出一个 final 的“库”类和“客
    户”类,这两者都可以毫无问题地通过编译,然后在库类中添加一个私有成员,
    使得库类仍然能够编译,而客户类却再也不能编译了?
    如果你的解谜方案是要对库类添加一个私有构造器,以抑制通过缺省的公共构造
    器而创建实例的行为,那么你只是一知半解。本谜题要求你添加一个私有成员,
    严格地讲,构造器不是成员[JLS 6.4.3]。
    本谜题有数个解谜方案,其中一个是使用遮蔽:

    package library;
    public final class Api {
    // private static class String{ }
    public static String newString() {
    return new String();
    }
    }
    package client;
    import library.Api;
    public class Client {
    String s = Api.newString();
    }
    

      


    如上编写,该程序就可以毫无问题地通过编译。如果我们不注释掉 library.Api
    中的局部类 String 的私有声明,那么 Api.newString 方法就再也不会返回
    java.lang.String 类型了,因此变量 Client.s 的初始化将不能通过编译:

    client/Client.java:4: incompatible types
    found: library.Api.String, required: java.lang.String
    String s = Api.newString();
    

      


    尽管我们所做的文本修改仅仅是添加了一个私有类声明,但是我们间接地修改了
    一个现有公共方法的返回类型,而这是一个不兼容的 API 修改,因为我们修改了
    一个被导出 API 所使用的名字的含义。
    这种解谜方案的数个变种也都可以实现这个目的。被遮蔽类型也可以来自一个外
    围类而不是来自 java.lang;你可以遮蔽一个变量而不是一个类型,而被遮蔽变
    量可以来自一个 static import 声明或者是来自一个外围类。

    不修改类库的某个被导出成员的类型也可以解决本谜题。下面就是这样的一个解
    谜方案,它使用的是隐藏而不是遮蔽:

    package library;
    class ApiBase {
    public static final int ANSWER = 42;
    }
    public final class Api extends ApiBase() {
    // private static final int ANSWER = 6 * 9;
    }
    package client;
    import library.Api;
    public class Client {
    int answer = Api.ANSWER;
    }
    


    如上编写,该程序就可以毫无问题地通过编译。如果我们不注释掉 library.Api
    中的私有声明,那么客户类将不能通过编译:

    client/Client.java:4: ANSWER has private access in library.Api
    int answer = Api.ANSWER;
    


    这个新的私有域 Api.ANSWER 隐藏了公共域 ApiBase.ANSWER,而这个域本来是应
    该被继承到 Api 中的。因为新的域被声明为是 private 的,所以它不能被 Client
    访问。这种解谜方案的数个变种也都可以实现这个目的。你可以用隐藏一个实例
    域去替代隐藏一个静态域,或者用隐藏一个类型去替代隐藏一个域。
    你还可以用遮掩来解决本谜题。所有的解谜方案都是通过重用某个名字来破坏客
    户类。重用名字是危险的;应该避免隐藏、遮蔽和遮掩。是不是对此已经耳熟能
    详了?很好!

  • 相关阅读:
    犀牛书学习笔记(2):对象和数组
    犀牛书学习笔记(1):语法结构、数据类型和值、表达式和运算符
    小学了一下css hack
    git学习系列--六分之一
    稍览了一下CommonJS
    意识流_六分之一
    两升的心思系列之-----粒子的预备
    mybatis_延迟加载
    mybatis_动态SQL
    mybatis_mapper动态代理
  • 原文地址:https://www.cnblogs.com/yuyu666/p/9841019.html
Copyright © 2020-2023  润新知