• 注册
  • Java 关注:2 内容:3

    烂代码的自我修养

  • 查看作者
  • 打赏作者
  • 拉黑名单
    • 没错,我就是烂代码,自从世界上有了程序的那一天我就存在了, 并且我也将会伴随着程序存在于今后的每一天,就是我让无数阅读我 的程序员焦头烂额,也是我制造出了千奇百怪的 BUG,还是我亲手断 送了一个项目发展壮大的可能性。当然,我也不是凭空产生,我的存 在,要感谢那些糊涂、粗心、随意的程序员,是他们一手造就了我, 成就了我传奇的一生。
      今天,我就讲一讲作为烂代码的心得,如何成为一段优秀的烂代 码。
      首先,起名一定要烂,只要你名字起得烂,那你就真的够优秀。 当然要想名字起得烂,那英文一定要足够烂、想象力也要足够差,这 样你才能不管是在给变量取名字、还是给方法什么的起名字的时候, 大量使用拼音,大量使用 123...这些数字,大量使用 abc... 这些字母,大量的起一些不知所云的词语。当然你也会用 当 to, 用 当 for,毕竟这两个词还是很有难度的、毕竟从两三个字符缩短 到一个字符性能和效率可能有着巨大的提升,哈哈。当然从不输出日 志、从不捕获异常也是很好的。
      其次,结构一定要乱,别管项目结构、模块划分、还是路径包名、 再或者是接口和类的结构,那一定要随心所欲,千万不要有规律,千 万不能讲原则。管他什么低耦合、高内聚,管他什么稳定依赖、依赖 稳定,管他什么可扩展、可复用,什么迪米特、什么里式替换、什么 开闭统统一边玩去,我没听说、我不知道、我不会。哪有什么分层, 当然是想写哪里写哪里,最好是所有逻辑全都写一个方法里,千万不
      要拆分,拆分出来,万一别人复用了可咋整。 最后,性能一定要差,现在的项目,尤其是互联网项目,就喜欢
      提高并发、低延时,所以,性能这东西必须安排的妥妥的。循环那是 必须想方设法的多用用的,最好是一层套一层,一层套一层,一层套 一层,当然循环的最里层,new 几个大对象那也是很有必要的。去数 据库查数据那当然是需要连表的,越多越好,不要少于三个,索引尽 量避开,别管用不用所有字段都给我查了,列表查询最好是在循环里 一条条给我查。锁是个好东西,别管有没有同步的需要,先把锁加上, 各种容器类,优先使用线程安全的,并发包里的一概不考虑。如果你 有写日志的习惯,那也好,不过记得一定要把数据量特别大的给输出 了,对,就是每输出一条本地调试卡半个点的那种。
      当然,上面这三个方面我也不过是简单总结了下,不过你要是能 做好上面的三条,你已经够烂了,只不过可惜的是那样你就烂的太明 显了,估计你的项目经理不答应。所以,作为烂代码,我们的层次还 要提高,要烂的有内涵、烂的有技术,要烂出新高度、烂出一片新天 地。我觉得吧,这事还得在细节上下功夫,下面我就手把手的教你们 一些小技巧。
      1、字符串判空
      我们经常需要判断某个对象的属性是不是 null 或者“”,这时 候记得不要用任何工具类,也不要写成:
      if(user != null && "".equals(user.getId())){
          // 业务逻辑
      }

      这样写不管是 user 是 null 还是 user.getId()是 null都可以
      避免空指针异常,所以你应该写成这样:
      if(user.getId().equals("") && user != null){
          // 业务逻辑
      }

      当然看起来没毛病,也没有编译异常,但是只要 user 是 null 
      者 user.getId()是 null,立刻会出现空指针异常。

      2switchcase 不带 break
      举个例子:
      switch (type) {
          case "alipay":
          transferByAlipay(amount);// 支付宝case "weixin":
          transferByWeiXin(amount);// 微信default:
          transferBydefault(amount);// 默认
      }
      模拟一个转账的场景,你觉得如果 amount 是 10000 元,最终会 转走多少钱呢?如果用的支付宝那你转走了 30000,如果用微信那么 你转走了 20000,如果都不是你转走了 10000。为什么呢,因为没有break,程序不会终止,会执行匹配的那条和它后面的每一条。试想 一下,如果几乎所有场景都是走默认的,那天不小心走了前面两个分 支,那结果...

      3String 的 split 方法
      举个例子,假设下面的这个字符串 str 是由五个属性拼接而来的, 而现在需要拆开成字符串数组 来使用:
            
      String str = ",1,2,3,";
      String[] s = str.split(",");
      那么你认为 里面会有几个元素?当然既不是 个,也不是 个, 其实里面是有 个元素的,第一个逗号前面空字符串会保留,而末尾 的会自动去掉,所以,你要做的就是每次按照 个元素去使用就好了。

      4for 循环你真会用么 比如,有这样一个任务,去遍历一个集合,每次输出一个元素并
      移除它,你会怎么做?
      List<String> list = new ArrayList<>();
      list.add("a");
      list.add("b");
      list.add("c");
      for(String s : list){ 
          System.out.println(s);list.remove(s);
      }
      你可能觉得这样做很好,嗯,但我觉得很不好,因为你运行一遍
      就知道程序会抛异常,也就是所谓的 fail-fast,抛出异常就等于提
      醒你有问题,所以不好,我建议你要这么写:
      for (int i = 0; i < list.size(); i++) {
          System.out.println(list.get(i));
          list.remove(i);
      }

      这么写程序是不会抛出异常的,只是会少输出一个“b”,因为 当你在循环中移除“a”的时候,“b”、“c”相当于各向前移了一 位,“c”变成了第二个元素,你遍历不到“b”了,所以这样写 BUG更隐蔽。不过,只要你不写成这样:
      Iterator<String> it = list.iterator();
      while(it.hasNext()){
          System.out.println(it.next());it.remove();
      }

      我也就放心了。
      同样会有类似问题的,比如ArrayListsubList方法,会得到原 集合的一个子集合,这个子集合仅仅只是原集合的一个视图,不能强 转为ArrayList,并且对原集合元素的增加或删除,均会导致子列表 的遍历、增加、删除产生ConcurrentModificationException 异常; 比如Arrays.asList()返回对象只是一个Arrays内部类,并没有实现 集合的修改方法,只是用了适配器模式,后台的数据仍是数组, 它的addremoveclear方法会抛出UnsupportedOperationException异常。
      5ArrayList 的扩容你了解么
      比如,还是给你个任务,把从 到 10000 的数字放进 ArrayList里,我猜一定会有人这么干:
      List<Integer> list = new ArrayList<>();
      for (int i = 1; i <= 10000; i++) {
          list.add(i);
      }
      你要是这么干的,那我就得表扬你了,你这个干的非常优秀,要 知道ArrayList是会自动扩容的,容量从10开始,每次增加至1.5倍, 这期间会先创建一个新的集合,把旧集合的元素复制过去,这样一来 放10000个元素进去已经扩容了十几次了,那性能肯定差。那么当放 几百万、上千万甚至更多的元素呢?所以,干的漂亮!当然,千万要 注意不要直接给ArrayList一步到位初始化一个合适的容量,或者调
      用它的ensureCapacity方法,那样简直太low了。 记住,类似的还有集合的toArray方法,也不要指定足够的初始
      容量。

      6、看见过类似于这样的一段代码,自己体会
      List<User> list = new ArrayList<>();
      for (int i = 1; i <= 10; i++) {
          for (int j = 18; j <= 60; j++) {
              User user = new User();
              user.setAge(j);
              list.add(user);
          }
          userService.insert(list);
      }
      嗯,可能是觉得不应该在循环里创建大对象。

      7HashSet 怎么用
      这个简单,要知道 HashSet 是在 HashMap 的基础上实现的,
      HashSet 其实就是把元素存在了 HashMap 的 key 里,但是 HashMap key 是可以存 null 的,这下你就懂了吧,用 HashSet 的时候记得往 里面放个 null,等你拿出来用的时候...
      8、常量类你用过么
      有没有遇见过多模块的工程,把 Constant 类放在 Common 模块里, 其他模块引用这个模块,那么你有没有试过,改动了 Common Constant 类中的一个常量后,只重新编译了 Common 模块,没有重新 编译其他模块?你以为用到常量的地方编译后是这样的:
      if (Constant.IS_YES.equals(flag)) {
      }

      会用常量名称占位,然后动态的获取常量的值,事实上用到常量
      的地方编译完是这样的:
      if ("yes".equals(flag)) { }

        其实早就在编译的时候把常量的值替换进去了,所以改了常量
      类,不要重新编译引用它的模块,你懂了吧。
      9、时间戳要统一么
      当然不要统一了,一般我们用的时间戳 java 里大概有两种:
      new Date().getTime();
      System.currentTimeMillis();

      他们的结果是一致的 Long 型的 13 位数字,比如:1566037849986, 但是数据库里的就不一样了,比如 MySQL:
              
        select unix_timestamp();
        select current_timestamp;
      第一种是十位的数字:1566037849;第二种转成数值型的时间戳 的话,会变成 20190817190253 这种格式。所以,多用用不同的时间 戳,在需要比较的时候,就很难比较了。
      10、数据库里的 null 也是个利器 比如 MySQL 数据库:
             
         SELECT * FROM user WHERE name = null;
         SELECT * FROM user WHERE name != null;
         SELECT * FROM user WHERE name <> null;
      这三条 SQL 不管 user 表里有没有 name 为 null 的数据,都不会 查出数据(需要用 is 或者 is not);
               
      SELECT COUNT(*) FROM user;
      SELECT COUNT(name) FROM user;
      这两条 SQL 在 name 有 null 的情况下,返回结果不同(COUNT(name)会忽略 name 为 null 的行);
      SELECT "123" + NULL ;
      SELECT CONCAT("123",NULL) FROM DUAL;
      这两条结果都是 null;
      SELECT * FROM user WHERE name NOT IN (NULL); SELECT * FROM user WHERE name != ‘张三’;
      这两条里,第一条永远不会查到结果,第二条永远查不到 name为 null 的行。至于性能方面,实在太多了,不说了。
      最后,我觉得怎样写出一手优秀且优雅的烂代码还有很多的技 巧,今天只是个开个头,相信经过你不断的学习和努力,尤其是多关 注关注细节、多研究研究原理,你一定成为烂代码中的精英。

      请登录之后再进行评论

      登录
    • 帖子间隔 侧栏位置: