编写高质量代码:改善Java程序的151个建议(第1章:Java开发中通用的方法和准则___建议14~20)...

2023-11-07

作为一个由影视圈转行做Java的菜鸟来说,读书是很关键的,本系列是用来记录《编写高质量代码 改善java程序的151个建议》这本书的读书笔记。方便自己查看,也方便大家查阅。

建议14:使用序列化类的私有方法巧妙解决部分属性持久化问题

建议15:break万万不可忘

建议16:易变业务使用脚本语言编写

建议17:慎用动态编译

建议18:浅谈Java instanceof

建议19:断言绝对不是鸡肋

建议20:不要只替换一个类

建议14:使用序列化类的私有方法巧妙解决部分属性持久化问题

例如:一个计税系统和一个HR系统,计税系统需要从HR系统获得人员的姓名和基本工资,而HR系统的工资分为两部分:基本工资和绩效工资,绩效工资是保密的,不能泄露到外系统。

public class Salary implements Serializable {
    private static final long serialVersionUID = 2706085398747859680L;
    // 基本工资
    private int basePay;
    // 绩效工资
    private int bonus;

    public Salary(int _basepay, int _bonus) {
        this.basePay = _basepay;
        this.bonus = _bonus;
    }
//Setter和Getter方法略
}
public class Person implements Serializable {

    private static final long serialVersionUID = 9146176880143026279L;

    private String name;

    private Salary salary;

    public Person(String _name, Salary _salary) {
        this.name = _name;
        this.salary = _salary;
    }

    //Setter和Getter方法略

}
public class Serialize {
    public static void main(String[] args) {
        // 基本工资1000元,绩效工资2500元
        Salary salary = new Salary(1000, 2500);
        // 记录人员信息
        Person person = new Person("张三", salary);
        // HR系统持久化,并传递到计税系统
        SerializationUtils.writeObject(person);
    }
}
public class Deserialize {
    public static void main(String[] args) {
        Person p = (Person) SerializationUtils.readObject();
        StringBuffer buf = new StringBuffer();
        buf.append("姓名: "+p.getName());
        buf.append("\t基本工资: "+p.getSalary().getBasePay());
        buf.append("\t绩效工资: "+p.getSalary().getBonus());
        System.out.println(buf);
    }
}

af478a1afde9b65ffcc8911a60f2900dc5d.jpg

但这个不符合需求,你可能会想到一下四种解决方案:

1、java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

static修饰的变量也不能序列化。

在bonus前加上关键字transient,使用transient关键字就标志着salary失去了分布式部署的功能,一旦出现性能问题,再想分布式部署就不可能了,此方案否定。

注:分布式部署是将数据分散的存储于多台独立的机器设备上,采用可扩展的系统结构,利用多台存储服务器分担存储负担,利用未知服务器定位存储信息,提高了系统的可靠性、可用性和扩展性。

2、新增业务对象:增加一个Person4Tax类,完全为计税系统服务,就是说它只有两个属性:姓名和基本工资。符合开闭原则,而且对原系统也没有侵入性,只是增加了工作量而已。但是这个方法不是最优方法;

下面展示一个优秀的方案,其中实现了Serializable接口的类可以实现两个私有方法:writeObject和readObject,以影响和控制序列化和反序列化的过程。

public class Person implements Serializable {

    private static final long serialVersionUID = 9146176880143026279L;

    private String name;

    private transient Salary salary;

    public Person(String _name, Salary _salary) {
        this.name = _name;
        this.salary = _salary;
    }
    //序列化委托方法
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        oos.writeInt(salary.getBasePay());
    }
    //反序列化委托方法
    private void readObject(ObjectInputStream input)throws ClassNotFoundException, IOException {
        input.defaultReadObject();
        salary = new Salary(input.readInt(), 0);
    }
}

其它代码不做任何改动,运行之后结果为:

08661a2169c7a43566650ae1fac47f496a5.jpg

这里用到了序列化的独有机制:序列化回调。

Java调用ObjectOutputStream类把一个对象转换成数据流时,会通过反射(refection)检查被序列化的类是否有writeObject方法,并且检查其实否符合私有,无返回值的特性,若有,则会委托该方法进行对象序列化,若没有,则由ObjectOutputStream按照默认规则继续序列化。同样,从流数据恢复成实例对象时,也会检查是否有一个私有的readObject方法,如果有通过该方法读取属性值。

① oos.defaultWriteObject():告知JVM按照默认规则写入对象

② ois.defaultWriteObject():告知JVM按照默认规则读出对象

③ oos.writeXX和ois.readXX

分别是写入和对出响应的值,类似一个队列,先进先出,如果此处有复杂的数据逻辑,建议按封装Collection对象处理。

上面的方式也是Person失去了分布式部署的能了,确实是,但是HR系统的难点和重点是薪水的计算,特别是绩效工资,它所依赖的参数很复杂,计算公式也不简单(一般是引入脚本语言,个性化公式定制)而相对来说Person类基本上都是静态属性,计算的可能性不大,所以即使为性能考虑,Person类为分布式部署的意义也不大。

既然这样,为何不直接使用transient???

建议15:break万万不可忘

建议16:易变业务使用脚本语言编写

比如PHP、Ruby、groovy、JavaScript等

建议17:慎用动态编译

动态编译一直是Java的梦想,从Java6开始支持动态编译,可以在运行期直接编译.Java文件,执行.class文件,并且获得相关的输入输出,甚至还能监听相关的事件。

1、概念

静态编译:一次性编译,在编译的时候把你所有的模块都编译进去。

动态编译:按需编译,程序在运行的时候,用到哪个模块就编译哪个模块。

2、代码实例

public class Ay{
    public static void main(String[] args) throws Exception{
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int flag = compiler.run(null, null, null,"D:\\HelloWorld.java");
        System.out.println(flag == 0 ? "编译成功" : "编译失败");
    }
}

/**
 * D盘放置的类的内容
 */
public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

解释一下:
第一个参数:为java编译器提供参数
第二个参数:得到java编译器的输出信息
第三个参数:接受编译器的错误信息
第四个参数:可变参数(是一个String数组)能传入一个或多个java源文件
返回值:0表示编译成功,非0表示编译失败

3、动态运行编译好的类

public class Ay{
    public static void main(String[] args) throws Exception{
        //获得系统的java编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        //编译文件,编译成功返回 0 否则 返回 1
        int flag = compiler.run(null, null, null,"D:\\HelloWorld.java");
        System.out.println(flag == 0 ? "编译成功" : "编译失败");
        //指定class路径,默认和源代码路径一致,加载class
        URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("file:/d:/")});
        Object printer = classLoader.loadClass("HelloWorld").newInstance();
        System.out.println(printer.toString());

    }
}

运行结果:

编译成功
HelloWorld@4c583ecf

4、慎用动态编译

① 在框架中谨慎使用

比如要在struts中使用动态编译,动态实现一个类,它若继承自ActionSupport就希望它成为一个Action。能做到,但是debug很困难;再比如在Spring中,写一个动态类,要让它注入到Spring容器中,这是需要花费老大功夫的。

② 不要在要求高性能的项目中使用

动态编译毕竟需要一个编译过程,与静态编译相比多了一个执行环节,因此在高性能的项目中不要使用动态编译

③ 动态编译要考虑安全问题

它是非常典型的注入漏洞,只要上传一个恶意Java程序就可以让你所有的安全工作毁于一旦。

④ 记录动态编译过程

建议记录源文件、目标文件、编译过程、执行过程等日志。不仅仅为了诊断,还是为了安全和审计,对Java项目来说,动态编译和运行时很不让人放心的,留下这些依据可以更好地优化程序。

建议18:浅谈Java instanceof

1、instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建的对象时,返回true,否者,返回false。

注:

① 类的实例包含本身的实例,以及所有直接或间接子类的实例

② instanceof左边显示声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否者会编译报错

2、instanceof用法

① 左边的对象实例不能是基本数据类型

6f3272bfdb1c090287463bb4adb590d4c61.jpg

② 左边的对象和右边的类不在同一个继承树上

67efa36fdfa9648a3cc0e29a95c963858e5.jpg

③ null用instanceof跟任何类型比较时都是false

建议19:断言绝对不是鸡肋

1、简介

断言也就是所谓的assert,是jdk1.4中加入的新功能。

他主要使用在代码开发和测试阶段,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出。

当软件正式发布后,可以取消断言部分的代码。

2、语法:

assert<布尔表达式>

assert<布尔表达式> : <错误信息>

在布尔表达式为假时,跑出AssertionError错误,并附带了错误信息。

assert的语法比较简单,有以下两个特性:

① assert默认是不开启的

cde470b73d4bc7b404a406f78b6ca53ee2b.jpg

0307f0638093822c376fc439f86f44d9a41.jpg

然后再VM栏里输入-enableassertions或者-ea即可

② assert跑出的异常AssertionError继承自Error

断言失败后,JVM会跑出一个AssertionError的错误,它继承自Error,这是一个错误,不可恢复。

3、assert的使用禁忌

① 对外的公开方法中不可使用

防御式编程最核心的部分就是:所有的外部因素(输入参数、环境变量、上下文)都是“邪恶”的,都存在着企图摧毁程序的罪恶本源,为了抵制它,我们要在程序处处设置合法性检查,不满足条件就不执行后续程序,以保护后续程序的正确性,但此时不能用断言做输入检查,特别是公开方法。

② 在执行逻辑代码的情况下

assert的支持是可选的,在开发时运行,生产环境下停止运行即可,因此在assert的布尔表达式中不能执行逻辑代码,否者会因为环境的不同产生不同的逻辑。

    public void doSomething(List list, Object element) {
        assert list.remove(element) : "删除元素" + element + "失败";
        /*业务处理*/
    }

这段代码在assert启用的环境下,没有任何问题,但是一旦投入生产环境,就不会启用断言了,这个方法就彻底完蛋,list的删除动作永远不会执行,永远不会报错或异常,因为根本没有执行!

4、assert的使用场景

按照正常的执行逻辑不可能到达的代码区域可以使用assert。

① 在私有方法中放置assert作为输入参数的校验

私有方法的使用者是自己,是自己可以控制的,因此加上assert可以更好地预防自己犯错或者无意的程序犯错。

② 流程控制中不可能到达的区域

程序执行到assert这里就是错误的

③ 建立程序探针

我们可能会在一段程序中定义两个变量,分别代两个不同的业务含义,但是两者有固定的关系,例如:var1=var2 * 2,那我们就可以在程序中到处设"桩"了,断言这两者的关系,如果不满足即表明程序已经出现了异常,业务也就没有必要运行下去了。

建议20:不要只替换一个类

注意:发布应用系统时禁止使用类文件替换方式,整体WAR包发布才是万全之策。

 

编写高质量代码:改善Java程序的151个建议@目录

转载于:https://my.oschina.net/u/4006148/blog/3072740

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编写高质量代码:改善Java程序的151个建议(第1章:Java开发中通用的方法和准则___建议14~20)... 的相关文章

随机推荐

  • 按钮卡片特效代码集锦

    css最好看最全的按钮卡片样式 动画效果大全 纯css样式打造的20款按钮特效和11款卡片合集 喜欢的可以收藏 备开发时使用 按钮代码
  • 机器学习模型评估指标

    在机器学习建模过程中 针对不同的问题 需采用不同的模型评估指标 主要分为两大类 分类 回归 一 分类 1 混淆矩阵 2 准确率 Accuracy 3 错误率 Error rate 4 精确率 Precision 5 召回率 Recall 6
  • 【6】测试用例设计-输入域+输出域+异常分析+错误出错法

    目录 输入域测试 输出域测试 异常分析 错误猜测 输入域测试 极端测试如学生成绩0分 1分 2分 情况很少这种 特殊值如 99 0 99 长时间输入内存溢出 内存泄露 输出域测试 异常分析 异常操作验证系统容错性 出现错误时 故障恢复的能力
  • 泛型和包装类

    1 泛型 1 1泛型的定义 泛型是程序设计语言的一种特性 允许程序员在强类型程序设计语言中编写代码时定义一些可变部分 那些部分在使用前必须作出指明 各种程序设计语言和其编译器 运行环境对泛型的支持均不一样 将类型参数化以达到代码复用提高软件
  • 构建seq2seq模型的常见问题

    1 seq2seq模型 输入是一个词向量 而不是词向量列表 对吧 是的 对于seq2seq模型 输入和输出都需要被转换成词向量形式 对于输入来说 通常会将一个句子转换成一个词向量序列 具体地 对于每个单词或者字符 都会将其对应成一个词向量
  • 广联达C++一面(坐等感谢信)

    括号中是我的回答 自我介绍 介绍一个最近做过的项目 不需要非得是C 相关的 回答了节点属性标注系统 在里面负责哪一部分 介绍了三个部分 说了自己负责的是统计和二阶段预测 主要是用主动探测的方法扩展节点信息 随机性检测是怎么做的 用的什么信息
  • Java EE学习笔记(1:Servlet & JSP)

    Servlet简介 Servlet技术规范是JavaEE技术规范中的一个重要组成部分 Servlet是一种独立于平台和协议的服务器端的Java应用程序 可以生成动态的Web页面 实际上 Servlet不仅仅是用于返回HTML的页面的 比如
  • Python_FontTools使用

    目录 Font Tools的使用 1 fontTools使用总结 2 加载字体文件 3 保存为xml文件 4 获取各节点名称 返回为列表 5 获取getGlyphOrder节点的name值 返回为列表 6 获取cmap节点code与name
  • 51单片机有几个通用io口_我在职高教单片机——02零基础学51单片机IO口(1)

    大家好 我是老王 职高老师一枚 一直从事单片机 计算机 电子技术基础等课程的教学 对于职高的学生层次 同行应该都懂的 老师在课堂上教学几乎是没什么成就感的 正是如此 才有了借助头条平台寻求认同感和成就感的想法 在这里 我准备陆续把自己花了很
  • 程序是如何从编辑到执行的——我的初步理解

    目录 1 预处理 2 编译 3 汇编 3 1 机器码的格式 3 2 编码过程 4 链接 5 取指 6 译码 7 执行 8 访存 9 写回 10 更新PC 今天来谈谈 一句代码 是如何从被编辑到被执行的 以下为本人在阅读相关书籍资料后的初步理
  • IDEA项目上传至git常见问题

    问题一 Push rejected Push to origin master was rejected 方案一 git pull origin master allow unrelated histories 打开git命令行窗口 依次输
  • 数据挖掘:基本概念理解

    定义 数据挖掘 从大量数据中挖掘有趣模式和知识的过程 一 知识发现过程 1 数据预处理 1 数据清理 消除噪声和删除不一致数据 2 数据集成 多种数据源组合在一起 3 数据选择 从数据库中提取与分析任务相关的数据 4 数据变换 通过汇总或聚
  • 卸载掉360之后无法删除360safe文件夹解决办法!

    卸载掉360之后无法删除360safe文件夹解决办法 问题是这样的 选择 360safe 文件夹 右键选择 删除 结果如下 一开始觉得是360未卸载干净 比如 360主动防御模块 之类的 但是 打开任务管理器一看 没有与360相关的东东了呀
  • 紫光云服务器芯片,紫光云与新华三半导体共建芯片设计云2.0 携手打造一站式云端芯片平台...

    原标题 紫光云与新华三半导体共建芯片设计云2 0 携手打造一站式云端芯片平台 2021年是芯片设计云元年 行业需求呈现井喷态势 中国云服务新势力脱颖而出 在世界舞台崭露头角 为中国集成电路行业发展插上了隐形翅膀 近日 紫光云就正式通过三星F
  • 【牛客网 - 华为机试】HJ1 字符串最后一个单词的长度

    HJ1 字符串最后一个单词的长度 题目描述 计算字符串最后一个单词的长度 单词以空格隔开 输入描述 输入一行 代表要计算的字符串 非空 长度小于5000 输出描述 输出一个整数 表示输入字符串最后一个单词的长度 示例1 输入 hello n
  • redis输入密码去除提示Warning: Using a password redis输入密码去除提示Warning: Using a password...

    2019独角兽企业重金招聘Python工程师标准 gt gt gt redis输入密码去除提示Warning Using a password hello运维 百家号11 0510 41 问题现象 redis的监控 经常使用zabbix 通
  • MVC框架理解(整理)

    MVC是三个单词的首字母缩写 它们是Model 模型 View 视图 和Controller 控制 视图 视图 View 代表用户交互界面 对于Web应用来说 可以概括为HTML界面 但有可能为XHTML XML和Applet 随着应用的复
  • 通过命令行关闭Bitlocker

    cmd中输入以下命令关闭 manage bde off C 但是有时候出现如下提示 此时需要先执行如下命令 系统分区不是C的话更改下面的盘符 manage bde autounlock ClearAllKeys c 然后再执行即可 mana
  • 干货来了!!简单操作让你的GitHub格子重新绿起来

    自从得知了GitHub这个网站 我就开始把自己的代码每天提交上去 看着小绿格子慢慢的特别有成就感 事故 发生了 就在上周一之后我的小绿格子突然不变色了 我一直以为是学校网太差 一周过去了总觉得不对劲 专门找时间查了一下 经过在网上搜索 我的
  • 编写高质量代码:改善Java程序的151个建议(第1章:Java开发中通用的方法和准则___建议14~20)...

    作为一个由影视圈转行做Java的菜鸟来说 读书是很关键的 本系列是用来记录 编写高质量代码 改善java程序的151个建议 这本书的读书笔记 方便自己查看 也方便大家查阅 建议14 使用序列化类的私有方法巧妙解决部分属性持久化问题 建议15