[翻译]现代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

相关文章