[翻译]现代java开发指南 第一组成部分

现代java开发指南 第一部分

率先有些:Java就非是若爸爸那么期之则

先是有,第二局部

跟史上外其他的语言相比,这里而破除c语言和cobol语言,现在越发多的行事被,有用之代码用Java语言形容起。在20年前Java首破发表时,它引起了软件界的风口浪尖。在当下,相对c++语言,Java语言要重新简约,更安全,而且当一段时间后,Java语言的特性为博得了晋级(这仗让现实的使用状况,一个巨型的Java程序于一致的c++程序相比,可能会慢一点,或者千篇一律快,或者重新快有)。比起c++,Java牺牲非常少性能,却提供了英雄的生产力提升。

Java是一门blue-collar
language,程序员值得信赖的家伙,它才会采用已经给别的语言尝试了之不利的眼光,同时增加新的特征只见面去化解重大的痛点问题。Java是否一直钟情它的沉重是一个开放性的题材,但她的确是竭力为从曾的征程未深受当下底时尚所左右最远。在智能芯片,嵌入式设备与重型主机上,java还当用于编写代码。甚至吃用来修对职责和安全要求苛刻的硬件实时软件。

可是,最近有些年,Java获得了过多负面的品,特别是在互联网初创公司面临。相对于别的语言如Ruby和python,Java显得死板,而且跟安排自由的框架如Rails相比,java的网页开发框架需要利用大量底xml文件举行吗布局文件。进一步说,java于巨型商厦遭到广大应用导致了java所动的编程模式与做法在一个老特别的有强烈等级关系的技艺集团中会充分有因此,但是这些编程模式与做法对迅速开打破常规的初创企业吧,不是殊方便。

而是,Java就改变。Java最近增加了lambda表达式和traits。以库的花样提供了例如erlang和go所支持之轻量级线程。并且极重点的凡,提供了一个现代底、轻量级的章程用于代替陈旧笨重以大量xml为底蕴之方,指导API、库以及框架的规划。

近年来片年,Java生态圈出了有的幽默之行:大量之坐jvm为根基的程序语言变得流行;其中一些言语设计的好吓(我个人喜欢Clojure和Kotlin)。但是同这些有效或者推荐的语言相比,Java和其它基于JVM的言语来说,确实发几乎单长:熟悉,技持,成熟,和社区。通过新代器和新代的库房,Java实际上以就几个点开了重重底行事。因此,许多的硅谷初创店,一而他俩成长壮大后,就会回到Java,或者至少是回去JVM上,这点就算不会见另外人奇怪了。

就卖介绍性指南的靶子是想念深造怎么样勾勒现代精简Java代码的程序员(900万),或者是那些听到了或者体验过Java坏的点的Python/Ruby/Javascript程序员。并且指南展示了Java中曾经改变的端与这些反之方面如何让Java获得其他人拍手叫好的性质,灵活性与而监控性而未会见牺牲太多的Java沉稳方面。

JVM

针对Java术语简单价绍一下,Java于概念上被分成三单部分:Java,Java运行时库和Java虚拟机,或者叫JVM。如果你熟悉Node.js,Java语言类同于JavaScript,运行时库类同于Node.js,JVM类同于V8引擎。JVM和运转时库被于包改成大家所熟知的Java运行时环境,或者叫JRE(虽然常人们说JVM实际上指的是JRE)。Java开发工具,JDK,是赖某一个JRE的发行版,通常包括广大开发工具像java编绎器javac,还有多次监控和属性分析工具。JRE通常有几独分支,如支持嵌入式设备支出版本,但是以博客中,我们只见面波及到JRE支持服务器(桌面)开发之本子,这即是强烈的
JavaSE(Java标准版)。

来部分型落实了JVM和JRE的科班,其中有的是开源的档次,还有一部分凡是买卖项目。有些JVM非常非常,如小JVM运行硬件实时嵌入式设备软件,还有JVM可以在伟大的内存达到运行软件。但是咱用会见使用HotSpot,一个出于Oracle支持的的任性,通用的JVM实现,同时HotSpot也是始于源OpenJDK花色之等同有些。

Java构建JVM,JVM同时运行Java(虽然JVM最近为其他语言做了有特别的修改)。但是什么是JVM,Cliff
Click的是演讲说了什么是JVM,简单来说,JVM是一致大抽象现实的魔法机器。JVM使用可以,简单与行的空洞,好像无限的内存和多态,这些听起实现代价十分高,并且实现这些特点用如此高效之形式以致被他们能够充分容易能够与无供这些有效抽象的运作时竞争。更需要证明的是,JVM拥有无限好内存回收算法并能在死范围之成品面临以,JVM的JIT允许内联和优化虚方法的调用(这是过多言语中极其可行的抽像的为主),在保存虚方法的用处的而,使调用虚方法非常便于与速。JVM的JIT(即经常编绎器)是基础之高等性能优化编绎器,和你的动一起运行。

自然JVM也藏了众多底操作系统级别的细节,如内存模型(代码在不同之CPU上运行怎样对待外的CPU操作引起的变量的状态的转移)和采用定时器。JVM还提供周转时动态链接,热代码交换,监控几乎有在JVM上运行的代码,还出库中之代码。

顿时并无是说JVM是全面的。当前Java的数组缺失存放复杂结构体的力(计划将在Java9受化解),还有相当的尾调用优化。尽管JVM有这么的题目,但是JVM的秋,测试好,快速,灵活,还发增长的运转时解析与监督,让自家无见面设想运行一个关键要之服务器进程在别的任何基础之上(除了JVM别无选择)。

辩护都够了。在我们深切教之前,你应该下载在这里下载最新的JDK,或者使用你系统自带的保管管理器安装新型的OpenJDK。

构建

为我们开现代Java构建工具旅程。在老丰富的一致段落历史时空内,Java出现了几个构建工具,如Ant和Maven,他们多数都根据XML。但是现代底Java开发者使用Gradle(最近成为Android的官构建工具)。Gradle是一个秋,深入开发,现代Java构建工具,它使用了在Groovy基础及之DSL语言来证实构建过程。他合了Maven的简单性和Ant的强大性和灵活性,同时抛弃有的XML。但是Gradle并无是从未有过不当:当他若尽通用的有的简单和可声明式的还要,就会见生出诸多作业变得不得了不通用,这就要求返回回来使用命令式的Groovy。

现在深受咱们采取Gradle创建一个初的Java项目。首先,我们从这边下载Gradle,安装。现在我们初步创办项目,项目名为JModern。创建一个叫Jmodern的目录,切换到击刚才创建的目,执行:

gradle init --type java-library

Gradle
创建了种类的始文件夹结构,包括子类(Library.javaLibraryTest.java),我们以在后边去这简单只文件:

figure1

代码在src/main/java/目录下,测试代码在src/test/java目下。我们将主类命名吧jmodern.Main(所以主类的源文件就于src/main/java/jmodern/Main.java),这个顺序用会管Hello World先后召开一些纤的别。同时为利用Gradle更有利,将会见利用Google's Guava。使用你喜欢的编辑器创建src/main/java/jmodern/Main.java,初始的代码如下:

package jmodern;

import com.google.common.base.Strings;

public class Main {
    public static void main(String[] args) {
        System.out.println(triple("Hello World!"));
        System.out.println("My name is " + System.getProperty("jmodern.name"));
    }

    static String triple(String str) {
        return Strings.repeat(str, 3);
    }
}

相应创建一个粗的测试用例:在src/test/java/jmodern/MainTest.java:

package jmodern;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;

public class MainTest {
    @Test
    public void testTriple() {
        assertThat(Main.triple("AB"), equalTo("ABABAB"));
    }
}

在列根本目录,找到build.gradle文件,修改该公文:

apply plugin: 'java'
apply plugin: 'application'

sourceCompatibility = '1.8'

mainClassName = 'jmodern.Main'

repositories {
    mavenCentral()
}

dependencies {
    compile 'com.google.guava:guava:17.0'

    testCompile 'junit:junit:4.11' // A dependency for a test framework.
}

run {
    systemProperty 'jmodern.name', 'Jack'
}

构建程序设置jmoder.Main为主类,声明Guava为该次的倚重库,并且jmodern.name呢系统特性,方便运行时读取。当输入以下命令:

gradle run

Gradle会从Maven中心库下载Guava,编绎程序,然后运行程序,把jmodern.name设置成"Jack"。总的过程就是这般。

接通下去,运行一下测试:

gradle build

转移的测试报告当build/reports/tests/index.html

figure2

IDE

稍加人说IDE会稳藏编程语言的问题。好吧,对于此题材,我无见,但是无论是您下任何语言,一个吓之IDE总是有帮衬的,而Java在即时方面举行的极其好。当然在篇章中精选IDE不是重要的一对,总是要取一下,在Java世界被,有三颇IDE:Eclipse,IntelliJ
IDEA,和NetBeans,你应有下使用一下后两者。IntelliJ可能是三者之中最精锐的IDE,而NetBeans应该是绝适合程序员直觉和极易使(我当呢最尴尬)的IDE。NetBeans通过Gradle的插件对Gradle有最为好的支持。Eclipse是极度让欢迎之IDE。我当广大年前感觉Eclipse变得乱七八糟,就未采用Eclipse了。当然如果你是一个马拉松使用Eclipse的用户,也尚无啊问题。

安完毕Gradle插件,我们的略微品种于NetBeans中的范如下:

figure3

自顶爱NetBeans的Gradle插件功能不仅是以IDE列出了具有有关项目之依靠,还起另的部署插件也克排有,所以我们只需要在构建文件被宣示他们一样浅。如果您当品种中益新的仗库,在NetBeans中右键单击项目,选择Reload Project,然后IDE将生充斥而初长的依赖库。如果你右键单击Dependencies结点,选择Download Sources,IDE会下载依赖库的源代码和血脉相通javadoc,这样您就可以调节第三方库的代码,还能查看第三方库的文档。

故Markdown编写文档

长期以来,Java通过Javadoc生成特别好之API文档,而且Java开发者也习惯写Javadoc形式的注释。但是现代底Java开发者喜欢用Markdown,喜欢用Markdown为Javadoc增加点乐趣。为了达到在Javadoc使用Markdown,我们以构建文件中dependencies有的的前面,增加Pegdown DocletJavadoc插件:

configurations {
    markdownDoclet
}

然后,在dependencies中添加一行:

markdownDoclet 'ch.raffael.pegdown-doclet:pegdown-doclet:1.1.1'

末了,构建文件的结尾加这片:

javadoc.options {
    docletpath = configurations.markdownDoclet.files.asType(List) // gradle should relly make this simpler
    doclet = "ch.raffael.doclets.pegdown.PegdownDoclet"
    addStringOption("parse-timeout", "10")
}

归根到底,可以在Javadoc注释使用Markdown,还有语法高亮。

公或会见怀念关掉你的IDE的笺注格式化功能(在Netbeans: Preferences ->
Editor -> Formatting, choose Java and Comments, and uncheck Enable
Comments Formatting)。IntelliJ
有一个插件能高显示在Javadoc中之Markdown语法。

为了测试新增的安,我们给艺术randomString追加Markdown格式的javadoc,函数如下:

/**
 * ## The Random String Generator
 *
 * This method doesn't do much, except for generating a random string. It:
 *
 *  * Generates a random string at a given length, `length`
 *  * Uses only characters in the range given by `from` and `to`.
 *
 * Example:
 *
 *  // 这里有问题
 * randomString(new Random(), 'a', 'z', 10);
 *  
 *
 * @param r      the random number generator
 * @param from   the first character in the character range, inclusive
 * @param to     the last character in the character range, inclusive
 * @param length the length of the generated string
 * @return the generated string of length `length`
 */
public static String randomString(Random r, char from, char to, int length) ...

然后下命令gradle javadocbuild/docs/javadoc/生成html格式文档:

figure4

一般自己无常用之职能,因为IDE对这个功效的语法高亮支持的不顶好。但是当您用以文档中写例子时,这个效应会为您的做事易得更轻松。

所以Java8写简洁之代码

新近披露的Java8于Java语言带来了酷死之改变,因为java原生支持lambda表达式。lambda表达式解决了一个根本的题材,在过去人们解决做片大概从可写不客观的长篇大论的代码。为了展示lambda有差不多要命之赞助,我拿出我能想到的使人万分恼火的,简单的数操作代码,并拿当时段代码改用Java8状起。这个例子产生了一个list,里面包含了任性变化的生名字,然后开展以他们的头字母进行分组,并因为美的款式打印出来。现在,修改Main类:

package jmodern;

import java.util.List;
import java.util.Map;
import java.util.Random;
import static java.util.stream.Collectors.*;
import static java.util.stream.IntStream.range;

public class Main {
    public static void main(String[] args) {
        // generate a list of 100 random names
        List<String> students = range(0, 100).mapToObj(i -> randomString(new Random(), 'A', 'Z', 10)).collect(toList());

        // sort names and group by the first letter
        Map<Character, List<String>> directory = students.stream().sorted().collect(groupingBy(name -> name.charAt(0)));

        // print a nicely-formatted student directory
        directory.forEach((letter, names) -> System.out.println(letter + "\n\t" + names.stream().collect(joining("\n\t"))));
    }

    public static String randomString(Random r, char from, char to, int length) {
        return r.ints(from, to + 1).limit(length).mapToObj(x -> Character.toString((char)x)).collect(Collectors.joining());
    }
}

Java自动推导了独具lambda的参数类型,Java确保了参数是种类安全的,并且要您利用IDE,IDE中之自行就及重构功能对这些参数还得就此的。Java不会见像c++使用auto和c#中的var再有Go一样,自动推导局部变量,因为这样会为代码的可读性降低。但是这并无意味一旦用手动输入这些类别。例如,光标在students.stream().sorted().collect(Collectors.groupingBy(name -> name.charAt(0)))立刻一行代码上,在NetBeans中按下Alt+Enter,IDE会推导出结果正好的类型(这里是Map<Character, String>)。

要想发一下函数式编程的作风,将main函数改成为下面的样式:

public static void main(String[] args) {
    range(0, 100)
            .mapToObj(i -> randomString(new Random(), 'A', 'Z', 10))
            .sorted()
            .collect(groupingBy(name -> name.charAt(0)))
            .forEach((letter, names) -> System.out.println(letter + "\n\t" + names.stream().collect(joining("\n\t"))));
}

同以前的代码确实无均等(看哪,没有路),但是这应无绝容易理解这段代码的意思。

即使Java有lambda,但是Java仍然没有函数类型。其实,lambda在java中给撤换成类似为functional接口,即来一个空洞方法的接口。这种活动转换使遗留代码能够与lambda在一起死好的工作。例如:Arrays.sort方法举凡待一个Comparateor接口的实例,这个接口简单描述成单一的揭露抽象
int compare(T o1, T o2)方法。在java8受,可以使用lambda表达式对字符串数组进行排序,根据数组元素的老三独字符:

Arrays.sort(array, (a, b) -> a.charAt(2) - b.charAt(2));

Java8呢长了力所能及实现方式的接口(将这种接口换变成“traits”)。例如,FooBar接口有三三两两独法子,一个凡纸上谈兵方法foo,另一个是发默认实现之bar。另一个useFooBar调用FooBar

interface FooBar {
    int foo(int x);
    default boolean bar(int x) { return true; }
}

int useFooBar(int x, FooBar fb) {
    return fb.bar(x) ? fb.foo(x) : -1;
}

虽然FooBar发生点儿只主意,但是偏偏发一个foo凡是空虚的,所以FooBar啊是一个函数接口,并且可应用lambda表达式创建FooBar,例如:

useFooBar(3, x -> x * x)

拿会回9。

由此Fibers实现轻量级并发控制

产生广大人口及自我平,都针对出现数据结构感兴趣,而立等同片是JVM的后花园。一方面,JVM对于CPU的面世原语提供了初级方法要CAS结构和内存栅栏,另一方面结合内存回收机制提供了阳台中立的内存模型。但是,对那些使用并发控制的程序员来说,并无是为扩大他们之软件,而采用并发控制,而是他俩只能利用并发控制而自己之软件而扩大。从立面说,Java并作控制并无是怪好,是发生题目。

委,Java从开就是深受规划改为出现控制,并且在每一个本子被还强调他的产出控制数据结构。Java都大质量之实现了多雅管用的起数据结构(如并发HashMap,并发SkipListMap,并发LinkedQueue),有些还并未当Erlang和Go中实现。Java的面世控制一般性领先c++5年或更增长的时日。但是若会意识科学高效地应用这些出现控制数据结构异常紧。当我们应用线程和钉经常,刚开你见面发觉她工作之百般好,到了背后当你用再多并作控制时,发现这些点子不能够好好之扩展。然后我们使用线程池和事件,这片独东西发生甚好之扩展性,但是若晤面发现死麻烦去讲共享变量,特别是在语言级别没有针对共享变量的可变性进行限。进一步说,如果您的题目是内核级线程不能够很好之恢弘,那么对事件之异步处理是一个坏想法。为什么未略修补线程的题材啊?这刚刚是Erlang和Go所采用的计:轻量级的用户线程。轻量级用户线程通过简单,阻塞式的编程方法迅速利用并结构,将内核级的出现控制映射到程序级的起控制,而不用牺牲可扩展性,同时比锁和信号还简明。

Quasar是一个我们创建的开源库,它吃JVM增加了着实的轻量级线程(在Quasar叫纤程),同得能生好的和系统级线程很好于齐的做事。Quasar同Go的CSP一样,同时发出一个基结Erlang的Actor系统。对付并发控制,纤程是一个那个好的选。纤程简单、优美和便捷。现在于咱们来探望它:

率先,我们安构建脚本,添加以下的代码在build.gradle中:

configurations {
    quasar
}

dependencies {
    compile "co.paralleluniverse:quasar-core:0.5.0:jdk8"
    quasar "co.paralleluniverse:quasar-core:0.5.0:jdk8"
}

run {
    jvmArgs "-javaagent:${configurations.quasar.iterator().next()}" // gradle should make this simpler, too
}

履新依赖,编辑Main.java:

package jmodern;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.strands.Strand;
import co.paralleluniverse.strands.channels.Channel;
import co.paralleluniverse.strands.channels.Channels;

public class Main {
    public static void main(String[] args) throws Exception {
        final Channel<Integer> ch = Channels.newChannel(0);

        new Fiber<Void>(() -> {
            for (int i = 0; i < 10; i++) {
                Strand.sleep(100);
                ch.send(i);
            }
            ch.close();
        }).start();

        new Fiber<Void>(() -> {
            Integer x;
            while((x = ch.receive()) != null)
                System.out.println("--> " + x);
        }).start().join(); // join waits for this fiber to finish
    }
}

现起经过channel,有个别只纤程可以开展通信。

Strand.sleep,和Strand类似的所有方,在原来生Java线程和fiber中还能够好好之运行。现在咱们用首先只fiber替换成原生的线程:

new Thread(Strand.toRunnable(() -> {
    for (int i = 0; i < 10; i++) {
        Strand.sleep(100);
        ch.send(i);
    }
    ch.close();
})).start();

即吗运行的死好(当然我们曾经于咱们的以被运行百万层的fiber,也因而了几千线程)。

咱处于时而channel selection (模拟Go的select)。

package jmodern;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.strands.Strand;
import co.paralleluniverse.strands.channels.Channel;
import co.paralleluniverse.strands.channels.Channels;
import co.paralleluniverse.strands.channels.SelectAction;
import static co.paralleluniverse.strands.channels.Selector.*;

public class Main {
    public static void main(String[] args) throws Exception {
        final Channel<Integer> ch1 = Channels.newChannel(0);
        final Channel<String> ch2 = Channels.newChannel(0);

        new Fiber<Void>(() -> {
            for (int i = 0; i < 10; i++) {
                Strand.sleep(100);
                ch1.send(i);
            }
            ch1.close();
        }).start();

        new Fiber<Void>(() -> {
            for (int i = 0; i < 10; i++) {
                Strand.sleep(130);
                ch2.send(Character.toString((char)('a' + i)));
            }
            ch2.close();
        }).start();

        new Fiber<Void>(() -> {
            for (int i = 0; i < 10; i++) {
                SelectAction<Object> sa
                        = select(receive(ch1),
                                receive(ch2));
                switch (sa.index()) {
                    case 0:
                        System.out.println(sa.message() != null ? "Got a number: " + (int) sa.message() : "ch1 closed");
                        break;
                    case 1:
                        System.out.println(sa.message() != null ? "Got a string: " + (String) sa.message() : "ch2 closed");
                        break;
                }
            }
        }).start().join(); // join waits for this fiber to finish
    }
}

自从Quasar
0.6.0开始,可以当甄选状态中采取下lambda表达式,最新的代码可以形容成这么:

for (int i = 0; i < 10; i++) {
    select(
        receive(ch1, x -> System.out.println(x != null ? "Got a number: " + x : "ch1 closed")),
        receive(ch2, x -> System.out.println(x != null ? "Got a string: " + x : "ch2 closed")));
}

探望fiber的大性能io:

package jmodern;

import co.paralleluniverse.fibers.*;
import co.paralleluniverse.fibers.io.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.*;
import java.nio.charset.*;

public class Main {
    static final int PORT = 1234;
    static final Charset charset = Charset.forName("UTF-8");

    public static void main(String[] args) throws Exception {
        new Fiber(() -> {
            try {
                System.out.println("Starting server");
                FiberServerSocketChannel socket = FiberServerSocketChannel.open().bind(new InetSocketAddress(PORT));
                for (;;) {
                    FiberSocketChannel ch = socket.accept();
                    new Fiber(() -> {
                        try {
                            ByteBuffer buf = ByteBuffer.allocateDirect(1024);
                            int n = ch.read(buf);
                            String response = "HTTP/1.0 200 OK\r\nDate: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
                                            + "Content-Type: text/html\r\nContent-Length: 0\r\n\r\n";
                            n = ch.write(charset.newEncoder().encode(CharBuffer.wrap(response)));
                            ch.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        System.out.println("started");
        Thread.sleep(Long.MAX_VALUE);
    }
}

我们开了哟?首先我们启动了一个直接循环的fiber,用于收纳TCP连接。对于每一个连上之连日,这个fiber会启动另外一个fiber去读请求,发送回应,然后关门。这段代码是死IO的,在后台使用异步EPoll
IO,所以她同异步IO服务器,有同一的扩展性。(我们拿当Quasar中大幅度的增高IO性能)。

只是容错的Actor和热代码的变

Actor模型,受欢迎是产生一半原因是Erlang,意图是编写而容错,高而保护的使。它用以细分成单身可容错的器皿单元-Actors,标准化处理不当受还原措施。

当我们开Actor,将compile "co.paralleluniverse:quasar-actors:0.5.0"
加到公的构建脚论被的依中失。

咱俩重写Main函数,要让咱们的运用可容错,代码会变的更错综复杂。

package jmodern;

import co.paralleluniverse.actors.*;
import co.paralleluniverse.fibers.*;
import co.paralleluniverse.strands.Strand;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) throws Exception {
        new NaiveActor("naive").spawn();
        Strand.sleep(Long.MAX_VALUE);
    }

    static class BadActor extends BasicActor<String, Void> {
        private int count;

        @Override
        protected Void doRun() throws InterruptedException, SuspendExecution {
            System.out.println("(re)starting actor");
            for (;;) {
                String m = receive(300, TimeUnit.MILLISECONDS);
                if (m != null)
                    System.out.println("Got a message: " + m);
                System.out.println("I am but a lowly actor that sometimes fails: - " + (count++));

                if (ThreadLocalRandom.current().nextInt(30) == 0)
                    throw new RuntimeException("darn");

                checkCodeSwap(); // this is a convenient time for a code swap
            }
        }
    }

    static class NaiveActor extends BasicActor<Void, Void> {
        private ActorRef<String> myBadActor;

        public NaiveActor(String name) {
            super(name);
        }

        @Override
        protected Void doRun() throws InterruptedException, SuspendExecution {
            spawnBadActor();

            int count = 0;
            for (;;) {
                receive(500, TimeUnit.MILLISECONDS);
                myBadActor.send("hi from " + self() + " number " + (count++));
            }
        }

        private void spawnBadActor() {
            myBadActor = new BadActor().spawn();
            watch(myBadActor);
        }

        @Override
        protected Void handleLifecycleMessage(LifecycleMessage m) {
            if (m instanceof ExitMessage && Objects.equals(((ExitMessage) m).getActor(), myBadActor)) {
                System.out.println("My bad actor has just died of '" + ((ExitMessage) m).getCause() + "'. Restarting.");
                spawnBadActor();
            }
            return super.handleLifecycleMessage(m);
        }
    }
}

代码中生一个NaiveActor有一个BadActor,这个来出来的底Actor会偶然失败。由于我们的父actor监控子Actor,当子Actor过早的要命去,父actor会得到通知,然后重新启航一个初的Actor。

斯事例,Java相当之讨厌,特别是当她之所以instanceof测试消息之类型和转移消息的门类的当儿。这单通过模式匹配Clojure同Kotlin做的可比好(以后我会犯一样首关于Kotlin的篇章)。所以,是的,所有的路检查以及类型转换相当另人讨厌。这种类型代码鼓励而去摸索一下Kotlin,你真的该去使用一下(我就是试过,我杀喜欢Kotlin,但是要是用于生产环境下她还有待成熟)。就个人来说,这种令人作呕非常小。

返回主要问题来。一个基于Actor的可容错系统关键的组件是压缩宕机时间管是出于使用的不当,还是由于系统保障。我们用于次片追JVM的治本,接下去展示一下Actor的热代码交换。

每当热代码交换的问题达成,有几乎种植艺术(例如:JMX,将以其次有说话)。但是本我们通过监控文件系统来贯彻。首先以列目录下开创一个叫modules分段文件夹,在build.gradlerun加加以下代码:

systemProperty "co.paralleluniverse.actors.moduleDir", "${rootProject.projectDir}/modules"

开辟终端,启动程序。程序启动后,回到IDE,修改BadActor

@Upgrade
static class BadActor extends BasicActor<String, Void> {
    private int count;

    @Override
    protected Void doRun() throws InterruptedException, SuspendExecution {
        System.out.println("(re)starting actor");
        for (;;) {
            String m = receive(300, TimeUnit.MILLISECONDS);
            if (m != null)
                System.out.println("Got a message: " + m);
            System.out.println("I am a lowly, but improved, actor that still sometimes fails: - " + (count++));

            if (ThreadLocalRandom.current().nextInt(100) == 0)
                throw new RuntimeException("darn");

            checkCodeSwap(); // this is a convenient time for a code swap
        }
    }
}

俺们加了@Upgrade注解,因为我们纪念给这仿佛进行升级换代,这个类似修改后砸变少了。现在次还以运作,新开始一个终极,通过gradle jar,重新构建程序。不熟悉java程序员,JAR(Java
Archive)用来起包Java模块(在第二部分会讨论Java打包和布局)。最后,在次独极端中,复制build/libs/jmodern.jarmodeules文本夹着,使用命令:

cp build/libs/jmodern.jar modules

而会见到程序更新运行了(这个时节在你的操作系统,大概要十秒)。注意不像咱于挫折后再行起动BadActor,当我们交换代码时,程序中的中变量保存下来了。

筹一个基于Actor设计而容错的网是一个良特别的主题,但是本人愿意而曾针对性其有点感觉。

尖端话题:可插拔类型

毕之前,我们用探讨一个惊险的天地。我们对接下去介绍的家伙还从未参加到现代Java开发工具箱被,因为使用她还是异常麻烦,不过其以会从IDE融合着收获好处,现在是家伙还非常陌生。虽然这样,如果这家伙持继开发以不断长,它带来的可能大之特别,如果他莫见面在疯子手中被乱用,它用会见怪有价,这即是为何咱们拿它们列在此处。

当Java8蒙,一个机密最实用之新特性,是路注解和可拔类型系统。Java编绎器现在同意以另地方多对项目的笺注(一会我们举个例子)。这里做注解预处理器,打发可插拔类型系统。这些是可选的项目系统,可以关闭或打开,能被Java代码够长强大的基于项目检查的静态验证功能。Checker框架不畏这么一个仓房,它同意高档开发者写好的可插拔类型系统,包括继续,类型接口等。它好包了几栽类型系统,如检查只是空类型,污染种类,正则表达式,物理单位项目,不可变数据等等。

Checker目前还非克可怜好之同IDE一起干活,所有这节,我拿未采用IDE。首先修改build.gradle,增加:

configurations {
    checker
}

dependencies {
    checker 'org.checkerframework:jdk8:1.8.1'
    compile 'org.checkerframework:checker:1.8.1'
}

顶相应的configurations,dependencies部分。

然后,增加下面有及构建文件被:

compileJava {
    options.fork = true
    options.forkOptions.jvmArgs = ["-Xbootclasspath/p:${configurations.checker.asPath}:${System.getenv('JAVA_HOME')}/lib/tools.jar"]
    options.compilerArgs = ['-processor', 'org.checkerframework.checker.nullness.NullnessChecker,org.checkerframework.checker.units.UnitsChecker,org.checkerframework.checker.tainting.TaintingChecker']
}

巧使己说之,笨重的。

终极一行说明我们以Checker的空值类型系统,物理单位项目系统,污染数据类型系统。

当今咱们召开片试行。首先,试一下空值类型系统,他能够防止空指针的荒唐。

package jmodern;

import org.checkerframework.checker.nullness.qual.*;

public class Main {
    public static void main(String[] args) {
        String str1 = "hi";
        foo(str1); // we know str1 to be non-null

        String str2 = System.getProperty("foo");
        // foo(str2); // <-- doesn't compile as str2 may be null
        if (str2 != null)
            foo(str2); // after the null test it compiles
    }

    static void foo(@NonNull String s) {
        System.out.println("==> " + s.length());
    }
}

Checker的开发者很团结,注解了全套JD可空的归来路,所以当有@NonNull注解时,从库中返回值不要回来null值,。

连着下,我们尝试一下单位类型系统,防止单位类型转换错误。

package jmodern;

import org.checkerframework.checker.units.qual.*;

public class Main {
    @SuppressWarnings("unsafe") private static final @m int m = (@m int)1; // define 1 meter
    @SuppressWarnings("unsafe") private static final @s int s = (@s int)1; // define 1 second

    public static void main(String[] args) {
        @m double meters = 5.0 * m;
        @s double seconds = 2.0 * s;
        // @kmPERh double speed = meters / seconds; // <-- doesn't compile
        @mPERs double speed = meters / seconds;

        System.out.println("Speed: " + speed);
    }
}

十分酷吧,根据Checker的文档,你吗可以定义自己之物理单位。

说到底,试试污染项目系统,它亦可协助你跟被传染(潜在的责任险)的数,例如用户数录入的数目:

package jmodern;

import org.checkerframework.checker.tainting.qual.*;

public class Main {
    public static void main(String[] args) {
        // process(parse(read())); // <-- doesn't compile, as process cannot accept tainted data
        process(parse(sanitize(read())));
    }

    static @Tainted String read() {
        return "12345"; // pretend we've got this from the user
    }

    @SuppressWarnings("tainting")
    static @Untainted String sanitize(@Tainted String s) {
        if(s.length() > 10)
            throw new IllegalArgumentException("I don't wanna do that!");
        return (@Untainted String)s;
    }

    // doesn't change the tainted qualifier of the data
    @SuppressWarnings("tainting")
    static @PolyTainted int parse(@PolyTainted String s) {
        return (@PolyTainted int)Integer.parseInt(s); // apparently the JDK libraries aren't annotated with @PolyTainted
    }

    static void process(@Untainted int data) {
        System.out.println("--> " + data);
    }
}

Checker通过类型接口给于Java可插拔交互类型。并且可以透过工具葡京娱乐棋牌官网和预编绎库增加品种注解。Haskell都开不顶即一点。

Checker还尚未到他的黄金时段,如果下明智之言语,它见面化为现代Java开发者手中强有力的家伙之一。

结束

咱俩曾观望了Java8遭之变,还来照应现代之工具与货栈,Java相对于跟原的版本的话,相似性不赛。但是Java仍然是巨型应用中之长,而且Jva和其的生态圈比新的简易的语言,更为成熟与迅速。我们了解现代Java程序员是何许形容代码的,但是我们充分不便平开便解开Java和Jvm的漫天力。特别当我们清楚了Java的督查与性能分析工具,和新的微应用网络以开发框架。在对接下的文章被我们会谈及马上几只话题。

比方你想了解一个开端,亚有,我们见面谈论现代Java打包方法(使用Capsule,有硌像npm,但是更酷),监控以及治本(使用VisualVM,
JMX,
Jolokia
和Metrics)
,性能分析(使用 Java Flight
Recorder,
Mission
Control,

Byteman),基准测试(JMH)。其三有些,我们见面谈论用Dropwizard,Comsat和Web
Actors,JSR-330写一个轻量级可扩大的HTTP服务。

初稿地址:Not Your Father’s Java: An Opinionated Guide to Modern Java
Development, Part
1

发表评论

电子邮件地址不会被公开。 必填项已用*标注