标签 Java 下的文章

这篇漫画生动的解释了 Java 的垃圾回收机制:什么是垃圾回收(GC)、什么是并行垃圾回收以及什么是垃圾回收的标记清除算法。

如果你想更深入的了解,不妨看一下垃圾回收) 和并行标记清除算法


via: https://turnoff.us/geek/java-gc-explained/

作者:Daniel Stori 译者&点评:bestony 校对&合成:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

开始投入一件事比远离它更容易。 — Donald Rumsfeld

没有 Kotlin 的生活就像在触摸板上玩魔兽争霸 3。购买鼠标很简单,但如果你的新雇主不想让你在生产中使用 Kotlin,你该怎么办?

下面有一些选择。

  • 与你的产品负责人争取获得使用 Kotlin 的权利。
  • 使用 Kotlin 并且不告诉其他人因为你知道最好的东西是只适合你的。
  • 擦掉你的眼泪,自豪地使用 Java。

想象一下,你在和产品负责人的斗争中失败,作为一个专业的工程师,你不能在没有同意的情况下私自去使用那些时髦的技术。我知道这听起来非常恐怖,特别当你已经品尝到 Kotlin 的好处时,不过不要失去生活的信念。

在文章接下来的部分,我想简短地描述一些 Kotlin 的特征,使你通过一些知名的工具和库,可以应用到你的 Android 里的 Java 代码中去。对于 Kotlin 和 Java 的基本认识是需要的。

数据类

我想你肯定已经喜欢上 Kotlin 的数据类。对于你来说,得到 equals()hashCode()toString()copy() 这些是很容易的。具体来说,data 关键字还可以按照声明顺序生成对应于属性的 componentN() 函数。 它们用于解构声明。

data class Person(val name: String)
val (riddle) = Person("Peter")
println(riddle)

你知道什么会被打印出来吗?确实,它不会是从 Person 类的 toString() 返回的值。这是解构声明的作用,它赋值从 nameriddle。使用园括号 (riddle) 编译器知道它必须使用解构声明机制。

val (riddle): String = Person("Peter").component1()
println(riddle) // prints Peter)
这个代码没编译。它就是展示了构造声明怎么工作的。

正如你可以看到 data 关键字是一个超级有用的语言特性,所以你能做什么把它带到你的 Java 世界? 使用注释处理器并修改抽象语法树(Abstract Syntax Tree)。 如果你想更深入,请阅读文章末尾列出的文章(Project Lombok— Trick Explained)。

使用项目 Lombok 你可以实现 data关键字所提供的几乎相同的功能。 不幸的是,没有办法进行解构声明。

import lombok.Data;

@Data class Person {
    final String name;
}

@Data 注解生成 equals()hashCode()toString()。 此外,它为所有字段创建 getter,为所有非最终字段创建setter,并为所有必填字段(final)创建构造函数。 值得注意的是,Lombok 仅用于编译,因此库代码不会添加到您的最终的 .apk。

Lambda 表达式

Android 工程师有一个非常艰难的生活,因为 Android 中缺乏 Java 8 的特性,而且其中之一是 lambda 表达式。 Lambda 是很棒的,因为它们为你减少了成吨的样板。 你可以在回调和流中使用它们。 在 Kotlin 中,lambda 表达式是内置的,它们看起来比它们在 Java 中看起来好多了。 此外,lambda 的字节码可以直接插入到调用方法的字节码中,因此方法计数不会增加。 它可以使用内联函数。

button.setOnClickListener { println("Hello World") }

最近 Google 宣布在 Android 中支持 Java 8 的特性,由于 Jack 编译器,你可以在你的代码中使用 lambda。还要提及的是,它们在 API 23 或者更低的级别都可用。

button.setOnClickListener(view -> System.out.println("Hello World!"));

怎样使用它们?就只用添加下面几行到你的 build.gradle 文件中。

defaultConfig {
    jackOptions {
        enabled true
    }
}

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

如果你不喜欢用 Jack 编译器,或者你由于一些原因不能使用它,这里有一个不同的解决方案提供给你。Retrolambda 项目允许你在 Java 7,6 或者 5 上运行带有 lambda 表达式的 Java 8 代码,下面是设置过程。

dependencies {
    classpath 'me.tatarka:gradle-retrolambda:3.4.0'
}

apply plugin: 'me.tatarka.retrolambda'

compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

正如我前面提到的,在 Kotlin 下的 lambda 内联函数不增加方法计数,但是如何在 Jack 或者 Retrolambda 下使用它们呢? 显然,它们不是没成本的,隐藏的成本如下。

该表展示了使用不同版本的 Retrolambda 和 Jack 编译器生成的方法数量。该比较结果来自 Jake Wharton 的“探索 Java 的隐藏成本” 技术讨论之中。

数据操作

Kotlin 引入了高阶函数作为流的替代。 当您必须将一组数据转换为另一组数据或过滤集合时,它们非常有用。

fun foo(persons: MutableList<Person>) {
    persons.filter { it.age >= 21 }
           .filter { it.name.startsWith("P") }
           .map { it.name }
           .sorted()
           .forEach(::println)
}

data class Person(val name: String, val age: Int)

流也由 Google 通过 Jack 编译器提供。 不幸的是,Jack 不使用 Lombok,因为它在编译代码时跳过生成中间的 .class 文件,而 Lombok 却依赖于这些文件。

void foo(List<Person> persons) {
    persons.stream()
           .filter(it -> it.getAge() >= 21)
           .filter(it -> it.getName().startsWith("P"))
           .map(Person::getName)
           .sorted()
           .forEach(System.out::println);
}

class Person {
    final private String name;
    final private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    String getName() { return name; }
    int getAge() { return age; }
}

这简直太好了,所以 catch 在哪里? 令人悲伤的是,流从 API 24 才可用。谷歌做了好事,但哪个应用程序有用 minSdkVersion = 24

幸运的是,Android 平台有一个很好的提供许多很棒的库的开源社区。Lightweight-Stream-API 就是其中的一个,它包含了 Java 7 及以下版本的基于迭代器的流实现。

import lombok.Data;
import com.annimon.stream.Stream;

void foo(List<Person> persons) {
    Stream.of(persons)
          .filter(it -> it.getAge() >= 21)
          .filter(it -> it.getName().startsWith("P"))
          .map(Person::getName)
          .sorted()
          .forEach(System.out::println);
}

@Data class Person {
    final String name;
    final int age;
}

上面的例子结合了 Lombok、Retrolambda 和 Lightweight-Stream-API,它看起来几乎和 Kotlin 一样棒。使用静态工厂方法允许您将任何 Iterable 转换为流,并对其应用 lambda,就像 Java 8 流一样。 将静态调用 Stream.of(persons) 包装为 Iterable 类型的扩展函数是完美的,但是 Java 不支持它。

扩展函数

扩展机制提供了向类添加功能而无需继承它的能力。 这个众所周知的概念非常适合 Android 世界,这就是 Kotlin 在该社区很受欢迎的原因。

有没有技术或魔术将扩展功能添加到你的 Java 工具箱? 因 Lombok,你可以使用它们作为一个实验功能。 根据 Lombok 文档的说明,他们想把它从实验状态移出,基本上没有什么变化的话很快。 让我们重构最后一个例子,并将 Stream.of(persons) 包装成扩展函数。

import lombok.Data;
import lombok.experimental.ExtensionMethod;

@ExtensionMethod(Streams.class)
public class Foo {
    void foo(List<Person> persons) {
        persons.toStream()
               .filter(it -> it.getAge() >= 21)
               .filter(it -> it.getName().startsWith("P"))
               .map(Person::getName)
               .sorted()
               .forEach(System.out::println);
    }
}

@Data class Person {
    final String name;
    final int age;
}

class Streams {
    static <T> Stream<T> toStream(List<T> list) {
        return Stream.of(list);
    }
}

所有的方法是 publicstatic 的,并且至少有一个参数的类型不是原始的,因而是扩展方法。 @ExtensionMethod 注解允许你指定一个包含你的扩展函数的类。 你也可以传递数组,而不是使用一个 .class 对象。


我完全知道我的一些想法是非常有争议的,特别是 Lombok,我也知道,有很多的库,可以使你的生活更轻松。请不要犹豫在评论里分享你的经验。干杯!


作者简介:

Coder and professional dreamer @ Grid Dynamics


via: https://medium.com/proandroiddev/living-android-without-kotlin-db7391a2b170

作者:Piotr Ślesarew 译者:DockerChen 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

随着需求不断的增加,数据管理也将变得越发复杂。

队列 list 集合 set ,甚至用 映射 map 来解决……

迟早有一天,坐在办公桌前的鸭子爸爸发现,这些只不过都是个“ 堆栈 stack ”而已~

via: https://turnoff.us/geek/java-collections/

作者:Daniel Stori 译者&点评:martin2011qi 校对&合成:GHLandy

本文由 LCTT 原创编译,Linux中国 荣誉推出

刚开始学习软件工程的时候,我们经常会碰到像这样的事情:

软件应该符合 SOLID 原则。

但这句话实际是什么意思?让我们看看 SOLID 中每个字母在架构里所代表的重要含义,例如:

简单来说,我们需要提供一个类,这个类有它所需要的所有对象,以便实现其功能。

概述

依赖注入听起来像是描述非常复杂的东西的一个术语,但实际上它很简单,看下面这个例子你就明白了:

class NoDependencyInjection {
  private Dependency d;

  public NoDependencyInjection() {
    d = new Dependency();
  }
}

class DependencyInjection {
  private Dependency d;

  public DependencyInjection(Dependency d) {
    this.d = d;
  }
}

正如我们所见,第一种情况是我们在构造器里创建了依赖对象,但在第二种情况下,它作为参数被传递给构造器,这就是我们所说的 依赖注入 dependency injection 。这样做是为了让我们所写的类不依靠特定依赖关系的实现,却能直接使用它。

参数传递的目标是构造器,我们就称之为构造器依赖注入;或者是某个方法,就称之为方法依赖注入:

class Example {
  private ConstructorDependency cd;
  private MethodDependency md;
  Example(ConstructorDependency cd) {
    this.cd = cd; //Constructor Dependency Injection
  }

  public setMethodDependency(MethodDependency md) {
    this.md = md; //Method Dependency Injection
  }
}

要是你想总体深入地了解依赖注入,可以看看由 Dan Lew 发表的精彩的演讲,事实上是这个演讲启迪了这篇概述。

在 Android 平台,当需要框架来处理依赖注入这个特殊的问题时,我们有不同的选择,其中最有名的框架就是 Dagger 2。它最开始是由 Square 公司(LCTT 译注:Square 是美国一家移动支付公司)的一些很棒的开发者开发出来的,然后慢慢发展成由 Google 自己开发。首先开发出来的是 Dagger 1,然后 Big G 接手这个项目发布了第二个版本,做了很多改动,比如以 注解 annotation 为基础,在编译的时候完成其任务。

导入框架

安装 Dagger 并不难,但需要导入 android-apt 插件,通过向项目的根目录下的 build.gradle 文件中添加它的依赖关系:

buildscript{
  ...
  dependencies{
    ...
    classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’
  }
}

然后,我们需要将 android-apt 插件应用到项目 build.gradle 文件,放在文件顶部 Android application 那一句的下一行:

apply plugin: ‘com.neenbedankt.android-apt’

这个时候,我们只用添加依赖关系,然后就能使用库及其 注解 annotation 了:

dependencies{
    ...
    compile ‘com.google.dagger:dagger:2.6’ 
    apt ‘com.google.dagger:dagger-compiler:2.6’
    provided ‘javax.annotation:jsr250-api:1.0’
}
需要加上最后一个依赖关系是因为 @Generated 注解在 Android 里还不可用,但它是原生的 Java 注解

Dagger 模块

要注入依赖,首先需要告诉框架我们能提供什么(比如说上下文)以及特定的对象应该怎样创建。为了完成注入,我们用 @Module 注释对一个特殊的类进行了注解(这样 Dagger 就能识别它了),寻找 @Provide 注解的方法,生成图表,能够返回我们所请求的对象。

看下面的例子,这里我们创建了一个模块,它会返回给我们 ConnectivityManager,所以我们要把 Context 对象传给这个模块的构造器。

@Module
public class ApplicationModule {
  private final Context context;

  public ApplicationModule(Context context) {
    this.context = context;
  }

  @Provides @Singleton
  public Context providesContext() {
    return context;
  }

  @Provides @Singleton
  public ConnectivityManager providesConnectivityManager(Context context) {
    return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  }
}
Dagger 中十分有意思的一点是简单地注解一个方法来提供一个单例(Singleton),就能处理所有从 Java 中继承过来的问题。

组件

当我们有一个模块的时候,我们需要告诉 Dagger 想把依赖注入到哪里:我们在一个 组件 Component 里完成依赖注入,这是一个我们特别创建的特殊注解接口。我们在这个接口里创造不同的方法,而接口的参数是我们想注入依赖关系的类。

下面给出一个例子并告诉 Dagger 我们想要 MainActivity 类能够接受 ConnectivityManager(或者在图表里的其它依赖对象)。我们只要做类似以下的事:

@Singleton
@Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {

  void inject(MainActivity activity);
}
正如我们所见,@Component 注解有几个参数,一个是所支持的模块的数组,代表它能提供的依赖。这里既可以是 Context 也可以是 ConnectivityManager,因为它们在 ApplicationModule 类中有声明。

用法

这时,我们要做的是尽快创建组件(比如在应用的 onCreate 阶段)并返回它,那么类就能用它来注入依赖了:

为了让框架自动生成 DaggerApplicationComponent,我们需要构建项目以便 Dagger 能够扫描我们的代码,并生成我们需要的部分。

MainActivity 里,我们要做的两件事是用 @Inject 注解符对想要注入的属性进行注解,调用我们在 ApplicationComponent 接口中声明的方法(请注意后面一部分会因我们使用的注入类型的不同而变化,但这里简单起见我们不去管它),然后依赖就被注入了,我们就能自由使用他们:

public class MainActivity extends AppCompatActivity {
  @Inject
  ConnectivityManager manager;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    ...
    ((App) getApplication()).getComponent().inject(this);
  }
}

总结

当然了,我们可以手动注入依赖,管理所有不同的对象,但 Dagger 消除了很多比如模板这样的“噪声”,给我们提供有用的附加品(比如 Singleton),而仅用 Java 处理将会很糟糕。


via: https://medium.com/di-101/di-101-part-1-81896c2858a0#.3hg0jj14o

作者:Roberto Orgiu 译者:GitFuture 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Java 是免费的?你想多了。Oracle 的律师已经在赶来的路上了。

Oracle 开始追索 Java SE 许可费用

在收购了 Sun 微系统公司六年之后,Oracle 终于开始大张旗鼓地审计 Java 客户,他们接触了越来越多的 Oracle 客户和合作伙伴(没错,包括合作伙伴),宣称它们违背了它的许可协议!

2010 年,随着 Oracle 收购 Sun 微系统公司的同时,它也拥有了 Java,但直到现在,它的 许可管理服务部门 License Management Services (LMS)才开始追着人们讨债。

这家数据库巨头今年在全球雇佣了 20 名雇员,他们的唯一的工作就是向违反了他们的 Java 许可协议的企业追索费用。作为呼应,行业合规专家组也在扩招人马,雇佣 Java 专家,预计 2017 年会有来自 LMS 针对 Java 的更多行动。此举牵扯到大笔金额,而使用 Java 的客户们则面临数万乃至于数十万美元的追缴。

这次引起争论的 Java 版本是 Java SE,有三个付费的分支版本,每用户付费为 $40 到 $300,每处理器付费为 $5000 到 $15000。

The Register 消息,某零售业拥有八万台 PC 的客户已经接到了 Oracle 通知,声称其违背了 Java 协议。 Oracle 还告诉另一个 Java 客户它欠了十万美金 – 不过这个帐单后来被砍到了三万美金。

某不具名专家称,“Oracle 已经将此事定性为是一个 问题 issue 。”。更令人吃惊的是,据 Palisade Compliance 的 CEO 兼创始人 Craig Guarente 说,Oracle 此举不只针对客户,连合作伙伴也一视同仁。

“Oracle 也同样针对了其合作伙伴。这使人们感到愤怒,因为他们在一直在帮助 Oracle 啊!” 他告诉我们。合作伙伴们想知道:“Oracle 怎么可以这样对待我们?!”

Java 是开源的,但是 Java SE 的安装包不是免费的

导致这一切的根本原因是大家对 Java 错误的认知,都认为 Java 是“免费”的!

这种观点可以追溯到 Sun 时代; Sun 下的 Java 是免费可用的,在 Oracle 下也是!但是 Sun 也确实曾对 IBM 和蓝光播放器制作商等收取许可证费用,尽管对绝大多数的使用者是不收费的。这是因为 Sun 利用 Java 作为“楔子的尖端”,帮助其推动销售它的系统。

而 Oracle 决定让 Java 商业化的步伐迈得更大——至于会不会扯着什么,谁知道呢。

Java SE 是一个泛称,它的下载版本有很多,包括 Oracle 在 2014 年 2 月推出的 Java SE Advanced Desktop,以及在 2011 年 5 月推出的 Java SE Advanced 和 Java SE Suite。Java SE 是免费的没错,但是这几个版本都是付费的。

Java SE 包括免费的 JDK 和 JRE,但是 Advanced Desktop、Java SE Advanced 和 Java SE Suite 具备额外的功能,比如 Java Mission Control 和 Flight Recorder (也叫 JRockit Mission Control 和 JRockit Flight Recorder)。

此外还有用于大规模 Java 部署的 Microsoft Windows Installer Enterprise JRE Installer。

Java SE 在用于 Oracle 所谓的“通用计算”的场合时是免费的 – 用其许可协议中的话来说,涉及到的设备包括桌面电脑、笔记本、智能手机和平板电脑。但在 Oracle 许可协议定义为“用于智能系统中专业嵌入式计算机”场合使用时,不是免费的,后来 Oracle 又进一步对此作了补充定义,除了移动电话、手持设备、网络交换机以及蓝光播放器等。

听起来很简单,不是吗?但是正是在这些通用设备上的客户在受到 LMS 的打击。原因是没有办法在下载时将那些需要付费的 Java SE 子产品从免费的 Java SE 封装之中剥离开来,因为 Oracle 并没有提供单独的安装软件。当你只想用其中的一部分功能时,你只能成为 Java SE Suite 这个大套件的指定用户,然后相应付费。

如果你根据你的 app 开发情况,想要大规模的部署 Java SE,则需要 Microsoft Windows Installer Enterprise JRE Installer 这个工具 ,然而这也不是免费 Java SE 的一部分。

“人们没有意识到,他们以为 Jave 是免费的 - 因为它是开源的,所以你可以使用它。不是合约不清晰,而是这里有一个基本的误解。”Guarente 告诉我们。

某不愿具名的合规专家也补充说:

如果你下载了 Java,你就得到了全部软件,你需要确保安装你被授权的组件,而且要将不使用的部分移除。“商业用途”是指对任何付费功能的使用。“通用目的”是很含糊的定义,因此导致了很多争议。

一旦你作为一个组织分发了一些 Java 的东西给终端用户 – 越来越多的公司在这样做,他们发布 app,用户可以获得产品和服务 - 这就不再称为“通用目的”了...Oracle 要从中收取费用。

为什么 Oracle 现在才收网?

为什么 Oracle 现在才行动,在通过收购 Sun 获得 Java 所有权的六年以后?

人们普遍认为,LMS 用了很长的时间来设计审计方法并详细了解客户 Java 的使用状态,等待猪养肥了。

LMS 现在已经做好准备,要在 2017 年向 Java SE 用户开刀了。

“我预计 Oracle 2017 年会继续更加关注此事。所有趋势显示,Oracle 的 LMS 审计团队在变得更加强势,他们在试图得到比往年更高的收入。我不认为 2017 年 Oracle 会变得温和仁慈。”Guarente 说道。

你们该怎么做呢?

专家们现在建议下载 Java SE 时要格外小心,而那些已经下载过的客户则要重新审视下使用的方式,在 LMS 找上门来之前做好准备。 据闻,2016 下半年被 LMS 接触过的客户数量有了较大增长,他们纷纷寻求 Java 许可方面的帮助。

“如果你下载了 Java,你就得到了全部软件,你需要确保只安装你被授权的组件,而且要将不使用的部分移除。”专家警告说。“如果你已经有了 Java 应用,先确定你实际使用的特定组件,以及你们在怎样使用它们,然后基于这些,在 Oracle 找到你们前,验证你们是否有使用问题。”

截止到现在,仍未看到 Oracle 对此的官方回应。

参考:The RegisterLMS

沉默了九个月之后,自去年十月份的 Java One 大会之后一直没有对 Java EE 的发展停滞进行回应的甲骨文终于对外透露了该公司在 Java EE 方面的发展计划。

正如之前 arstechnica 的报道中所说,自去年秋天伊始,甲骨文在 Java EE 项目上的开发投入就已经几乎完全停止了,该公司不但将该项目上的工程师们投入到了其它项目当中,而且对社区的意见和请愿视若不闻,将 Java 社区进程 Java Community Process (JCP)执行委员会的成员们和 Java EE 的合作伙伴们丢在了深深绝望之中。

据 Java EE 守护者的观察,甲骨文在其自己的 JavaServer Faces 开发上的几个月前就陷入了完全停止,如下图:

甲骨文在其自己的 JSF 实现上的提交数变化

甲骨文在其自己的 JSF 实现上的解决问题数的变化

此事经由 arstechnica 报道之后,在整个技术世界引来了巨大反响。作为对这些质疑和请愿的回应,甲骨文的发言人 Mike Moeller 通过邮件发布了下述申明:

甲骨文致力于 Java 的发展,已经制定好了 Java EE 规范的下一个版本 Java EE 8 草案。 Java EE 8 将支持那些寻求在大规模分布式计算和基于容器的云环境中设计使用微服务来构建新应用的开发者们。甲骨文正在与 Java 社区中的关键合作伙伴们密切合作,以最终推出该草案,并将在九月份的 Java One 大会上与广大的 Java 社区分享该草案的完整细节。

按其说法,甲骨文将于今年九月份在旧金山召开的 Java One 大会上发布相关信息,并于 2017 年上半年发布 Java EE 8

当被问及“关键合作伙伴”是否是同时指 JCP、 Java EE 专家组 Java EE Expert Group 和特定的行业合作伙伴时,甲骨文的一位发言人回应道并不全是。该发言人说,该草案很显然需要在 Java 社区内进行讨论,这包括 Java EE 专家组以及 JCP 内的更多成员,甲骨文希望得到更多的反馈。

Java 社区的一些开发人员对此表示了惊喜和疑虑。不过总体来说该申明所带来的消息还是比较乐观的,这表示甲骨文最终对 Java 社区的意见进行了澄清。这也许是对 Java EE 守护者 Java EE Guardians 请愿投票的回应,截止至 7 月 7 日,该请愿已经得到了超过 2700 位开发者的支持。

“这真是一件令人惊喜的好消息。我们非常高兴甲骨文听到了社区的声音,并努力去寻求解决方案。“前甲骨文雇员、Java 专家,也是现在 Java EE 守护者的发言人 Reza Rahman 对甲骨文的回应表示了感谢,他说,”我们希望甲骨文可以将 Java EE 当成一个标准而不是一个产品,从而取得进一步的发展。……社区应当将此视为继续建设性地伴随甲骨文前进的一个机会,我们需要彼此密切配合以造福 Java 和 Java EE 生态,希望 Java EE 8 的发展计划可以在包括 Java 社区在内的广泛的合作中完整达成。”

一位 Java EE 的开发者及作者 Josh Juneau 说,“这是正确的一步,甲骨文最终就 Java EE 发布了申明。”,但是他又说,“我们需要谨慎从事,通过 Java EE 守护者继续推进,以帮助社区紧密参与到 Java EE 的发展当中。”

Josh Juneau 也有些担心甲骨文申明中提及的新方向可能会影响到 Java EE 8 已经取得的一些进展,“我们需要继续发出声音,推进 Java EE 8 的每个 JSR (Java 规范请求)的发展,让甲骨文知道它们每个都很重要。我们不希望看到甲骨文缩减范围,丢掉一些 JSR。”

甲骨文在 Java EE 8 中重点提到微服务和其它针对云的应用并不令人惊奇,甲骨文日益将其重点放在了云方面的架构、平台和应用服务方面。在过去九个月里 Java EE 8 方面的停摆表明,在最近的财年报告中甲骨文在传统软件许可销售方面的收入首次下降后,它正在努力加大其在云方面的投入。

参考信息来源:arstechnicatheregisterjavaee-guardians