编程语言发展全史
本文以通俗易懂的视角,从编程的本质出发,完整梳理了从机械计算时代到人工智能时代近两百年的编程语言演进脉络。文中详细介绍了机器码、汇编语言、Fortran、C 语言、Java、Python、Go、Rust 等各代主流语言的诞生背景、核心特性与适用场景,并用生活化类比讲解了编译与解释、静态与动态类型、面向对象与函数式等核心底层原理。文章最后还为零基础学习者提供了清晰的编程语言选择建议与正确的学习心态指引,帮助读者快速建立对编程世界的全局认知,轻松推开编程的大门。
前言
你此刻正在阅读的这篇文档,从屏幕上的文字显示、页面的滑动交互,到后台的数据传输,背后都是无数行代码在运行。从手机里的APP、电脑上的游戏,到银行的转账系统、航天火箭的控制程序,我们的数字世界完全建立在编程语言之上。
但你有没有想过:
- 为什么计算机能听懂人类写的代码?
- 世界上第一行代码是怎么诞生的?
- 为什么会有C、Java、Python、JavaScript这么多不一样的编程语言?
- 它们之间到底有什么本质区别?各自又适合做什么事情?
这篇文档将带你穿越80年的编程历史,从最原始的二进制机器码,讲到如今人工智能时代的Python、Rust等现代语言,用最通俗的类比、最贴近生活的例子,讲透编程语言的来源、发展、原理与应用。
无论你是完全零基础的编程小白,还是已经会写几行代码、想了解编程世界全貌的初学者,都能在这篇文档里找到答案。
第一部分 编程的本质:我们为什么需要编程语言?
1.1 什么是编程?——给计算机下命令的艺术
在理解编程语言之前,我们先搞懂一个最基础的问题:编程到底是什么?
你可以把计算机想象成一个极度听话、但又极度笨的“工具人”:它算力极强,一秒钟能做上亿次计算,但它完全不会主动思考,也听不懂人类的自然语言。你必须用它能听懂的方式,一步不差地告诉它每一步要做什么,它才能完成任务。
而编程,就是给计算机下达一系列指令、让它完成特定任务的过程。这一系列指令的集合,就叫做“程序”或者“代码”。
比如你想让计算机算一下“1+2等于几”,你不能直接对着电脑说“帮我算1加2”,它听不懂。你必须用它能识别的方式,把“取数字1、取数字2、执行加法、输出结果”这几个步骤清清楚楚地告诉它。
再举个生活中的例子:编程就像教一个完全不会做饭的人做菜。你不能只说“做个番茄炒蛋”,你得告诉他:
- 拿出两个番茄,洗干净,切成小块
- 拿出三个鸡蛋,打到碗里,加一点盐,搅拌均匀
- 开火把锅烧热,倒一点油
- 把鸡蛋倒进去,炒熟后盛出来
- 锅里再倒一点油,放番茄翻炒,炒出汁水
- 把鸡蛋倒回去,加一点糖和盐,翻炒均匀就出锅
每一步都必须具体、明确、没有歧义,对方才能照着做出来。编程也是一样的道理——你面对的是一个比厨房小白更“笨”的计算机,必须把任务拆分成它能执行的最小步骤,它才能正确完成。
1.2 编程语言是什么?——人与计算机的翻译官
那问题来了:计算机到底能听懂什么语言?我们又该怎么把人类的想法,变成计算机能执行的指令?
这就需要编程语言登场了。
简单来说,编程语言就是人类和计算机之间沟通的“中间语言”,是一套双方都能理解的沟通规则。它既贴近人类的思维逻辑,让我们能看得懂、写得出;又能通过特定的工具,转换成计算机能直接执行的机器指令。
你可以把编程语言想象成一个“翻译官”:
- 人类用编程语言写出自己的需求(源代码)
- 翻译官(编译器/解释器)把这些代码翻译成计算机能听懂的二进制指令
- 计算机执行这些指令,完成任务
为什么不直接让计算机听懂中文或者英文?因为自然语言太模糊、太灵活了,充满了歧义、省略和语境。比如“我喜欢炒土豆”,到底是喜欢吃炒土豆,还是喜欢炒土豆这个动作?人类能靠语境理解,但计算机做不到。
而编程语言有严格、明确、没有任何歧义的语法规则。每一条语句对应什么操作,都是规定死的。这样才能保证计算机执行的结果,和程序员想表达的意思完全一致。
1.3 编程语言的进化方向:越来越贴近人类思维
编程语言的整个发展史,本质上就是不断从“贴近机器”向“贴近人类”进化的历史:
- 最早的编程语言,就是纯二进制的0和1,完全是机器的语言,人类写起来极其痛苦
- 后来人们给二进制指令起了英文缩写的名字,诞生了汇编语言,稍微好记了一点
- 再后来出现了高级语言,比如Fortran、C语言,语法已经很接近数学公式和英文了
- 到了Python、JavaScript这些现代语言,语法几乎和自然英语差不多,普通人也能快速上手
进化的核心动力,就是提升开发效率,降低编程门槛:让程序员不用再纠结机器的细节,把更多精力放在解决实际问题上。
但这并不代表越“高级”的语言就越好。越贴近人类的语言,运行效率往往越低,对硬件的控制力也越弱。就像你指挥一个人做事,说“做个番茄炒蛋”很省事,但对方可能做出来不符合你的口味;你一步步详细指令,虽然麻烦,但能精准控制每一个细节。
所以直到今天,底层的操作系统、嵌入式设备还是用C语言甚至汇编来写,而快速开发网站、做数据分析就用Python。没有最好的编程语言,只有最适合场景的编程语言——这也是为什么我们需要这么多不同编程语言的核心原因。
第二部分 史前时代:从机械计算到电子计算机
2.1 世界上第一位程序员:阿达·洛芙莱斯
很多人以为编程是电子计算机诞生之后才有的事,其实早在19世纪,就已经有了编程思想的雏形。而世界上公认的第一位程序员,是一位女性——阿达·洛芙莱斯(Ada Lovelace)。
故事要从一位叫查尔斯·巴贝奇(Charles Babbage)的英国数学家说起。1837年,巴贝奇提出了一个叫做“分析机”(Analytical Engine)的机械计算机设计。这台机器完全用齿轮、杠杆、转轴这些机械零件构成,设计思路已经具备了现代计算机的核心要素:
- 有“存储器”:用来存放数字和计算结果
- 有“运算器”:用来执行加减乘除等运算
- 有“控制器”:用来控制计算的步骤
- 有输入输出设备:用打孔卡片来输入数据和指令
这是什么概念?在还没有电的年代,巴贝奇就已经在脑子里设计出了一台通用可编程计算机的架构。只可惜受限于当时的工业制造水平,这台分析机直到巴贝奇去世都没能真正造出来。
而阿达·洛芙莱斯是英国诗人拜伦的女儿,同时也是一位极具天赋的数学家。她对巴贝奇的分析机非常着迷,并且意识到:这台机器不只能用来算数学题,它还能处理任何可以用数字表示的信息,甚至可以创作音乐。
1843年,阿达翻译了一篇关于分析机的意大利文论文,并且在论文后面加了大量自己的注解。其中最著名的,就是她详细设计了一套用分析机计算“伯努利数”的步骤——这是世界上第一套完整的、为计算机编写的算法,也就是世界上第一段“程序”。
也正因为这个贡献,阿达被公认为世界上第一位程序员。后来美国国防部发明的Ada编程语言,就是以她的名字命名的。
2.2 图灵机:给计算机划下边界的理论模型
如果说巴贝奇和阿达是编程思想的先驱,那么艾伦·图灵(Alan Turing)就是现代计算机理论的奠基人。
1936年,年仅24岁的图灵发表了一篇划时代的论文《论可计算数及其在判定问题上的应用》。在这篇论文里,他提出了一个假想的计算模型——图灵机(Turing Machine)。
图灵机的结构非常简单,它由三个部分组成:
- 一条无限长的纸带:被分成一个个小格子,每个格子里可以写一个符号(比如0或1)
- 一个读写头:可以在纸带上左右移动,读取当前格子的符号,也可以修改格子里的符号
- 一套控制规则:根据当前机器的状态和读写头读到的符号,决定读写头下一步是左移、右移、写符号,还是停机
就是这么一个看起来极其简单的模型,却蕴含了计算机最本质的原理:任何可以通过一步步规则描述的计算问题,都可以用图灵机来解决。换句话说,只要是能编出程序解决的问题,理论上图灵机都能算。
图灵机的伟大之处在于:它从数学上定义了“什么是可计算的”,给所有计算机的能力划下了边界。直到今天,我们用的手机、电脑、超级计算机,本质上都还是图灵机的实现,没有跳出图灵当年画下的框架。
而我们写的所有程序,本质上都是在给图灵机编写规则——告诉它遇到什么情况该做什么操作。所有的编程语言,最终都是在描述图灵机的行为。
2.3 冯·诺依曼架构:现代计算机的标准蓝图
理论有了,那真正的电子计算机该怎么造?1945年,数学家约翰·冯·诺依曼(John von Neumann)提出了一个计算机设计方案,也就是大名鼎鼎的冯·诺依曼架构。
这个架构的核心思想有两个:
- 二进制:计算机里所有的数据和指令,都用二进制的0和1来表示
- 存储程序:把程序和数据都存放在同一个存储器里,计算机自动从存储器里读取指令并执行
听起来好像没什么,但在当时这是革命性的设计。最早的电子计算机,比如1946年诞生的世界上第一台通用电子计算机ENIAC,编程是靠插拔电线和拧开关来实现的。每次换一个计算任务,都得重新接一遍线路,改一次程序可能要花好几天。
而冯·诺依曼架构把程序也变成了数据,存在内存里。计算机可以自动读取、执行指令,换程序只需要修改内存里的数据就行,不用再动硬件了。这一下子就把编程的效率提升了无数倍,也让通用计算机成为了可能。
直到今天,我们用的几乎所有计算机,不管是电脑、手机还是服务器,依然遵循着冯·诺依曼架构。而我们写的所有代码,最终都会变成二进制的指令和数据,存在内存里,让CPU一条一条地取出来执行。
2.4 第一代编程语言:机器语言——纯二进制的原始时代
世界上第一台电子计算机ENIAC诞生之后,最早的编程方式就是机器语言(Machine Language)。
什么是机器语言?就是纯二进制的0和1组成的指令集。CPU能直接听懂的,只有这门语言。
每一款CPU都有自己的指令集,比如加法指令可能是10110000,减法指令可能是10110001,把数据存到内存的指令可能是10100000。程序员要做的,就是把自己的任务,拆成一条条这样的二进制指令,然后输入到计算机里。
举个最简单的例子:如果想让CPU计算“3+5”,用机器语言大概要这么写:
10110000 00000011 // 把数字3放到寄存器A里
00000101 00000101 // 把数字5加到寄存器A上
10100000 00001000 // 把计算结果存到内存地址8的位置这还只是个位数的加法,如果要做复杂的运算,比如算个三角函数、解个方程,那要写成千上万行二进制代码。
可想而知,用机器语言编程有多痛苦:
- 太难记了:全是0和1,谁也记不住哪个二进制串对应哪个指令
- 太容易错了:写错一个0或者1,程序就完全不对了,而且根本找不到哪里错了
- 没法移植:不同的CPU指令集不一样,给Intel CPU写的程序,放到AMD CPU上就完全不能用,得重写
- 效率极低:写一个简单的数学计算,可能要花好几天
但没办法,最早的计算机只有机器语言这一种编程语言。那时候的程序员,都是记忆力超强的“人形编译器”,每天的工作就是和01二进制打交道。
2.5 第二代编程语言:汇编语言——给二进制起个名字
机器语言实在太难用了,程序员们苦不堪言。于是很快就有人想到:既然二进制指令不好记,那我给每个指令起个英文缩写的名字不就行了?
比如加法指令10110000,就叫ADD(add,加);数据传送指令10110000就叫MOV(move,移动);跳转指令就叫JMP(jump,跳)。这些英文缩写就叫做助记符。
就这样,汇编语言(Assembly Language)诞生了。
还是刚才“3+5”的例子,用汇编语言写出来就变成了这样:
MOV AX, 3 ; 把数字3放到AX寄存器里
ADD AX, 5 ; 把数字5加到AX寄存器上
MOV [8], AX ; 把AX里的结果存到内存地址8的位置是不是一下子就好懂多了?虽然还是很底层,但至少不用记二进制了,看英文单词就能大概知道这条指令是干嘛的。
当然,CPU还是只认二进制,不认这些英文助记符的。所以人们又写了一个叫做汇编器(Assembler)的程序,专门负责把汇编语言的助记符,自动翻译成对应的二进制机器指令。
汇编语言是编程语言发展史上的一大进步,但它依然有很多局限:
- 还是很底层:写代码依然要和寄存器、内存地址打交道,非常繁琐。实现一个简单的功能,也要写一大堆代码
- 和硬件绑定:不同的CPU有不同的汇编指令集,还是没法跨平台移植
- 学习门槛高:要先懂计算机硬件结构,才能写好汇编
所以汇编语言也叫“第二代编程语言”,或者“低级语言”——这里的“低级”不是说它不好,而是说它离硬件很近,抽象程度很低。
直到今天,汇编语言也没有被淘汰。在对性能要求极致、需要直接操控硬件的场景,比如操作系统内核、嵌入式设备、病毒逆向分析等领域,汇编依然有不可替代的作用。但普通应用开发,已经几乎没人直接写汇编了。
第三部分 高级语言的黎明:第一代高级语言(1950s-1960s)
3.1 为什么会有高级语言?——软件危机的前夜
汇编语言虽然比机器语言好用,但生产力还是太低了。随着计算机硬件的飞速发展,计算机的算力越来越强,能解决的问题也越来越复杂。人们发现:硬件的进步速度,远远超过了软件的开发速度。
比如当时科学家用计算机算导弹轨道、气象预报,需要大量复杂的数学计算。用汇编语言写这些计算程序,不仅开发慢,而且特别容易出错,调试起来也极其麻烦。
大家迫切需要一种更贴近人类思维、不用关心硬件细节的编程语言。让程序员可以专注于解决问题本身,而不是纠结数据放哪个寄存器、内存地址是多少。
于是,高级编程语言(High-level Programming Language)应运而生。
为什么叫“高级”?因为它离硬件远,抽象程度高。它把底层的硬件细节都包装起来了,程序员不用再管寄存器、内存地址这些东西,直接用接近数学公式和自然语言的语法写代码就行。
比如同样是计算“3+5”,用高级语言写可能就一行:
result = 3 + 5你不用管这个数字存在哪个寄存器、存在内存哪个位置,语言会自动帮你处理好。你只需要告诉它“把3加5的结果赋值给result”就行,是不是简单多了?
当然,高级语言写的代码,CPU还是不认的。所以需要更复杂的翻译工具——编译器(Compiler)或者解释器(Interpreter),负责把高级语言的代码,翻译成机器能执行的二进制指令。
整个翻译过程非常复杂,相当于把一篇中文长篇小说翻译成外文。但好处是:编译器只需要写一次,之后所有程序员都可以用高级语言快速写代码了。这笔账怎么算都划算。
3.2 Fortran:世界上第一个成功的高级编程语言
3.2.1 诞生背景:科学家的痛点
第一个真正成功并广泛流行的高级编程语言,叫做Fortran(也译作福传)。它的名字来源于“Formula Translation”,意思是“公式翻译”——顾名思义,这门语言就是专门用来翻译数学公式的。
Fortran的发明者叫约翰·巴克斯(John Backus),是IBM公司的程序员。上世纪50年代初,巴克斯在IBM负责给科学家们编写计算程序。他每天的工作就是用汇编语言写各种数学计算代码,痛苦不堪。
他后来回忆说:“编程是件非常累人的工作,大部分时间都花在找bug上,要记住无数的细节,很没有成就感。我只是个懒人,我想让计算机帮我干活,所以我发明了Fortran。”
1954年,巴克斯向IBM提交了一份计划书,提议开发一种更贴近数学公式的编程语言,以及对应的编译器。IBM管理层同意了这个项目,巴克斯组建了一个十几人的小团队,开始了艰苦的开发工作。
要知道,在那个年代,很多人根本不相信高级语言能行。他们觉得:只有手工写的汇编代码,才能榨干计算机的性能。编译器自动翻译出来的代码,肯定又慢又臃肿,根本没法用。
巴克斯团队顶着质疑,花了将近三年时间,终于在1957年发布了第一个版本的Fortran编译器,给IBM 704计算机使用。
结果让所有人都大吃一惊:Fortran写出来的代码,运行效率居然和手工编写的汇编语言差不多!而且开发效率提升了几十倍——原来要写一周的程序,现在一天就能写完。
3.2.2 Fortran的核心特点
Fortran的设计目标非常明确:就是服务于科学计算。所以它的语法几乎就是数学公式的翻版。
比如你想计算一元二次方程的解,公式是: x = [-b ± √(b²-4ac)] / 2a
用Fortran写出来几乎和公式一模一样:
PROGRAM QUADRATIC
REAL A, B, C, DISCRIM, X1, X2
READ *, A, B, C
DISCRIM = B**2 - 4*A*C
X1 = (-B + SQRT(DISCRIM)) / (2*A)
X2 = (-B - SQRT(DISCRIM)) / (2*A)
PRINT *, "X1 = ", X1
PRINT *, "X2 = ", X2
END对于科学家和工程师来说,这简直太友好了。不用再把数学公式拆成一条条汇编指令,直接照着公式写代码就行。
Fortran还有几个划时代的创新:
- 变量命名:可以用英文单词给变量起名,比如用
A、B、C表示系数,用X1、X2表示解,不用再管内存地址 - 内置数学函数:自带平方根、三角函数、对数等常用数学函数,直接调用就行,不用自己写
- 循环和分支:有
DO循环、IF条件判断,不用再手动写跳转指令 - 数组支持:可以很方便地处理矩阵、向量数据,这对科学计算太重要了
3.2.3 历史地位与应用
Fortran的诞生,是编程语言发展史上的里程碑。它第一次证明了高级语言是可行的,彻底打开了高级语言的大门。
从1957年到今天,Fortran已经走过了快70年,经过了Fortran II、Fortran IV、Fortran 77、Fortran 90、Fortran 2008等多个版本的迭代,至今依然活跃在科学计算领域。
你可能会觉得,这么老的语言,早就该被淘汰了吧?恰恰相反,在高性能计算(HPC)、数值计算、物理仿真、气象预报、航空航天这些领域,Fortran至今都是绝对的主流之一。
为什么?原因很简单:
- 性能极强:Fortran编译器经过了几十年的优化,数值计算性能是所有语言里最顶尖的
- 生态极其成熟:几十年积累下来,有大量经过验证的科学计算库,可靠性极高
- 简单稳定:语法简单,专注于数值计算,没有花里胡哨的特性,版本兼容性好
所以直到今天,天气预报的模型、航天飞机的轨道计算、核聚变的模拟、大型物理实验的数据处理,很多依然是用Fortran写的。它就像一把精准的老手术刀,虽然古老,但在自己的领域里无可替代。
3.3 LISP:函数式编程的鼻祖,人工智能的母语
3.3.1 诞生背景:达特茅斯会议与AI的诞生
几乎和Fortran同时代,另一门完全不同风格的编程语言也诞生了,它就是LISP(也译作利是普)。LISP的名字来源于“List Processing”,也就是“列表处理”。
LISP的发明者是约翰·麦卡锡(John McCarthy),他也是“人工智能”(Artificial Intelligence)这个词的发明者之一。
1956年,麦卡锡和马文·明斯基、克劳德·香农等几位科学家,在达特茅斯学院开了一个研讨会,正式提出了“人工智能”这个概念,立志要让计算机拥有像人一样的智能。这次会议也被称为“达特茅斯会议”,是人工智能的起点。
那时候大家觉得,人工智能要处理的是符号、逻辑、语言这些东西,而不是数值计算。Fortran是用来算数字的,不适合做AI研究。所以麦卡锡就想设计一门专门用来做符号处理的编程语言。
1958年,麦卡锡在麻省理工学院发明了LISP语言。这门语言一诞生,就和其他所有编程语言都不一样——它的语法极其简洁,核心思想也完全不同。
3.3.2 LISP的核心特点
LISP最独特的地方,就是它的代码和数据是同一种格式:都是用括号括起来的列表(S-表达式)。
比如一个简单的加法,在其他语言里是3 + 5,在LISP里是这样的:
(+ 3 5)再复杂一点,计算“3加5再乘以2”,其他语言是(3 + 5) * 2,LISP里是这样:
(* (+ 3 5) 2)所有的操作都用“括号+操作符+参数”的形式表示。代码就是列表,列表就是代码,代码和数据的结构完全一样。这个特性叫做“同像性”。
这有什么好处?好处是:程序可以把自己当成数据来处理,可以自己修改自己、自己生成自己。这对于人工智能研究来说太重要了——因为智能本身就包含了自我学习、自我修改的能力。
LISP的另一个核心特点,就是它是函数式编程的鼻祖。它的核心思想是:把计算当成数学函数的求值,尽量避免修改变量和状态,用函数的组合来解决问题。
举个通俗的例子:面向过程编程就像一条流水线,原料一步步加工成成品,中间会修改各种状态;而函数式编程就像搭积木,每个函数都是一块积木,你把不同的积木拼在一起,就组合出了复杂的功能。每块积木都是独立的,输入什么就输出什么,不会影响外面的东西。
LISP还有很多在当时看来惊世骇俗的特性:递归、垃圾回收、高阶函数、闭包……这些特性,很多现代语言直到最近二三十年才慢慢普及。可以说,LISP在思想上领先了时代几十年。
3.3.3 发展与影响
LISP诞生之后,很快就成了人工智能研究的标准语言。整个六七十年代,几乎所有的AI研究都是用LISP做的。专家系统、自然语言处理、定理证明……这些早期AI领域的成果,几乎都是LISP的功劳。
LISP也衍生出了很多方言,比如Common Lisp、Scheme、Clojure等等。其中Clojure是近年来比较流行的现代LISP方言,运行在Java虚拟机上,在大数据领域有不少应用。
当然,LISP也有自己的问题:它的思维方式和传统语言差异太大,学习门槛很高;括号太多,被很多人戏称为“一堆括号在跳舞”;而且执行效率相对较低,不太适合做底层系统开发。
所以LISP一直没有成为工业界的主流语言,主要活跃在学术和AI研究领域。但它对编程语言的影响是极其深远的:几乎所有现代编程语言里的高级特性,都能在LISP里找到源头。很多程序员都说,学了LISP之后,才真正理解了编程是什么。
3.4 COBOL:商业数据处理的常青树
3.4.1 诞生背景:企业的数据处理需求
Fortran适合科学家,LISP适合AI研究者,那企业和政府部门呢?银行要记账、保险公司要算保单、政府要统计人口数据,这些商业数据处理场景,也需要自己的编程语言。
上世纪50年代末,美国国防部发现:各个部门都在自己搞编程语言,互不兼容,数据没法互通,浪费了大量人力物力。于是他们牵头,联合了IBM等几家大公司,想制定一个统一的、面向商业数据处理的编程语言标准。
1959年,这个标准正式出台,命名为COBOL,全称是“Common Business-Oriented Language”,也就是“面向商业的通用语言”。
COBOL的主要设计者是一位叫格蕾丝·霍珀(Grace Hopper)的女科学家,她被称为“COBOL之母”。霍珀是编程界的传奇人物:她是世界上最早的一批程序员之一,参与过马克一号计算机的开发;“bug”(程序漏洞)这个词,就是她发明的——当年有一只飞蛾飞进了计算机里导致故障,她把飞蛾贴在日志本上,标注了“first actual case of bug being found”(第一个真正被发现的bug)。
3.4.2 COBOL的核心特点
COBOL的设计目标非常明确:让业务人员也能看懂代码。所以它的语法极度接近英语句子,几乎就是用英语写文章。
比如一段简单的计算员工工资的代码:
IDENTIFICATION DIVISION.
PROGRAM-ID. CALCULATE-SALARY.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 EMPLOYEE-NAME PIC X(20).
01 BASE-SALARY PIC 9(6)V99 VALUE 5000.00.
01 BONUS PIC 9(5)V99 VALUE 1000.00.
01 TOTAL-SALARY PIC 9(7)V99.
PROCEDURE DIVISION.
DISPLAY "请输入员工姓名: ".
ACCEPT EMPLOYEE-NAME.
COMPUTE TOTAL-SALARY = BASE-SALARY + BONUS.
DISPLAY "员工 " EMPLOYEE-NAME " 的总工资是: " TOTAL-SALARY.
STOP RUN.你哪怕完全没学过COBOL,也能大概看懂这段代码是干嘛的:定义了员工姓名、基本工资、奖金这些数据,然后计算总工资并显示出来。
COBOL把程序分成了四个部分:标识部、数据部、过程部等等,结构非常规整。它最擅长的就是处理大量结构化的业务数据,比如批量记账、生成报表、统计数据等等。
3.4.3 历史影响与现状
COBOL诞生之后,迅速成了商业数据处理领域的标准。从银行、保险、证券,到政府、税务、制造业,几乎所有的企业和机构都在用COBOL写核心业务系统。
最鼎盛的时候,全世界有超过80%的商业数据都是用COBOL处理的。无数的银行核心系统、社保系统、工资系统、机票预订系统,都是用COBOL写的。
最神奇的是,这门诞生于1959年的古老语言,直到今天还在广泛使用。据统计,目前全球还有超过万亿行COBOL代码在生产环境中运行,每天支撑着数万亿美元的金融交易。
为什么不换掉它?原因很现实:
- 系统太核心了:银行的核心记账系统、政府的社保系统,一旦出问题就是天大的事,没人敢轻易换
- 代码量太大了:几十年积累下来,动辄几百万、几千万行代码,重写一遍成本极高,风险也极大
- 够用就好:这些系统的业务逻辑很稳定,几十年都没什么大变化,COBOL完全能胜任
当然,COBOL也面临着严重的人才断层问题。现在的年轻人几乎没人愿意学这么古老的语言,而会COBOL的老程序员大多已经退休了。前几年美国的银行系统升级,高薪聘请退休的COBOL程序员回来救场,还成了新闻。
COBOL就像一个埋在地下的老管道,你平时看不见它,但它默默地支撑着整个现代商业社会的运转。
3.5 Algol:算法语言的先驱,现代语法的奠基人
3.5.1 诞生背景:学术界的标准化努力
Fortran是IBM的产品,COBOL是美国国防部主导的,都是美国的。那欧洲的科学家们呢?他们也想要自己的编程语言。
1958年,欧洲和美国的计算机科学家们在瑞士开了个会,决定联合设计一门通用的算法语言。这门语言就叫做Algol,全称是“Algorithmic Language”,也就是算法语言。
Algol的定位和Fortran不一样:Fortran是工业界的产品,追求实用和性能;而Algol是学术界主导设计的,追求严谨、优雅、标准化。它更像是一个“理想中的编程语言”。
3.5.2 Algol的核心贡献
Algol本身在工业界的应用不算特别广泛,但它对后世编程语言的影响,怎么形容都不为过。我们现在用的C、Java、Python、JavaScript……几乎所有主流编程语言的语法,都源自Algol。
Algol的核心贡献有这么几个:
- BNF范式:Algol第一次用严谨的数学方法来定义编程语言的语法,也就是巴科斯-瑙尔范式(BNF)。从此之后,设计编程语言有了科学的方法,不再是拍脑袋瞎写了
- 块级结构:Algol发明了用
begin和end来划分代码块的语法。后来的C语言用{},其实就是begin和end的简化 - 局部变量:Algol第一次引入了局部变量的概念,代码块里定义的变量,外面访问不到。这是软件工程史上的一大进步,大大减少了变量之间的互相干扰
- 结构化控制语句:我们现在熟悉的
if-else、for循环、while循环,都是Algol最早标准化的
可以说,Algol奠定了现代命令式编程语言的语法基础。它就像编程语言界的“拉丁语”,自己虽然不常用了,但后世几乎所有语言都是从它衍生出来的。
Algol主要有两个重要版本:1960年的Algol 60和1968年的Algol 68。Algol 60是最经典的版本,影响也最大。Algol 68设计得过于复杂,反而没有那么成功。
3.6 BASIC:让普通人也能编程的启蒙语言
3.6.1 诞生背景:给大学生的入门语言
前面讲的Fortran、LISP、COBOL、Algol,都是给专业程序员、科学家、工程师用的,门槛都不低。那普通人想学习编程怎么办?
1964年,美国达特茅斯学院的两位教授——约翰·凯梅尼(John Kemeny)和托马斯·卡茨(Thomas Kurtz),想设计一门简单易学的编程语言,让非计算机专业的大学生也能快速上手。
他们给这门语言起名叫BASIC,全称是“Beginner’s All-purpose Symbolic Instruction Code”,也就是“初学者通用符号指令代码”。光看名字就知道,这门语言就是专门给初学者设计的。
3.6.2 BASIC的核心特点
BASIC的设计理念就是“简单”:语法简单、规则少、容易上手。它是交互式的——你输入一行代码,它就立刻执行并给你反馈,不用等整个程序编译完。
比如经典的Hello World程序,用BASIC写就一行:
10 PRINT "Hello, World!"
20 END再比如一个从1加到10的循环:
10 SUM = 0
20 FOR I = 1 TO 10
30 SUM = SUM + I
40 NEXT I
50 PRINT "总和是"; SUM
60 END是不是特别简单?几乎和英语没什么区别。
早期的BASIC每一行前面都要加行号,程序按行号顺序执行。这也是很多人对BASIC的经典印象。
3.6.3 普及与发展:个人电脑时代的标配
BASIC诞生之后,很快就成了大学编程入门的标准教学语言。但真正让BASIC风靡全球的,是个人电脑的兴起。
上世纪70年代末80年代初,苹果II、IBM PC等个人电脑开始普及。那时候的个人电脑性能很差,内存很小,装不下复杂的编译器。而BASIC体积小、简单易用,非常适合个人电脑。
所以几乎所有的早期个人电脑,开机自带的就是BASIC语言。你打开电脑,不用装任何软件,直接就能写BASIC代码。
那时候无数的电脑爱好者,就是靠着BASIC入门,学会了编程。很多后来的程序员大佬,比如比尔·盖茨,最早都是靠BASIC起家的——比尔·盖茨创业的第一个产品,就是给Altair 8800电脑做的BASIC解释器。
BASIC也衍生出了很多版本:微软的GW-BASIC、QBASIC,还有Visual Basic(VB)等等。其中Visual Basic在90年代非常火,是Windows平台上快速开发桌面软件的首选。
当然,BASIC也有很多被人诟病的地方:早期版本功能很弱,不支持结构化编程,很容易写出“面条式代码”,也就是GOTO乱飞,逻辑缠成一团。很多人说BASIC会教坏初学者的编程习惯。
但不可否认的是,BASIC大大降低了编程的门槛,让编程从科学家和专业人士的专属技能,变成了普通人也能接触和学习的东西。它是无数人的编程启蒙老师,为计算机的普及立下了汗马功劳。
第四部分 结构化编程革命:软件工业化的基石(1970s)
4.1 什么是结构化编程?——告别“面条代码”
4.1.1 软件危机的到来
到了上世纪60年代末,软件行业遇到了一个大问题:软件变得越来越大、越来越复杂,项目经常延期、超预算,bug满天飞,维护起来极其困难。这个问题就叫做软件危机。
为什么会出现软件危机?其中一个很重要的原因,就是当时的编程方式太混乱了。
早期的编程语言,比如Fortran、BASIC,都支持GOTO语句。GOTO语句可以让程序跳转到任意一行代码去执行。程序员写代码的时候,想到哪跳到哪,逻辑非常随意。
如果程序小还好,一旦程序大了,几千几万行代码,到处都是GOTO跳转,整个程序的逻辑就像一团乱麻一样,没人能看得懂。这种代码就被戏称为“面条式代码”——缠成一团,理都理不清。
当时有个程序员说过一句名言:“如果代码是我写的,只有上帝和我知道它是干嘛的;如果过了三个月,那就只有上帝知道了。”
这种代码写起来爽,但维护起来就是噩梦。改一个小功能,可能要牵一发而动全身,到处都是坑。项目越大,问题越严重。
4.1.2 迪克斯特拉与《GOTO语句有害论》
1968年,荷兰计算机科学家艾兹格·迪克斯特拉(Edsger Dijkstra)发表了一篇著名的短文,标题叫做《GOTO语句有害论》(Go To Statement Considered Harmful)。
在这篇文章里,迪克斯特拉猛烈抨击了滥用GOTO语句的编程方式。他说:GOTO语句会让程序的控制流变得混乱不堪,大大增加程序的复杂度,是很多bug的根源。程序员应该尽量避免使用GOTO语句。
迪克斯特拉提出,任何复杂的程序逻辑,都只需要用三种基本结构组合而成:
- 顺序结构:代码按顺序一行一行往下执行
- 分支结构:根据条件判断,选择执行不同的代码(也就是if-else)
- 循环结构:重复执行某一段代码,直到满足条件(也就是for、while循环)
他证明了:只要有这三种结构,就能实现所有的计算逻辑。完全不需要GOTO语句。
这种只用顺序、分支、循环三种结构来组织代码的编程方式,就叫做结构化编程(Structured Programming)。
结构化编程的核心思想,就是“分而治之”:把一个大的复杂程序,拆分成一个个小的模块(函数/过程),每个模块只负责一件事,模块之间通过参数传递数据。每个模块内部都只用三种基本结构,逻辑清晰,容易理解,也容易修改。
打个通俗的比方:非结构化编程就像一团乱麻的电线,找个故障要把整团线都拆开;结构化编程就像整理好的电线,每根线都有明确的功能,用接线盒连在一起,哪里出问题就换哪根,非常方便。
4.1.3 结构化编程的意义
《GOTO语句有害论》发表之后,在编程界引发了一场大辩论。辩论的结果是:结构化编程思想大获全胜。
从70年代开始,结构化编程成了行业标准。新诞生的编程语言,比如Pascal、C语言,都是天生支持结构化编程的。老的语言也纷纷升级,加入结构化特性。
结构化编程是软件工程发展史上的第一次革命。它第一次让编程从“手工作坊”式的个人技艺,变成了有章法、可协作的工程化工作。它大大降低了大型软件的开发和维护成本,支撑了之后几十年软件行业的飞速发展。
直到今天,结构化编程依然是所有程序员的基本功。哪怕是面向对象、函数式这些新的编程范式,底层也依然遵循着结构化编程的基本原则。
4.2 Pascal:严谨的教学典范
4.2.1 诞生背景:沃斯的教学理念
结构化编程思想普及之后,需要一门语言来作为教学载体,教学生们写规范的结构化代码。这门语言就是Pascal。
Pascal的发明者是瑞士计算机科学家尼克劳斯·沃斯(Niklaus Wirth)。沃斯是结构化编程的坚定支持者,他觉得当时的语言要么太复杂,要么太随意,不适合教学。
于是他在1970年设计了一门新的编程语言,以17世纪法国数学家、物理学家帕斯卡(Blaise Pascal)的名字命名,这就是Pascal语言。
4.2.2 Pascal的核心特点
Pascal的设计目标非常明确:就是用来教编程的。所以它的特点就是:语法严谨、结构清晰、强类型、强制结构化。
Pascal的语法非常规整,比如代码块用begin和end包裹,变量必须先声明类型才能使用,函数必须明确声明返回值类型。它不允许乱七八糟的写法,语法检查非常严格。
比如一段简单的Pascal代码:
program Hello;
var
name: string;
age: integer;
begin
writeln('请输入你的名字:');
readln(name);
writeln('请输入你的年龄:');
readln(age);
writeln('你好,', name, ',你今年', age, '岁了。');
end.可以看到,Pascal的结构非常清晰:程序开头声明变量,然后是主程序代码,所有的执行代码都包在begin和end里。
沃斯的理念是:好的编程语言,应该逼着程序员养成良好的编程习惯。Pascal严格的语法,能让初学者从一开始就写出规范、整洁的结构化代码,避免养成写“面条代码”的坏习惯。
4.2.3 历史地位与影响
Pascal诞生之后,迅速成了全球大学计算机专业的标准教学语言。整整几代程序员,都是学Pascal入门的。
除了教学之外,Pascal也有不少工业应用。比如著名的Delphi开发工具,就是基于Object Pascal(面向对象版本的Pascal),在90年代的Windows桌面开发领域非常流行。
当然,Pascal也有自己的局限性:它太偏向教学了,语法过于死板,灵活性不足,做大型项目的时候有点束手束脚。而且标准库功能比较弱,生态也不如C语言丰富。
所以现在Pascal已经不再是主流的编程语言,教学领域也慢慢被Python取代了。但它在编程语言发展史上的地位是不可磨灭的:它把结构化编程思想普及到了每一个程序员身上,为软件工程的规范化做出了巨大贡献。
4.3 C语言:系统编程的永恒经典
4.3.1 诞生背景:贝尔实验室与UNIX的副产品
在所有的编程语言里,C语言绝对是最传奇、影响力最大的那一个。它不仅是系统编程的王者,更是几乎所有现代编程语言的“母语”。
C语言的诞生,和一个传奇的项目有关——UNIX操作系统。而这个项目,诞生于传奇的贝尔实验室。
故事要从1965年说起。当时贝尔实验室、麻省理工学院和通用电气三家联合,启动了一个叫做Multics的操作系统项目。这个项目的目标非常宏大:要做一个功能强大、支持多用户多任务的通用操作系统。
结果项目越做越复杂,进度严重滞后,最后贝尔实验室觉得这个项目没希望,就退出了。
贝尔实验室退出之后,参与项目的两个程序员——肯·汤普森(Ken Thompson)和丹尼斯·里奇(Dennis Ritchie),手痒难耐,觉得Multics虽然失败了,但思路是好的。他们想自己搞一个简单版的操作系统。
汤普森找了一台没人用的老旧PDP-7小型机,利用工作之余的时间,开始写操作系统。为了写这个系统,他还自己写了一个汇编器和一些工具。
1969年,第一个版本的操作系统写出来了。实验室的同事开玩笑说,这个系统和Multics正好相反,Multics是“多”,那这个就叫“单”吧,于是起名叫UNIX(UNI是单一的意思)。
最早的UNIX是用汇编写的,移植起来非常麻烦。每换一种硬件,几乎都要重写一遍。汤普森和里奇觉得,这样下去不是办法,得用高级语言来重写UNIX。
当时已经有Fortran、COBOL这些高级语言了,但都不适合写操作系统。写操作系统需要直接操控硬件、管理内存,对性能要求极高,普通高级语言做不到。
那怎么办?自己造一门语言呗。
最开始汤普森把BCPL语言简化了一下,做了个B语言。但B语言功能还是太弱,尤其是对数据类型的支持很差,没法很好地利用新硬件的特性。
于是里奇在B语言的基础上做了大量改进,加入了数据类型、指针等特性,设计出了一门新的语言。因为是B语言的下一代,所以就叫C语言。
1972年,C语言正式诞生。之后汤普森和里奇用C语言重写了UNIX的绝大部分代码。这也是历史上第一个用高级语言编写的操作系统。
4.3.2 C语言的设计哲学
C语言的设计哲学,可以用一句话概括:信任程序员。
C语言几乎不给程序员设限,你想怎么写就怎么写。你可以直接操作内存、可以访问任意地址、可以随便做类型转换。编译器只负责翻译代码,几乎不做额外的检查,完全相信程序员知道自己在做什么。
这种设计理念,带来了几个核心优势:
- 性能极高:C语言非常接近硬件,几乎没有额外的开销,运行效率和汇编语言差不多,是所有高级语言里性能最顶尖的
- 灵活性极强:指针可以让你直接操控内存,想怎么玩就怎么玩,能实现很多高级语言做不到的操作
- 可移植性好:相比汇编语言,C语言的跨平台移植性好太多了。只要给不同的硬件写个C编译器,代码基本不用改就能跑
- 简洁高效:语法简单,关键字不多,没有花里胡哨的高级特性,编译速度快,生成的机器码也很精简
当然,这种设计也有代价:
- 容易出错:直接操作内存很容易写出bug,比如空指针、野指针、内存泄漏、缓冲区溢出等等。这些bug往往很隐蔽,调试起来非常困难
- 学习门槛高:要写好C语言,必须懂计算机底层原理,懂内存、懂指针、懂硬件,对初学者不友好
- 缺少高级特性:没有垃圾回收、没有面向对象原生支持、没有异常处理,做大型应用开发的时候,生产力不如高级语言
4.3.3 C语言的核心特性
我们用一段简单的代码,来感受一下C语言的特点:
#include <stdio.h>
// 计算两个数的和
int add(int a, int b) {
return a + b;
}
int main() {
int num1 = 3;
int num2 = 5;
int result;
result = add(num1, num2);
printf("%d + %d = %d\n", num1, num2, result);
return 0;
}C语言最核心、最有特色的特性,就是指针。很多人学C语言,就是栽在指针上。
指针到底是什么?通俗地说,指针就是内存地址。一个指针变量,存的不是数据本身,而是数据在内存里的地址。就像你家门牌号,门牌号不是你家本身,但通过门牌号就能找到你家。
比如:
int a = 10; // 定义一个整型变量a,值是10
int *p; // 定义一个整型指针p
p = &a; // 把a的地址赋值给p,也就是p指向a
*p = 20; // 通过指针p修改a的值,现在a变成20了指针非常强大,有了指针,你就可以直接读写任意内存地址,可以灵活地操作数据。但也非常危险,一旦指针指错了地方,轻则程序崩溃,重则数据损坏,甚至出现安全漏洞。
除了指针,C语言还有很多经典特性:手动内存管理(malloc/free)、结构体、宏定义、预处理等等。这些特性共同构成了C语言强大而灵活的底层能力。
4.3.4 历史地位与应用
C语言是编程语言发展史上的里程碑。它第一次证明了:高级语言也能用来写操作系统,也能达到接近汇编的性能。它彻底改变了系统编程的方式。
因为UNIX的成功,C语言也跟着迅速普及。整个70到80年代,几乎所有的系统软件都是用C写的。后来的Linux、Windows、macOS这些主流操作系统,内核也都是用C写的。
更重要的是,后世几乎所有的主流编程语言,都深受C语言的影响。C++、Java、C#、Python、JavaScript、Go、Rust……它们的语法几乎都继承自C语言的风格。我们现在熟悉的大括号{}、分号结尾、if-else、for循环这些语法,都是从C语言普及开来的。
可以说,C语言是现代编程语言的“母语”。学好了C语言,再学其他任何语言,都会觉得似曾相识,上手非常快。
直到今天,C语言依然是最主流的编程语言之一。它的主要应用场景包括:
- 操作系统内核:Linux、Windows、macOS、Unix的内核都是C写的
- 嵌入式开发:单片机、物联网设备、智能家居、工业控制,几乎都是C语言的天下
- 驱动程序:显卡驱动、声卡驱动、各种硬件驱动,基本都是C写的
- 数据库内核:MySQL、Redis、SQLite这些数据库的核心都是C写的
- 编译器和解释器:很多编程语言的编译器/解释器本身,也是用C写的,比如Python解释器
- 高性能计算:对性能要求极致的场景,比如高频交易、音视频编解码
C语言就像编程世界的“拉丁语”,古老但生命力极强。它可能不是最流行、最好用的语言,但它永远是最基础、最重要的那一个。
第五部分 面向对象的浪潮:软件复用的革命(1980s-1990s初)
5.1 什么是面向对象编程?——从“怎么做”到“谁来做”
5.1.1 结构化编程的局限
结构化编程解决了“面条代码”的问题,让代码逻辑更清晰了。但随着软件越来越大、越来越复杂,结构化编程也遇到了瓶颈。
结构化编程是面向过程的,核心是“函数”和“数据”。数据和函数是分开的:数据存在变量里,函数用来操作数据。
打个比方:你要做一个学生管理系统。结构化编程的思路是:
- 定义学生的数据结构:姓名、学号、年龄、成绩
- 写一堆函数来操作这些数据:增加学生、删除学生、修改学生信息、打印学生信息
这种方式在系统小的时候没问题,但系统一大,问题就来了:
- 数据和操作分离,容易出错:数据是公开的,任何函数都能随便修改。如果有十几个函数都操作同一份数据,很容易改出问题,而且不知道是谁改的
- 复用性差:如果想做一个老师管理系统,很多功能和学生管理是类似的,但代码没法直接复用,只能复制粘贴,改一改
- 扩展性差:如果要加新功能,比如给学生加个“选课”功能,往往要改很多地方的代码,牵一发而动全身
简单说,面向过程编程适合解决简单的、线性的问题。但当问题变得复杂,涉及到大量实体和交互的时候,面向过程就有点力不从心了。
5.1.2 面向对象的核心思想
那面向对象编程(Object-Oriented Programming,简称OOP)是怎么解决这些问题的呢?
面向对象的核心思路是:把数据和操作数据的方法,打包在一起,变成一个“对象”。对象是一个独立的整体,对外只暴露必要的接口,内部的细节藏起来。
还是用学生管理系统举例。面向对象的思路是:
- 定义一个“学生类”(Student),这个类里面包含了学生的数据(姓名、学号、年龄),也包含了操作这些数据的方法(自我介绍、修改成绩、选课等等)
- 每个具体的学生,就是这个类生成的一个“对象”。比如“张三”是一个对象,“李四”是另一个对象
打个更通俗的比方:
- 面向过程编程就像修空调。你把空调拆了,里面有零件(数据),你拿着工具(函数)去操作零件,一步步把空调修好。你得清楚空调内部的所有结构
- 面向对象编程就像用空调。空调就是一个对象,它内部有压缩机、蒸发器、电路板这些零件(数据),也有制冷、制热、调温这些功能(方法)。你不用知道空调内部怎么工作的,只要按遥控器(调用方法)就行
面向对象把思考问题的方式,从“我要一步步怎么做”,变成了“我要让谁来做这件事”。它更符合人类认识世界的方式——我们认识世界,就是通过一个个具体的事物,以及事物之间的交互来认识的。
5.1.3 面向对象的三大特性
面向对象有三大核心特性:封装、继承、多态。
1. 封装 封装就是把对象的内部数据和实现细节藏起来,只对外暴露必要的接口。外部不能直接修改对象内部的数据,只能通过暴露的方法来访问和操作。
就像空调:内部的零件你是碰不到的,你只能通过遥控器上的按钮来控制它。这样有什么好处?
- 安全:你不会乱改内部零件,把空调搞坏
- 简单:你不用懂空调原理,会用遥控器就行
- 易维护:厂家内部怎么升级零件都可以,只要遥控器按钮不变,用户就不用改使用习惯
2. 继承 继承就是让一个类可以继承另一个类的属性和方法,不用重新写一遍。被继承的叫父类,继承的叫子类。
比如我们已经有了一个“人类”(Person),有姓名、年龄,会吃饭、睡觉。现在要做一个“学生类”(Student),学生也是人,也有姓名年龄,也会吃饭睡觉,只是多了学号、成绩、上课这些特性。
如果用继承,学生类直接继承人类,人类已经有的属性和方法,学生类就不用再写一遍了,只要加上自己特有的部分就行。这样大大提高了代码的复用性。
3. 多态 多态就是同一个操作,作用在不同的对象上,可以有不同的结果。
比如“叫”这个动作:猫叫是“喵喵”,狗叫是“汪汪”,鸟叫是“叽叽喳喳”。都是“叫”,但不同的动物对象,表现出来不一样。
多态的好处是:调用者不用管具体是什么对象,只要知道它有这个方法就行,直接调用就可以了。增加新的对象类型,也不用改调用的代码,扩展性非常好。
封装解决了数据安全和代码隔离的问题,继承解决了代码复用的问题,多态解决了扩展性的问题。这三大特性,共同构成了面向对象编程的核心。
5.2 Simula:世界上第一个面向对象语言
面向对象的思想,最早起源于仿真领域。
上世纪60年代,挪威计算中心的两位科学家——奥利-约翰·达尔(Ole-Johan Dahl)和克里斯汀·奈加特(Kristen Nygaard),想做一个计算机仿真程序,用来模拟现实世界里的各种系统,比如交通系统、工厂生产线等等。
现实世界里的系统,都是由一个个独立的实体组成的。比如交通系统里有汽车、行人、红绿灯,每个实体都有自己的状态和行为,实体之间会互相交互。
如果用面向过程的方式来写仿真程序,会非常别扭,因为现实世界不是按“过程”组织的,而是按“实体”组织的。
于是达尔和奈加特就想:能不能让程序里的代码,也对应现实世界里的实体?每个实体就是一个独立的单元,有自己的数据和行为。
基于这个想法,他们在Algol 60的基础上,设计了一门新的语言,叫做Simula,也就是“模拟语言”的意思。Simula 67版本在1967年正式发布,这是世界上第一门支持面向对象的编程语言。
Simula第一次引入了类(Class)、对象(Object)、继承(Inheritance)、虚函数这些面向对象的核心概念。它证明了:用面向对象的方式来写仿真程序,非常自然,也非常高效。
不过Simula主要还是用在仿真领域,没有广泛普及开来。但它的思想影响了后来无数的编程语言。所有的面向对象语言,追溯源头都能追到Simula。
5.3 Smalltalk:纯面向对象的极致探索
如果说Simula发明了面向对象,那把面向对象思想做到极致的,就是Smalltalk。
Smalltalk诞生于传奇的施乐帕克研究中心(Xerox PARC)。70年代初,艾伦·凯(Alan Kay)带领团队,设计了这门全新的编程语言。
Smalltalk的设计理念非常激进:一切皆对象。所有的东西都是对象:数字是对象、字符串是对象、函数是对象、甚至类本身也是对象。程序的运行,就是对象之间互相发送消息。
在Smalltalk里,你想让两个数相加,不是写3 + 5,而是给3这个对象发送一个“+”消息,参数是5。就像这样:
3 + 5看起来写法差不多,但底层的思想完全不一样。
Smalltalk不只是一门语言,它还是一个完整的交互式开发环境。它有图形界面、有IDE、有调试器、有实时的代码修改功能。在70年代,这简直是黑科技一样的存在。
我们现在熟悉的很多东西,比如图形用户界面(GUI)、窗口、鼠标、IDE、所见即所得编辑,都是施乐帕克研究中心发明的,而Smalltalk就是运行在这个环境里的语言。
后来乔布斯参观了施乐帕克,看到了图形界面和Smalltalk,大受震撼。回去之后就推出了麦金塔电脑,开启了个人电脑的图形界面时代。
Smalltalk对后世的影响非常深远:
- 它把面向对象思想发扬光大,定义了什么是真正的面向对象
- 它发明的交互式开发环境、实时调试、图形界面,影响了整个软件行业
- 很多后来的面向对象语言,比如Ruby、Python、Objective-C,都深受Smalltalk的影响
当然,Smalltalk也有自己的问题:它太独特了,和当时主流的编程方式差异太大;而且性能不高,商业应用比较少。它更多的是作为思想领袖,影响着整个行业的发展。
5.4 C++:C语言与面向对象的完美结合
5.4.1 诞生背景:本贾尼的“C with Classes”
Simula和Smalltalk虽然思想很先进,但性能不够高,不适合做系统级开发。那有没有办法,既保留C语言的高性能和底层能力,又能用上面向对象的特性呢?
有,这就是C++。
C++的发明者是本贾尼·斯特劳斯特鲁普(Bjarne Stroustrup),一位丹麦的计算机科学家。1979年,他来到贝尔实验室工作。他之前用Simula写过仿真程序,非常喜欢面向对象的特性。但他同时也需要C语言的高性能来做系统开发。
于是他就想:能不能给C语言加上类的功能,让C也能写面向对象的程序?
他给这个项目起名叫“C with Classes”(带类的C)。他的设计原则非常明确:
- 完全兼容C语言:C语言的代码,不用改就能在C++里编译运行。C程序员可以无缝迁移过来
- 零成本抽象:增加的面向对象特性,不能带来额外的性能开销。用不用这些特性,性能都和C语言一样
- 信任程序员:继承C语言的设计哲学,不给程序员设限,提供足够的灵活性
1983年,这门语言正式命名为C++。“++”是C语言里的自增运算符,意思就是“C的增强版”。
5.4.2 C++的核心特性
C++在C语言的基础上,加入了大量的新特性。最核心的当然是面向对象的支持:类、对象、继承、多态、虚函数等等。
我们用一段简单的代码,感受一下C++的面向对象:
#include <iostream>
using namespace std;
// 定义一个人类
class Person {
private:
// 私有属性,外部不能直接访问
string name;
int age;
public:
// 构造方法
Person(string n, int a) {
name = n;
age = a;
}
// 自我介绍的方法
void introduce() {
cout << "我叫" << name << ",今年" << age << "岁。" << endl;
}
// 修改年龄
void setAge(int a) {
if (a > 0 && a < 150) {
age = a;
}
}
};
// 学生类,继承自人类
class Student : public Person {
private:
int studentId;
public:
// 构造方法,调用父类构造
Student(string n, int a, int id) : Person(n, a) {
studentId = id;
}
// 学生特有的学习方法
void study() {
cout << "我正在学习。" << endl;
}
};
int main() {
// 创建一个学生对象
Student zhangsan("张三", 18, 2024001);
// 调用继承来的方法
zhangsan.introduce();
// 调用自己的方法
zhangsan.study();
return 0;
}除了面向对象,C++还加入了很多其他的高级特性:
- 模板:支持泛型编程,写一份代码就能适配不同的数据类型
- 异常处理:try-catch机制,更优雅地处理程序错误
- STL标准库:提供了现成的容器(向量、链表、哈希表等)和算法,大大提高了开发效率
- 运算符重载:可以给自定义类型定义运算符的行为,比如让两个对象可以相加
C++是一门非常“大”的语言,特性极多,编程范式非常丰富。它支持面向过程、面向对象、泛型编程、函数式编程等多种范式。你想用什么风格写都可以,非常灵活。
5.4.3 优缺点与争议
C++是一门极具争议的语言。喜欢它的人爱得要死,讨厌它的人也恨得要死。
C++的优点非常突出:
- 性能极强:和C语言处于同一水平,是所有高级语言里性能第一梯队的
- 功能极强:特性丰富,范式多样,几乎没有它做不了的事情。从底层系统到上层应用,从游戏到金融,都能胜任
- 完全兼容C:可以无缝调用C语言代码,生态极其丰富
但C++的缺点也同样突出:
- 语法太复杂:特性太多,而且很多特性非常晦涩难懂。C++是出了名的难学,入门门槛高,精通更是难上加难
- 容易踩坑:继承了C语言的手动内存管理,指针问题一样不少。再加上多继承、虚函数、模板这些新特性,坑更多了
- 编译速度慢:代码稍微大一点,编译就要等半天,非常影响开发效率
- 标准更新慢:历史包袱重,很多不合理的设计因为要兼容旧代码,没法改
有一句很有名的吐槽:“C++不是一门语言,是一堆语言的集合。” 因为它特性太多了,不同的人写出来的C++代码,可能风格完全不一样,甚至互相看不懂。
5.4.4 应用与发展
尽管有很多争议,但C++依然是这个世界上最重要的编程语言之一。它凭借着极致的性能和强大的功能,在很多领域都是无可替代的。
C++的主要应用场景包括:
- 游戏开发:几乎所有的3A游戏引擎,比如虚幻引擎、Unity的底层,都是用C++写的。游戏对性能要求极高,C++是最佳选择
- 桌面软件:大型桌面软件,比如Photoshop、Office、AutoCAD这些,很多都是用C++写的
- 系统级软件:操作系统组件、驱动程序、编译器、数据库内核
- 高性能计算:金融高频交易、物理仿真、科学计算
- 嵌入式和智能硬件:对性能要求高的嵌入式设备
- 音视频编解码、图形渲染:这些计算密集型领域,都是C++的天下
C++从诞生到现在,一直在不断进化。从最早的C++98,到C++11、C++14、C++17、C++20,每个版本都加入了大量新特性,让语言变得更现代、更好用。
虽然现在有Rust这些新的系统编程语言挑战C++的地位,但C++积累了几十年的庞大生态和代码库,是不可能被轻易取代的。在未来很长一段时间里,C++依然会是系统编程和高性能领域的主流选择。
5.5 Objective-C:苹果生态的初代语言
讲完C++,就不得不提另一门C语言的面向对象扩展——Objective-C。
Objective-C诞生于1984年,由布莱德·考克斯(Brad Cox)和汤姆·洛夫(Tom Love)发明。它也是在C语言的基础上,加入了面向对象的特性。
但和C++不一样,Objective-C的面向对象思想,不是来自Simula,而是来自Smalltalk。所以它的消息传递机制、运行时特性,都非常有Smalltalk的风格。
Objective-C的语法非常有特点,比如调用方法是用中括号:
// 创建一个对象
Person *zhangsan = [[Person alloc] init];
// 调用方法
[zhangsan introduce];这种语法和C、C++差异很大,很多人第一次见都会觉得很奇怪。
Objective-C本身其实不算特别主流,但它遇到了自己的“伯乐”——苹果公司。
80年代末,乔布斯被苹果赶走之后,创办了NeXT公司。NeXT选择了Objective-C作为自己的系统开发语言,并且基于Objective-C开发了NextStep操作系统和Cocoa开发框架。
后来苹果收购了NeXT,乔布斯回归苹果。NextStep就变成了Mac OS X的基础,Objective-C也顺理成章地成了苹果官方的开发语言。
之后iPhone发布,iOS系统继承了macOS的技术栈。Objective-C又成了iOS开发的唯一官方语言。在移动互联网浪潮下,Objective-C借着苹果的东风,火了整整十几年。
Objective-C有自己的优势:动态运行时非常灵活,运行时可以修改类的结构、交换方法,非常适合做UI开发。但它的缺点也很明显:语法怪异、学习门槛高、没有垃圾回收(后来加入了ARC)、安全性一般。
2014年,苹果推出了Swift语言,作为Objective-C的替代品。Swift更现代、更安全、语法更简洁。现在苹果生态的开发,已经慢慢从Objective-C向Swift迁移了。
但Objective-C作为苹果生态的功臣,在编程语言发展史上,也留下了自己的印记。
第六部分 互联网黄金时代:百花齐放的90年代
6.1 互联网浪潮带来的新需求
进入90年代,世界发生了一件大事——互联网开始普及了。
互联网的兴起,给软件行业带来了全新的场景:网站、网页交互、后端服务、电子商务……这些新场景,对编程语言提出了新的要求。
传统的编程语言,比如C、C++,虽然性能好,但开发效率低,做网站开发太笨重了。企业需要更灵活、开发速度更快、更适合Web场景的语言。
于是,90年代成了编程语言的“黄金十年”。一大批我们现在耳熟能详的主流语言,都是在这十年里诞生的:Java、JavaScript、Python、PHP、Ruby……
每一门语言都瞄准了自己的目标场景,在互联网浪潮中茁壮成长。
6.2 Java:“一次编写,到处运行”的企业级王者
6.2.1 诞生背景:从Oak到Java
Java的诞生,充满了传奇色彩。
故事要从1990年说起。当时Sun公司有一个内部项目,叫Green项目。项目的目标是做智能家电的控制系统,比如智能电视、智能冰箱之类的。
项目负责人是詹姆斯·高斯林(James Gosling),后来被称为“Java之父”。最开始他们打算用C++来做,但C++太复杂了,而且和硬件绑定,跨平台不方便。家电的芯片种类很多,总不能每种芯片都重写一遍吧。
于是高斯林决定自己设计一门新的语言。这门语言要简单、安全、跨平台,能在不同的硬件上运行。他给这门语言起名叫Oak(橡树),因为他办公室窗外有一棵橡树。
结果项目做着做着,智能家电市场没做起来,Green项目面临被砍的命运。
正好这时候,互联网开始兴起了,浏览器和网页火了起来。Sun公司发现:Oak语言的跨平台特性,简直就是为互联网量身定做的啊!
当时网页都是静态的,只能显示文字和图片。如果能把Oak程序嵌入到网页里,让网页动起来,那该多酷?
于是Sun公司把Green项目转型,瞄准了互联网方向。他们把Oak语言改了个名字,叫Java。之所以叫Java,是因为团队成员都很喜欢喝爪哇岛产的咖啡。Java的logo,也是一杯冒着热气的咖啡。
1995年5月23日,Sun公司正式发布了Java语言。同时发布的,还有可以嵌入网页的Java Applet技术。
“一次编写,到处运行”(Write Once, Run Anywhere),这句经典的口号,瞬间点燃了整个行业。大家第一次见到:居然有语言能写一份代码,在Windows、Mac、Linux上都能跑。这在当时简直是不可思议的事情。
Java一炮而红,迅速成了最热门的编程语言。
6.2.2 Java的核心原理:JVM虚拟机
Java能实现跨平台,核心秘诀就是Java虚拟机(JVM)。
传统的编译型语言,比如C语言,是直接把源代码编译成特定平台的机器码。Windows编译的程序,只能在Windows跑,Linux就跑不了。
而Java不一样。Java编译器不直接编译成机器码,而是编译成一种中间代码,叫做字节码(Bytecode)。字节码是平台无关的,不管什么系统,生成的字节码都是一样的。
然后,每个平台都有自己的Java虚拟机(JVM)。JVM负责把字节码翻译成当前平台的机器码,然后执行。
打个通俗的比方:
- C语言就像直接把中文书翻译成英文书,英国人能看,但法国人看不了。要给法国人看,得重新翻译一遍
- Java就像把中文书翻译成世界语,世界语哪个国家都不认,但每个国家都有世界语翻译。翻译当场翻译成当地语言,当地人就能看懂了
字节码就是那个“世界语”,JVM就是那个“翻译”。只要你的系统装了JVM,就能运行Java程序。
这种设计,完美地实现了跨平台。当然,代价就是性能会比原生编译的C/C++差一点。但随着技术的发展,JVM的性能越来越强,尤其是加入了即时编译(JIT)技术之后,热点代码会被直接编译成本地机器码,性能已经非常接近C/C++了。
除了跨平台,JVM还有一个巨大的优势:自动垃圾回收(GC)。
在C/C++里,内存需要程序员手动申请和释放。申请了不释放,就会内存泄漏;释放早了,就会出现野指针。写大型程序的时候,内存问题非常头疼。
而Java有自动垃圾回收机制。JVM会自动帮你管理内存,不用的内存会自动回收掉。程序员不用再操心内存申请释放的问题,大大降低了开发难度,也减少了内存相关的bug。
6.2.3 Java的核心特性
Java是一门非常纯粹的面向对象语言。它的设计理念是“一切皆对象”(当然基本类型除外)。
Java有几个非常鲜明的特点:
- 纯面向对象:所有的代码都必须写在类里面,没有游离的函数。强制使用面向对象的方式组织代码
- 强类型静态语言:变量必须声明类型,编译的时候就会做类型检查。很多错误在编译阶段就能发现,适合大型项目
- 没有指针:去掉了C/C++里最容易出错的指针。虽然底层还是有指针,但程序员不用直接操作,大大提高了安全性
- 自动垃圾回收:刚才讲过了,自动管理内存
- 异常处理机制:try-catch-finally,规范的错误处理方式
- 丰富的标准库:Java的标准库非常庞大,网络、IO、集合、并发……常用的功能都有,不用自己造轮子
我们用一段简单的代码感受一下:
// 所有代码都在类里面
public class HelloWorld {
// 主方法,程序入口
public static void main(String[] args) {
// 定义变量,必须声明类型
String name = "张三";
int age = 18;
// 输出
System.out.println("你好,我叫" + name + ",今年" + age + "岁。");
}
}Java的语法非常规范,也非常啰嗦。同样的功能,Python可能一行就写完了,Java要写好几行。但这种啰嗦也有好处:代码结构清晰,可读性强,不同的人写出来的代码风格差异小,非常适合团队协作。
6.2.4 企业级王者的地位
Java诞生之后,发展速度惊人。尤其是在企业级开发领域,Java迅速击败了C++、COBOL等对手,成了绝对的王者。
为什么企业这么喜欢Java?原因有几个:
- 跨平台:企业的服务器可能有各种系统,Java一次编写到处运行,太方便了
- 稳定可靠:强类型、垃圾回收、异常处理,大大降低了bug率。企业系统最怕出问题,稳定性是第一位的
- 适合大型项目:纯面向对象、规范的语法,非常适合多人协作开发大型系统
- 生态极其完善:有Spring、MyBatis、Spring Boot等大量成熟的企业级框架,还有各种中间件、开源库。开发企业应用,基本上不用自己造轮子
- 人才多:学Java的程序员多,企业招人容易
到了2000年左右,Java已经成了企业后端开发的事实标准。银行、证券、电商、政务、电信……几乎所有的大型企业,后端都是用Java做的。
后来大数据兴起,Hadoop、Spark这些大数据框架,也都是用Java/Scala写的。Java又成了大数据领域的主流语言。
再加上安卓移动端开发,Java的应用场景非常广泛。
当然,Java也有很多被吐槽的地方:
- 语法太啰嗦,代码量大会显得很臃肿
- 启动慢,内存占用高,不适合轻量级的小服务
- 版本更新慢,历史包袱重,新特性加得慢
但这些缺点,都不影响Java的霸主地位。直到今天,Java依然是全世界最主流的编程语言之一,在企业级领域的地位依然无可撼动。
6.3 JavaScript:浏览器里的意外传奇
6.3.1 诞生背景:10天写出来的语言
如果说Java是精心设计的企业级王者,那JavaScript就是一个“意外”诞生的草根传奇。
故事要从1995年说起。那时候网景公司(Netscape)的网景浏览器,正是市场上的霸主。当时的网页都是静态的,只能显示文字和图片,没有任何交互能力。
网景公司想给网页加上动态功能。比如用户点个按钮能弹出个框,填个表单能做个简单校验。他们需要一门脚本语言,嵌入在网页里,在浏览器端运行。
当时网景公司和Sun公司正在合作,推广Java Applet。网景本来打算让Java来做这件事。但Java太笨重了,语法也复杂,不适合给网页设计师和非专业程序员用。
网景的工程师布兰登·艾克(Brendan Eich)接到了一个任务:设计一门新的脚本语言,用来做网页的简单交互。这门语言要简单、易学、语法和Java有点像,能嵌入到HTML里。
而且,这个任务非常急。艾克只用了10天,就把这门语言设计出来了。
最开始这门语言叫LiveScript。后来网景为了蹭Java的热度,和Sun公司商量了一下,把名字改成了JavaScript。
对,就是这么草率。JavaScript和Java,名字虽然像,但本质上是完全不同的两门语言,设计理念、语法特性、应用场景,几乎没有任何关系。只是当年为了营销,起了个相似的名字而已。
谁也没想到,这个只用10天设计出来的、本来只打算做简单网页特效的脚本语言,后来会发展成世界上最流行、使用人数最多的编程语言之一。
6.3.2 JavaScript的核心特点
因为设计时间太短,JavaScript不可避免地有很多设计缺陷和奇怪的特性。也因此,它成了被吐槽最多的编程语言之一。
但瑕不掩瑜,JavaScript也有很多非常优秀的特性,正是这些特性,让它笑到了最后。
我们先看一段最简单的JavaScript代码:
// 弹出一个提示框
alert("Hello World!");
// 定义变量,不用声明类型
let name = "张三";
let age = 18;
// 在控制台输出
console.log(`你好,我叫${name},今年${age}岁。`);JavaScript的核心特点:
- 解释型脚本语言:不用编译,写好之后直接在浏览器里就能运行。改完代码刷新一下页面就能看到效果,开发效率非常高
- 动态弱类型语言:变量不用声明类型,同一个变量可以存不同类型的值。类型转换非常灵活,但也很容易出奇怪的bug
- 事件驱动:天生适合处理交互。比如用户点击按钮、鼠标移动、页面加载完成,都可以触发对应的代码执行
- 原型链继承:和Java的类继承不一样,JavaScript是基于原型的继承。每个对象都有一个原型对象,继承原型的属性和方法
- 单线程异步非阻塞:JavaScript是单线程的,但通过事件循环机制,可以实现异步非阻塞IO。非常适合处理大量IO密集型的场景,比如网络请求
- 运行在浏览器里:这是JavaScript最大的优势。只要有浏览器就能跑,不用安装任何环境。所有的浏览器都内置了JavaScript引擎
6.3.3 从玩具语言到全栈王者
JavaScript诞生之后的很长一段时间里,都被认为是一门“玩具语言”。大家觉得它也就做做网页特效、表单校验这些简单的事情,做不了正经的大型开发。
专业的程序员都看不起JavaScript,觉得它太简单、太不严谨、bug太多。
转折点发生在2004年。那一年,谷歌推出了Gmail邮箱。Gmail用了大量的JavaScript技术,实现了非常流畅的网页交互体验。大家第一次意识到:原来JavaScript还能做这么复杂的事情!
之后,AJAX技术开始普及。网页不再是静态的了,可以局部刷新、动态加载数据。Web应用变得越来越复杂,功能越来越强大。前端开发慢慢成了一个独立的岗位。
再后来,2009年,一个叫瑞安·达尔(Ryan Dahl)的工程师,做了一件惊天动地的事情。他把Chrome浏览器的V8 JavaScript引擎拿出来,包装了一下,做成了一个服务端运行环境。这就是Node.js。
Node.js的诞生,意味着JavaScript不再只能跑在浏览器里了,它还能跑在服务器上,写后端代码。
一门语言,既能写前端,又能写后端。全栈开发从此变成了现实。这对开发者来说,吸引力太大了。
Node.js之后,JavaScript的生态迎来了爆炸式发展:
- 前端框架:React、Vue、Angular
- 后端框架:Express、Koa、Nest.js
- 移动端开发:React Native
- 桌面端开发:Electron
- 小程序开发:微信小程序、支付宝小程序
- 构建工具:Webpack、Vite
现在的JavaScript,已经不再是当年那个做特效的玩具语言了。它成了一个生态极其庞大、应用场景极其广泛的全平台语言。
从前端网页、后端服务、移动端APP、桌面软件,到小程序、游戏、物联网,几乎没有JavaScript做不了的事情。
有一句很有名的话:“凡是能用JavaScript写的应用,最终都会用JavaScript写出来。” 这句话正在一步步变成现实。
当然,JavaScript也有很多问题:
- 动态弱类型,大型项目里很容易出类型错误
- 历史包袱重,很多不合理的设计没法改
- 生态更新太快,框架轮子层出不穷,学习成本高
为了解决类型问题,微软在2012年推出了TypeScript。TypeScript是JavaScript的超集,给JavaScript加上了静态类型系统。现在大型前端项目,基本都在用TypeScript开发了。
不管怎么说,JavaScript的传奇还在继续。作为Web平台的唯一原生语言,只要互联网还在,JavaScript就不会衰落。
6.4 Python:“人生苦短,我用Python”
6.4.1 诞生背景:ABC语言的遗产
Python的发明者,是荷兰程序员吉多·范罗苏姆(Guido van Rossum)。大家都亲切地叫他“龟叔”。
时间回到1989年的圣诞节。龟叔当时在荷兰的CWI研究所工作。他之前参与过一门叫ABC的教学语言的开发。ABC语言设计得非常优雅,可读性很强,但可惜没有流行起来。
圣诞节假期,龟叔闲着没事,就想写一门新的脚本语言。这门语言要继承ABC的优点:简洁、优雅、可读性强。同时也要弥补ABC的缺点:开放、可扩展、能和系统很好地集成。
龟叔给这门语言起名叫Python。名字的来源不是蟒蛇,而是英国的喜剧团体“蒙提·派森的飞行马戏团”(Monty Python’s Flying Circus)。龟叔是这个喜剧团体的粉丝,觉得这个名字很好记,又有点神秘感。
1991年,Python第一个版本正式发布。
6.4.2 Python的设计哲学:优美胜于丑陋
Python最核心的设计理念,就是“简洁”、“优雅”、“可读性强”。
龟叔说过:“代码是写给人看的,顺便给机器运行一下。”
Python的语法设计,极力贴近自然语言。很多Python代码,你哪怕没学过编程,也能大概看懂是什么意思。
比如经典的“斐波那契数列”,用Python写出来是这样的:
def fib(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a是不是非常简洁清晰?
Python有几个非常鲜明的特点:
- 强制缩进:Python不用大括号来划分代码块,而是用缩进。缩进不对,代码就运行不起来。很多人一开始觉得很别扭,但习惯了之后会发现:强制缩进让代码的可读性大大提高了,所有人写出来的代码风格都很统一
- 动态强类型语言:变量不用声明类型,非常灵活。但类型检查很严格,不同类型不能随便运算
- 解释型语言:不用编译,写完直接运行。交互式的解释器,写一行执行一行,非常适合学习和调试
- “电池 included”:Python的标准库非常丰富,内置了大量的模块。文件处理、网络、正则、多线程……常用的功能都有,不用额外安装,拿来就能用
- 极强的扩展性:可以很方便地调用C/C++写的扩展库。性能瓶颈可以用C来写,Python来调用。兼顾了开发效率和运行效率
- 多范式:支持面向过程、面向对象、函数式编程多种范式,你想用什么风格写都可以
Python社区有一首著名的“Python之禅”,很好地概括了Python的设计哲学:
优美胜于丑陋 明了胜于晦涩 简洁胜于复杂 复杂胜于凌乱 扁平胜于嵌套 间隔胜于紧凑 可读性很重要 即便假借特例的实用性之名,也不可违背这些规则 不要包容所有错误,除非你确定需要这样做 当存在多种可能,不要尝试去猜测 而是尽量找一种,最好是唯一一种明显的解决方案 虽然这并不容易,因为你不是Python之父 做也许好过不做,但不假思索就动手还不如不做 如果你无法向人描述你的方案,那肯定不是一个好方案 命名空间是一种绝妙的理念,我们应当多加利用
6.4.3 从脚本语言到全场景霸主
Python诞生之后,最开始只是作为一门脚本语言,用来写一些自动化脚本、系统管理工具之类的。
它的发展一直不温不火,虽然有一批忠实的粉丝,但始终没有进入主流。
真正让Python起飞的,是几个领域的爆发:
1. Web开发 2005年,Django框架发布。Django是一个全能型的Web后端框架,开发效率极高。用Python做Web开发,速度比Java快好几倍。很多创业公司、互联网公司,都选择了Python+Django的技术栈。
之后又出现了Flask、FastAPI等轻量级框架,Python的Web生态越来越完善。
2. 科学计算与数据分析 NumPy、Pandas、Matplotlib这些科学计算库的出现,让Python成了数据分析的利器。
以前科学家们用Matlab、R语言。但Python不仅免费开源,而且是通用语言,能做数据分析,也能做工程开发。越来越多的人转向了Python。
3. 人工智能与机器学习 这是让Python登顶王座的最重要的领域。
2015年之后,深度学习开始爆发。TensorFlow、PyTorch这些主流的深度学习框架,都把Python作为首选的接口语言。
为什么选Python?因为Python简单灵活,胶水语言特性强。底层的高性能计算用C/C++写,上层用Python封装,科研人员用Python调用,非常方便。
一夜之间,所有做AI、做算法的人,都开始学Python。Python成了人工智能时代的标准语言。
4. 自动化运维与测试 Python简单易学,运维工程师、测试工程师不用专业的编程背景,也能快速上手写自动化脚本。现在DevOps、自动化测试,基本都是Python的天下。
5. 爬虫 Python强大的网络处理能力和丰富的库,让它成了爬虫开发的首选语言。
现在的Python,已经是名副其实的“万能语言”了。从后端开发、数据分析、人工智能,到自动化运维、测试、爬虫、教育,几乎无所不能。
“人生苦短,我用Python。” 这句经典的口号,道出了无数开发者的心声。Python最大的优势,就是开发效率高。同样的功能,用Python写,代码量可能只有Java的一半,开发时间可能只有几分之一。
当然,Python也有缺点:
- 运行速度慢。作为解释型语言,Python的速度比C/C++、Java慢很多。但在大多数场景下,开发效率比运行效率更重要。而且性能瓶颈可以用C扩展来解决
- 不适合做大型客户端软件、高性能游戏
- 动态类型在超大型项目里,维护成本会比较高
但这些缺点,都不影响Python成为现在世界上最流行的编程语言。在TIOBE编程语言排行榜上,Python已经连续多年稳居第一了。
6.5 PHP:Web后端的曾经王者
讲完Python,就不得不提另一门曾经在Web领域风光无限的语言——PHP。
PHP的诞生也非常有传奇色彩。1994年,一个叫拉斯姆斯·勒多夫(Rasmus Lerdorf)的程序员,想给自己的个人主页做一个访问统计的小程序。
他用C语言写了几个CGI脚本,用来统计自己主页的访问量。他给这个工具起名叫“Personal Home Page Tools”,简称PHP Tools。
后来他把这个工具发布到了网上,没想到还挺多人用。大家纷纷给他提需求,加功能。慢慢的,这个小工具就发展成了一门脚本语言。
1995年,PHP/FI正式发布。这就是PHP的雏形。
PHP最大的特点,就是能直接嵌入到HTML里写。比如:
<html>
<body>
<?php
echo "Hello World!";
?>
</body>
</html>这种方式,做网页开发简直太方便了。写一点HTML,插一段PHP,页面就动态起来了。学习成本极低,哪怕不是专业程序员,稍微学一学也能做出动态网站。
再加上PHP是免费开源的,和Linux、Apache、MySQL搭配,组成了著名的LAMP架构。这套架构完全免费,部署简单,开发速度快,简直是中小网站的福音。
2000年左右,互联网第一次浪潮爆发。无数的个人网站、论坛、电商网站,都是用PHP做的。著名的WordPress博客系统、Discuz论坛、phpWind,都是PHP的代表作。
“PHP是世界上最好的语言”,这句著名的梗,就是从那个时候流传开来的。虽然现在经常被用来调侃,但在当年,PHP确实是很多人的Web开发启蒙。
PHP的巅峰时期,全世界超过80%的网站后端都是用PHP写的。
但随着互联网的发展,网站越来越大,越来越复杂。PHP的缺点也慢慢暴露出来了:
- 语言设计比较混乱,函数命名不统一,有很多历史遗留问题
- 生态相对比较草根,大型企业级框架不如Java完善
- 性能一般,高并发场景下表现不好
- 语言特性相对落后,更新迭代慢
后来,Java企业级方案越来越成熟,Python、Go、Node.js等新的后端语言也崛起了。PHP的市场份额慢慢被蚕食。
现在PHP已经不再是Web后端的主流选择了,大部分互联网公司的新业务,都不会再用PHP了。
但PHP依然有它的生存空间。比如传统的建站、中小企业的官网、WordPress生态,还有大量的存量PHP项目。它依然是世界上最流行的编程语言之一。
6.6 Perl:文本处理的瑞士军刀
最后再简单提一下Perl。
Perl诞生于1987年,发明者是拉里·沃尔(Larry Wall)。Perl的定位是系统管理和文本处理。
Perl最强大的地方,就是正则表达式和文本处理能力。它处理字符串、解析文本文件,简直是得心应手。在当年,系统管理员几乎人手一本Perl教程,写脚本处理日志、管理系统,效率极高。
90年代Web刚兴起的时候,Perl也火过一段时间。早期的CGI脚本,很多都是用Perl写的。
但Perl有个问题:语法太灵活了,甚至有点混乱。同样的功能,有好多种写法。Perl社区有一句名言:“做一件事,不止有一种方法。”(There’s more than one way to do it.)
灵活是灵活,但也导致Perl的代码可读性很差。别人写的Perl代码,你可能根本看不懂。甚至你自己写的代码,过几个月再看,也可能看不懂。所以Perl也被称为“写一次,读不懂”的语言。
后来Python崛起了。Python同样能做文本处理、系统管理,而且语法更清晰、可读性更好、生态更完善。慢慢的,大家就都从Perl转向Python了。
现在Perl已经比较小众了,只有一些老的系统和老程序员还在用。但它在编程语言发展史上,也留下了自己的印记。后来的PHP、Python、Ruby,都或多或少受到了Perl的影响。
第七部分 多元化时代:21世纪的新语言与新范式
7.1 新世纪的新需求
进入21世纪,软件行业又在发生新的变化。
多核CPU开始普及,并发编程成了刚需。互联网规模越来越大,分布式系统、微服务成了主流。云原生、容器化技术兴起。软件的安全性、可靠性越来越受重视。
传统的编程语言,在这些新场景下,慢慢显得有些力不从心了。
于是,一批新的编程语言应运而生。它们针对新时代的痛点,做了专门的优化和设计。Go、Rust、Swift、Kotlin、Scala……这些新语言,各有各的定位,各有各的杀手锏。
同时,函数式编程这种古老的范式,也开始重新流行起来。大家发现,函数式编程的不可变数据、纯函数这些特性,在并发和分布式场景下,有天然的优势。
编程语言的世界,变得越来越多元化了。
7.2 Ruby:优雅的开发者之友
7.2.1 诞生背景:松本行弘的“快乐编程”
Ruby是日本人松本行弘(Matz)在1995年发明的。没错,和Java、JavaScript同一年。
松本行弘是Smalltalk和Lisp的粉丝。他想设计一门语言,既像Smalltalk一样纯面向对象,又像Perl一样灵活强大,还要足够优雅,让程序员写代码的时候感到快乐。
他的设计理念是:“编程语言首先是要让人舒服,其次才是让机器舒服。”
Ruby是一门非常有“人情味”的语言。它的语法非常灵活,非常优雅,充满了“语法糖”。写Ruby代码,就像写英语句子一样自然。
比如一段简单的Ruby代码:
# 从1加到10
sum = 0
1.upto(10) { |i| sum += i }
puts "总和是#{sum}"是不是非常简洁优雅?
Ruby是纯面向对象的,一切皆对象。数字、字符串、函数、类本身,都是对象。
Ruby还有非常强大的元编程能力。程序可以在运行时修改自己的结构和行为。这给框架开发带来了极大的便利。
7.2.2 Ruby on Rails的爆发
Ruby诞生之后的很长时间里,都只是一个小众语言,主要在日本和欧美小范围流行。
真正让Ruby火遍全球的,是2004年发布的Ruby on Rails框架,简称Rails。
Rails是一个Web开发框架,由大卫·海涅迈尔·汉森(DHH)开发。Rails把Ruby的优雅和元编程能力发挥到了极致。
用Rails开发Web应用,速度快得惊人。传统Java可能要几周才能做完的项目,用Rails可能几天就做完了。
Rails提出了很多革命性的理念,比如“约定优于配置”(Convention over Configuration)、“不要重复自己”(Don’t Repeat Yourself)。这些理念,后来深深地影响了整个Web开发行业。
2005年左右,Rails火得一塌糊涂。无数的创业公司、互联网初创项目,都选择了Ruby on Rails。Twitter最开始就是用Rails写的。
Ruby也借着Rails的东风,一跃成为主流编程语言之一。
7.2.3 现状与未来
Ruby的巅峰期大概在2010年左右。之后,随着项目规模越来越大,Ruby的缺点也慢慢显现出来了。
最主要的问题就是性能。Ruby的运行速度确实比较慢。高并发、大流量的场景下,Ruby的表现不如Java、Go这些语言。Twitter后来也从Rails迁移到了Java。
另外,Ruby的生态主要集中在Web开发领域,其他领域的生态相对薄弱。
现在Ruby已经不再是最热门的语言了,但依然有一批忠实的用户。很多创业公司、小团队,依然喜欢用Rails快速开发产品。
Ruby对编程语言的影响是深远的。后来的很多语言和框架,都从Ruby和Rails身上借鉴了很多优秀的设计理念。
7.3 C#:微软的Java对标之作
7.3.1 诞生背景:微软与Sun的官司
90年代Java火了之后,微软也坐不住了。
微软最开始是想和Sun合作,把Java集成到Windows里。但合作着合作着,双方就闹掰了。微软在自己的Java实现里加了很多Windows特有的扩展,破坏了Java的跨平台特性。Sun公司一气之下,把微软告上了法庭。
官司打输了之后,微软决定自己搞一门语言,对标Java。
微软找来了传奇程序员安德斯·海尔斯伯格(Anders Hejlsberg)。这位大神是Delphi的发明者,编译器领域的顶级专家。
2000年,微软正式发布了C#(读作C Sharp)。同时发布的还有.NET平台。
C#从诞生之日起,就被认为是“微软版的Java”。确实,它们在语法、面向对象、虚拟机等方面,有很多相似之处。
但C#并不是简单的抄袭。安德斯大神在设计C#的时候,借鉴了Java的优点,也规避了Java的很多缺点。C#在语言特性上,其实比Java更先进、更优雅。
7.3.2 C#的核心特点
C#是一门设计得非常优秀的语言。它语法简洁优雅,特性丰富,更新迭代快,一直在积极吸收新的编程理念。
C#的核心特点:
- 纯面向对象:和Java类似,一切皆对象(值类型除外)
- 强类型静态语言:编译时类型检查,安全可靠
- 运行在.NET虚拟机上:和JVM类似,有垃圾回收、即时编译
- 语法糖丰富:属性、委托、事件、LINQ、异步/await、模式匹配……好用的特性非常多,写起来非常舒服
- 和Windows深度集成:做Windows桌面开发、Windows服务,是最佳选择
比如C#的LINQ,写数据查询非常优雅:
// 从数组里筛选出大于5的偶数,乘以10,最后转换成列表
var result = numbers
.Where(x => x > 5)
.Where(x => x % 2 == 0)
.Select(x => x * 10)
.ToList();C#还有async/await异步编程模型,写异步代码非常方便,这个特性后来也被JavaScript、Python等很多语言借鉴了。
7.3.3 发展与应用
C#最开始是完全绑定Windows平台的。所以它的主要应用场景,也集中在Windows生态里。
主要应用场景:
- Windows桌面软件开发:WinForm、WPF,做Windows客户端程序,C#是最佳选择
- 游戏开发:Unity游戏引擎用的就是C#。现在绝大多数手游、独立游戏,都是用Unity做的。借着游戏行业的东风,C#也一直保持着很高的热度
- Web后端开发:http://ASP.NET,做企业级Web应用
- 物联网、嵌入式:.NET Micro Framework
很长一段时间里,C#因为绑定Windows,在跨平台方面不如Java。在Linux服务器为主的互联网行业,C#用得不多。
转折点发生在2016年。微软发布了跨平台的.NET Core,后来改名为.NET。现在的.NET,已经完全支持跨平台了,Windows、Linux、Mac都能跑。
微软也变得越来越开放,拥抱开源。C#和.NET的发展前景,也变得越来越好了。
7.4 领域特定语言DSL:专注才能专业
讲完通用编程语言,我们再来讲讲领域特定语言,也就是DSL(Domain Specific Language)。
什么是DSL?和通用编程语言不一样,DSL不是用来解决所有问题的,它只专注于某一个特定的领域。
DSL只做一件事,但把这件事做到极致。
我们每天都在接触DSL,只是你可能没意识到。比如:
- SQL:结构化查询语言,专门用来操作数据库。它就是最典型、最成功的DSL
- HTML/CSS:HTML专门用来描述网页结构,CSS专门用来描述网页样式。它们也是DSL
- 正则表达式:专门用来匹配字符串的DSL
- Makefile/CMake:专门用来构建编译的DSL
- Shell脚本:某种程度上也算是系统操作的DSL
我们以SQL为例,来看看DSL的特点。
SQL是专门用来查询和操作数据库的。你不用告诉数据库怎么查,只要告诉它你想要什么数据就行。数据库自己会决定用什么索引、什么执行计划。
比如你想查“年龄大于18岁的学生的姓名和成绩”,只要写:
SELECT name, score FROM students WHERE age > 18;就这么简单一句话,数据库就帮你把结果查出来了。你不用管底层数据怎么存的,不用写循环遍历,不用自己做判断过滤。
这就是DSL的优势:
- 表达力强:在自己的领域里,用极少的代码就能完成复杂的功能
- 简单易学:只针对特定领域,概念少,容易上手
- 可读性好:接近自然语言,业务人员也能看懂
- 性能优化好:专门针对领域做了优化,执行效率高
当然,DSL也有局限性。它只能用在特定领域,出了这个领域就没用了。你总不能用SQL写个游戏吧?
通用语言和DSL是互补的关系。通用语言搭骨架,DSL处理特定领域的问题。
在实际开发中,我们经常会自己定义一些简单的DSL,来简化特定场景的开发。比如配置文件格式、模板引擎、规则引擎,本质上都是DSL。
好的DSL,能大大提升特定领域的开发效率。
7.5 函数式编程的复兴
函数式编程并不是什么新东西。早在1958年LISP诞生的时候,函数式编程就已经存在了。但很长时间里,它都只是学术界的宠儿,工业界用得不多。
最近十几年,函数式编程开始慢慢复兴了。越来越多的主流语言,开始加入函数式特性。Java 8加入了Lambda表达式,Python一直都有函数式特性,JavaScript本身就有很强的函数式基因,Scala、Haskell这些纯函数式语言,也开始有了更多的工业应用。
为什么函数式编程突然又火了?
主要原因是,我们现在遇到的问题变了。现在的系统,越来越复杂,并发越来越多,分布式越来越普遍。传统的命令式编程,面对这些问题,越来越吃力。
而函数式编程的很多特性,天生就适合解决这些问题。
我们来通俗地讲一讲函数式编程的核心理念。
7.5.1 纯函数:相同的输入,永远得到相同的输出
函数式编程最核心的概念,就是纯函数。
什么是纯函数?满足两个条件:
- 相同的输入,永远得到相同的输出。函数的结果只和参数有关,和外部状态没关系
- 没有副作用。函数执行不会改变外部的任何东西,不会修改全局变量,不会修改参数,不会打印输出,不会发网络请求
打个比方:
- 纯函数就像数学里的函数。f(x) = x + 1,你输入3,永远得到4。不管什么时候算、算多少次,结果都一样
- 非纯函数就像自动售货机。你投币买饮料,有时候机器坏了,你投了钱不出货。同样是投钱这个输入,结果可能不一样。而且机器里的存货减少了,这就是副作用
纯函数有什么好处?
- 容易测试:不用搭复杂的测试环境,传参数看结果就行
- 容易调试:出了问题肯定是函数本身的问题,不用找外部原因
- 可以缓存:同样的输入,结果永远一样。算过一次就可以缓存起来,下次直接用
- 并发安全:纯函数不修改共享状态,多线程跑也不会有竞争问题
7.5.2 不可变数据:数据一旦创建,就永远不能修改
函数式编程里,所有的数据都是不可变的。你想修改一个对象?不行,你只能创建一个新的对象。原来的对象,保持不变。
比如你有一个列表[1, 2, 3],你想加个4变成[1, 2, 3, 4]。
- 命令式的做法:直接在原来的列表后面加个4,原来的列表就变了
- 函数式的做法:创建一个新的列表[1, 2, 3, 4],原来的列表还是[1, 2, 3],保持不变
不可变数据看起来有点浪费内存,但好处是巨大的:
- 没有副作用:数据不会被意外修改
- 并发安全:多线程读同一个数据,完全不用加锁,因为没人能修改它
- 容易追溯:任何状态变化都有记录,出了问题可以回溯
7.5.3 函数是一等公民:函数和普通数据没区别
函数式编程里,函数是“一等公民”。意思是,函数和其他数据类型是平等的。
你可以把函数赋值给变量,可以把函数作为参数传给另一个函数,可以把函数作为返回值。
能接收函数作为参数,或者返回函数的函数,叫做高阶函数。
我们熟悉的map、filter、reduce,都是典型的高阶函数。
比如用Python的map,把列表里每个数都乘以2:
numbers = [1, 2, 3, 4, 5]
result = list(map(lambda x: x * 2, numbers))
# 结果是 [2, 4, 6, 8, 10]高阶函数让代码的抽象能力更强了。我们可以把通用的逻辑抽出来,把变化的部分用函数传进去。
7.5.4 声明式编程:说“做什么”,而不是“怎么做”
命令式编程,我们是一步步告诉计算机怎么做。比如遍历列表,写个for循环,一个个判断,一个个处理。
而函数式编程是声明式的。我们告诉计算机我们想要什么结果,具体怎么做,语言自己去处理。
比如筛选列表里的偶数:
- 命令式写法:写循环,一个个判断,符合条件的加到新列表里
- 声明式写法:调用filter函数,传一个判断条件进去
声明式的代码,更简洁,可读性更高,更接近人的思维。我们不用关心底层的循环怎么实现,只要关注业务逻辑本身。
7.5.5 函数式不是银弹
讲了这么多函数式的好处,并不是说函数式编程就比面向对象好,就要完全取代命令式编程。
函数式编程也有它的缺点:
- 思维方式和传统编程差异大,学习门槛高
- 纯函数和不可变数据,有时候会牺牲性能
- 有些场景用函数式写会很别扭,比如有大量IO交互的场景
现在的趋势是,多范式融合。主流的语言,基本都是多范式的。你既可以写面向对象,也可以写函数式。什么时候用什么范式,看具体的场景。
该封装对象的时候用面向对象,该处理数据集合的时候用函数式。取长补短,结合起来用,才是最佳实践。
第八部分 新时代的王者:Go与Rust
8.1 Go语言:云原生时代的C语言
8.1.1 诞生背景:谷歌的痛点
2007年,谷歌公司的三个大神级程序员:肯·汤普森(Ken Thompson,C语言和UNIX的发明者之一)、罗布·派克(Rob Pike,贝尔实验室的老伙计,Plan 9操作系统的开发者)、罗伯特·格瑞史莫(Robert Griesemer),在一起吐槽C++。
他们觉得C++太复杂了,编译太慢了,写起来太费劲了。而且并发编程太麻烦,谷歌有大量的分布式系统和服务器程序,用C++开发效率太低了。
当时也有其他选择,比如Java,但Java太臃肿,虚拟机启动慢,内存占用高。Python开发效率高,但性能太差,不适合做底层的基础设施。
三个大神一合计:既然都不满意,那我们自己造一门语言吧。
他们给这门语言定下了几个核心目标:
- 简单:语法要简单,学习成本低,就像C语言一样简洁。新人上手快,老人写起来也舒服
- 快:编译速度要快,运行速度也要快。最好接近C语言的性能
- 原生支持并发:专门针对多核和分布式场景设计,写并发程序要简单
- 工程友好:适合大型团队协作开发,代码规范,依赖管理清晰
2009年,Go语言正式对外发布。因为是谷歌出品,再加上三位大神的光环,Go一出生就备受关注。
因为Go语言的Logo是一只地鼠,所以大家也亲切地叫它“golang”,或者直接叫“狗浪”。
8.1.2 Go的设计哲学:少即是多
Go语言最与众不同的地方,就是它的“减法哲学”。
别的语言都在拼命加特性,泛型、继承、异常、宏……功能越多越好。而Go反其道而行之,它拼命做减法。
Go故意去掉了很多大家习以为常的特性:
- 没有类继承,只有结构体组合
- 没有构造函数和析构函数
- 没有运算符重载
- 没有默认参数
- 没有异常,只有错误返回值
- 没有泛型(早期版本,后来1.18才加上)
- 没有宏
很多人第一次接触Go,都会觉得:这语言怎么这么“简陋”?连这些特性都没有?
但这正是Go的设计智慧。Go的设计者认为,特性越多,语言就越复杂,团队里每个人的写法都不一样,代码反而更难维护。
少即是多。少一些特性,反而能让代码更统一,可读性更好,维护成本更低。
Go语言的设计者罗布·派克说过:“简单不是理所当然的,简单是努力的结果。” 设计一门简单的语言,比设计一门复杂的语言,难得多。
8.1.3 Go的杀手锏:goroutine与channel
Go最大的亮点,就是原生的并发支持。
传统的并发编程,用的是多线程。线程很重,创建和销毁开销大,切换开销也大。而且线程之间通信很麻烦,要靠共享内存,还要加锁,很容易出bug。
Go提出了一个全新的并发模型:goroutine(协程) + channel(通道)。
goroutine是用户态的轻量级线程。它非常轻,一个goroutine只占几KB的栈空间。一个程序里,可以轻松跑几十上百万个goroutine。这要是换成线程,早就把系统资源耗尽了。
goroutine的调度由Go运行时自己管理,不用操作系统操心。切换开销非常小。
而goroutine之间的通信,靠channel。你可以把channel想象成一个管道,一个goroutine往里面发数据,另一个goroutine从里面收数据。
Go的并发哲学是:“不要通过共享内存来通信,而要通过通信来共享内存。”
用channel通信,就避免了共享内存的竞争问题,写并发程序简单多了,也安全多了。
我们看一个最简单的Go并发例子:
package main
import "fmt"
func worker(name string, ch chan int) {
for num := range ch {
fmt.Printf("%s 处理了数字 %d\n", name, num)
}
}
func main() {
ch := make(chan int, 10)
// 启动3个goroutine并发处理
go worker("工人1", ch)
go worker("工人2", ch)
go worker("工人3", ch)
// 往通道里发10个数字
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
// 等待一会儿,让goroutine处理完
fmt.Scanln()
}就这么简单几行代码,就实现了一个多worker的并发处理模型。这要是换成C++或者Java,要写一大堆代码。
8.1.4 Go的其他优点
除了并发,Go还有很多优点:
1. 编译速度极快 Go的编译速度快得惊人。一个大型项目,C++可能要编译几十分钟,Go可能几十秒就编译完了。 这大大提升了开发效率。改完代码,马上就能编译运行,不用等。
2. 静态类型,编译成单一可执行文件 Go是静态编译的。编译完成之后,就是一个单独的可执行文件,没有任何依赖。 部署的时候,把文件拷过去就能跑,不用装运行环境,不用装各种库。这对于运维来说,简直太友好了。
3. 垃圾回收,不用手动管理内存 Go有自动垃圾回收,不用像C/C++那样手动申请释放内存。大大减少了内存泄漏、野指针之类的bug。 而且Go的垃圾回收器做得非常优秀,停顿时间极短,几乎感知不到。
4. 标准库强大,工具链完善 Go的标准库非常丰富,网络、加密、编码、并发……常用的功能都有。 自带的工具链也非常好用:格式化、测试、性能分析、依赖管理……全都内置了,不用自己找第三方工具。
5. 代码风格统一 Go自带gofmt格式化工具,所有人写出来的代码格式都是一样的。再也不用争论括号换不换行、缩进用空格还是Tab了。 团队协作的时候,代码风格统一,可读性大大提高。
8.1.5 应用场景与生态
Go语言诞生之后,发展速度非常快。尤其是在云原生领域,Go简直成了标配语言。
主要应用场景:
1. 云原生基础设施 Docker、Kubernetes(K8s),这两个云原生时代的核心软件,都是用Go写的。 还有etcd、Prometheus、Istio……几乎所有的云原生工具,都是Go语言开发的。 想做云原生、做基础设施,Go是必学的语言。
2. 微服务后端开发 Go性能好,开发效率高,天生适合做高并发的后端服务。 现在很多互联网公司,尤其是创业公司,新的后端服务都开始用Go写了。Go的微服务生态也越来越完善,gRPC、gin、kratos等等。 在很多场景下,Go正在慢慢取代Java的位置。
3. DevOps工具与自动化脚本 Go编译出来的是单一可执行文件,部署方便。非常适合写各种运维工具、命令行工具。 很多CLI工具,比如Hugo静态博客生成器,都是用Go写的。
4. 区块链 以太坊、超级账本等很多区块链项目,都是用Go开发的。
Go现在已经稳稳地进入了主流编程语言的第一梯队。TIOBE排行榜上,Go常年稳居前十。
当然,Go也不是完美的。比如错误处理比较啰嗦,泛型支持还比较初级,做桌面应用、前端开发不太合适。
但瑕不掩瑜。在它擅长的领域——服务端开发、云原生、基础设施,Go几乎是最佳选择。
8.2 Rust:系统编程的安全革命
8.2.1 诞生背景:Mozilla的探索
如果说Go是给应用层开发者准备的,那Rust就是给系统层开发者准备的。
Rust的发明者是格雷顿·霍雷尔(Graydon Hoare),他是Mozilla公司的研究员。
2006年,霍雷尔开始利用业余时间开发Rust。他的目标是设计一门新的系统编程语言,既要有C/C++的性能,又要保证内存安全和线程安全,避免C/C++里常见的各种内存bug。
2010年,Mozilla公司正式赞助了这个项目。2015年,Rust 1.0正式发布。
Rust的定位非常明确:取代C/C++,成为下一代系统编程语言。
8.2.2 Rust的杀手锏:所有权系统
Rust最核心、最独特、也是最革命性的设计,就是所有权系统(Ownership)。
我们知道,C/C++里的内存问题,比如空指针、野指针、悬垂指针、内存泄漏、缓冲区溢出、数据竞争……这些问题,是无数程序员的噩梦。这些bug非常隐蔽,很难调试,而且经常导致严重的安全漏洞。
为什么会有这些问题?因为手动管理内存太容易出错了。你申请了内存,忘了释放,就内存泄漏。你释放了内存,还继续用,就野指针。多个线程同时改同一块内存,就数据竞争。
那有垃圾回收的语言,比如Java、Go,不就没这些问题了吗?是的,但垃圾回收有代价:运行时开销、停顿时间、额外的内存占用。对于系统编程、嵌入式、高性能场景,这些代价是不可接受的。
那有没有办法,既不用垃圾回收,又能保证内存安全?
Rust说:有。用所有权系统。
Rust通过一套严格的所有权规则,在编译的时候就检查所有的内存问题。不符合规则的代码,根本编译不过。
所有权的核心规则有三条:
- 每个值,都有且只有一个所有者变量
- 所有者离开作用域的时候,值会被自动释放
- 值可以被移动,也可以被借用,但同一时间,可变借用只能有一个,不可变借用可以有多个
听起来有点抽象?我们用一个通俗的类比来理解。
你可以把内存想象成一本书。
- 所有权,就是这本书的所有权。你拥有这本书,你可以随便看,也可以在上面写字。书你不要了,就自动回收(扔了)
- 移动,就是把书送给别人。你把书给了张三,那这本书就归张三了,你就不能再看了
- 不可变借用,就是把书借给别人看。你可以同时借给很多人看,大家都只能看,不能在上面写字。书的所有权还是你的
- 可变借用,就是把书借给别人,允许他在上面写字。但同一时间,你只能借给一个人。而且借出去的时候,你自己不能看也不能改
有了这套规则,会怎么样呢?
- 不会有double free:因为只有一个所有者,只会释放一次
- 不会有悬垂指针:借用的时候,编译器会保证所有者还在,不会提前释放
- 不会有数据竞争:同一时间不可能有两个可变引用,不可能同时写
- 不会有内存泄漏:所有者离开作用域自动释放,不会忘
所有这些检查,都是在编译期完成的。程序运行的时候,没有任何额外开销。不用垃圾回收,不用运行时检查,性能和C/C++一样。
这就是Rust最牛的地方:零成本抽象,编译期保证内存安全。
当然,代价就是:学习曲线非常陡峭。所有权、借用、生命周期……这些概念,对于习惯了其他语言的程序员来说,非常反直觉。很多人学Rust,都会经历一段“和编译器打架”的时期。你写的代码,编译器各种报错,就是不让你编译过。
很多人调侃:Rust编译器就像一个极其严格的老师,你的代码有一丁点不安全的隐患,它都毫不留情地给你指出来,逼你改到正确为止。
但当你真正习惯了这套规则之后,你会发现:编译器是在帮你避坑。凡是能编译通过的Rust代码,内存安全和线程安全基本就有保证了。再也不用熬夜调试莫名其妙的野指针bug了。
很多用过Rust的程序员都说:写完代码,编译通过,跑起来就没bug了。这种感觉,在C/C++里是不敢想象的。
8.2.3 Rust的其他优秀特性
除了所有权系统,Rust还有很多非常优秀的设计。
1. 零成本抽象 Rust的设计哲学和C++一样:你不用的特性,你就不用为它付出代价。你用的高级特性,编译出来的机器码和手写的C一样快。 迭代器、泛型、模式匹配……这些高级特性,全都是编译期完成的,运行时零开销。
2. 强大的类型系统与模式匹配 Rust的类型系统非常强大。比如Option类型,专门用来表示“可能为空”的值。Rust里没有null,也就不会有空指针异常。一个值要么是Some(有值),要么是None,你必须处理为空的情况,不处理就编译不过。 还有Result类型,专门用来处理错误。函数执行可能成功也可能失败,成功返回Ok(结果),失败返回Err(错误信息)。调用者必须处理错误的情况,不能忽略。 这两个设计,从根源上避免了大量的bug。 再加上强大的模式匹配,处理各种分支逻辑优雅又安全。
举个简单的例子,用Option和模式匹配:
fn divide(a: f64, b: f64) -> Option<f64> {
if b == 0.0 {
None
} else {
Some(a / b)
}
}
fn main() {
let result = divide(10.0, 2.0);
match result {
Some(value) => println!("结果是: {}", value),
None => println!("除数不能为0"),
}
}你必须处理None的情况,不然编译不通过。根本不会出现运行时突然空指针崩溃的问题。
3. 优秀的包管理与工具链 Rust有官方的包管理工具Cargo,非常好用。创建项目、编译、运行、测试、发布、管理依赖……一条命令全搞定。 相比C/C++混乱的构建工具和依赖管理,Cargo简直是天堂。 还有rustfmt自动格式化代码,clippy静态代码检查,工具链非常完善。
4. 没有历史包袱 Rust是一门全新的语言,没有历史包袱。它可以站在巨人的肩膀上,吸收之前所有语言的优点,规避它们的缺点。 它不用兼容几十年前的老代码,可以大胆地采用最先进的设计。
8.2.4 Rust的应用场景
Rust虽然难学,但它的优势太突出了。所以诞生之后,发展速度非常快。
主要应用场景:
1. 系统编程 这是Rust的本职工作。操作系统、驱动程序、嵌入式开发,这些C语言的传统领地,Rust都在慢慢渗透。 Linux内核已经正式支持Rust了,Windows内核也在引入Rust。这是一个非常重要的信号。 嵌入式领域,Rust也越来越火。内存安全对于嵌入式设备来说太重要了,很多物联网设备的漏洞,都是内存bug导致的。
2. 高性能基础设施 数据库、搜索引擎、网络框架、文件系统……这些对性能要求极高,同时对安全性要求也高的基础设施,非常适合用Rust开发。 比如TiKV、SurrealDB这些新一代数据库,都是用Rust写的。 还有Deno,一个新一代的JavaScript/TypeScript运行时,也是Rust写的,性能比Node.js更好。
3. WebAssembly(Wasm) WebAssembly是可以在浏览器里运行的高性能字节码。Rust是目前开发Wasm体验最好的语言。 因为Rust没有垃圾回收,体积小,性能高,非常适合编译成Wasm,在浏览器里跑高性能的应用,比如网页版的Photoshop、网页游戏、音视频处理。
4. 命令行工具 Rust编译出来的二进制文件体积小、启动快、性能高。非常适合写命令行工具。 现在很多经典的Linux命令,都出了Rust重写的性能加强版。比如grep的替代品ripgrep,find的替代品fd,ls的替代品exa……速度比原版快好几倍。
5. 区块链 区块链对性能和安全性要求都极高。很多新一代的区块链项目,比如Solana、Polkadot,都是用Rust开发的。
8.2.5 Rust的现状与未来
Rust连续很多年在Stack Overflow开发者调查里,被评为“最受喜爱的编程语言”第一名。 用过Rust的程序员,几乎都赞不绝口。
但它也有自己的问题:
- 学习曲线太陡:所有权、生命周期这些概念,门槛很高。很多人学了一半就放弃了。
- 编译速度慢:为了做严格的安全检查,Rust的编译速度比Go慢不少。
- 生态还不够成熟:虽然发展很快,但和C/C++几十年的积累比,生态还是差一些。很多领域的库还不够完善。
- 人才少:会Rust的程序员,比Java、Go少多了。团队招人难。
所以现在Rust还处于上升期,还没有成为绝对的主流。但它的潜力巨大。 很多人认为,Rust就是未来的系统编程语言,会慢慢取代C/C++的很多场景。 至少在对安全性、可靠性要求高的领域,Rust会越来越普及。
第九部分 移动时代的专属语言:Swift与Kotlin
9.1 Swift:苹果生态的未来
9.1.1 诞生背景:取代Objective-C
之前我们讲过,苹果的生态,不管是macOS还是iOS,长期以来都是用Objective-C开发的。 Objective-C这门语言,是C语言加Smalltalk风格的面向对象。优点是动态灵活,缺点是语法怪异,学习门槛高,安全性一般。 而且Objective-C太老了,很多现代语言的特性都没有。开发者早就怨声载道了。
2014年的苹果开发者大会(WWDC)上,苹果毫无预兆地发布了一门全新的编程语言:Swift。 全场沸腾。 Swift就是苹果用来取代Objective-C的官方语言。它的目标是:更现代、更安全、更快、更简单。
Swift的设计者,是克里斯·拉特纳(Chris Lattner)。他也是LLVM编译器项目的创始人。 有LLVM大神坐镇,Swift的性能和编译优化,天生就有很高的起点。
9.1.2 Swift的核心特点
Swift吸收了很多现代语言的优秀设计,同时又和苹果的生态深度集成。
1. 语法简洁现代 Swift的语法非常清爽,没有多余的符号。比如不用写分号结尾,不用写头文件。 类型推断非常强大,大部分时候不用声明变量类型。 举个简单的例子:
// 定义常量和变量
let name = "张三" // let声明常量,不可修改
var age = 18 // var声明变量,可以修改
// 定义函数
func greet(person: String, day: String) -> String {
return "你好\(person),今天是\(day)"
}语法接近自然语言,读起来很顺畅。
2. 安全优先 Swift非常重视安全性。
- 类型安全:强类型静态语言,编译期检查类型错误
- 可选类型(Optional):和Rust的Option类似,没有空指针。值可能为空的话,必须显式声明,显式处理
- 数组越界会直接崩溃,而不是出现不可预期的行为
- 变量必须初始化之后才能使用
这些设计,大大减少了常见的低级bug。
3. 性能极高 基于LLVM编译器,Swift编译出来的机器码性能非常高,接近C语言。 苹果官方表示,Swift的数值计算性能比Objective-C提升明显。
4. 多范式兼容 Swift支持面向对象、函数式、泛型编程等多种范式,非常灵活。 它既有类、继承这些面向对象特性,也有闭包、高阶函数这些函数式特性。
5. 与Objective-C无缝混编 Swift可以和Objective-C无缝混编。老项目里的Objective-C代码,可以直接在Swift里调用。 这大大降低了迁移成本。老项目可以慢慢往Swift迁移,不用一次性推倒重来。
9.1.3 现状与未来
Swift发布之后,版本迭代非常快,每年WWDC都会有大量新特性加入。 现在苹果官方已经明确,Swift是生态内的首选开发语言。新的API、新的框架,都优先适配Swift。
目前的行业现状:
- 新的iOS/macOS项目,基本都采用Swift开发
- 大型历史项目,正逐步从Objective-C向Swift迁移
- SwiftUI声明式UI框架的推出,进一步降低了UI开发的复杂度
Swift的局限性也很明显:
- 深度绑定苹果生态,离开苹果平台几乎没有用武之地
- 早期版本兼容性较差,经过多年迭代才逐步稳定
如果你打算深耕苹果生态,做iOS应用、Mac软件或者跨苹果设备开发,Swift是现在的必学语言。
9.2 Kotlin:安卓开发的新正统
9.2.1 诞生背景:JetBrains的“改良Java”
和苹果有Swift对应,安卓阵营也有自己的新一代官方语言——Kotlin。 Kotlin由知名IDE厂商JetBrains开发,这家公司打造了IntelliJ IDEA、PyCharm等广受开发者欢迎的开发工具。
JetBrains自身有大量Java开发需求,但他们觉得Java语法过于啰嗦,很多现代语言特性缺失,开发效率不够高。 于是他们决定设计一门新语言:既能够100%兼容Java生态,又具备现代语言的简洁与优雅。 这就是Kotlin的诞生初衷。
2011年Kotlin正式发布,它运行在JVM虚拟机之上,和Java可以无缝互操作。 2017年,谷歌宣布Kotlin成为安卓开发官方支持语言;2019年,谷歌进一步将Kotlin定为安卓开发首选语言。 自此,Kotlin彻底坐稳了安卓开发“新正统”的位置。
9.2.2 Kotlin的核心特点
Kotlin的设计理念非常明确:做“更好的Java”。它保留了Java的所有优势,同时解决了Java长期被诟病的痛点。
1. 语法极度简洁 同样的功能,Kotlin代码量通常只有Java的一半甚至更少。
- 强大的类型推断,绝大多数场景无需手写类型声明
- 数据类(data class):一行代码即可完成Java中十几行的POJO类定义,自动生成getter、setter、equals、hashCode、toString
- 扩展函数:可以给已有的类新增方法,无需继承,也不用修改原类代码
- 空安全设计:默认变量非空,想要允许为空必须显式标记,从根源上减少空指针异常
举个数据类的例子:
// 一行定义一个学生数据类
data class Student(val name: String, val age: Int, val studentId: String)如果用Java实现同等功能,需要编写字段、构造方法、getter/setter、equals等大量样板代码。
2. 100% Java兼容 这是Kotlin最大的护城河。你可以在Kotlin代码中调用任何Java库,也可以在Java代码中调用Kotlin编写的类。 Java生态几十年的积累,Kotlin可以全部继承。 对于现有Java项目,可以实现无缝迁移:逐个文件将Java改为Kotlin即可,项目可以正常运行。
3. 丰富的现代语言特性 Kotlin内置了很多现代语言特性:协程、Lambda表达式、流式API、模式匹配、运算符重载等等。 其中协程是Kotlin的杀手级特性:用同步的写法写异步代码,优雅地处理并发与IO操作,比Java的线程池+回调写法简洁太多。
4. 多平台潜力 Kotlin不止能跑在JVM上。通过Kotlin/JS可以编译成JavaScript,通过Kotlin/Native可以编译成原生机器码。 理论上,Kotlin可以实现“一门语言,全端开发”:后端、前端、安卓、iOS、桌面端都可以用Kotlin编写。 当然,目前最成熟的还是安卓和JVM后端场景。
9.2.3 应用与现状
现在Kotlin最核心的应用场景就是安卓开发。 在谷歌的大力推动下,安卓官方文档、示例、新API都优先适配Kotlin。现在新启动的安卓项目,绝大多数都会选择Kotlin作为开发语言。 Java写安卓,正在逐步成为历史。
除了移动端,Kotlin在后端开发领域也在稳步增长。 Spring Boot等主流后端框架已经完美支持Kotlin。用Kotlin写后端业务,开发效率比Java高很多,同时又能完全复用Java的技术栈。 很多团队会选择“Java生态 + Kotlin语法”的组合,兼顾稳定性与开发效率。
Kotlin的不足:
- 编译速度略逊于Java
- 后端领域的生态和纯Java相比还有差距
- 协程等高级特性有一定学习门槛
总的来说,如果你从事安卓开发,Kotlin是必须掌握的语言。如果你是Java后端开发者,想提升开发效率,Kotlin也是非常值得学习的选择。
第十部分 编程语言的底层原理:小白也能懂
讲了这么多编程语言的历史和各色语言的特点,我们来聊点本质的东西。 很多初学者都会困惑:这么多编程语言,本质区别到底是什么?编译型和解释型有什么不同?静态类型和动态类型又该怎么理解? 这一部分,我们用最通俗的类比,把这些基础概念讲清楚。
10.1 计算机的本质:只认识0和1
无论多么高级的编程语言,无论写出来的代码多么酷炫,最终落到CPU那里,都只有二进制的0和1。 CPU只认识机器指令,也就是一串二进制数字。不同的指令对应不同的操作:加法、减法、读取内存、写入内存、跳转…… 我们写的所有代码,最终都要翻译成机器指令,CPU才能执行。
那为什么不直接写机器指令?因为太反人类了。 人类根本记不住那些枯燥的二进制串。所以才有了汇编语言,给每条指令起了英文助记符。 但汇编还是太底层,写一个简单功能就要一大堆代码。所以才有了高级语言。
高级语言的作用,就是让人类用更接近自然语言和数学逻辑的方式写代码,然后通过专门的工具,翻译成机器能懂的指令。 根据翻译方式的不同,编程语言分成了编译型和解释型两大类。
10.2 编译型 vs 解释型:提前翻译 vs 边读边译
10.2.1 编译型语言:一次性全部翻译完
编译型语言,就像专业的图书翻译。你把一整本书(源代码)交给它,它一次性全部翻译成外文(机器码),翻译完成后,给你一个成品可执行文件。 之后你再想看内容,直接看成品就行,不用再重新翻译。
C、C++、Go、Rust,都是典型的编译型语言。 你写好代码,用编译器编译一次,生成一个exe或者二进制文件。之后直接运行这个文件就行,和源代码、编译器都没关系了。
优点:
- 运行速度快:只翻译一次,运行时直接执行机器码,没有额外开销
- 优化空间大:编译器可以全局分析代码,做深度优化,生成的机器码效率更高
缺点:
- 编译耗时:代码量大的项目,编译一次可能要几分钟甚至几小时
- 跨平台差:编译好的可执行文件,只能在对应操作系统、对应CPU架构上运行。Windows的exe不能在Linux上跑。换平台必须重新编译
10.2.2 解释型语言:读一句翻译一句
解释型语言,就像同声传译。演讲者说一句,翻译员翻一句,听众马上就能听到。 运行程序的时候,解释器逐行读取源代码,读一句翻译一句,然后立刻执行。
Python、JavaScript、PHP、Ruby,都是典型的解释型语言。 你写好py文件,直接用Python解释器运行就行。没有生成可执行文件的过程。
优点:
- 开发效率高:改完代码立刻就能运行,不用等编译,调试非常方便
- 跨平台好:只要有对应平台的解释器,同一份源代码就能跑。Windows、Mac、Linux都能用
- 灵活度高:动态语言的很多特性,都依赖解释执行的机制
缺点:
- 运行速度慢:边解释边执行,有额外的解释开销。通常比编译型语言慢几倍到几十倍
- 难以深度优化:因为是逐行执行,看不到全局代码,没法做复杂的全局优化
10.2.3 中间态:字节码 + 虚拟机
现在很多语言,都不是纯粹的编译型或者解释型,而是走了中间路线。 比如Java、C#,包括Python,本质上都是这种模式。
它们的执行过程分两步:
- 编译期:先把源代码编译成一种中间代码,叫做字节码(Bytecode)
- 运行期:虚拟机(JVM、CLR等)执行字节码。执行的时候,可以解释执行,也可以把热点代码编译成本地机器码(即时编译JIT)
这种模式,兼顾了跨平台性和性能。
- 跨平台:字节码是平台无关的。只要有对应平台的虚拟机,同一份字节码就能跑。这就是Java“一次编写,到处运行”的基础
- 性能:比纯解释型快很多。有了JIT即时编译之后,热点代码的性能可以接近编译型语言
现在的编程语言,编译和解释的界限已经越来越模糊了。
10.3 静态类型 vs 动态类型:编译期确定 vs 运行期确定
类型系统,是编程语言另一个核心分类维度。 简单来说,分类标准就是:变量的类型,是在什么时候确定的?
10.3.1 静态类型语言:编译的时候就定死了
静态类型语言,变量的类型在编译阶段就确定了。而且类型一旦确定,就不能再改变。 你声明变量的时候,要么显式指定类型,要么编译器能自动推断出来。 如果你把字符串赋值给一个整数变量,编译的时候就直接报错,根本跑不起来。
Java、C、C++、C#、Go、Rust、Swift,都是静态类型语言。
优点:
- 错误早发现:很多类型相关的bug,编译的时候就能揪出来,不用等到上线运行才暴露
- 性能更好:编译期就确定了类型,可以做很多针对性的优化
- 代码可读性强:看变量声明就知道是什么类型,大型项目里更清晰
- 工具支持好:IDE可以做精准的智能提示、代码补全、自动重构
缺点:
- 代码啰嗦:到处都要写类型声明,样板代码多
- 不够灵活:写通用逻辑比较麻烦,需要泛型等特性支持
- 开发周期稍长:改代码要等编译,类型不匹配还要调整
10.3.2 动态类型语言:运行的时候才知道类型
动态类型语言,变量本身没有类型,值才有类型。变量的类型要到程序运行到那一行的时候才确定。 同一个变量,你可以先给它赋整数,再给它赋字符串,完全没问题。
Python、JavaScript、PHP、Ruby,都是动态类型语言。
优点:
- 代码简洁:不用写类型声明,写起来快
- 灵活度高:写代码非常自由,快速迭代很方便
- 入门门槛低:新手不用一开始就理解复杂的类型系统
缺点:
- bug隐藏深:类型错误要运行到对应代码才会触发。如果测试覆盖不到,很容易留到线上
- 性能较差:运行时才做类型检查和转换,有额外开销
- 大型项目难维护:代码量一大,调用一个函数你都不知道该传什么类型、返回什么类型,全靠文档和记忆
- 工具支持弱:IDE很难做精准的智能提示和重构
10.3.3 发展趋势:互相借鉴,取长补短
现在的趋势是两种类型系统互相融合。 动态语言开始加静态类型支持:JavaScript有了TypeScript,Python有了类型注解语法。 给动态语言加上静态类型检查,既保留了灵活的优点,又能获得类型安全,大型项目也能hold住。
静态语言则在加强类型推断:Java、C#、Go、Rust都支持类型推断。很多场景不用手写类型,编译器自己能猜出来。 代码可以写得很简洁,同时又保留静态类型的所有好处。
所以现在,静态和动态的界限也越来越模糊了。
10.4 强类型 vs 弱类型:转换严不严格
还有一个常见的分类:强类型和弱类型。 这个分类没有绝对严谨的定义,核心区别是:不同类型之间的隐式转换,管得严不严?
10.4.1 强类型语言:类型界限分明
强类型语言,不同类型之间不能随便隐式转换。 你想把数字转成字符串,必须显式调用转换函数。不写的话,直接报错。
比如Python就是典型的强类型:
print(1 + "2") # 直接报错:整数和字符串不能相加你必须显式写str(1) + "2"或者1 + int("2")才行。
Java、C#、Go、Rust也都是强类型语言。
10.4.2 弱类型语言:偷偷帮你转换
弱类型语言,不同类型之间可以偷偷做隐式转换。哪怕类型不一样,也能凑到一起运算。 经常会出现很多反直觉的结果。
最典型的就是JavaScript:
console.log(1 + "20"); // 结果是字符串 "120"
console.log(true + 1); // 结果是数字 2
console.log([] + {}); // 结果是字符串 "[object Object]"JS的隐式转换规则非常迷,经常出现各种匪夷所思的结果,也是被吐槽最多的点。 PHP也是典型的弱类型语言。
弱类型写起来好像很方便,不用管类型。但实际上非常容易出bug。很多你以为是数字的变量,偷偷变成了字符串,计算出错误结果,而且很难排查。
现在普遍认为,强类型更安全、更可靠。新设计的编程语言,基本都是强类型的。
第十一部分 AI时代的编程语言:未来会怎样?
11.1 大模型时代,编程变了
最近几年,人工智能大模型爆发式发展。ChatGPT、Claude、各类代码助手……AI写代码的能力越来越强。 很多人都会问:AI会取代程序员吗?以后还需要学编程吗?编程语言会变成什么样?
我们先说说当下的现状: AI确实已经能写代码了。你给它一个明确的需求,它能生成完整的代码片段,甚至能搭建一个小型项目。 对于常见的功能、重复的样板代码,AI写得又快又好。程序员的生产效率,确实得到了很大提升。 以前要写一天的业务代码,现在AI生成出来,自己改一改、调一调,一两个小时就能搞定。
但目前来说,AI完全取代程序员还不现实。 因为AI只能生成它“见过”的、类似的代码。对于全新的业务场景、复杂的系统架构设计、深度的性能优化、疑难bug排查,AI还是力不从心。 而且AI生成的代码经常有逻辑漏洞、安全问题,甚至会一本正经地胡说八道。你必须自己能看懂、能判断、能调试修改。 不然AI生成一堆有问题的代码,你连哪里错了都不知道,反而会出大问题。
所以现在的真实情况是:会用AI的程序员,效率会大大提升;不会用AI的程序员,会逐渐跟不上节奏。 编程的语法门槛降低了,但对逻辑思维、架构设计、问题排查能力的要求反而更高了。
11.2 AI对编程语言发展的影响
那AI会怎么改变编程语言的发展方向呢? 大概有这么几个趋势。
11.2.1 语法细节越来越不重要
以前学编程,很大一部分精力是在背语法:这个关键字怎么用、那个API有几个参数、语言有哪些坑。 以后这些会越来越不重要。语法细节、API用法,AI都能帮你搞定。你只要说清楚需求,AI就能生成对应语言的正确代码。
你不用再纠结记不住语法,也不用再为了调一个API查半天文档。 未来的程序员,核心竞争力会从“记住多少语法”转向“能不能清晰地定义问题、拆解问题、设计系统”。
11.2.2 自然语言编程门槛降低
以后的编程,会越来越不像“写代码”,而越来越像“说话”。 你用自然语言描述你想要的功能,AI就能把它转换成可运行的程序。 现在的低代码平台、AI编程助手,都在往这个方向走。
但这并不代表编程语言会消失。 因为自然语言天然有歧义,不够精确。复杂的逻辑、严谨的系统,还是需要精确的编程语言来描述。 而且AI生成的底层,依然是传统的编程语言代码。
更可能出现的是分层:
- 顶层:普通人用自然语言和AI交互,生成简单工具、小型应用
- 底层:专业程序员用编程语言实现核心逻辑、优化性能、搭建架构、排查问题
普通应用的开发门槛会越来越低,普通人也能做出自己需要的小工具。 但专业程序员的价值不会消失,反而会向更核心、更复杂的方向集中。
11.2.3 面向AI的编程语言会不会出现?
未来会不会出现专门为AI设计的编程语言? 很有可能。
现在的编程语言,都是为人类阅读和书写设计的。讲究可读性、符合人类思维习惯。 如果未来代码主要由AI生成、AI维护,那编程语言的设计逻辑可能就变了。 它可能不需要那么符合人类阅读习惯,但会更严谨、更不容易产生歧义、更方便AI做自动验证和自动优化。
当然,这还是比较遥远的设想。至少在可预见的未来,主流的编程语言格局不会发生颠覆性变化。
11.3 编程语言本身的发展趋势
抛开AI不谈,编程语言自身的发展,也有几个清晰的方向。
11.3.1 安全性越来越受重视
以前大家更看重性能、开发效率。 随着软件规模越来越大,安全漏洞的危害也越来越大。内存安全、类型安全、并发安全,越来越被重视。 Rust的流行就是最好的例子。用编译期检查保证安全,付出一点学习成本,换来运行时的稳定和安全,这笔账越来越多的团队觉得划算。
以后新设计的编程语言,一定会把安全性作为核心设计目标。
11.3.2 并发与分布式原生支持
现在的软件,几乎都是跑在多核CPU上,几乎都是分布式系统。 传统语言的并发都是后来加上去的,用起来别扭,也容易出问题。 新一代语言,比如Go的goroutine+channel,Rust的所有权+async/await,都是从语言层面原生解决并发问题。
以后的编程语言,一定会把并发、分布式作为核心特性来设计,而不是事后打补丁。
11.3.3 开发者体验优先
现在程序员的时间成本,远高于机器的硬件成本。 所以语言设计越来越注重开发者体验:语法简洁不啰嗦、工具链完善好用、报错信息清晰友好、调试方便。 Go和Rust之所以受欢迎,除了本身的特性,完善好用的工具链也是很大的加分项。
未来的编程语言,一定会越来越“好用”,帮程序员省更多的时间。
11.3.4 多范式融合
以前的语言,流派分得很清:面向过程、面向对象、函数式。 现在的新语言,基本都是多范式的。你想用面向对象组织代码结构就用面向对象,想处理数据就用函数式写法,想写底层操作就用命令式。 没有最好的范式,只有最合适的场景。 取长补短,灵活搭配,是未来的主流。
第十二部分 给小白的建议:怎么选编程语言?
讲了这么多语言的历史、原理和特点,很多初学者最关心的问题还是:我到底该学哪门语言? 这一部分,给大家一些非常实在的建议。
12.1 没有最好的语言,只有最合适的
首先一定要建立一个观念:没有所谓“最好”的编程语言。 每门语言都有自己擅长的领域,也有自己的短板。 选语言,核心是看你的目标是什么。你想做什么方向的工作、解决什么类型的问题,就选对应的语言。
不要听别人说什么语言火就跟风学什么,也不要纠结哪个语言“更厉害”。 编程语言永远只是工具。解决问题的能力,才是程序员的核心竞争力。
12.2 不同方向的语言选择
我们按常见的学习目标和就业方向,给大家梳理一下对应的主流选择。
12.2.1 纯零基础入门,培养编程思维
如果你是完全的小白,之前从没接触过编程,只是想了解一下编程、锻炼逻辑思维,还没确定以后做什么方向。 首选:Python
为什么入门推荐Python?
- 语法最简单:最接近自然英语,几乎没有奇怪的语法符号。新手不用纠结各种括号、分号、类型声明
- 正反馈快:写几行代码就能做出有意思的东西,容易获得成就感,能坚持学下去
- 应用面极广:以后不管转后端、数据分析、自动化、人工智能,Python都能用得上,不会白学
- 生态极其丰富:想做什么都有现成的库,不用自己从零造轮子
不建议一上来就学C/C++、Java。这些语言概念多、语法繁琐,入门很容易产生挫败感,从入门到放弃。
12.2.2 想做前端开发(网页、小程序)
如果你想做前端开发,就是写网页、做小程序、开发用户能直接看到的交互界面。 必学三件套:HTML + CSS + JavaScript
这三个是Web前端的根基,绕不开的。 HTML负责页面结构,CSS负责样式美观,JavaScript负责交互逻辑。 进阶之后,一定要学TypeScript,给JavaScript加上类型系统,大型项目更稳。 框架方面,Vue和React至少学一个,现在前端开发基本都是基于框架。
前端的优点:入门快,能快速看到效果,岗位需求量大。 缺点:技术更新快,需要持续学习新东西。
12.2.3 想做后端开发(服务器、接口)
后端开发的选择比较多,不同语言有不同的适配场景。
1. Java
- 地位:后端开发的绝对王者,市场份额最大,岗位最多
- 适合:大型企业、金融、电商、传统软件公司,求稳首选
- 优点:生态极其完善,就业机会多,薪资体系成熟
- 缺点:学习内容多,入门周期稍长
2. Go
- 地位:后端新势力,发展速度最快
- 适合:互联网公司、云原生、微服务、高并发场景
- 优点:语法简单好学,开发效率高,性能好,前景看好
- 缺点:岗位数量目前还不如Java,但增长非常快
3. Python
- 地位:脚本万能语言,全场景通吃
- 适合:创业公司、快速迭代项目、自动化运维、数据分析结合的后端
- 优点:开发速度极快,上手简单
- 缺点:高并发性能一般,纯后端岗位不如Java和Go多
怎么选?
- 想进大厂、求稳、岗位最多:选Java
- 想追新技术、做云原生高并发:选Go
- 想快速上手、兼顾其他方向:选Python
12.2.4 想做移动开发(手机APP)
1. 安卓开发:Kotlin 现在安卓开发,官方首选是Kotlin。Java已经不是新项目的首选了。
2. iOS开发:Swift 苹果生态,毫无疑问选Swift。Objective-C只有维护老项目才会用到。
3. 跨平台开发:Flutter(Dart语言) 如果想一套代码同时跑安卓和iOS,可以学Flutter。性能和体验比早期跨平台方案好很多。
12.2.5 想做游戏开发
1. 独立游戏、手游:C#(Unity) Unity引擎是现在最流行的游戏引擎,绝大多数手游、独立游戏都是用Unity做的。 入门友好,生态完善,找工作也容易。首选C# + Unity。
2. 3A大作、主机游戏:C++ 顶级3A游戏、虚幻引擎,基本都是C++。门槛更高,但上限也更高。
12.2.6 想做数据分析、人工智能
首选:Python 这个基本没有争议。数据分析、机器学习、深度学习,全都是Python的天下。 NumPy、Pandas、Matplotlib、TensorFlow、PyTorch……所有主流工具,Python都是第一优先级。
当然,真正做算法,光会Python是不够的,还要学数学、学算法理论。 但语言层面,Python是唯一的选择。
12.2.7 想做嵌入式、底层开发
首选:C语言 嵌入式、单片机、驱动开发、操作系统内核,这些底层领域,C语言是绝对的主流。 想往底层走,C语言是绕不开的基本功。
进阶可以学C++或者Rust。Rust是未来的方向,现在嵌入式领域也开始慢慢普及。
12.3 学习编程的正确心态
最后,给初学者几个心态上的建议,能帮你少走很多弯路。
不要贪多,先精通一门 很多人今天学Python,明天学Java,后天又去看Go。结果每样都只会一点皮毛,哪样都拿不出手。 编程的核心是思维,不是语法。把一门语言学透,掌握了编程的核心思想,再学其他语言会非常快。 先深度,再广度。
多写代码,光看没用 编程是手艺活,不是看书看会的。 很多人看了很多教程、很多视频,感觉都懂了,一动手写就啥也不会。 一定要多敲代码,多做练习,多做小项目。写得多了,自然就熟练了。
不要怕报错 初学者最怕看到红色的错误信息,一报错就慌。 其实报错是好事。报错就是编译器/解释器在告诉你:哪里错了、为什么错。你照着改就行。 每解决一个错误,你就进步一点。 程序员的日常,就是写代码、报错、改bug、再报错、再改。太正常了。
编程的本质是解决问题 不要为了学语言而学语言。语言只是工具。 最好的学习方式,是带着目标去学。想做一个自己用的小工具、一个小网站、一个小游戏。 为了完成这个目标,去学对应的知识。这样学的东西最扎实,也最有成就感。
结语
从阿达·洛芙莱斯写下第一段程序算法,到今天,编程已经走过了近两百年的历史。 从原始的二进制机器码,到助记符汇编,再到高级语言百花齐放;从面向过程到面向对象,从命令式到函数式;从单机到互联网,从云原生到AI时代。 编程语言一直在进化,变得越来越强大,越来越易用,越来越贴近人类的思维。
但编程的本质从来没有变过:用清晰的逻辑和规则,让计算机帮我们解决问题。 无论语言怎么变,无论AI怎么发展,这个核心永远不会变。
希望这篇长文,能帮你推开编程世界的大门。 不管你是打算入门学编程,还是只是想了解一下这个数字世界的基石,都能有所收获。
编程的世界很大,也很有趣。 祝你在这条路上,走得开心,有所成就。
