Everybody wants go to heaven, but nobody wants to die.

Java 标签

Hutool简介

Hutool是一款小而全的开源开发工具类库,在github上拥有将近一万九的star,基本上你能想到的开发当中常需要用到的小轮子,基本上都有具备。学习一下hutool工具包可以避免我们在平常开发中重复造轮子。这款hutool开源库,更新频率快,jar包小仅1.5Mb。对比其他同款类型的基础工具类库来说,是一大优势。因为其他同款类型的基础工具类库多多少少都有基于apache commons做了一些封装。

对于很多小型公司来说,公司内部并没有完善的基础工具类库,使用hutool可以节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务同时可以最大限度的避免封装不完善带来的bug。

hutool github地址

Hutool主要组件

模块介绍
hutool-aopJDK动态代理封装,提供非IOC下的切面支持
hutool-bloomFilter布隆过滤,提供一些Hash算法的布隆过滤
hutool-cache简单缓存实现
hutool-core核心,包括Bean操作、日期、各种Util等
hutool-cron定时任务模块,提供类Crontab表达式的定时任务
hutool-crypto加密解密模块,提供对称、非对称和摘要算法封装
hutool-dbJDBC封装后的数据操作,基于ActiveRecord思想
hutool-dfa基于DFA模型的多关键字查找
hutool-extra扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等)
hutool-http基于HttpUrlConnection的Http客户端封装
hutool-log自动识别日志实现的日志门面
hutool-script脚本执行封装,例如Javascript
hutool-setting功能更强大的Setting配置文件和Properties封装
hutool-system系统参数调用封装(JVM信息等)
hutool-jsonJSON实现
hutool-captcha图片验证码实现
hutool-poi针对POI中Excel的封装
hutool-socket基于Java的NIO和AIO的Socket封装

hutool库的工具类非常全,一篇文章难以概括。以下就列举开发中常用到的工具类。

2020-04-02 0 评论 9 浏览
阅读全文

整理自《编写高质量代码:改善Java程序的151条建议》

一、Java开发中通用的方法和准则

  • 不要在常量和变量中出现易混淆的字母;
  • 莫让常量蜕变成变量;
  • 三元操作符的类型务必一致;
  • 避免带有变长参数的方法重载;
  • 别让null值和空值威胁到变长方法;
  • 覆写变长方法也要循规蹈矩;
  • 警惕自增的陷阱;
  • 不要让旧语法困扰你;
  • 少用静态导入;
  • 不要在本类中覆盖静态导入的变量和方法;
  • 养成良好习惯,显式声明UID;
  • 避免用序列化类在构造函数中为不变量赋值
  • 避免为final变量复杂赋值;
  • 使用序列化类的私有方法巧妙解决部分属性持久化问题;
  • break万万不可忘;
  • 易变业务使用脚本语言编写;
  • 慎用动态编译;
  • 避免instantceof非预期结果;
  • 断言对决不是鸡肋;
  • 不要只替换一个类;

二、基本类型

  • 使用偶判断,不用奇判断;
  • 用整数类型处理货币;
  • 不要让类型默默转换;
  • 边界,边界,还是边界;
  • 不要让四舍五入亏了一方;
  • 提防包装类型的null值;
  • 谨慎包装类型的大小比较;
  • 优先使用整型池;
  • 优先选择基本类型;
  • 不要随便设置随机种子;

三、类、对象及方法

  • 在接口中不要存在实现代码;
  • 静态变量一定要先声明后赋值;
  • 不要覆写静态方法;
  • 构造函数尽量简化;
  • 避免在构造函数中初始化其他类;
  • 使用构造代码块精炼程序;
  • 使用静态内部类提供封装性;
  • 使用匿名类的构造函数;
  • 匿名类的构造函数很特殊;
  • 让多重继承成为现实;
  • 让工具类不可实例化;
  • 避免对象的浅拷贝;
  • 推荐使用序列化实现对象的拷贝;
  • 覆写equals方法时不要识别不出自己;
  • equals应该考虑null值情景;
  • 在equals中使用getClass进行类型判断;
  • 覆写equals方法必须覆写hashCode方法;
  • 推荐覆写toString方法;
  • 使用package-info类为包服务;
  • 不要主动进行垃圾回收;

2019-12-31 0 评论 194 浏览
阅读全文

  • 尽可能使用局部基本数据类型变量。
  • 及时关闭流。
  • 尽可能多使用三目运算符,代码看起来会比较清晰
  • 尽量减少对变量的重复计算。
  • 尽量采用懒加载的策略,即在需要的时候才创建。
    如果已知列表的长度,为底层以数组方式实现的集合、工具类指定初始长度。
  • 基于效率和类型检查的考虑,应该尽可能使用Array,无法确定数组大小时才使用ArrayList。
  • 当复制大量数据时,使用System.arraycopy()命令。
  • 乘法和除法可以使用移位操作进行优化。除非所需性能要求高,不然不必用移位来进行优化。例如我们平常的业务开发使用移位进行优化,反而使得代码的可读性降低。
  • 接口中的方法、属性尽量不要加任何访问修饰符。依据Alibaba的java代码规范。
  • 不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
  • 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
  • 尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销。
  • 不要将数组声明为public static final。否则不管这个数组有没有使用大,JVM都会为这个数组保留内存空间。
  • 尽量在合适的场合使用单例。
  • 尽量避免随意使用静态变量。

2019-12-30 0 评论 47 浏览
阅读全文

之前读完《重构,改善代码既有设计》一书,书中内容虽然简单,但却有效。一些小小的重构积累起来便使得我们的代码开始变得更优雅。印象很深的是作者倡导的事不过三的原则,代码出现三次重复即需要进行重构。我深有体会,经常觉得自己的某些设计不好,但是又跟自己说下次再一起重构吧?结果日复一日需要重构的设计越来越多,导致重构的成本越高,越不敢重构。这篇文章再温习一下重构一书中的内容。

尽信书不如无书。书中只是建议并非标准,有些时候我们需要具体情况具体分析。

代码的坏味道

1. 重复代码

重复代码意味着冗余,当重复的代码需要修改时要修改所有重复的地方,稍微疏忽便会出现bug。

2. 过长函数

当函数过长时,人很难一下掌握太多的细节,使得修改这个函数的成本很高。

3. 过大的类

单个类承担的责任过多,违反单一职责而形成过大的类。

4. 过长的参数列

长的参数列简直是噩梦,因为稍有不慎将类型相同的参数填错顺序就引发Bug了。(好在IDEA编译器能对填入的每个参数提示参数名)

5. 发散式变化

就是类受多个变化的影响,是违反迪米特法则的结果。类应该尽可能少的与其他类打交道,避免非必要的关联。

6. 霰弹式修改

单个变化引发多个类的相应修改。

7. 依恋情节

当一个类的行为严重依赖其他类的时候,我们需要思考这个行为真正的归属。

8. 数据泥团

两个类中相同的字段或者方法签名中相同的参数总是一起出现,可能这些字段可以自成一类。

9. 基本类型偏执

此基本类型并不是指int、long这类,而是指类中出现很多小字段,比如省、市、区、住址可以封装成一个地址对象作为User类中的一个属性。

10. Switch问题

当出现Switch重复时,同样的Switch散布在不同的地方,增加一个新的case,需要找到所有Switch进行增加。

11. 平行继承体系

每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。

12. 夸夸奇谈的未来性

过度设计。

13. 冗赘类

用处微乎其微以致于不如不作为一个类。或者作为内部类。

14. 令人迷惑的临时字段

类中的某些字段仅为特殊情况而定,或者类中的字段仅为了某个函数的方便声明为成员变量而没有其他用处。

15. 过度耦合的调用链

一个对象向另一个对象发起一个请求,再由另一个对象请求其他对象。

16. 中间人

无用的委托,过多中间层。

17. 狎昵关系

两个类关系过于紧密。一个类过于关注另一个类的成员,使得高耦合。

18. 异曲同工的类

不同的类或者函数,做着相同的事。

19. 不完美的类库

类库不能满足实际开发需求。

20. 纯数据类

类似于DDD中所阐述的贫血模型,仅有数据没有行为的类。

21. 被拒绝的遗赠

子类继承了父类不必要的函数或者数据。

22. 过多的注释

过多的注释说明代码的自解释能力很差。需要重构。


2019-12-30 0 评论 208 浏览
阅读全文

原文地址:《The JVM Architecture Explained 》

每个Java开发者都知道字节码是被JRE(Java运行时环境)所执行的。但是许多人并不知道实际上JRE是Java虚拟机(JVM)的实现。它分析字节码,解释代码并执行它。作为一个开发者,了解JVM的体系结构是非常重要的,因为它使我们能更有效的编写代码。在这篇文章中,我们将更深入的了解Java中的JVM体系结构和JVM的不同组件。

什么是JVM?

虚拟机是物理机器的软件实现。Java语言当时是基于编写一次到处运行的理念被开发出来的,其中到处运行指的是运行在虚拟机上。编译器将java文件编译成.class文件,然后.class文件被输入到JVM中,JVM会加载并运行这些.class文件。下图展示了JVM的体系结构。

JVM体系结构
JVMArchitecture.png

2019-12-22 2 评论 336 浏览
阅读全文
[译]Java NIO vs. IO 有更新!

英文原文地址:《Java NIO vs. IO》

当学习Java NIO和IO的API用法时,一个问题就从脑海里冒出:什么时候我们该用IO,什么时候我们该用NIO呢?在这篇文章中我会阐明Java NIO和IO之间的不同点和使用场景以及它们是如何影响代码的设计。

NIO和IO主要的不同点

以下的表格总结了NIO和IO之间主要的不同点。我会以更多的细节去阐明这些不同点在接下来的几节中。

IONIO
面向流面向缓冲区(Buffer)
阻塞IO非阻塞IO
Selector(选择器)

面向流与面向缓冲区

第一个不同点在于IO是面向流的而NIO是面向缓冲区的。所以这意味着什么?

Java IO是面向流的意味着你从一个流中一次读取一个或者多个字节。如何处理读出的字节取决于你。它们不会缓存到任何地方。此外你不能在流中来回移动数据。如果你需要来回移动从流中读取的数据,你需要将其先缓存到一个缓冲区中。

Java面向缓冲区的方法稍微有点不同。数据先被读入到一个缓冲区,然后再处理。你也可以将你的数据在缓冲区中来回移动。这使得你在处理过程中多了一些灵活性。但是,你必须检查缓冲区内是否包含了完整处理过程所需的所有数据。并且你必须确保当读入更多数据到缓冲区时,不能覆盖尚未处理的数据。

2019-12-17 0 评论 215 浏览
阅读全文

看到一篇关于Java内存模型的技术文章于是翻译一下供大家学习交流。

原文地址:《Java Memory Model》

java内存模型指定了java虚拟机如何与计算机的内存(RAM)进行工作。java虚拟机是一整个计算机的抽象模型,所以这个模型天然的包括了内存模型,它被称之为java内存模型。

如果你想设计正确运作的并发程序的话,那么理解java内存模型是非常重要的。Java内存模型指定了不同线程如何以及何时可以看到其他线程写入共享变量的值,以及在必要时如何同步对共享变量的访问。
在最初版本的Java内存模型是不足的,所以在Java 1.5版本中进行了修正。这个版本的Java内存模型沿用至Java 8中。

Java内存模型的内部

在JVM的内部使用了Java内存模型将内存划分给线程栈和堆。下图从逻辑视角说明了Java内存模型。

javamemorymodel1.png

每一个运行在虚拟机的线程都拥有一个它独有的线程栈。线程栈包含了关于线程方法调用中已到达当前执行点方法的信息。我们将它称之为调用栈。当线程执行到它的代码,它的调用栈就会改变。

线程栈同时包含了被调用的每一个方法的所有局部变量(所有方法都在调用栈上)。一个线程只能访问它自己的线程栈。局部变量除了创建它们的线程之外,对于其他线程都是不可见的。甚至两个线程正在执行同一段代码,这两个线程仍然会创建代码中的局部变量在它们各自的线程栈之中。因此,每个线程都有每个自己版本的局部变量。

2019-12-11 0 评论 188 浏览
阅读全文

业务场景

在进行业务开发时经常会有状态值的业务需要,例如一场考试有未开考、考试中、考试结束等状态或者一年四季有春天、夏天、秋天、冬天等状态。从前台传参到我们的业务模型再到数据库,其实这些状态是贯穿整个开发流程的。如果仅仅使用1、2、3、4来代表春夏秋冬的话,那代码的自解释能力就太差了。那如何利用好枚举类来更优雅的编码呢?

枚举类需要满足的功能

  1. 消除代码中的魔术数

  2. 根据数值获取状态的名称

  3. 根据数值获取状态

  4. 获取数值和状态名称为键值对的Map作为前端筛选项。

第一种枚举类实现

public enum SeasonEnum {

    /**/
    SPRING(1,"春天"),
    SUMMER(2,"夏天"),
    AUTUMN(3,"秋天"),
    WINTER(4,"冬天");

    public final int id;
    public final String desc;

    SeasonEnum(int id, String desc) {
        this.id = id;
        this.desc = desc;
    }


    /**
     * 根据数值获取对应的枚举
     * @param id
     * @return
     */
    public static SeasonEnum getEnumById(int id) {
        for (SeasonEnum seasonEnum : SeasonEnum.values()) {
            if (seasonEnum.id == id) {
                return seasonEnum;
            }
        }
        return null;
    }


    /**
     * 根据数值获取对应的枚举描述
     * @param id
     * @return
     */
    public static String getEnumDescById(int id) {
        final String unknown = "未知";
        for (SeasonEnum seasonEnum : SeasonEnum.values()) {
            if (seasonEnum.id == id) {
                return seasonEnum.desc;
            }
        }
        return unknown;
    }


    /**
     * 获取数值-描述键值对map
     * @return
     */
    public static Map getEnumMap() {
        Map<Object, Object> map = new HashMap<>();
        for (SeasonEnum seasonEnum : SeasonEnum.values()) {
            map.put(seasonEnum.id, seasonEnum.desc);
        }
        return map;
    }

}

2019-10-16 0 评论 267 浏览
阅读全文