以前一直使用C#开发,最近因为眼馋Java生态环境,并借着工做服务化改造的契机,直接将新项目的开发都转到Java上去。积攒些Java开发经验,应该对.NET开发也会有所启发和益处。json
欢迎工做一到八年的Java工程师朋友们加入Java高级交流:854630135网络
本群提供免费的学习指导 架构资料 以及免费的解答架构
不懂得问题均可以在本群提出来 以后还会有直播平台和讲师直接交流噢
函数
从理论上说,Java和C#语言差异不大,毕竟难听地说,C#就是抄Java出来的。程序语言简史如是介绍这两种语言:学习
然而随着时间流逝语言发展,我的认为,C#在语言层面已经大大领先了Java。下面我总结一下我在趟过的坑,以供转型或学习的同窗参考。spa
本文并不是要比出这些语言谁优谁劣。有时候,好或坏是很是主观的判断,不一样人有着不一样的见解,强行判定好坏只会引发无畏的争论。这些语言有着各自的特色,有各自适合的场景。就像下面要谈到的Checked Exception特性,这是个很好的特性,可是在一些状况下也会引发很多麻烦。code
Java是Checked Exception的。这就是说,若是你写了一个方法,这个方法会抛出一些异常,那么你须要用throws
关键字标明这个方法会抛出哪些异常。这个特性很难说是好仍是很差。Checked Exception本质上是一种类型系统,它明确规定了一个方法除了返回值类型之外,还可能抛出什么异常。这样调用方函数就可以明确地知晓应该处理或者传递哪些异常。这个特性在用得好的人手里,对正确处理各类边边角角的异常十分有用。然而,若是在你没法本身选队友,没法控制开发人员的水平的状况下,你极可能会发现,全部的方法都被标记为throws Exception
。对象
Java的Lambda本质上仍然是一个对象。事实上,Java的Lambda函数是一个知足Functional Interface接口的对象。好比下面代码,声明了一个具备一个int
参数,返回一个int
参数的函数。blog
@FunctionalInterface interface AFunction { int invokeBalaBala(int a); }
咱们能够这样定义一个这个函数的变量:AFunction f = x -> 2 * x;
。接口
Java的Lambda和Checked Exception结合在一块儿后,产生了一个很是棘手的问题。因为Checked Exception是类型系统的一部分,一个不抛出异常的函数和一个会抛出异常的函数,它们的类型是不相同的。这就致使了Java的Lambda泛用性大大减小并且不是很好用。以对List的map
操做为例,咱们能够用以下代码将list
里的每一个元素翻倍:
list = list.stream().map(x -> 2 * x).collect(Collectors.toList());
这里map
接收一个类型为输入一个int
参数,返回一个int
值的函数。然而,若是咱们须要给它的函数有可能抛出异常,好比这个函数会去读取文件、访问网络服务、或者作Json反序列化,则因为类型不一样,Java编译器将会报错。
// 这个编译器会报错 list.stream().map(x -> JsonUtil.parse(x)).collect(Collectors.toList());
解决方案一种是在函数体中使用try cache处理异常。可是不少时候,异常没办法在这个时刻处理,必需要抛出。那么还有另外一种方案:将异常转换为RuntimeException
,RuntimeException
是所谓的Unchecked Exception,它不是类型系统的一部分,不须要用throws
标注,因此不会致使函数类型变化。另外一方面,编译器也没法检测出是否可能会抛出RuntimeException。不管采用哪一种方案,都使得这个Lambda函数变得没那么好看。
Java的泛型原理和C#不一样。C#是运行时泛型,在程序运行的时候仍然能获取泛型的类型信息。而Java的泛型是类型擦除(Type Erasure)式泛型。名称听起来很高大上,意思是Java的泛型仅仅用于编译时类型检查,类型检查完成后,类型信息就被编译器擦除。在最后生成的字节码中中,泛型类型都被改成Object
类型。
好比这句:
HashMap<TK, TV> map = new HashMap<TK, TV>();
编译后变成:
HashMap map = new HashMap();
Type Erasure方式的影响主要有两个:
像下面两句:
x instanceof T new T()
在Java中都会编译出错。而这在C#中都是很常见的代码。在C#中,咱们能够有这样的Json反序列化方法:
T parse<T>(string jsonStr)
这个方法将jsonStr
反序列化为类型T
的一个对象。这种写法看起来十分天然。然而在Java中没法实现。由于在parse
方法中须要在运行时实例化T
的一个对象,而Java在运行时这些泛型都已经被擦除,没法获取类型T
的信息,从而没法实例化。要在Java实现相似的方法,须要额外将一个Class
对象放到参数:
T parse(String jsonStr, Class<T> type)
这样Java才能使用这个type
,在运行时使用反射的方式生成类型T
的实例。
在面向对象哲学中,字段属于实现细节,应该设为private
使它隐藏在类的内部。可是在实际中,有不少字段须要直接访问和修改。从功能实现上讲,直接把字段设为public
也是能够的。可是这样作的坏处在于将来功能扩展时,这个字段的含义、存储方式可能发生变化,致使每一个使用了这个字段的代码都须要修改。所以,应该将字段的访问封装的方法中,即便只是很简单的访问和设置,也应该实现getter方法和setter方法。
C#和Python有property特性支持快速定义和调用getter方法和setter方法。Ruby则依靠函数调用能够省略括号的特性,使getter方法看起来很像直接访问字段。Java没有使用特性支持getter和setter方法,而是约定必须实现字段名前加get
的getter方法(然而这里有个不一致的地方,若是字段是布尔类型,则加is
)和字段名前加set
的setter方法。这致使的一个问题是开发时须要编写大量的getter方法和setter方法。为Java冗长的特色贡献了一份力量。遵循这个规范很重要,觉得在不少经常使用库,好比Json序列化,会以getter方法做为字段存在的依据。
为了减小开发工做量,可使用IDE自动生成getter方法和setter方法。常见的Java IDE都支持自动生成getter方法和setter方法。另外一个方案是使用Lombok,经过Data
,Getter
,Setter
等注解,让编译器在编译时自动生成getter方法和setter。