
什么是线程栈
继续纠缠 Java 9 的新特性,仍然是一个边角料,即 Java 9 增加了对线程栈遍历的 API。那么什么是线程栈,JVM 在创建每一个线程的同时都会创建一个私有的虚拟机栈,每一桢代表着一个方法调用,每次方法的调用与退出意味着压栈与出栈。每一桢上有局部变量,操作数常量引用等信息,这也是为什么局部变量是能最快被销毁的对象。过深的栈(比如过多的递归调用) 会出现我们程序员赖以生存的 StackOverflow。
浅显些说,线程栈就是通常我们捕获到异常后,用e.printStackTrace()看到自 main 方法追溯到当前方法的调用。例如:java.lang.RuntimeException: stack
调用层次是 main() 调用 m1(), m1() 调用 m2(), m2() 中的代码如下
at cc.unmi.TestStackWalking.m2(TestStackWalking.java:15)
at cc.unmi.TestStackWalking.m1(TestStackWalking.java:10)
at cc.unmi.TestStackWalking.main(TestStackWalking.java:6)1try { 2 throw new RuntimeException("stack"); 3} catch (Exception ex) { 4 ex.printStackTrace(); 5}
上面输出的每一行就是一个栈桢,输出了当前类名,方法名,代码行号。 Read More
我们知道 Java 的合法命名是以字母或下划线开头的字符串,当然,以前单个下划线
_也是一个合法的变量命名。但是自 Java 8 的第一个版本开始,单个下划线的变量名编译时会有警告int _ = 99;
用 Java 8 编译时提示警告:
Test.java:2: warning: '_' used as an identifier
int _ = 99;
^
(use of '_' as an identifier might not be supported in releases after Java SE 8)
1 warning这正是为 Java 9 作的预谋,以一个 LTS 版的 Java 8 作为过渡。因此,来到了 Java 9 后,单个下划线不再担当普通变量名的角色,变成了一个保留关键字,只说是另有任用。
先来看下 Java 9 中的单个下划线变量名编译时的错误提示
Test.java:2: error: as of release 9, '_' is a keyword, and may not be used as an identifier
int _ = 99;
^
1 error到底单下划线会作为什么用途呢? 已发布的 Java 10 没有给出答案,正在演进中的 Java 11 也没有相关的信息。 Read More
关于 Java 9 的新特性从某本书的最后一个说起:平台日志 API。个人没感觉这个有什么实质的用途,所谓的平台日志是指 JDK 自身代码,或者是 JVM 组件中的日志输出,而在自己应用程序代码中却不会去用这个平台日志 API。这个所谓的 Platform Logging API 名称的意义也就是在这里,平台用的,在诊断时用来观察 JDK 类或 JVM 中的日志输出,比如应该可以截获到 JVM 本地代码实现中的日志输出。对我们在项目中如何处理日志并不会有什么影响,该怎么还是怎么,不过了解多一点东西应该不会浪费脑容量的。
新加的平台日志体现在java.lang.System中新加的几个方法和类
我们可以尝试着在代码使用一下它1System.Logger logger = System.getLogger(TestLogging.class.getName()); 2logger.log(System.Logger.Level.INFO, "Hello Java 9 Platform Logging API");
输出如下May 26, 2018 10:56:51 AM cc.unmi.TestLogging main
Read More
INFO: Hello Java 9 Platform Logging API
Java 9 出来了很久,买的书《Java 9 Revealed - For Earyly Adoption and Migration》,说怎么迁移到 Java 9,可是突然间 Java 9 就无法通过正常渠道从 Oracle 官网下载了,这书还让不让人看。当然要看,因为尽管 Java 10 出来了,但实际的变化全压在 Java 9 这个版本上的,就当是通过 Java 10 来学习 Java 9 吧。
本文随便说说 Java 8 之后的版本变迁,不涉及 Java 9 或是 Java 10 的具体新特性,并不能回答标题中的问题。作为一个不甘落后的 IT 从业人员,总是希望能紧跟技术(某一狭小特定领域的技术)的步伐。譬如说当从 Java 1.4 升级到 Java 1.5 之后,对范型也是跃跃欲试,无奈当时公司追求的是稳定压倒一切,不在服务器上升级 JDK,所以只能创造条件也要上。于是弄了个 Retrotranslator让你用JDK1.5的特性写出的代码能在JVM1.4中运行 来适配。
后来的公司,也就是现在更为激进一些,来了 JDK 6,跟; JDK 7,跟; JDK 8, 继续跟; JDK 9 发布后,不跟了。不是不想跟,而是 Java 9 实在是变化有点大,模块化带来的不仅仅语言方面的改变,而是影响到如何组织,发布应用,这也就是为什么 jigsaw 雪藏多年的缘故。其实也可以对模块化不欲理会,但是单纯的把 Java 8 换成 Java 9 造成原来的项目不能正常构建的概率也比以往要高。
Java 8 是 2014 年 3 月发布,四后半后的 Java 9 在 2017 年 9 月发布,然而 2018 年 3 月 Java 10 就出来了。谁说 Oracle 在收购 Java 后就对它不作为了呢?Oracle 在 Java 9 之后开始了 6 个月的发布周期(见 Oracle Java SE Support Roadmap),这让人如何受得了,Oracle 自己也是。更何况我们都没来得急品尝 Java 9 的滋味,Oracle 自己就把 Java 9 的生命周期给结束了,进到 Java 9 的下载而面 Java SE 9 Downloads, 自动会导向到 Java SE 10 的下载页面,想要下载 Java SE 9 的话,只能到历史存档中去找 Java SE 9 Archive Downloads。 Read More
SQL Server 自 2008 版起引入了uniqueidentifier字段,它存储的是一个 UUID, 或者叫 GUID,内部存储为 16 个字节。SQL Server 可用两个函数来生成uniqueidentifier, 分别是NEWID()和NEWSEQUENTIALID(), 后者只能用作字段的默认值。Java 也有一个 UUID 工具类java.uti.UUID,UUID.randomUUID().toString()生成一个随机的 UUID 字符串,在java.util.UUID也是用两个long字段表示内部状态。
SQL Server 的uniqueidentifier类型字段表明了内部如何存储,在我们操作它时,它的外在表现形式都是一个固定格式 `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` 的字符串,不区分大小写的。
本文所使用的 SQL Server 是 2017 版,通过 Docker 来启动的docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=yourStrong(!)Password' -p 1433:1433 -d microsoft/mssql-server-linux:2017-latest
然后我们创建一个带有uniqueidentifier类型字段的表 Read More
以实际 Java 项目中的单元测试 Mock 框架基本是 Mockito 2 了,因为它有一个十分流畅的 API。Mockito 2也为 JUnit 5 配上了 MockitoExtension, 所以 JUnit 5 下使用 Mockito 2 的关节也打通了。但在我们享受 Mockito 2 便利的同时,与 JMockit 相比局限性就很明显,因为 Mockito 2 是通过创建匿名子类来进行 Mock 的,所以任何子类对父类无法突破的方面它都存在。譬如,final 类, final 方法, 私有方法, 静态方法, 构造函数都是无法通过子类型进行重写的。所以除非特别需要,在 Mockito 2 无法胜任时都求助于 JMockit,JMockit 借助于 javaagent 取得了 JVM 的高控制权才得已为所欲为。
当 Mockito 来到了 2.1.0 版本,它也觉得不能对以上所有的限制置若罔闻, 首先带给我们的突破是它也可以 Mock final 类和 final 方法,虽然仍处于孵化器中,但毕竟是应用在单元测试中,能用就很不错了,只要以后不被拿走就行。这是官方对它的介绍 Mock the unmockable: opt-in mocking of final classes/methods
下面我亲自操作一遍,并给出更全方位的测试样例 Read More
JUnit 5 刚出来那时,也就是第一个版本 5.0.0 时,还不能很好的支持 Mockito 的测试,因为 Mockito 没能跟得那么紧密。那时候 JUnit 5 只能试验性的提供了一个极不正式的 com.example.mockito.MockitoExtension, 看那包名就知道不是来真的,所以决定再等。JUnit 5 不再原生支持 JUnit 4 的 Rule,一切都将是 Extension,那也是要求 Mockito 能够与之俱进。现在等来了,JUnit 5 进化到了 5.2.0, Mockito 也早已有了一个单独的模块mockito-junit-jupiter来迎接它。
在 Mockito 2.1.0 的 What's new in Mockito 2 中记述了 JUnit 5 为 Mockito 2 开发了一个 MockitoExtension。追溯到 Mockito 2 的 Release Notes, 我们发现 Mockito 2 官方最早引入 MockitoExtension 的版本是 2.16.3(2018-03023)。我对 Mockito 对 JUnit 5 支持的最新更新是从这个 Pull Request MockitoExtension for JUnit5 得知的。
一句话讲就是现在的 Mockito 2 有原生态的 MockitoExtension 来支援 JUnit 5, 可以非常放心可靠的让 JUnit 5 和 Mockito 2 一起稳定工作。因此前面那个包名带example字样的 MockitoExtension 链接也就无效了。 Read More刚开始阅读 《Akka IN ACTION》这本书,刚开始是对
Revolution这个词翻译成中文是革命感到诧异,因为革命通俗来讲就是杀人的意思。至于Revolution英文解释不深究了,只是感叹何以颠覆性的变化就一定要杀人吗?也由此引出了编程中经常面对的
Concurrent(名词为:Concurrency) 和Parallel(名词为:Parallelism) 这两个词,基本上是认为它们是同一个意思。其实不然,下面慢慢道来。如果从英文字典对它们的解释也没有多大区别,差不多都是说同是发生,但字面上
Parallel多了一个平行的意思。所以在中文上,在计算机领域我们约定的翻译是- Concurrent(Concurrency) -- 并发
- Parallel(Parallelism) -- 并行
比如在多线程环境中它们的区别具体体现在:
并发:多个任务在同一个 CPU 核上按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。针对 CPU 内核来说,任务仍然是按细粒度的串行执行。也难怪在 Java 5 中新加的并发 API 的包名是
java.uti.concurrent。 Read More
初识 Mockito 这个测试框架后,我们要使用 Mock 的属性创建一个被测试类实例时,大概会下面这么纯手工来打造。
假定类
UserService有一个属性UserDao userDao, 需要构造UserService实例时 Mock 内部状态UserDao userDao = Mockito.mock(UserDao.class);
UserService testMe = new UserService(userDao);如此,userDao 的行为就可以自由模拟了,这种纯手工方式都不需要给测试类添加
@RunWith(MockitoJunitRuner.class)
//或
MockitoAnnotations.initMocks(this);因为上面两句是给 Mockito 的注解使用的。
如果所有的 Mock 对象全部通过手工来创建,那就不容易体现出 Mockito 的优越性出来。因此对于被测试对象的创建,Mock 属性的注入应该让
@Mock和@InjectMocks这两个注解大显身手了。标注在实例变量上的
@Mock相当于是Mockito.mock(Class)创建了一个 Mock 对象,而@InjectMock标的实例会寻找到相应 Mock 属性想法构造出被测试类的实例。看下面的例子: Read More- 标准 SQL 都提供了下面这种方式一条 INSERT INTO 语句插入多条记录
VALUES 之后用括号列出每一条记录。但是在 Java 中想把上面的语句转换成 PreparedStatement 来插入多条记录时就有些问题。要么写成INSERT INTO Customers(Id, Name, Age) VALUES (1, 'Name1', 21.5), (2, 'Name2', 32.3)
INSERT INTO Customers(Id, Name, Age) VALUES(?, ?, ?), (?, ?, ?), (?, ?, ?) ....
我们不知道 VALUES 后应该列多少个问号,而且 JDBC 对参数的个数是有限制的,最多 2000 个参数。如果根据字段个数来算一次添加多少条记录,那么这条 SQL 语句也是动态的,不能很好的作为 PreparedStatement 进行预编译。以一个表三个字段为例,2000 个参数下一次性最多能插入记录数 666 条,也可能由于输入是 666 条记录的任意数量,所以生成的语句非静态的。
这种方式与每次手动拼凑一个完全静态的 INSERT INTO 语句应该不会有太多的差别。
如果只是写成INSERT INTO Customers(Id, Name, Age) VALUES(?, ?, ?)
然后试图进行下面的操作 Read More