- 本书的阅读又搁置了许久,虽然感觉 Manning 出版社的这一
100 Mistakes系列从书的质量不是那么的高,但开了头还是继续从本书 40% 的位置往下。
开始要讲述到异常了,异常还是有必要认真对待的,比如- Java 中很容易被 CheckedException 弄得代码不整洁
- 缺少必要的参数检查,不舍得抛出异常,视异常为 Bug
- 不明确出现异常时后续如何处理,
- 或者是捕获而隐藏了异常致使定位错误变得更难。
Java 的主要异常大分类是Throwable
NullPointerException, 这恐怕是一个最常见的异常,Java 对一个对象是否能为 null 值没什么约束,甚至用 null 来表示业务上的空。比如说方法的参数与返回值,Java 都可以是 null 值,而在 Kotlin 中非明确可为 null 的时不能为 null Read More
├── Error
└── Exception
└── RuntimeException
继续阅读本书,编程语言处理数值都有可能出现问题,如溢出,整数的最大最小值不对称,Double.NaN 等。
由于 Java 学了 C,也用 0 开始的数字来表示 8 进制数,如 037, 010 分别是十进制的 31 和 8,这与现实不相符。因为如果你在纸上写下 037, 010, 几乎所有人(除了某些程序员)都会认为它们就是十进制的 37 和 10。但是 Java 表示 2 进制, 16 进制的方式没有问题的,如 0b10, 0x37。IntelliJ IDEA 看到使用 0 开头的 8 进制数会不建议那么使用. 8 进制数字的范围是 0~8, 所以 09 是错误的, 但是 Java 编译器似乎对此很陌生.
int a = 09;
IntelliJ IDEA 会提示Integer number too large, 编译器提示说java: ';' expected, 有点驴唇不对马嘴.
现在几乎没有必要使用 0 开始的 8 进制数的方式, 或许还有用的就是表示 Unix 下文件权限, 如
int fileMode = 0644
所以任何时候看到 0 开头的数字都必须仔细检视, 基本可以禁止使用这种方式 Read More- 现在流行数据都有 Schema 的概念,一般作为数据库对象(表,函数,存储过程等)的命名空间。所以在数据库端往往存在 实例/数据库/Schema 这样层级划分。对于 DB2 和 Oracle 用客户端创建一个新的数据库并非易事,灵活的在数据库中较轻量的划分隔离空间的办法因数据库类型而异
- MySQL: 创建数据库(create database), create schema 是 create database 的别名
- PostgreSQL: create database 创建新的数据库,或在当前数据库下用 create schema 创建 schema
- SQLServer: 和 PostgreSQL 一样的自由,create database 创建新的数据库,或在当前数据库下用 create schema 创建 schema
- DB2: 用 create schema 创建新的 schema, 或创建数据库对象时直接加上前缀,create table abc.test1..., 没有 abc schema 则会自动创建
- Oracle: create schema 较麻烦,涉及到 authorization. 但可以通过 create user 创建新用户后就有了对应的新 schema
下面我们来了解下在 PostgreSQL/SQLServer 中创建新的 schema,如何在 JDBC 连接字串中指定默认 schema, 同时也涉及到 database/schema/user 的创建以及在 SQL 中如何切换。 Read More
Java 一路突突突, 版本 16 在 2021-03-16 都发布了, 而我们一直碍于 Java 9 的大改还在 Java 8 上原地踏步, 以往每当有新版本 JDK 发布后都是很快就验证,立马升级。Java SE versions history) 列出了所有 Java 的历史版本的发布日期。在今天(2021-05-04) 网站 Java SE Downloads 上直接提供下载的 Java SE 版本有以下三- Java SE 16.0.1
- Java SE 11.0.11(LTS)
- Java SE 8u291
两个 LTS(长期支持) 版 8 和 11,外加一个目前最新的非 LTS 版本 16, 其他的版本都被归档到了 Java Archive. 查看一下 Java 支持的 roadmap, 几个 LTS 版本的服务支持年限版本本 发布日 原定支持至 延期支持至
注意到 Java 8 将比 Java 11 和将来的 Java 17 生命力还顽强,一下就觉得这么久坚守在 Java 8 的阵地上不应觉得有什么好害羞的。眼看着下一个 LTS 版本的 Java 17 就要在今年 9 月份发布了,Java 11 看来是要错过了,等准备好和 Java 8 告别时要直接跳到 Java 17 了。 Read More
Java 8 2014/3 2022/3 2030/12
Java 11 2018/9 2023/9 2026/9
Java 17 2021/9 2026/9 2029/9
在进行数据流处理过程中,需要一个高效苗条的流处理组件,比如对输入流能进行分组(窗口),能进行流量控制(Back Pressure - 背压),这也就涉及到响应式编程,流处理框架。这方面如果直接基于 Akka actor 来构建 Akka ActorSystem 也是比较复杂,依赖的组件也不少。还有构筑在 Akka actor 之上的 Akka Streams,再往上的 Flink Streaming,它们都有像滑动,滚动窗口的概念,但是依赖更不得了。一个基本的 Flink Streaming 的项目会依赖到 45 M 以上的第三方组件,如果用它来写一个数据流处理的共享组件,那真是要命。Spring 5 也开始带上了自己的 Reactive-Streams 实现 Spring Reactor, 想要把它从 Spring 中单独抽离出也非易事。Flink Streaming 组件依赖:org.apache.fling:flink-streaming-java_2.12:1.80, 会依赖于其他诸如 akka-stream, akka-actor, flink-core, flink-clients, scala-library 等非常多的东西
而另一个著名的响应式框架 RxJava 2 就清爽多了,完全没有第三方依赖,要说有也就是定义了四个接口的 reactive-streams(2 KB 大小),就自身那个 rxjava-2.2.9.jar 包只有 2.3 M,这才叫轻量级。因为它设计来是能被应用于 Android 客户端应用的,Andriod 上的 rxandriod-1.2.1.aar 只有 9 K。所以 RxJava 2.x 太适合用来写一些小的共享组件了。 Read More
在我们对数据库 DAO 类进行单元测试时,通常不应该依赖于一个外部数据库,所以会选用特定比较接近于真实数据库类型的内存或嵌入式数据库,如 HSQLDB(HyperSQL), H2, Derby 等。但总难免会用到特定数据库的特性,这时候就无法用前述各种数据库进行测试了。非要单元测试中覆盖到所用的数据库特性的话可以选择用 docker,如 Testcontainers, 经过模块扩展,它可以由 docker 来启动许多种类型的数据库,MySQL, Postgres, Oracle-XE, MS SQL Server, Couchbase 等等,详情见 Database containers。刚了解到的是它的模块化的无限可能,像支持 Kafka Containers 和 Localstack Module 等。
这里就不走 Testcontainers 那条路 -- 要求构建服务器上也要有 docker。早先希望能找到一种嵌入式或内存 PostgreSQL 数据库,后来发现 PostgreSQL 未能提供 In-Process 和 In-Memory 的启动方式,好在 PostgreSQL 是开源,有人可以把它改造为小型的可由测试代码启停的本地数据库。有两个具有代表性的组件,分别是 OpenTable Embedded PostgreSQL Component 和 Embedded PostgreSQL Server,它们都号称是 Embedded,所谓嵌入式,其实是进测试进程外的数据库。
下面简单体验下两个组件的用法 Read More
- 实际中可能有这样的应用场景,得到一个记录不需要立即去处理它,而是等累积到一定数量时再批量处理它们。我们可以用一个计数器,来一个加一个,量大时一块处理,然后又重零开始计数。如果记录的来源单一还好办,要是有多个数据源来提供记录就会有多线程环境下数据丢失的问题。
这里我编写了一个最简单的任务批处理的队列,构造了告诉它批处理数量,消费者,然后就只管往队列里添加记录,队列在满足条件时自动进行批处理。因为内部使用的是BlockingQuque来存储记录,所以多线程往里同时添加记录也没关系,最后的未达到batchSize, 的那些记录可主动调用completeAll()函数或在达到 timeout 后来触发批处理,并且结束队列内的循环线程。
注意: 多线程环境下往一个无线程保护的集合或结构中,如 ArrayList, LinkedList, HashMap, StringBuilder 中添加记录非常容易造成数据的丢失,而往有线程保护的目的地写东西就安全了,如 Vector, Hashtable, StringBuffer, BlockingQueue。当然性能上要付出一点代价,不过对于使用了可重入锁(ReentrantLock), 而非同步锁(synchronized) 的数据结构还是可以放心使用的。
下面是 BatchQueue 的简单实现 Read More - Java 在定义字符串的时候不支持字符串插值, 即不能在字符串中捕获作用域中的变量, 用来组成当前字符串. 而这可以说是其他各种言都具备的基本特性. 例如 Bash 中可以这样
bash-3.2$ name=Yanbin
Java 中不支持类似的方式
bash-3.2$ echo "Hello $name"
Hello YanbinString name = "Yanbin";
而使用 Java 必须用 String 的静态方法 format (注意是静态方法哦) 来间接的格式化出一个字符串, 可以这么写
String greeting = "Hello $name"; //Java 中无法把 $name 替换为 name 变量值 "Yanbin"String.format("Hello %s", "World");
我们看到在用 format 格式化时匹配参数会是一个不小的问题, 相同使用法的打印输出方法是
String.format("%1$s %1$s %s %s %2$s", "aa", 10); //aa aa aa 10 10System.out.printf(). 别说字符串插值了, 如果 format 方法是 String 的一个实例方法应用起来都会便利些, 如"Hello %s".format("World")会容易读些, Scala 的字符串实例就有format方法了. Read More - 希望 Java 能支持动态对象(匿名对象) 的特性是源于想要 Java 方法能优雅的返回多个值. 目前如果希望 Java 方法返回多个值的做法有返回一个自定义对象, 数组或列表, 或 Map. 这种需求多发生在私有方法上, 但目前的解决办法有如下弊端:
- 如果用自定义类来作为返回类型的话, 会使得类过于杂乱, 而且这些自定义类的复用性不高
- 数组或列表有太强的顺序依赖, 没有属性名告知每个位置上值的意义, 而且类型都必须为 Object
- 返回 Map 的, 在维护 Key 上容易出错(或须为 Key 定义很多字符串常量), 且类型也是为 Object.
说了上面许多, 各位可能还不定清楚我想要的是什么, 其实就是类似于 C# 的匿名对象(或曰动态对象), 看一段 C# 的方法返回一个匿名对象的例子(.NET3.5 开始引入匿名对象, .NET 4.0 后匿名对象可以作为函数值) Read More - 曾几何时在业务分层结构中的 VO 或 DTO 层充斥着无数的标准 JavaBean 类, 那些碍手脚的 getter/setter 方法简值不忍直视. 或许 JavaBean 设定规范的用意是当某些属性为只读时不提供 setter 方法, 而实际使用时, 因 getter/setter 都同时具备, 那么 JavaBean 的所有私有属性又何异于公有属性呢.
更别说对于某些形式的属性名, 若属性名为xCoordinate时, 它所对应的 getter 方法分别是getxCoordinate(),一般的 IDE 都会为它自动生成getXCoordinate()方法, 这是错误的. 实际上getXCoordinate()对应的属性名是XCoordinate.
所以 Play Framework 1 以及 Play Framework 2.4.6 之前的版本采用了字节码增强的技术, 实现了像 Objective-C 的 @property 的特性, 即只要声明公有属性, 编译器为该属性生成默认的 getter/setter 方法, 您也可以手工去覆盖个别默认的 getter/setter 方法.
因此在 Play Framework 中书写的的 model 类就只需要属性了, 像1public class User { 2 public int id; 3 public String name; 4 public String email; 5 public String address; 6}
就这么简单, 想像一下如果我们为一个众多属性的 model 类补全所有的 getter/setter 方法读起来有多恐怖. Read More