我有多年的 C++ 开发经验,但是最近由于公司的关系,不得不转 Java。看了几天 Java,谈谈感想。
经验丰富的 C++ 程序员应该可以很容易转到 Java 上来,因为他们有许多相似之处,基本语法是完全一样的,只是 Java 砍掉了大量的 C++ 特性,仅仅保留了类相关的部分,在 Java 的世界里,一切都是类。
1. 对象 & 指针
大部分的 Java 书籍都会告诉你,Java 是没有指针的,因为指针太过灵活,十分难用。但其实,对 C++ 程序员来说,Java 里到处都是指针!
比如 Java 代码:
MyObject my=new MyObject();
MyObject my2;
my2 = my;
my2 和 my 指向的是同一个对象!另外,my2 可以是 Null,也可以指向其他对象, 所以,my 和 my2 其实是对象指针!Java 社区里还经常对参数到底是传值还是传引用争论不休,其实把他们看成指针,一切就都可以解释通了。当然,由于垃圾收集器的存在,所以 Java 的对象都不需要释放,所以可以看成,Java 里 new 出来的,都是智能指针。
C++ 传递参数的时候,可以传值,传引用,传指针,C++11里还有右值引用,还可以配上 const 修饰,完全由程序员自己指定,真是太灵活了,同时也实在是太复杂了。多少初学者看着眼晕?比如对象的传值,有用吗?99%以上其实是不会这么用的,而传指针也可以完全替代传引用的功能,右值引用是为了移动对象,其实是对传值的优化。所以 Java 语言本身将它简化成了一种,这点上比C++简单多了。
2. 操作符重载
C++ 里的操作符重载是相当灵活的一个特性,但是 Java 里由于对象其实都是指针,因此操作符重载会带来歧义,所以它很干脆完全不支持这个特性。
从 C++ 转过来的 Java 程序员应该都写过 if( str == "Val" )... 这样的代码吧!由于没有操作符重载,而 str 其实是指针,所以这个代码在 Java 中是在做指针比较,其实 Java 要写成 if( str.equal("Val") ) ... 。个人感觉还是 C++ 的写法比较自然,特别是一些强大的库,甚至可以把其他语言用 C++ 模拟出来,就代码的表现力来说,强大不少。
简单的说,C++ 的代码看起来就像数学公式,而 Java 看起来就是把公式用英文描述一遍。初学者也许喜欢后者,但是如果你看得懂,绝对是前者更能准确的表达作者的目的。
好比给你看一个公式 X=2Y/3 和一段文字,大部分学过数学的人应该都会喜欢公式吧。
当然,由于 Java 语言本身的特性,操作符重载是绝对不可能出现的,所以大家都习惯了。
3. 回调函数 & Lambda 表达式
C++中的回调函数最初是用函数指针,成员函数指针来实现的,语法古怪而难以使用,幸运的是,C++ 有许许多多有趣或者奇怪的特性,而大神们使用各种技巧,完成了对语言本身的改造。boost::function 和 bind 库的出现,重新定义了回调函数的写法,简单而优雅。
那么在 Java 中呢?它是用对象来模拟的,也就是先定义一个接口,然后把它给继承并实现,然后把它当回调函数来用,毕竟 Java 中一切都是对象。相当麻烦,而且限制多多,不是么?幸运的是,Java 中提供了一个语法糖,可以在 new 对象的同时定义它,这减少了部分代码,也基本实现了 Lambda 表达式的功能。顺便吐槽一下 Java 1.8 里的 Lambda, 希望它不仅仅是另一颗语法糖而已。
Java 事件处理机制完全是通过类的继承来模拟回调的机制,要定义一个事件并发送它?ok,先定义一个事件类,然后定义一个对应的监听接口,再然后还要实现它。你看,3个文件就干了定义事件并且发送这么点事。
其实这在很多语言都是内置机制,比如C#的委托。顺便说一句,个人感觉 C#设计得其实比 Java 好得多,可惜由于商业原因被扼杀了。
上面说过 C++ 已经通过 function, bind 这样的类库完全实现了闭包,而通过挖掘类似语言细节,也可以写出相当方便使用的库。我写过一个 observer 的库,定义事件仅需要一句:
OBSERVER_EVENT( MyEvent, int, std::string, long )
而注册也只需要调用一个函数:
observer a;
a.subscribe<Name>( Handle );
当事件发生时,可以通过 observer 对象来发送事件,当然也只有一个调用:
a.shot<Name>( <Params> );
我特意设计为强类型,编译器会帮你检查错误,参数填错是完全不可能的!如果想用弱类型的事件机制,请参考 QT。
顺便吐槽一下 bean 关于 seter, geter 的机制,对程序员来说其实除了增加代码量基本没好处。仅仅是系统需要你就不得不输入这么多文本,还好 IDE 会帮你干这个,但是文件还是会看得你眼晕啊!
4. 类库,或者说“框架”
Java 类库和 C++ 类库的设计哲学完全不一样。C++ 里的一个库只干一件事,比如 Boost.Asio 库的设计,就是完全只封装了网络 IO,完全没有多余的东西。而 Java 里的库追求大而全,所以一般叫“框架”,框架越大越被人推崇。看看同样处理网络的 netty 吧!
经验丰富的 C++ 程序员应该可以很容易转到 Java 上来,因为他们有许多相似之处,基本语法是完全一样的,只是 Java 砍掉了大量的 C++ 特性,仅仅保留了类相关的部分,在 Java 的世界里,一切都是类。
1. 对象 & 指针
大部分的 Java 书籍都会告诉你,Java 是没有指针的,因为指针太过灵活,十分难用。但其实,对 C++ 程序员来说,Java 里到处都是指针!
比如 Java 代码:
MyObject my=new MyObject();
MyObject my2;
my2 = my;
my2 和 my 指向的是同一个对象!另外,my2 可以是 Null,也可以指向其他对象, 所以,my 和 my2 其实是对象指针!Java 社区里还经常对参数到底是传值还是传引用争论不休,其实把他们看成指针,一切就都可以解释通了。当然,由于垃圾收集器的存在,所以 Java 的对象都不需要释放,所以可以看成,Java 里 new 出来的,都是智能指针。
C++ 传递参数的时候,可以传值,传引用,传指针,C++11里还有右值引用,还可以配上 const 修饰,完全由程序员自己指定,真是太灵活了,同时也实在是太复杂了。多少初学者看着眼晕?比如对象的传值,有用吗?99%以上其实是不会这么用的,而传指针也可以完全替代传引用的功能,右值引用是为了移动对象,其实是对传值的优化。所以 Java 语言本身将它简化成了一种,这点上比C++简单多了。
2. 操作符重载
C++ 里的操作符重载是相当灵活的一个特性,但是 Java 里由于对象其实都是指针,因此操作符重载会带来歧义,所以它很干脆完全不支持这个特性。
从 C++ 转过来的 Java 程序员应该都写过 if( str == "Val" )... 这样的代码吧!由于没有操作符重载,而 str 其实是指针,所以这个代码在 Java 中是在做指针比较,其实 Java 要写成 if( str.equal("Val") ) ... 。个人感觉还是 C++ 的写法比较自然,特别是一些强大的库,甚至可以把其他语言用 C++ 模拟出来,就代码的表现力来说,强大不少。
简单的说,C++ 的代码看起来就像数学公式,而 Java 看起来就是把公式用英文描述一遍。初学者也许喜欢后者,但是如果你看得懂,绝对是前者更能准确的表达作者的目的。
好比给你看一个公式 X=2Y/3 和一段文字,大部分学过数学的人应该都会喜欢公式吧。
当然,由于 Java 语言本身的特性,操作符重载是绝对不可能出现的,所以大家都习惯了。
3. 回调函数 & Lambda 表达式
C++中的回调函数最初是用函数指针,成员函数指针来实现的,语法古怪而难以使用,幸运的是,C++ 有许许多多有趣或者奇怪的特性,而大神们使用各种技巧,完成了对语言本身的改造。boost::function 和 bind 库的出现,重新定义了回调函数的写法,简单而优雅。
那么在 Java 中呢?它是用对象来模拟的,也就是先定义一个接口,然后把它给继承并实现,然后把它当回调函数来用,毕竟 Java 中一切都是对象。相当麻烦,而且限制多多,不是么?幸运的是,Java 中提供了一个语法糖,可以在 new 对象的同时定义它,这减少了部分代码,也基本实现了 Lambda 表达式的功能。顺便吐槽一下 Java 1.8 里的 Lambda, 希望它不仅仅是另一颗语法糖而已。
Java 事件处理机制完全是通过类的继承来模拟回调的机制,要定义一个事件并发送它?ok,先定义一个事件类,然后定义一个对应的监听接口,再然后还要实现它。你看,3个文件就干了定义事件并且发送这么点事。
其实这在很多语言都是内置机制,比如C#的委托。顺便说一句,个人感觉 C#设计得其实比 Java 好得多,可惜由于商业原因被扼杀了。
上面说过 C++ 已经通过 function, bind 这样的类库完全实现了闭包,而通过挖掘类似语言细节,也可以写出相当方便使用的库。我写过一个 observer 的库,定义事件仅需要一句:
OBSERVER_EVENT( MyEvent, int, std::string, long )
而注册也只需要调用一个函数:
observer a;
a.subscribe<Name>( Handle );
当事件发生时,可以通过 observer 对象来发送事件,当然也只有一个调用:
a.shot<Name>( <Params> );
我特意设计为强类型,编译器会帮你检查错误,参数填错是完全不可能的!如果想用弱类型的事件机制,请参考 QT。
顺便吐槽一下 bean 关于 seter, geter 的机制,对程序员来说其实除了增加代码量基本没好处。仅仅是系统需要你就不得不输入这么多文本,还好 IDE 会帮你干这个,但是文件还是会看得你眼晕啊!
4. 类库,或者说“框架”
Java 类库和 C++ 类库的设计哲学完全不一样。C++ 里的一个库只干一件事,比如 Boost.Asio 库的设计,就是完全只封装了网络 IO,完全没有多余的东西。而 Java 里的库追求大而全,所以一般叫“框架”,框架越大越被人推崇。看看同样处理网络的 netty 吧!
你看,它不但包含了网络部分,同时还包括 HTTP, SSL 等一大堆东西,它已经把做 Web 服务器的所有东西都包含进去了。
另外,为什么要叫框架?因为它的设计者已经把程序的结构也完全帮你设计好了,你只需要填入少量的代码,就能完成“框架”可以完成的功能,比如搭建一个 Web 服务器,傻瓜式编程!Java 的流行框架大多有相当清晰结构和设计。并且 Java 的框架相当多,并且得益于 Java 本身的简单。因此“快速开发”成为了 Java 成功的一大推动力,这也是 Java 成功的基础。当初 Sun 就聪明在推出 Java 的同时,同步推出了强大的 Java SDK 的类库!
于是 Java 有了庞大的人口基数,因为几天的学习后他们就会觉得自己“无所不能”!
值得警惕端的是,一直在框架的帮助下傻瓜式编程,对程序员的提升没什么帮助。很多工作多年的 Java 程序员,从来不问为什么,只是知道怎么做,因此极度缺乏设计的能力,因为他们从来不需要设计;完全不了解细节,因为所有的细节都被封装了;他们会缺乏探索精神,不去关心新技术,因为他们会觉得没必要。Java 的语言本身比较简单,只有 OO 一种思想,程序员也很容易养成一切都是 OO 的狭隘思维。他们会很难从 Java 里跳出来。
Java “高手”很容易在某些工作超出了框架的范围后,完全抓瞎。我看的一个服务器代码,需要对其他服务器进行大量的异步 http 访问,猜猜作者怎么干的?每个 Http 开个线程!这可是工业级别的代码。它能稳定工作,而且可以快速开发完毕,不是么?至于效率,从来不在 Java 程序员的脑海里。反正效率本身不是 Java 的强项。
程序是人写的,因此思想才是软件开发界更重要的东西。从这点上说,希望 Java 新手保持学习的态度,别满足于“我能做什么”,那都是人家帮你做好的。多想想为什么,多看看框架的源代码,真正搞清楚他们为什么要这么设计。最好能同时掌握另一门编程语言,而且最好是非 OO 的,比如 Go。
另外,为什么要叫框架?因为它的设计者已经把程序的结构也完全帮你设计好了,你只需要填入少量的代码,就能完成“框架”可以完成的功能,比如搭建一个 Web 服务器,傻瓜式编程!Java 的流行框架大多有相当清晰结构和设计。并且 Java 的框架相当多,并且得益于 Java 本身的简单。因此“快速开发”成为了 Java 成功的一大推动力,这也是 Java 成功的基础。当初 Sun 就聪明在推出 Java 的同时,同步推出了强大的 Java SDK 的类库!
于是 Java 有了庞大的人口基数,因为几天的学习后他们就会觉得自己“无所不能”!
值得警惕端的是,一直在框架的帮助下傻瓜式编程,对程序员的提升没什么帮助。很多工作多年的 Java 程序员,从来不问为什么,只是知道怎么做,因此极度缺乏设计的能力,因为他们从来不需要设计;完全不了解细节,因为所有的细节都被封装了;他们会缺乏探索精神,不去关心新技术,因为他们会觉得没必要。Java 的语言本身比较简单,只有 OO 一种思想,程序员也很容易养成一切都是 OO 的狭隘思维。他们会很难从 Java 里跳出来。
Java “高手”很容易在某些工作超出了框架的范围后,完全抓瞎。我看的一个服务器代码,需要对其他服务器进行大量的异步 http 访问,猜猜作者怎么干的?每个 Http 开个线程!这可是工业级别的代码。它能稳定工作,而且可以快速开发完毕,不是么?至于效率,从来不在 Java 程序员的脑海里。反正效率本身不是 Java 的强项。
程序是人写的,因此思想才是软件开发界更重要的东西。从这点上说,希望 Java 新手保持学习的态度,别满足于“我能做什么”,那都是人家帮你做好的。多想想为什么,多看看框架的源代码,真正搞清楚他们为什么要这么设计。最好能同时掌握另一门编程语言,而且最好是非 OO 的,比如 Go。
未完待续……