[Java]转知乎,一处编译,到处运行?

转知乎……原帖已找不到,不过可以去知乎搜下。当时记下来的,现在看看感觉蛮不错。

一、

——————————————————1st————————————————————

这句话说出来是有背景的,背景在于它是 sun。而 sun 搞的是什么?solaris系统。——你可以理解为是某个产商的私有 Unix 系统。

一次编写,到处运行,是指应用程序生成的二进制文件,可以直接在其他的支持 Java 的平台上运行,换句话说叫二进制兼容,也就是「编译一次,到处运行」。——与之对应的是源代码兼容,也就是在目标机器上重新编译源代码,生成新的二进制文件,才可以运行,换句话说就是「编译多次,到处运行」。

这句话诞生的时候,各种大型机,中型机,小型机服务器仍然在广泛服役中,而这些机器往往都使用了不同的 CPU ,不同的 Unix 操作系统(当时各主机产商都有自己的私有 Unix 系统),那时候 Linux 又还不太成熟。远远没有现在这样几乎一统服务器天下的地位。当时在服务器上跑应用,主要用的语言当然是 C,而每个服务器可能有不同的 CPU 不同的操作系统,你去编译 C 程序当然是会遇到各种问题的,而且由于 CPU 架构的不同,二进制兼容不可能做到。

Java
特定情况下解决了特定平台间的跨平台问题,解决方法是把 Java 本身做成一个平台。然后,只要把 Java 平台本身部署到目标机器,那么应用理论上就可以用了,这个方法对不同 flavor Unix 系统之间的程序移植带来了很多好处。——这所有的好处都来自服务器领域。——Java 虚拟机把这些「型号不同但实质上等价」的服务器虚拟成一致的。但 Java 无法解决那些「实质上就不一样的设备」之间的兼容性。

我们看到,Java 最初的目标,甚至都不是为了把 Windows 移植到 Linux,而是为了让不同 flavor Unix 之间能够做到二进制兼容。但无论如何,不同的 Unix 本质上都是 Unix,他们之间实现二进制兼容相对比较现实。但诸如 Linux/Windows/OSX 这样本质区别很大的平台,二进制兼容很大程度上只是过于美好的幻想。他们在一些系统核心调用方面可能存在完全不同的机制,以至于你为了选择一个所有系统可用的方式必须牺牲最优效率。

另外,对于用户界面应用来说,二进制跨平台要么意味着在每个平台上的用户体验都很糟糕,要么意味着只在一个平台上有可用的用户体验,其他平台都很糟糕。

因而,更广泛意义上的跨平台其实并不存在。在服务器之外的领域,跨平台要解决的问题更多,跨平台只是一句笑话,无论你用不用 Java,在当时,它真的只是一句宣传口号而已

 

——————————————————2nd———————————————————

1 跨平台不是要解决因为硬件的缺失而带来的问题
有人说使用jsr82J2ME程序在一个无蓝牙模块手机上跑起来有问题,所以Java跨平台是假的,这是没有意义的
在都有蓝牙模块的情况下,J2ME程序是不用关心因为厂商不同型号不同带来的底层差异,这才是有意义的

2
跨平台不是要解决因为库的缺失或版本不兼容而带来的问题
有人说用了EJB写了个程序,却不能裸跑在Tomcat上,所以Java跨平台是假的,这是没有意义的
只要是库齐全(而这基本是常态),你的程序就可以不用做任何修改,直接无障碍地跑起来,这才是有意义的

3
不是跨所有平台才叫跨平台
有人说Java程序没办法跑在计算器上、空调上,所以Java跨平台是假的,这是没有意义的
Java
跨平台已经可以做到可以跑在几乎所有主流操作系统上,这才是有意义的

4
不是必须能干所有事情并且这些事情都跨平台才叫跨平台
有人说做得好就说是java牛逼,做不好就说是java没打算解决,怎么说都行啦,这是没有意义的
Java
因为要做跨平台,所以根本不想让程序去操作底层硬件、去做系统管理,除了真的可以抽象出来的一些平台无关的操作以外,其他的压根都没有apiJava通过api定义了它能做的事情,并且做到了所有能做的事情都能跨平台,这才是有意义的

说了这么多,那么Java的跨平台到底是要解决什么问题?

使用C++开发过的人肯定多多少少都会被跨平台问题坑过,什么32/64位,什么x86/POWER,什么Big/Little Endian,什么Windows/Linux,虽然很多东西可以通过宏去控制,通过重新编译去解决,但总的说来还是很麻烦,而且坑非常多,于是才有了各种技术去尝试消除这些部分对于程序的影响,比如解释器,比如虚拟机,比如LLVM带来的一系列技术(例如pNaCl

所以回到Java来说,实际上Java的跨平台就是想利用虚拟机技术,通过限制一些功能,达到屏蔽底层细节的目的,这里有几个关键,一个是使用虚拟机,所以前提是你的目标平台要有达到标准的虚拟机,第二个是限制功能,让纯Java对底层开发无能为力,但也保证了提供的功能都能跨平台,第三个是屏蔽底层细节,注意只是屏蔽细节,不是无中生有,缺失硬件的情况一般不在它要屏蔽的范畴之内

最后谈一点个人的感想,我用Java开发有10年了,从服务器端,到桌面客户端,再到移动端,各类产品,各种技术也用的不少,但真正遇到跨平台的问题屈指可数,对比我同样长期使用的其他语言,至少在跨平台这个方面我真的很挺Java

退一万步讲,即便它只解决了90%的跨平台问题,我觉得依然意义重大,而不应该采用一种非黑即白的极端思考方式,不是吗?

 

——————————————————3nd———————————————————

JAVA的出现和流行跟跨平台真心没啥关系。
正如vczh所说,那就是一句广告。

其实这种问题在我上大学的时候就盖棺定论了,问题是一波又一波JAVA程序员,或者说只搞过一点JAVA懂得一点皮毛的程序员——很多是培训班出身的,不断的站出来热烈的讨论这一话题。

至于匿名用户的答案…… 虚拟机是怎么回事儿真心不用你们给大家科普。其实写模拟器(基于指令集或者库的)也不是什么高深技术。至于GC,你当大家没有JAVAC#的时候内存回收不是手动管理的么。为了防止内存碎片要注意的地方想来匿名的同学也是不关心的啦。

真要说跨平台,还是C/CPP靠谱。

再多说两句。当初JAVA还有J2SEJ2MEJ2EE的区分。现在呢。

多读点书多搞点开发比啥都好。
-----------------------------------------------------------------------------------------------
有人追问C/CPP跨平台的例子。那就举个比较易懂的。

10
年前有个内存受限的硬件平台,虽然有操作系统的支持但是可用的程序栈和堆都很有限。上面又没有能直接使用的压缩算法。为了适应古老的应用程序必须在那个平台上把数百KB至数MB的数据文件通过串行接口传输出去。串行接口的速率最大只有115200bps,实际上大家搞过的都知道最终能达到的实际传输速率只有大约10KB/s,传输数百KB就会接近一分钟。而美国人的设备可以在数秒中搞定,还有变态的断点续传功能。没错,串行设备的断点续传。
为了解决这些问题用了很多办法,当然提升速率的一个好办法就是压缩。
找了PC上运行的古老的Lz77CPP封装的算法库,然后小心的把每个压缩块大小调整至64KB用以将就可怜的内存,改写I/O函数,然后您猜怎么着,这货在我的设备上能用。这个工作量并不大。移植这个算法只花了一个大三暑假还没上大四的学生的半个工作日。

我还可以举出来很多把特定目标平台上的C/CPP程序完整的放到PC平台上模拟的例子。
我觉得这种跨平台才真的有意义,真的在解决实际问题。

搞过一阵子JAVA。感觉JAVA的所谓跨平台的特性对各个平台要求实在是太高了。当然高级语言嘛,重量级框架嘛,你没法要求更多。问题是这种事儿千万别吹出来,吹出来就会被PIAPIA的打脸。
------------------------------------------------------------------------------------------------
再更一个经历过的JAVA项目。大型企业应用的基础平台构建,基于JAVAEE技术,使用的平台级产品上面有工作流和BI组件。配置表中给出了一堆可用的JDK版本,中间件,WEB服务器,容器,数据库列表。不是专注搞JAVA的要是有表述上不准确的地方请不要介意……
遇到的问题接踵而至。说是能部署在Linux/Windows上其实要想愉快的运行只能上Windows,最终DB也是上了Oracle而不是Postgre。程序对JDK版本也很挑拣。作为一个懂点编译原理操作系统计算机组成原理的IT从业人员来说我明白这些其实不是JAVA本身的错,毕竟JAVA是要依存这些环境,中间件来运行的。但是我认为这些所有的一切都是平台的组成部分。不能说代码能在虚拟机上跑起来,API基本兼容这就是跨平台。因为这不解决任何实际问题。JAVAEE庞大的结构,JAVA高级语言的特性就决定了所谓跨平台只是一个文字游戏和一个笑话。
这个项目我会继续跟进。顺便说一句初期引入这个基础平台前后花了上千万,其中授权费用仅仅是几十万级别的费用。大量的时间和金钱用于让平台去适应JAVA代码。包括为了让平台的JAVA代码正常运行,反复对服务器环境进行调整。这里面有硬件的,软件的,也有分配给JVM的内存调整。

 

——————————————————4th———————————————————

Java能不能跨平台?当然能了。它是标准的二进制跨平台,就是所谓一次编译到处跑跑,在这个领域里简直毫无敌手,但问题还是在这里,作为这个领域的第一名,也几乎是唯一的一名,它还是没法突破谁信谁傻逼这点。

首先,二进制跨平台本身就是个很难靠谱的玩意儿,因为各个平台都有差异,这个差异要统一就要放弃很多东西,所谓Every abstraction is leaky,你二进制统一了又如何?你一次编译到处能跑又如何?用来做Client(随你SWT还是Swing了)慢得一逼丑得二逼。用来做Server,呵呵那高大上的NIO到了什么时候才在Windows上支持IOCP的?谁给查查是Java7还是Java 8来着?要知道IOCPNT 3.5里出现的功能啊,1994年啊,跟Java是特么的同龄人啊。

其次,二进制跨平台本身也是个伪需求。老是有人说Java流行是因为跨平台,真是跨个蛋蛋。你有过多少需求动不动把一个项目从一个平台搬到另一个平台上啊?别看我的屁股大部分是坐在.NET上面的,我在十八摸呆过啊,我也WebSphereTomcat之间迁移过应用啊(当然两件事没太大联系),修修改改调调到蛋疼。我一直纠正别人说,你就直接说Java可以跑在Linux上就行了,这跟跨平台关系不大。

当然我知道很多很多很多人是在Windows上写程序然后生产环境是Linux一样,我的博客也是在Win上写,然后部署在Ubuntu上面。那么都是怎么做的呢?开发一点部署一点测试一点啊,每天甚至每次提交都在测试环境的Linux服务器上部署一份啊,谁家开发的时候是在Windows上写到底,然后直接发布到生产环境的Linux里的?比如你开发出来的NIO服务器应用,在Windows上跑得性能肯定上不去啊,没IOCP啊。

总之二进制跨平台在我看来本身就是不靠谱的玩意儿,你Java想要做第一你自己去,反正没人跟你争十六强。

现在靠谱的跨平台都是源代码跨平台,就是大家都遵守一个标准,遵守一个子集,争取相互之间可共享的利益最大化,然后充分尊重各平台上的不同之处,提供各自独特的部分,要用的时候大家各自编译去。大伙儿不求100%代码共享,就求个80%万岁,然后三个平台各实现剩下的20%,你条件编译也好,通过项目结构来组织也罢,各管各的,这才叫棒。

Mono
,或者说Xamarin早就这么搞了,你们还以为Mono是跟Java一样那种跨平台法?人家早就发力到各平台的Native Binding了好么。.NET推出了Portable Class Library这个Profile了好么,.NET里的Profile概念早就出现了好么,谁像Java还设法用一个普世实现想统一全世界啊?有人还拿AndroidJava跨平台的例子,简直就是呵呵呵,它只是用了Java语言和一小部分核心API好吗?整套GUI框架都是不一样的,编译出的bytecode都是不一样的,完全摆脱了二进制跨平台的意图,充分进入了源代码跨平台领域好吗?这才有现实意义。

所以什么是靠谱的跨平台方式?跟我念:源代码跨平台CC++Mono啊,这种才叫靠谱的跨平台方式。

二、

商业公司选什么,和他跨不跨平台没有半毛钱关系,而且选择.NET的商业公司也不少。更重要的是JavaLinux上有诸多案例、技术和人才的积累,所以Linux服务器选择Java的居多,至于android,那是另一个非技术层面的问题不谈。。

事实上,与直觉相悖的是,跨平台的程序设计语言远比不跨平台的程序设计语言多,例如PythonPHPJavaScript。所以这里说的跨平台主要是指Java的那个广告语:一次编译,到处运行。

所以首先我们要明确定义,跨平台的含义是指一次编译,各个平台都可以运行。否则无需编译的脚本语言如JavaScript,本来就是跨平台的,各个平台分别编译的Hello World!C语言程序,也是跨平台的。


那么Java/C#的一次编译,到处运行的方式到底是如何实现的呢?首先我们要搞明白为什么C/C++语言不能一次编译到处执行,因为C/C++的编译的结果是针对特定平台操作系统、处理器指令集而生成的本地代码(nativecode),那么不同操作系统和处理器(事实上x86处理器的指令集都是兼容的)的本地代码是不一样的,自然也就不可能一次编译到处执行。

而直接解释执行的脚本语言,因为不存在编译到本地代码这一过程,所以也不存在跨平台的问题。


那么C#/Java是如何实现的呢?通过上面的知识我们就不难想到了,结合解释执行和编译执行的优点,C#/Java发明了一种叫做中间语言(IL.NETCILJavaByteCode)的东西,中间语言与特定的操作系统和处理器指令集都没有关系,C#/Java在编译时,是编译成为一种低阶的语言,即IL。然后通过在特定平台的运行时程序(CLRJRE),解释和编译IL来执行,,,,


补充阅读:

所以跨平台从来都不是一个新鲜事儿,不仅仅大多数程序设计语言都是跨平台的,而且跨了都好多年了,Java的革命性在于,其提供了一个新的跨平台的方案,由于在编译成低阶的IL之前有充分的编译器优化,所以其性能可以非常接近于C/C++直接编译出来的native code,同时还大大的提高了开发效率,而实现这一点的关键就是IL+JIT编译。事实上在今天,几乎所有的脚本语言、动态类型语言也都走上了这条路,例如JavaScriptPHP,也开始尝试即时编译执行大大的提升了性能。

但是,跨平台也从来不是简单的语言跨平台,否则在C语言的远古时代,我们就可以利用一份源代码面向不同的平台编译来实现跨平台了。跨平台最大的障碍不在于同一段程序代码不能在多个平台上运行,而是在于在不同的操作系统,我们可以使用的库是不同的,这才是跨平台最大的阻碍。

所以Java的口号,一次编译,到处运行从来都只是一句广告语而已,真正要实现这一点,需要在不同平台统一UI和其他乱七八糟的API和库函数,也就是说虚拟一个操作系统出来,问题是这和跑在虚拟机里面的操作系统还有什么区别呢?

也正是因为如此,Java在桌面系统上几乎从未实现过真正的跨平台,而Web服务这种完全没有UI,无需与操作系统直接交互的应用才是Java大显身手的地方(事实上.NET也是如此)。

由上可知,在不同的操作系统拥有不同的类库是非常正常且不可避免的事情,例如在Linux这种连个GUI都不自带的操作系统,WPF或是WinForm如何能迁移到Linux上面去呢。Mono作为一个成熟的Linux上的CLI运行时,已经提供了绝大多数平台无关的类库,完全满足生产使用的条件,什么没有完整的.NETFramework支持这种观点只不过是道听途说小白的笑话。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值