重要要点

  • Kotlin为JVM平台带来了编译时空检查,功能方面和表达语法
  • Kotlin可与Java互操作,并且可以逐步引入现有的Java项目中
  • 重于样板和逻辑的项目很适合采用Kotlin
  • Kotlin与包括Spring和HIbernate在内的流行框架很好地集成在一起
  • Kotlin通过消除样板可以大大减少Java代码库的大小

介绍Kotlin

Kotlin是IntelliJ的制造商JetBrains上JVM上的较新语言之一。 它是一种静态类型的语言,旨在提供OO和FP编程风格的混合。 Kotlin编译器创建与JVM兼容的字节码,从而使其可以在JVM上运行并与现有库互操作。 它在2017年被Google认可用于Android开发时取得了重大突破。

JetBrains的既定目标是使Kotlin成为多平台语言,并提供100%的Java互操作性。 Kotlin最近的成功和成熟度为它进军服务器端奠定了良好的条件。

Kotlin案

许多语言已尝试改善Java。 无论是语言还是生态系统,Kotlin都能正确处理许多事情。 在节省JVM和庞大的库空间的同时,向早已发展到更好的Java进行了漫长的过渡。 这种方法与JetBrains和Google的支持相结合,使其成为一个真正的竞争者。 让我们看一下Kotlin带来的一些功能。

类型推断 –类型推断是一流的功能。 Kotlin无需明确指定变量即可推断变量的类型。 在为清楚起见而需要类型的情况下,仍然可以指定它们。

Java 10通过引入var关键字而朝着相似的方向发展。 尽管从表面上看这很相似,但其范围仅限于局部变量。 它不能用于字段和方法签名。

严格的null检查 – Kotlin将可为空的代码流视为编译时错误。 它提供了其他语法来处理空检查。 值得注意的是,它可防止链式呼叫中的NPE。

与Java的互操作性 – Kotlin在该领域明显优于其他JVM语言。 它与Java无缝互操作。 可以从Kotlin导入并使用框架中的Java类,反之亦然。 值得注意的是,Kotlin集合可以与Java集合进行互操作。

不变性 – Kotlin鼓励不变的数据结构。 除非明确声明为可变的,否则常用的数据结构(Set / List / Map)是不可变的。 变量也被指定为不可变(val)和可变(var)。 这些变化加起来,对状态可管理性的影响是显而易见的。

简洁明了的语法 – Kotlin引入了许多改进,这些改进对代码的可读性有重大影响。 列举一些:

  • 分号是可选的
  • 在没有用的情况下,花括号是可选的
  • Getter / Setters是可选的
  • 一切都是对象-原语会在幕后自动使用
  • 表达式: 表达式在求值时返回结果。

在Kotlin中,所有函数都是表达式,因为它们至少返回Unit 。 控制流语句,如if,trywhen (类似于switch)也是表达式。 例如:

String result = null;
try {
result = callFn();
} catch (Exception ex) {
result = “”;
}
becomes:
val result = try {
callFn()
} catch (ex: Exception) {
“”
}

-循环支持范围。 例如:

for (i in 1..100) { println(i) }

我们还将讨论其他一些改进。

将Kotlin引入您的Java项目

小步

鉴于Java的互操作性,建议您一步一步将Kotlin添加到现有的Java项目中。 主要产品的支持项目通常是不错的选择。 一旦团队感到舒适,他们就可以评估他们是否更喜欢完全切换。

什么样的项目才是好的候选人?

所有Java项目都可以从Kotlin中受益。 但是,具有以下特征的项目可以使决策更加容易。

包含大量DTO或模型/实体对象的项目–这通常用于处理CRUD或数据转换的项目。 这些趋向于被吸气剂/装填剂弄乱。 在这里可以利用Kotlin属性,并大大简化类。

实用程序类繁重的项目 – Java中通常存在实用程序类,以弥补Java顶级功能的不足。 在许多情况下,它们通过公共静态包含全局无状态函数。 这些可以分解为纯函数。 进一步。 可以利用Kotlin对FP构造(如功能类型和高阶功能)的支持,使代码更易于维护和测试。

具有逻辑沉重类的项目 –这些往往容易受到空指针异常(NPE)的影响,这是Kotlin很好解决的问题领域之一。 通过让语言分析导致潜在NPE的代码路径,开发人员可以在此处获得支持。 Kotlin的when构造(更好的switch)在这里很有用,可以将嵌套的逻辑树分解为可管理的功能。 对变量和集合的不变性支持有助于简化逻辑,避免因泄漏的引用而导致难以发现的错误。 尽管上述某些功能可以使用Java来完成,但Kotlin的优势在于促进了这些范式并保持简洁一致。

让我们在这里暂停一下,看看一个典型的Java逻辑代码段,它是Kotlin的对应代码:

public class Sample {
public String logic(String paramA, String paramB) {
String result = null;
try {
if (paramA.length() > 10) {
throw new InvalidArgumentException(new String[]{"Unknown"});
} else if ("AB".equals(paramA) && paramB == null) {
result = subLogicA(paramA + "A", "DEFAULT");
} else if ("XX".equals(paramA) && "YY".equals(paramB)) {
result = subLogicA(paramA + "X", paramB + "Y");
} else if (paramB != null) {
result = subLogicA(paramA, paramB);
} else {
result = subLogicA(paramA, "DEFAULT");
}
} catch (Exception ex) {
result = ex.getMessage();
}
return result;
}
private String subLogicA(String paramA, String paramB) {
return paramA + "|" + paramB;
}
}

Kotlin对应者:

fun logic(paramA: String, paramB: String?): String {
return try {
when {
(paramA.length > 10) -> throw InvalidArgumentException(arrayOf("Unknown"))
(paramA == "AB" && paramB == null) -> subLogicA(paramA + "A")
(paramA == "XX" && paramB == "YY") -> subLogicA(paramA + "X", paramB + "X")
else -> if (paramB != null) subLogicA(paramA, paramB) else subLogicA(paramA)
}
} catch (ex: Exception) {
ex.message ?: "UNKNOWN"
}
}
private fun subLogicA(paramA: String, paramB: String = "DEFAULT"): String {
return "$paramA|$paramB"
}

这些片段在功能上是等效的,但有一些明显的不同。

logic()函数不需要在类中。 Kotlin具有顶级功能。 这开辟了一个广阔的空间,并鼓励我们思考是否确实需要某些东西成为对象。 独立运行,纯功能更易于测试。 这为团队提供了采用更清洁的功能方法的选择。

Kotlin引入了“ when”,这是用于组织条件流的强大构造。 if或switch语句的功能要强大得多。 可以使用`when`清晰地组织任意逻辑。

注意,在Kotlin版本中,我们从未声明过返回变量。 这是可能的,因为Kotlin允许我们使用“当”和“尝试”作为表达式。

subLogicA函数中,我们能够在函数声明中为paramB分配默认值。

private fun subLogicA(paramA: String, paramB: String = "DEFAULT"): String {

现在我们可以调用任一函数签名:

subLogicA(paramA, paramB)

要么

subLogicA(paramA) #在这种情况下,paramB使用了函数声明中的默认值

现在,逻辑更容易理解,行数减少了约35%

将Kotlin添加到Java构建

MavenGradle通过插件支持Kotlin。 Kotlin代码被编译为Java类,并包含在构建过​​程中。 较新的构建工具,例如 Kobalt看起来也很有前途。 Kobalt受到Maven / Gradle的启发,但纯粹是用Kotlin编写的

首先,将Kotlin插件依赖项添加到您的 Maven或 Gradle构建文件。

如果您使用的是Spring和JPA,则应添加kotlin-spring和kotlin-jpa 编译器插件 。 项目应在编译和构建时没有任何明显的差异。

该插件是生成JavaDoc for Kotlin代码库所必需的。

IDE插件可用于IntelliJ和Eclipse studio,但可以预料,Kotlin的开发和构建工具将从IntelliJ关联中受益匪浅。 从社区版开始,IDE对Kotlin具有一流的支持。 值得注意的功能之一是支持将现有的Java代码自动转换为Kotlin。 这种转换是准确的,并且是编写惯用的Kotlin的良好学习工具。

与流行的框架集成

由于我们将Kotlin引入到现有项目中,因此框架兼容性是一个问题。 Kotlin可以编译为Java字节码,因此可以无缝地融入Java生态系统。 几个流行的框架宣布了Kotlin支持-包括Spring,Vert.x,Spark等。 让我们看一下将Kotlin与Spring和Hibernate一起使用的感觉。

弹簧

Spring是Kotlin的早期支持者之一,于2016年首次增加支持。 Spring 5利用Kotlin证明了更清洁的DSL。 您可以期望现有的Java Spring代码可以继续工作而不做任何更改。

Kotlin中的Spring批注

Spring批注和AOP即开即用。 您可以像注释Java一样注释Kotlin类。 请考虑以下服务声明代码段。

@Service
@CacheConfig(cacheNames = [TOKEN_CACHE_NAME], cacheResolver = "envCacheResolver")
open class TokenCache @Autowired constructor(private val repo: TokenRepository) {

这些是标准的Spring注释

@Service: org.springframework.stereotype.Service

@CacheConfig: org.springframework.cache

注意,构造函数是类声明的一部分。

@Autowired constructor(private val tokenRepo: TokenRepository)

Kotlin将此称为主要构造函数,并且可以成为类声明的一部分。 在这种情况下,tokenRepo是一个声明为内联的属性。

可以在注释中使用编译时间常数,这些常数通常有助于避免输入错误。

处理期末班

默认情况下,Kotlin类是final。 它提倡允许继承作为有意识的设计选择的方法。 对于Spring AOP来说,这是行不通的,但是并不难弥补。 我们需要将相关的类标记为开放式-Kotlin的非最终关键字。

IntelliJ给您一个友好的警告。

您可以使用“全开” maven插件解决此问题。 该插件使带有特定注释的类打开。 比较简单的选项是将类标记为“打开”。

自动接线和空检查

Kotlin严格执行null检查。 它要求将所有标记为不可为空的属性初始化。 这些可以在声明站点或构造函数中初始化。 这与依赖注入相反,后者在运行时填充属性。

lateinit修饰符使您可以指定在使用之前将属性初始化。 在以下代码段中,Kotlin相信配置对象将在首次使用之前进行初始化。

@Component
class MyService {
@Autowired
lateinit var config: SessionConfig
}

虽然lateinit对于自动接线很有用,但我还是建议您谨慎使用。 另一方面,它关闭了对属性的编译时null检查。 如果初次使用null时,仍然会遇到运行时错误,但是会丢失很多编译时null检查。

构造函数注入可以用作替代方法。 这与Spring DI配合得很好,并且消除了很多混乱情况。 例:

@Component
class MyService constructor(val config: SessionConfig)

这是Kotlin哄骗您遵循最佳做法的一个很好的例子。

冬眠

Hibernate与Kotlin开箱即用,并且不需要进行重大更改。 典型的实体类如下所示:

@Entity
@Table(name = "device_model")
class Device {
@Id
@Column(name = "deviceId")
var deviceId: String? = null
@Column(unique = true)
@Type(type = "encryptedString")
var modelNumber = "AC-100"
override fun toString(): String = "Device(id=$id, channelId=$modelNumber)"
override fun equals(other: Any?) =
other is Device
&& other.deviceId?.length == this.deviceId?.length
&& other.modelNumber == this.modelNumber
override fun hashCode(): Int {
var result = deviceId?.hashCode() ?: 0
result = 31 * result + modelNumber.hashCode()
return result
}
}

在以上代码段中,我们利用了Kotlin的一些功能。

物产

通过使用属性语法,我们不必显式定义getter和setter。

这减少了混乱,使我们可以专注于数据模型。

类型推断

在可以提供初始值的情况下,我们可以跳过类型说明,因为可以推断出该值。 例如:

var modelNumber = "AC-100"

推断modelNumber属性为String类型。

表达方式

如果我们仔细研究toString()方法,则与Java有一些区别

override fun toString(): String = "Device(id=$id, channelId=$modelNumber)"

return语句丢失。 我们在这里使用Kotlin的表达式。 对于返回单个表达式的函数,我们可以跳过花括号并通过'='进行赋值。

字符串模板

"Device(id=$id, channelId=$modelNumber)"

在这里,我们可以更自然地使用模板。 Kotlin允许将$ {expression}嵌入任何String中。 这消除了麻烦的串联或诸如String.format之类的外部帮助器的需要

平等测试

equals方法中,您可能已经注意到此表达式。

other.deviceId?.length == this.deviceId?.length

它比较两个带有==符号的字符串。 这是Java中长期存在的陷阱,它把String视为相等性测试的特例。 Kotlin最后通过始终使用==来修复结构相等性(Java中的equals())。 引用相等性用===检查

资料类别

Kotlin还提供一种特殊的类,称为数据类。 这些特别适用于该类的主要目的是保存数据的方案。 数据类自动生成equals(),hashCode()和toString()方法,从而进一步减少了样板。

Data类会将我们的最后一个示例更改为:

@Entity
@Table(name = "device_model")
data class Device2(
@Id
@Column(name = "deviceId")
var deviceId: String? = null,
@Column(unique = true)
@Type(type = "encryptedString")
var modelNumber: String = "AC-100"
)

这两个属性均作为构造函数参数传递。 equals,hashCode和toString由数据类提供。

但是,数据类不提供默认的构造函数。 对于Hibernate来说,这是一个问题,它使用默认的构造函数来创建实体对象。 我们可以利用 这里的kotlin-jpa插件为JPA实体类生成了一个额外的零参数构造函数。

使Kotlin在JVM语言空间中与众不同的一件事是,它不仅涉及工程学优雅,而且还解决了现实世界中的问题。

采用Kotlin的实际好处

减少空指针异常

用Java解决NPE是Kotlin的主要目标之一。 将Kotlin引入项目时,显式空检查是最明显的更改。

Kotlin通过引入一些新的运营商来解决零安全问题。 ? 接线员Kotlin提供空安全呼叫。 例如:

val model: Model? = car?.model

仅当汽车对象不为null时,才会读取model属性。 如果car为null,则模型评估为null。 请注意,模型的类型是Model? -表示结果可以为空。 至此,流程分析开始了,我们可以在消耗模型变量的任何代码中对NPE进行编译时检查。

也可以在链接的呼叫中使用

val year = car?.model?.year

以下是等效的Java代码:

Integer year = null;
if (car != null && car.model != null) {
year = car.model.year;
}

在大型代码库上,很多这些空检查都将丢失。 通过编译时安全性自动执行这些检查可以节省大量开发时间。

如果表达式的计算结果为空,则可以使用Elvis运算符(?:)提供默认值

val year = car?.model?.year ?: 1990

在上面的代码段中,如果year最终为null,则使用值1990。 ?:运算符采用右侧的值,因为左侧的表达式为null。

功能编程选项

Kotlin建立在Java 8功能之上,并提供一流的功能。 一流的函数可以存储在变量/数据结构中并传递。 例如,在Java中,我们可以返回函数:

@FunctionalInterface
interface CalcStrategy {
Double calc(Double principal);
}
class StrategyFactory {
public static CalcStrategy getStrategy(Double taxRate) {
return (principal) -> (taxRate / 100) * principal;
}
}

Kotlin使这变得更加自然,使我们可以清楚地表达意图:

// Function as a type
typealias CalcStrategy = (principal: Double) -> Double
fun getStrategy(taxRate: Double): CalcStrategy = { principal -> (taxRate / 100) * principal }
Things change when we move to deeper usage of functions. The following snippet in Kotlin defines a function generating another function:
val fn1 = { principal: Double ->
{ taxRate: Double -> (taxRate / 100) * principal }
}

我们可以轻松地调用fn1及其结果函数:

fn1(1000.0) (2.5)

尽管以上内容在Java中是可以实现的,但并非一帆风顺,它涉及样板代码。

具备这些功能可以鼓励团队尝试使用FP概念。 这样可以更好地适应目标代码,最终产生更稳定的产品。

请注意,在Kotlin和Java中,lambda语法略有不同。 在早期,这可能会使人烦恼。

Java

( Integer first, Integer second ) -> first * second

等效Kotlin

{ first: Int, second: Int -> first * second }

随着时间的流逝,很明显,对于Kotlin支持的用例,需要更改语法。

减少项目足迹

Kotlin最被低估的优点之一是,它可以减少项目中的文件数。 Kotlin文件可以包含多个/混合的类声明,函数和其他构造(如枚举类)。 这开辟了Java所没有的许多可能性。 另一方面,它提供了一个新的选择–组织类和函数的正确方法是什么?

罗伯特·C·马丁(Robert C Martin)在他的《 清洁规范》一书中介绍了报纸的隐喻。 好的代码应该像报纸一样阅读-靠近顶部的高层结构,随着文件的向下移动,细节也会增加。 该文件应说明一个连贯的故事。 Kotlin中的代码布局可以从这个隐喻中得到启发。

建议在更大的范围内将相似的事物组合在一起

尽管Kotlin不会阻止您放弃结构,但这样做可能会使以后很难导航代码。 通过事物的“关系和使用顺序”来组织事物。 例如:

enum class Topic {
AUTHORIZE_REQUEST,
CANCEL_REQUEST,
DEREG_REQUEST,
CACHE_ENTRY_EXPIRED
}
enum class AuthTopicAttribute {APP_ID, DEVICE_ID}
enum class ExpiryTopicAttribute {APP_ID, REQ_ID}
typealias onPublish = (data: Map<String, String?>) -> Unit
interface IPubSub {
fun publish(topic: Topic, data: Map<String, String?>)
fun addSubscriber(topic: Topic, onPublish: onPublish): Long
fun unSubscribe(topic: Topic, subscriberId: Long)
}
class RedisPubSub constructor(internal val redis: RedissonClient): IPubSub {
...}

在实践中,这减少了为形成完整图片而必须跳转的文件数量,从而极大地减少了工作量。

一个常见的例子是Spring JPA存储库,它使程序包变得混乱。 这些可以在同一文件中重新组织:

@Repository
@Transactional
interface DeviceRepository : CrudRepository<DeviceModel, String> {
fun findFirstByDeviceId(deviceId: String): DeviceModel?
}
@Repository
@Transactional
interface MachineRepository : CrudRepository<MachineModel, String> {
fun findFirstByMachinePK(pk: MachinePKModel): MachineModel?
}
@Repository
@Transactional
interface UserRepository : CrudRepository<UserModel, String> {
fun findFirstByUserPK(pk: UserPKModel): UserModel?
}

上面的最终结果是代码行(LOC)的数量大大减少。 这直接影响交付速度和可维护性。

我们测量了移植到Kotlin的Java项目中文件和代码行的数量。 这是一个典型的REST服务,其中包含数据模型,一些逻辑和缓存。 在Kotlin版本中,LOC缩小了约50%。 开发人员花费在文件之间导航和编写样板代码的时间大大减少。

启用清晰,表达力强的代码

编写简洁的代码是一个广泛的话题,它取决于语言,设计和技术的结合。 但是,Kotlin通过提供良好的工具集来使团队成功。 以下是一些示例。

类型推断

类型推断最终减少了代码中的噪声。 这有助于开发人员专注于代码的目标。

人们普遍担心类型推断可能使跟踪我们正在处理的对象变得更加困难。 根据实际经验,这种担忧仅在少数情况下有效,通常少于5%。 在绝大多数情况下,类型都是显而易见的。

例:

LocalDate date = LocalDate.now();
String text = "Banner";

成为

val date = LocalDate.now()
val text = "Banner"

Kotlin也可以使用指定的类型。

val date: LocalDate = LocalDate.now()
val text: String = "Banner"

值得注意的是Kotlin提供了全面的解决方案。 例如,我们可以在Kotlin中将函数类型定义为:

val sq = { num: Int -> num * num }

另一方面,Java 10通过查看右侧表达式的类型来推断类型。 这引入了一些限制。 如果尝试使用Java执行上述操作,则会收到错误消息:

Typealias

这是Kotlin中的一项便捷功能,可让我们为现有类型分配别名。 它没有引入新的类型,但是允许我们使用替代名称来引用现有的类型。 例如:

typealias SerialNumber = String

现在, SerialNumberString类型的别名,可以与String类型互换使用。 例如:

val serial: SerialNumber = "FC-100-AC"

等于

val serial: String = "FC-100-AC"

很多时候, typealias可以充当“ 解释变量 ”,以引入清晰度。 考虑以下声明:

val myMap: Map<String, String> = HashMap()

我们知道myMap拥有字符串,但是我们不知道这些字符串代表什么。 我们可以通过引入String类型的类型别名来澄清此代码:

typealias ProductId = String
typealias SerialNumber = String

现在,上面的地图声明可以更改为:

val myMap: Map<ProductId, SerialNumber> = HashMap()

上面的myMap的两个定义是等效的,但是在后者中,我们可以轻松识别地图的内容。

Kotlin编译器将基础类型替换为类型别名。 因此, myMap的运行时行为不受影响,例如:

myMap.put(“MyKey”, “MyValue”)

这种钙化的累积效应是减少了细小虫子的数量。 在大型分布式团队中,错误通常是由于未能传达意图而导致的。

早期采用

尽早获得牵引力通常是引入变革的最难部分。 首先确定合适的实验项目。 通常,会有一些早期采用者愿意尝试并编写最初的Kotlin代码。 在未来几周内,较大的团队将有机会查看此代码。 人类早期的React是避免陌生和陌生。 给它一些时间来审核更改。 通过提供阅读资源和技术讲座来帮助评估。 在最初的几周结束时,一大群人可以决定采用的程度。

对于熟悉Java的开发人员来说,学习曲线很小。 以我的经验,大多数Java开发人员会在1周内使Kotlin高效。 初级开发人员无需特别培训即可接管并运行它。 以前使用其他语言或熟悉FP概念会进一步减少采用时间。

事情来了

自1.1版以来,协同例程已在Kotlin中提供。 从概念上讲,它们类似于JavaScript中的异步/等待。 它们允许我们在不阻塞线程的情况下暂停流,从而减少了异步编程的复杂性。

直到现在,它们都被标记为实验性的。 协同例程将从1.3版本的实验状态中毕业。 这带来了更多令人兴奋的机会。

Kotlin路线图是通过Kotlin演化和增强过程( KEEP )指导的。 请随时注意讨论和即将发布的功能。

翻译自: https://www.infoq.com/articles/intro-kotlin-java-developers/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

服务器端Java开发人员Kotlin简介相关推荐

  1. 为什么推荐 Java 开发人员都学习并使用 Kotlin?

    我使用 Java 已经有很长的时间了,工作中的使用有15年.如果算上在学校的时间的话,那就更长了.Java 的一个很大的优势是平台的开放性.这得益于 Java 字节代码和虚拟机的存在.由于 Java ...

  2. 适用于Java开发人员的Elasticsearch:简介

    本文是我们学院课程的一部分,该课程的标题为Java开发人员的Elasticsearch教程 . 在本课程中,我们提供了一系列教程,以便您可以开发自己的基于Elasticsearch的应用程序. 我们涵 ...

  3. 面向Java开发人员的Docker:简介

    本文是我们学院课程中名为< 面向Java开发人员的Docker教程 >的一部分. 在本课程中,我们提供了一系列教程,以便您可以开发自己的基于Docker的应用程序. 我们涵盖了广泛的主题, ...

  4. 黑马2016java_2016年成功的Java开发人员简介

    黑马2016java 2015年即将结束. 现在该总结过去一年中已完成的工作和未完成的工作. 此外,现在是预测下一个2016年的好时机. 您已经猜到这篇文章是关于2016年理想的Java开发人员的. ...

  5. 2016年成功的Java开发人员简介

    2015年即将结束. 现在是时候总结过去一年做了什么和没有做什么的时候了. 此外,现在是预测下一个2016年的好时机. 您已经猜到这篇文章是关于2016年理想的Java开发人员的. 我想给你一个惊喜, ...

  6. 适用于Java开发人员的Elasticsearch:Elasticsearch生态系统

    本文是我们学院课程的一部分,该课程的标题为Java开发人员的Elasticsearch教程 . 在本课程中,我们提供了一系列教程,以便您可以开发自己的基于Elasticsearch的应用程序. 我们涵 ...

  7. 面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序

    面向 Java 开发人员的 Ajax: 构建动态的 Java 应用程序 Ajax 为更好的 Web 应用程序铺平了道路 在 Web 应用程序开发中,页面重载循环是最大的一个使用障碍,对于 Java™ ...

  8. 还在重复造轮子?Java开发人员必知必会的20种常用类库和API

    介绍 一个有经验的Java开发人员特征之一就是善于使用已有的轮子来造车.<Effective Java>的作者Joshua Bloch曾经说过:"建议使用现有的API来开发,而不 ...

  9. 面向 Java 开发人员的 Scala 指南: 深入了解 Scala 并发性

    http://www.ibm.com/developerworks/cn/java/j-scala04109.html http://www.ibm.com/developerworks/cn/jav ...

最新文章

  1. win7发现不了无线网络_win10系统间设置共享文件夹后“网络”选项下仍然无任何文件(即发现不了共享电脑)...
  2. dockerfile php环境变量,docker - 在Dockerfile中,如何更新PATH环境变量?
  3. 福克斯1.8MT驾驶心得
  4. linux 指令引用变量,Linux之变量引用与命令替换
  5. java爬虫框架_Python,爬虫开发的不二选择
  6. javascript面试题(一)
  7. idea uml图各符号含义_设计模式-UML类图的各符号含义(转)
  8. 单链表的反转(C++)
  9. python 3d绘图模块_使用python和mayavi创建3D streamplot
  10. 计算机系统繁体环境,繁体简体转换
  11. XCode怎么搜索图片文件
  12. android switch的使用方法,Android UI控件Switch的使用方法
  13. 一位技术演讲家的自白
  14. 未来是现在的将来时,在社科院杜兰金融管理硕士项目酝酿灿烂的明天
  15. 计算机开机时间设置方法,win10系统设置电脑开关机时间的技巧介绍
  16. 牛顿迭代法求高次方程的根
  17. 如果你相中上了一个程序员小伙
  18. JDK环境配置及Python、Pycharm、Git
  19. 28.EOS的共识机制与区块生成
  20. ssm高校学生档案信息管理系统 毕业设计- 附源码010936

热门文章

  1. PS3111-S11-13开卡问题,显示FAIL,解决办法分享
  2. Linux创建虚拟块设备并格式化为文件系统
  3. FANUC 故障报警诊断维修
  4. 模拟器和服务器共享文件夹,天天模拟器中共享文件夹以及导入文件怎么操作?共享文件夹和导入文件流程一览...
  5. OMAPL138控制板链接问题
  6. 您试图访问的网站服务器报错,HTTP 403 禁止)意味着 Internet Explorer 可以连接到该网站,但是没有查看该网页的权限。...
  7. Springboot集成Flyway(适用于多数据源)
  8. erlang nif小结
  9. 用Python免费发短信,实现程序实时报警
  10. 网站备案:阿里云ICP备案服务码是什么?申请流程来了