-
c#教程之OOP语言
C#(C Sharp)是微软于2000年提出的一种源于C++、类似于Java的面向对象编程语言,适合于分布式环境中的组件开发。C# 是专门为.NET设计的,也是.NET编程的首选语言。
C# 语言的相关帮助文档位于MSND的“帮助查看器主页\Visual Studio 2010\Visual Studio\Visual Studio语言\ Visual Basic和Visual C# \Visual C#”目录中。
l 封装(encapsulation)—— 是对问题的抽象,以达到信息隐藏的目的。通过类和对象,将属性(数据/变量)和方法(操作/函数)封装在一个黑箱内,将细节隐藏起来。既可以保护数据不被他人恶意或大意地修改,又能防止其他程序员编写高耦合度的代码,还方便了今后对对象内部的修改和升级。
l 继承(inheritance)—— 利用问题和事物的相似性,通过类的(多层)继承机制,可以达到减少软件开发难度和重用已有对象的属性和方法之目的。
l 多态(polymorphism)—— 同一操作或响应,可以同时针对多个可选的类型和对象,并且能够自动在它们中间选择最合适的来进行。多态可以分为:
n 编译时多态:包括函数和运算符的重载(overload),通过早期的静态绑定(binding)来实现。
n 运行时多态:通过继承结合晚期动态绑定来实现——用同一基类的指针,可以访问各个不同派生类中的同名方法和函数。
l 抽象(abstraction)—— 一个问题的本质特征。忽略一个对象的细节,致力于一般的合适级别的程序能力。抽象是在事物的周围绘制透明的盒子,是定义事物接口的行为。
l 信息隐藏(information hiding)—— 限制外部对类属性(数据成员)的访问。
l 封装(encapsulation)—— 将某种保密和隐匿应用到类中的数据和一些方法(函数或子例程)上。封装确保一个对象只能通过确定的渠道(即类的公用方法)才可被修改。封装意味着将数据及相关函数打包到一个单一的单元(类)中。每个对象暴露一个接口(公用方法),规定其他对象可以如何读取或修改它。
l 类(class)—— 问题的抽象,对象的模版,接口的实现。例如int是整数的模版、CPoint是点的模版。
l 对象(object)—— 问题中的事物,类的实例(变量),拥有数据和功能。若int i; CPoint p; 则i是int的实例变量,p是CPoint的实例对象。
l 实例(instance)—— 一个对象,是某个类的示例。
l 实例化(intantiate)—— 由类定义创建对象。
l 继承(inheritance)—— 创建子类的机制,一种“is a”或“is like”关系,形成类层次结构。是OOP中对象重用的核心概念。
l 子类(subclass)—— 继承自另一个类的派生类(derived class)。
l 超类(superclass父类)—— 被另一个类继承的基类(base class)。
l 虚函数(virtual function)—— 可以在子类中被覆盖(override)的函数。
l 纯虚函数(pure virtual function)—— 只有声明没有定义的函数,必须在派生类中被覆盖。
l 抽象类(abstract class)—— 至少包含一个纯虚函数的类,不能被实例化。
l 具体类(concrete class)—— 可以实例化的类,不包含纯虚函数。
l 接口(interface)—— 纯抽象的类,类的模版。接口中的所有的方法(成员函数)都只有声明,没有定义(都是纯虚函数)。必须在接口的派生类中,实现接口的全部方法。接口可以看成是一个,定义了一套内部行为的,若干操作特征标记的集合。(在C++中,也把类的公用成员函数叫做接口)。
l 属性(attribute)—— 类和接口中的数据信息,又叫数据成员(data member)或成员变量(member variable)。属性是类知道的事物。
l 方法(method)—— 类操作的实现,又叫成员函数(member function)。方法是类要做的事情。(在C++中,也把虚函数叫做方法)。
l 重载(overload)—— 一个类中的,参数的数目、类型或排列顺序不同的多个同名方法(成员函数)。
l 覆盖(override)—— 在子类中重新定义(与父类中的定义有区别的)属性和方法。
l 持久化(persistence)—— 将对象存入永久存储中(如文件、数据库等)。
l 持久对象(persistent object)—— 存入永久存储的对象。
但是,作为一种程序设计方法,OOP最早出现在1967年,由挪威计算中心的Ole-Johan Dahl和Kristen Nygaard,在他们发明的Simula67语言中首先引入的。Simula语言是在Algol60基础上,加入了对象、类和继承等概念后开发出来的,它在OOP的历史中占有重要地位。
Smalltalk是1970年代,由美国施乐(Xerox)公司PARC(Palo Alto Research Center帕洛阿尔托研究中心)的Alan Kay、Dan Ingalls、Ted Kaehler和Adele Goldberg等人,开发的一种面向对象和动态类型的交互式程序设计语言。它受到了Sketchpad和Simula的影响,但是Smalltalk被设计成一个全动态系统的纯OOP,程序的所有成分都是对象(但是,为了提高运行效率,C++/Java/C# 中的基本数据类型都不是对象),对象可以被动态地创建、修改和销毁。在Smalltalk中还创造出词汇“'inheritance(继承)”和“Object-oriented programming(面向对象的程序设计)”。
使OOP走向辉煌的是1985年发明的C++,随着GUI(Graphical User Interfaces,图形用户界面)的流行,C++的地位更加巩固。但是,因特网的发展和万维网的流行,又催生了另一个重要的OOP语言——Java(1995年)。为了与Sun公司的Java竞争,微软公司也推出了一种用于.NET组件编程环境的新OOP语言——C#(2000)。
图17-1 C语言系列
图17-2给出了可用于.NET编程的主要高级语言(包括C#、VB、C++/CLI、J# 和F# 等)的发展脉络。
C语言是为编写操作系统而创建的一门高级语言,语法简洁、表达自由、库函数丰富,具有强大的功能和极大的灵活性,适用于高效的系统编程。
1)起源
1970年,AT&T贝尔实验室的美国计算机科学家Ken Thompson和Dennis Ritchie等人在DEC公司的PDP-7小型机上开发出了Unix操作系统,最初的实现是用汇编语言写成的。为了使Unix操作系统具有可移植性,迫切需要一种高级语言工具。为此, Dennis Ritchie以B语言为基础,参考了Algol68,于1972年设计出了C语言。1973年他们用C语言重写了Unix,1975年又利用C语言将Unix移植到了PDP-11上。
图17-2 主要编程语言工具的发展
2)特点
C语言是一种可移植的系统语言,拥有充分的控制语句和数据结构功能,包含丰富的操作符,从而能够提供强大的表达能力,可以用于许多不同的应用领域。但是,C语言并不是面向科学家和计算机业余爱好者的,而是专门为程序员设计的。
为了进行高效的系统编程,C语言提供了强大的功能和极大的灵活性。与其它高级语言相比,C语言的语法简洁、表达自由、库函数丰富。如果将编程比作造房子,则Fortran和Basic等语言就像一些已经预先造好的大预制件,使用起来简单快捷,但是灵活性差、且功能有限,只能造某些固定模式的房屋;而C语言就像一块块的小砖,使用起来虽然繁琐,但是灵活性强、而且功能无限,能够造各式各样的建筑物,不过这就要求C语言程序员具有很高的专业水平。
因此,C语言假设使用者都是计算机专家,采取的是程序员负责制。它不进行完备的类型检查,对数组越界也没有限制。为了进行高效的系统编程,C语言还提供了指针和指针运算,程序员可以随意操作全部内存,任意修改任何内容。
表达的自由性和操作的任意性,也给C语言带来了很多编程问题和安全隐患。特别是C语言的++/--运算符和指针运算,更是倍受指责。
与其它高级语言相比,C语言提供了一些低级语言特征,更面向机器。所以,也有人称C语言是介于高级语言和低级语言之间的一种中级语言。
3)标准
(1)K&R C
开始的很多年,C语言没有国际标准,只有一个事实标准——1978年Brian Kemighan和Dennis Ritchie编写的《C程序设计语言》(The C Programming Language)一书,通常称其为K&R C或经典C。该书的附录“C参考手册”(C Reference Manual)成为了C语言的实现指南,但是书中缺少对库函数标准的描述,一般以Unix实现的库函数所为事实标准。需要说明的是,因为C语言的语法成分简单,很多基本功能(例如I/O语句)都是靠库函数来实现的。所以,C语言比其它高级语言更依赖于库函数。
(2)C90
1983年ANSI(American National Standards Institute 美国国家标准协会)设立了一个X3J11小组,着手进行C语言的标准化。并最终于1989年推出ANSI C (ANSI X3.159-1989),1990年它又成为国际标准ISO C(ISO/IEC 9899:1990 Programming languages – C,程序设计语言——C),原来叫做ANSI C或ISO C,现在通常称其为C89或C90。
C90对K&R C的主要改变是,增加了函数原型(prototype),强调对函数的输入参数进行严格的类型检查;允许将结构本身作为参数传递给函数(原来只允许传地址);并补充定义了C语言的标准函数库。增加了关键字:const(常型变量)、enum(枚举类型)、signed(有符号的,例如signed char)、void(空/无,可用于函数返回值和形参、通用指针类型)、volatile(易变变量,防止编译器错误的优化)等。还增加了预处理指令:#elif(else if)、#error(错误,强制编译停止)、#line(修改当前行号和源文件名)、#pragma(附注/编译指令,编译器定义的与实现有关的指令)。
(3)C99
对C90的修订工作开始于1994年,在ISO C++(1998)标准推出之后,ISO又于1999年12月16日,推出了C语言标准的第2版:ISO/IEC 9899:1999 Programming languages – C(程序设计语言——C),一般称其为C99。
C99主要的修订目标有三点:①支持国际化编程,引入了支持国际字符集Unicode的数据类型和库函数;②修正原有版本的明显缺点。如整数的移植方法,例如int8_t、int16_t、int32_t和int64_t等类型;③针对科学和工程的需要,改进计算的实用性。例如添加了复数类型和新数学函数。
C99对C89/C90的具体修改有:①增加了C++的//注释风格:原来C语言只支持多行注释:/*……*/,C99现在也识别单行注释:// ……。②增加了关键字:inline(内联函数)、restrict(限制)、_Bool(布尔类型)、_Complex(复数)、_Imaginary(虚数)。③增加了数据类型:(unsigned) long long [int](64位整数)(对应的打印输出格式为%lld或%llu)。④定义了可移植整数类型,如:int8_t、int16_t、int32_t、int64_t,uint8_t、uint16_t、uint32_t、uint64_t;intptr_t、uintptr_t。以及表示对应类型常量的方法,如INT8_C(128)、INT32_C(1234)。⑤增加了浮点常量的十六进制格式:p或P表示后跟二进制指数(的十进制值)。例如:0xa.1cp10 = (10 + 1/16 + 12/256) * 210 = 10352.0。⑥增加了浮点数的十六进制打印格式符:%a或%A(代替十进制的%e或%E)、%La或%LA(代替十进制的%Le或%LE)。⑦可指定初始化的条目(未被初始化的条目全被置为0),如:int days[12] = {31, 28, [4] = 31, 30, 31, [1] = 29};⑧支持变长数组(即可用变量来定义数组的大小),如:float a[n], a2[m][n];⑨允许在代码块的任何地方定义变量(似C++)。⑩允许在结构的最后定义一个大小可伸缩的弹性数组成员,可以用于结构指针,根据允许情况来动态分配内存。但是,C99增加的新特性中,有许多当前的C++标准还不支持。
1985年,贝尔实验室的计算机科学家,丹麦人Bjarne Stroustrup在Simula 67的启发下,对C语言进行了扩充,在保留C语言优点的基础上,添加了面向对象的特征和功能后,将C语言扩展成为带类的C (C with Classes),1983年Rick Mascitti将其命名为C++。
1985年10月Bjarne Stroustrup实现了C++的第一个商用版本,与此同时他还出版了《The C++ Programming Language(C++程序设计语言)》的第1版,成为C++的事实标准。C++的第1版,提供了面向对象程序设计的基本特征:类和对象、单继承、虚函数、公有/私有成员的访问控制、函数重载等。
1989年推出C++的第2版,增加了多继承、抽象类、静态和常型成员函数、保护成员的访问控制、运算符重载等新特性,促使C++语言流行起来。1990年1月1日Margaret A. Ellis和Bjarne Stroustrup出版了《The Annotated C++ Reference Manual(带注释的C++参考手册)》。1991年Bjarne Stroustrup推出了《C++程序设计语言》的第2版:The C++ Programming Language (2nd edition)。
1993年推出C++的第3版,增加了模板、异常处理和嵌套类等新特性.1994年3月29日Bjarne Stroustrup出版了《The Design and Evolution of C++(C++的设计和演化)》。1997年Bjarne Stroustrup推出了《C++程序设计语言》的第3版:The C++ Programming Language (3rd edition)。
1998年9月1日C++成为国际标准(ISO/IEC 14882:1998 Programming languages -- C++,程序设计语言——C++),添加的新特性主要有:命名空间、新的强制类型转换、布尔类型和STL。2000年2月15日Bjarne Stroustrup推出了《C++程序设计语言》的特别版:The C++ Programming Language (Special Edition)。
2003年10月15日ISO推出了C++标准的第2版ISO/IEC 14882:2003,该版本在语法特征上没有什么变化,只是纠正了原版的各种错误,并进行了一些技术上的修订。
2007年11月15日ISO又推出了C++的扩展库标准(TR1,技术报告1):ISO/IEC TR 19768:2007 Information technology -- Programming languages -- Technical Report on C++ Library Extensions。微软公司在其Visual Studio 2008 SP1中的Visual C++ 2008功能包(Feature Pack)支持此TR1。
2010年9月3日ISO还推出了C++的支持数学特殊函数的扩展库标准:ISO/IEC 29124:2010 Information technology -- Programming languages, their environments and system software interfaces -- Extensions to the C++ Library to support mathematical special functions。
2011年8月12日ISO通过了新版C++语言标准——C++11(ISO/IEC 14882:2011 Information technology -- Programming languages -- C++, Edition 3),它包含了几个核心语言新功能,并扩展了C++标准库,还合并了大多数TR1库(数学特殊函数库除外)。
现在的C++已经成为了一种同时具有面向过程、面向对象和泛型编程的特征,功能强大、运行高效、使用广泛的混合型程序设计语言。
新的C++标准大幅改善了C++内核语言领域,包括多线程支持、泛型编程、统一的初始化,以及表现的加强等。具体的改进有:增加了右值引用(typename &&)、扩展了常数表达式(引入新关键字constexpr)、放宽了关于POD(Plain Old Data,简单旧数据)型结构的定义、引入了外部模板概念(extern template class)、可将初始化列表的概念绑定到类型上(std::initializer_list)、提供了一种统一语法来初始化任意对象、可进行类型推导(auto)、支持以范围为基础的for循环、允许定义λ(lambda)函数、引进了一种新的函数定义与声明的语法(template< typename LHS, typename RHS> [] AddingFunc(const LHS &lhs, const RHS &rhs) -> decltype(lhs+rhs) {return lhs + rhs;})、允许派生类手动继承基类的构造函数(using 基类名::基类名;)、引入了表示空指针常数的新关键字(nullptr)、引进了一种特别的枚举类(enum class)、关键字explicit修饰符可用于显示类型转换、可用于模板类型的别名定义(template<…> using TypedefName = SomeType<…>;)、移除了所有对 union 的使用限制(除了其成员仍然不能是引用类型)、加入了(允许任意个数和任意类别的模板实参的)变长参数模板、增加了两种新的字符类别char16_t(UTF-16)和char32_t(UTF-32)(传统的char类型对应于UTF-8)和新的字符串常量(u8"I'm a UTF-8 string."、u"This is a UTF-16 string."、U"This is a UTF-32 string.")、允许用户自定义新的常量修饰符(literal modifier)、引入了支持多线程编程的多任务存储器模型和线程区域的存储期限(thread_local)、可使用或禁用对象的默认函数(default和delete)、(同C99)增加了64位的整数类型(long long int)、增加了用于测试的静态声明(static_assert新关键字)、允许sizeof操作符作用在类的数据成员上(无须明确的对象)。
C++11标准程序库将引进了数个新功能(如线程功能、元组类型、散列表、正则表达式、通用智能[smart,灵巧]指针、可扩展随机数功能、包装引用、函数对象的多态包装、元程序的类型特性[trait]、用于计算函数对象返回类型的统一方法),其中许多可以在现行标准下实现,而另外一些则依赖于(或多或少)新的C++11内核语言机能。新的程序库的大部分被定义于于2005年发布发TR1(Library Technical Report,库技术报告),各式TR1的完全或部分实现原提供在命名空间std::tr1,现在C++11将其移置于命名空间std之下。
已被移除或是不包含在 C++11 标准的特色:
l 预计由 Technical Report 提供支持:模块、十进制类别、数学特殊函数。
l 延后讨论:概念(Concepts)、更完整或必备的垃圾回收支持、反射(Reflection)、宏域(Macro Scopes)。
l 将被移除或废弃的特色:循序点 (sequence point)、导出(export)、动态异常规范(dynamic exception specifications)、std::auto_ptr(被 std::unique_ptr 取代)、函数对象基类 (std::unary_function, std::binary_function)、函数指针适配器与类型成员指针适配器、绑定 (binder)类。
在VC2010中支持的C++11新特性有:lambda 表达式、rvalue 引用声明符(&&),以及auto(的新用途)、decltype、nullptr、static_assert 关键字等。
表17-1 C++关键字
其中:斜体为传统C++的关键字(15个)、粗体为C++98/03增加的关键字(15个)、红色的为C++11新增加的关键字(10个),其余(正常的)为C和C++共有的关键字(33个)。另外,标准C++没有的C99关键字有3个:_Bool、_Complex、_Imaginary。除了这些关键字外,标准C++中还有11个保留标识符,用作运算符的备选表示,参见表17-2。
表17-2 C++ 的保留标识符
C语言标识符有局部(代码块{…},如复合语句和函数体)和全局两种作用域,C++在这二者之间引入了类作用域(如类变量和成员函数)。标准C++又在类和全局之间,新添加了命名空间这一个作用域级别。
命名空间是一种描述逻辑分组的机制,可以将按某些标准在逻辑上属于同一个集团的声明放在同一个命名空间中。命名空间可以是全局的,也可以位于另一个命名空间之中,但是不能位于类和代码块中。所以,在命名空间中声明的名称(标识符),默认具有外部链接特性(除非它引用了常量)。在所有命名空间之外,还存在一个全局命名空间,它对应于文件级的声明域。因此,在命名空间机制中,原来的全局变量,现在被认为位于全局命名空间中。
标准C++库(不包括标准C库)中所包含的所有内容(包括常量、变量、结构、类和函数等)都被定义在命名空间std(standard,标准)中了。
1)定义命名空间
有两种形式的命名空间——有名的和无名的,它们的定义方法分别为:
namespace 命名空间名 { // 有名命名空间
[声明序列]
}
namespace { // 无名命名空间
[声明序列]
}
命名空间的成员,是在命名空间定义中的花括号内声明了的名称。可以在命名空间的定义内,定义命名空间的成员(内部定义)。也可以只在命名空间的定义内声明成员,而在命名空间的定义之外,定义命名空间的成员(外部定义)。
命名空间成员的外部定义的格式为:
命名空间名::成员名 ……
注意:不能在命名空间的定义中声明(另一个嵌套的)子命名空间,只能在命名空间的定义中定义子命名空间。也不能直接使用“命名空间名::成员名 ……”定义方式,为命名空间添加新成员,而必须先在命名空间的定义中添加新成员的声明。另外,命名空间是开放的,即可以随时把新的成员名称加入到已有的命名空间之中去。方法是,多次声明和定义同一命名空间,每次添加自己的新成员和名称。例如:
namespace A { int i; void f(); } // 现在A有成员i和f()
namespace A { int j; void g(); } // 现在A有成员i、f()、j和g()
2)使用命名空间
使用命名空间的方法有三种:
l 作用域限定(直接使用解析运算符“::”)
对命名空间中成员的引用,需要使用命名空间的作用域解析运算符::。例如:
std::cout << "Hello, World!" << std::endl;
l using指令(using namespace)
为了省去每次调用命名空间成员和标准库的函数和对象时,都要添加“命名空间名::”和“std::”的麻烦,可以使用标准C++的using编译指令来简化对命名空间中的名称的使用。格式为:
using namespace 命名空间名[::子命名空间名……];
在这条语句之后,就可以直接使用该命名空间中的标识符,而不必写前面的命名空间定位部分。因为using指令,使所指定的整个命名空间中的所有成员都直接可用。例如:
using namespace std;
cout << "Hello, World!" << endl;
又例如(.NET框架):
using namespace System::Drawing::Imaging;
using namespace System::Window::Forms::Design::Behavior;
l using声明(using)
除了可以使用using编译指令(组合关键字using namespace)外,还可以使用using声明来简化对命名空间中的名称的使用。格式为:
using 命名空间名::[命名空间名::……]成员名;
注意,关键字using后面并没有跟关键字namespace,而且最后必须为命名空间的成员名(而在using编译指令的最后,必须为命名空间名)。
与using指令不同的是,using声明只是把命名空间的特定成员的名称,添加该声明所在的区域中,使得该成员可以不需要采用,(多级)命名空间的作用域解析运算符来定位,而直接被使用。但是该命名空间的其他成员,仍然需要作用域解析运算符来定位。例如:
using std::cout;
cout << "Hello, World!" << std::endl;
l 使用方法的比较(参见表17-3)
表17-3 命名空间使用方法的比较
直接使用解析运算符“::”来访问命名空间成员,非常安全,没有命名冲突,但是书写非常麻烦,只适用于个别成员的偶尔使用。而using编译指令和using声明,都可以简化对命名空间中名称的访问。using指令使用后,可以一劳永逸,对整个命名空间的所有成员都有效,非常方便。而using声明,则必须对命名空间的不同成员名称,一个一个地去声明,也很麻烦。
但是,一般来说,使用using声明会更安全。因为,using声明只导入指定的名称,如果该名称与局部名称发生冲突,编译器会报错。而using指令导入整个命名空间中的所有成员的名称,包括那些可能根本用不到的名称,如果其中有名称与局部名称发生冲突,则编译器并不会发出任何警告信息,而只是用局部名去自动覆盖命名空间中的同名成员。特别是命名空间的开放性,使得一个命名空间的成员,可能分散在多个地方,程序员难以准确知道,别人到底为该命名空间添加了哪些名称。
虽然使用命名空间的方法有多种可供选择,但是不能贪图方便,一味使用using 指令,这样就完全背离了设计命名空间的初衷,也失去了命名空间所应该具有的防止名称冲突的功能。一般情况下,对偶尔使用的命名空间成员,应该使用命名空间的作用域解析运算符来直接给名称定位。而对一个大命名空间中的经常要使用的少数几个成员,提倡使用using声明,而不应该使用using编译指令。只有需要反复使用同一个命名空间的许多数成员时,使用using编译指令,才被认为是可取的。
Java具有面向对象、跨平台、安全稳定、多线程等特点,特别适合于网络编程。Java源自C++,它从C++中继承了大多数语言成分,但是它也对C++进行了大量简化(Sun的创始人之一/首席科学家/Java的主要作者之一Bill Joy称Java为C++--)。例如,它抛弃了C++中复杂和容易引起问题的头文件、编译指令、预处理器、指针、结构、隐式类型转换、操作符重载、多重继承和goto语句等。增加了字节码、虚拟机、垃圾(内存)回收(garbage collection)、接口、GUI支持、多线程、异常处理、网络编程、各种类库等内容。
Java语言的跨平台和安全性,都依赖于它的JVM(Java Virtual Machine,爪哇虚拟机)。Java是一种编译型解释语言(对比:Fortran/Pascal/C/C++为编译语言,早期的Basic为解释语言),它先将Java源代码编译成专用的字节码(bytecode),再利用各个计算机平台上安装的JVM来解释并运行这些字节码。
Java通过取消指针和添加虚拟机,在增强了安全性的同时,也具备了跨平台的能力。但这些却是以牺牲编程的灵活性和程序的运行效率为代价换来的。所以,Java并不太适合于,对运行效率要求很高的本地(客户端)的(系统)编程。
开始时,Java虽然从C++继承了大量面向过程和面向对象的特征,但是并没有继承模板。只是到了JDK 1.5(Java SE 5.0,2004年9月30日推出)才加入对编译时静态绑定的泛型编程的支持。所以,Java现在与C++一样,也可以算是一种同时具有面向过程、面向对象和泛型编程的特征的混合型程序设计语言。
l 简单(Simple)——易学好用,继承了C/C++的语法和许多C++的面向对象特征。去掉了C++中的一些复杂困难的东西(如头文件、预处理、结构、联合、多重继承、操作符重载、[模板]等),使语言更加精简。
l 安全(Secure)——为了适应分布式网络环境,采用基于策略(policy)的沙箱(sandbox)模型,在语言层次上实现了安全性。具有如下三个方面的安全保证:语言特性(包括检查数组边界、限制类型转换、取消指针变量等)、资源访问控制(包括本地文件系统访问和连接访问网络等)和代码数字签名(通过数字签名确认源代码源以及代码是否完整)。
l 可移植(Portable)——定长的整数型类型,如int始终为32位,而C/C++中的int在Win16、Win32和Win64平台上分别为16位、32位和64位。固定的多字节整数二进制格式,采用统一的高位字节在前的方案(Intel CPU中的多字节整数的低位字节在前)。统一的用户界面(AWT/Swing [/SWT])。
l 面向对象(Object Oriented)——由于没有设计C++时兼容C的负担,从零开始设计的Java具有更纯洁的OOP特性。但是为了高性能,仍然保留了基本数据类型的非对象性。在OOP方面与C++的区别主要有单一类继承、接口、反射等。
l 健壮(Robust)——异构网络环境要求程序可靠运行。采用的措施有:避免内存错误(取消指针变量、提供内存管理——自动分配和释放内存[垃圾内存自动回收])、强制异常处理等,可对潜在的问题进行早期和动态的检查。
l 多线程(Multithreaded)——交互式网络化的多任务程序(如Web服务器)及多核CPU都要求多线程。Java提供了语言级的多线程支持,JVM也具有成熟的线程同步解决方案。
l 体系结构中立(Architecture Neutral)——通过JVM和字节码,Java实现了体系结构中立(与计算机硬件和操作系统无关)。跨平台是Java最重要的特征和最大的优点,“一次编写、到处运行”(Write Once, Run Anywhere,WORA)是Java的口号。正是跨平台的特点,使得Java语言特别适合于网络编程。
l 解释型(Interpreted)——与Fortran、Pascal、C/C++等编译型高级语言不同,Java是一种(编译性)解释语言:Java语言的源程序(*.java),先通过Java编译器编译成特殊的字节码(*.class),然后由目标机上JVM将字节码解释(翻译成本机CPU指令代码)执行。解释型是JVM的需要,也是跨平台的基础。
l 高性能(High Performance)——与追求速度的编译型语言相比,解释型语言在本质上是低性能的,这也是Java语言跨平台的代价。为了克服Java程序运行慢的缺点,可用JIT(Just-In-Time,及时)编译器,在程序的第一次运行时将(部分)字节码先转换成本机代码,以提高Java程序的运行速度。
l 分布式(Distributed)或理解网络(Network-Savvy)——Java是为因特网的分布式环境设计的,所以它支持TCP/IP协议、URL资源访问和RMI(Remote Method Invocation,远程方法调用)。
l 动态(Dynamic)——Java程序带有大量运行时的类型信息,用于在运行时的验证和解决对象的访问问题。这使得在安全有效的方式下动态连接代码成为可能。程序员可从因特网上下载代码运行(浏览器),可全面控制对象的行为,如在特定的系统要求下,在运行时分析对象(如GUI构建器、智能调试器、可插式组件、对象数据库等)。
Java语言主要优势是跨平台和安全(及类库丰富),主要缺点是运行速度较慢和图形界面较差,主要适用范围是(服务器端的)网络编程。
1)JDK包含四种版本:
l Java SE(Standard Edition,标准版)——是JDK的主要版本,主要用于客户机。也是Java EE的基础。
l Java EE(Enterprise Edition,企业版)——是JDK的重要版本,用于服务器编程,建立在Java SE之上。它与Java SE的区别在于它增加了提供开发和部署容错、分布式、多层Java软件能力的库,主要基于运行在应用服务器上的模块化组件。
l Java ME(Micro Edition,微型版)——为Java API的一个子集,用于开发资源受限的小型设备上使用的软件,目标设备包括工业控制和手机与机顶盒中的自动装置。
l Java Card(智能卡版)——与允许小应用程序(applets)在智能卡(smart cards)以及类似的小内存设备(如SIM手机卡和ATM银行卡)上安全地运行相关,是Java在嵌入式设备中的最小目标。
2)JDK演化
下面是JDK主要版本的演化历史:
l 1995年5月23日,Sun发布Java语言。
l 1996年1月23日,JDK 1.0诞生。1996年4月,JDK 1.0.2发布,增加了对数据库连接及分布式对象的支持。1997年2月19日,JDK 1.1发布,增加了健壮的事件模型、内部类、反射机制、支持国际化、推出JavaBeans组件模型和JDBC。
l 1998年12月8日,JDK 1.2发布,增加了Swing、集合框架、Java IDL,将Swing图形API集成进核心类中,发布Java插件程序(Plug-in)、JVM装备了JIT编译器,提高了虚拟机和编程工具的性能。经过此次重大改进后的Java被更名为Java 2,表示第二代Java,标志Java新时代的开始。Sun还将JDK分成三个版本:标准版(J2SE,Java 2 Platform Standard Edition)、企业版(J2EE,Java 2 Platform Enterprise Edition)和微型版(J2ME,Java 2 Platform Micro Edition)。
l 1999年12月,J2EE 1.2 SDK诞生,引入服务器端的组件技术EJB(Enterprise JavaBeans,企业爪哇豆)、网页编程工具JSP(JavaServer Page, Java,服务器网页)、Servlet(小服务器程序)和 Container(容器)等。2000年5月8日,JDK 1.3发布,增加了JNDI(Java Naming and Directory Interface,Java命名与目录接口)、RMI over IIOP、CORBA ORB、Java Sound和JPDA(Java Platform Debugger Architecture,Java平台调试器架构)等,绑定了HotSpot JVM。2001年9月24日,J2EE 1.3发布,新特性有:EJB2.0、JMS (Java Message Service,Java信息服务)、用JAXP (Java API XML处理)建立JSP、IIOP(Internet Inter-Orb Protocol,因特网交互ORB协议,其中ORB=Object Request Brokers,对象请求代理)、EJB QL(Query Language,查询语言)。2002年2月6日,JDK 1.4发布,增加了assert关键字、涟式异常、基于通道的I/O子系统NIO(New I/O)、改进了集合框架和联网类。2003年11月24日,J2EE 1.4发布,新特性有:JSP 2.0 EL(Element Language,元素语言)、JSP标准标记库、Web服务、EJB计时器服务、EJB QL增强功能、增强的JMS API。
l 2004年9月30日,J2SE 1.5发布,增加了泛型、注解、自动装箱和拆箱、枚举、for-each循环、变长变量、静态导入、格式化I/O、并发实用工具等,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,Java 2更名为Java 5,J2SE 1.5也更名为Java SE 5.0。2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名,以取消其中的数字“2”:J2EE更名为Java EE,J2SE更名为Java SE,J2ME更名为Java ME。
l 2006年5月11日,Java EE 5发布,新特性有:EJB 3.0、JSF(JavaServer Faces,Java服务器外观)、Java持久性、JSTL(JSP Standard Tag Library,JSP 标准标签库),Java最新的Web服务API:JAX-WS(Java API for XML-Based Web Services,针对基于XML的Web服务的Java API)2.0、JAXB(Java Architecture for XML Binding,针对XML绑定的Java架构)2.0、WS Metadata(Web Services Metadata for the Java Platform,针对Java平台的Web服务元数据)。
l 2006年12月11日,Java SE 6发布,新特性包括:Web服务API、支持脚本语言、更新了JDBC、 新的桌面API(如Swingworker、JTable排序与过滤和GroupLayout等)、监视和管理、编译器访问、插件式的注释(在代码中定义自己的注释并处理它)、桌面部署、安全(与像PKI、Java GSS、Kerberos和LDAP这样的服务集成起来)、性能提高一倍。目前的最新版是2009年3月25日推出的JDK SE 6 Update 13。
l 2009年12月10日,Java EE 6发布,新特性有:可扩展性和协议子集、Web Beans 1.0、JAX-RS 1.1、Servlet 3.0、JASPIC(Java Authentication Service Provider Interface for Containers,Java验证服务提供者容器接口)。被砍掉的API有:JAX-RPC、EJB 2.x Entity Beans CMP、JAXR、Java EE应用开发(JSR-88)、Java EE管理(JSR-77)。
l 2011年7月28日,Java SE 7发布,主要新特性有:模块化(module关键字、与OSGi绑定系统的相互操作性)、NIO 2、数据和时间API、fork/join并行处理包、扩展Java类型标注注释、Swing应用程序框架、JMX(Java管理扩展)2.0、动态语言支持。其他可能的新特性有:闭包(Closure)、对Strings转换状态的支持、多异常捕获机制、对集合的方括号标记、带有类型推导的简洁构造器、其他语言的名称调用。
1)Java SE平台——图17-3是Java SE 7平台示意图。
a) 详细 b) 简略
图17-3 Java SE 7平台
可见,Java SE平台的底层为各种操作系统,包括主流的Windows、Linux、MacOS和各类Unix;上面是JVM层,包括客户端和服务器端的HotSpot(热点,Sun公司的JVM实现)虚拟机;再上面是Java SE API的各种类库;API类库的上面是RIA(Rich Internet Applications,富互联网应用)。Java运行环境JRE包括JVM、API和开发技术。JRE上面是工具及其API、最上层为Java语言本身。JDK包括JRE及其上面的两层。
2)Java EE平台——Java EE建立在Java SE之上,图17-4是Java EE 6的体系结构图。
图17-4 Java EE 6体系结构图
C#(C Sharp)语言源自C++(数字符# Sharp升半音符,可以视为由4个加号组成,相当于(C++)++),模仿了Java(微软不承认这点)。C# 的很多内容都与Java相似,例如虚拟机、垃圾内存收集、接口、GUI支持。但是,与Java不同的是,C# 保留了指针,不过限制了指针的使用。C# 还引入了值类型、引用类型、元数据、装箱和拆箱转换等概念(这些概念在最新版本的Java中也有,在C++0x中也会引入与元数据类似的“概念”)。
C# 是一种面向对象的程序设计语言,最初是作为.NET的一部分而开发的。换句话说,.NET是围绕C# 而开发的。C# 的面向过程和对象的语法,是基于C++的,但也包含了另外几种程序设计语言的特征(其中最显著的是Delphi、Visual Basic和Java),C# 特别强调简易性(如所需符号比C++的少、所需修饰比Java的少)。
从某种意义上来说,C# 是最直接地反映了底层CLI的一种程序设计语言,它非常依赖于.NET框架,因为它被特地设计成能充分利用CLI所提供的特征的一种语言。例如,绝大多数的C# 内置类型,全都对应于CLI框架所实现的值类型(value-types)。
用C# 编写的应用程序,需要CLR的一个实现,才能运行。这与Java的虚拟机JVM有点相似,但是与Java不同的是,CLI应用程序要被编译两遍,第1遍由C# 编译器,将C# 源程序被编译成平台抽象字节码,即IL(Intermediate Language,中间语言)代码,存放在PE(Portable Executable,可移植的可执行)文件中(似Java的.class);第2遍,在应用程序首次运行时,由CLR的JIT(Just-In-Time即时/实时)编译器,将IL代码编译成本地客户端的机器代码(.exe)。
C# 语言,已于2001年12月成为欧洲标准:ECMA-334 C# Language Specification(C#语言规范),2002年12月、2005年6月和2006年6月又分别推出ECMA-334的第2版、第3版和第4版。2003年4月C# 成为国际标准:ISO/IEC 23270:2003 Information technology -- C# Language Specification(信息技术——C# 语言规范),2006年8月23日又推出了第2版ISO/IEC 23270:2006。
l C# 被确定为一种简单、现代、通用、面向对象的编程语言。
l 该语言及其实现应该为强类型检查、数组界限检查、发现使用未初始化变量、自动垃圾回收等软件工程原则提供支持。
l 该语言适用于分布式环境中的软件组件开发。
l 源代码的可移植性是非常重要的,程序员的转移也同样重要,特别是对那些已经非常熟悉C和C++的程序员。
l 支持国际化是非常重要的。
l C# 适用于为主机和嵌入式系统编写应用程序,范围从非常大的复杂操作系统,到非常小的专用功能。
l 虽然C# 应用程序致力于在内存和处理能力需求上是经济的,但是该语言并不想与C或汇编语言在性能和大小方面进行直接竞争。
l 简单——相对于复杂的C++,C# 的语言简单,开发高效。例如,在安全上下文中,C# 没有指针,不许直接存取内存。用统一的“.”操作符,代替了C++中的“::”、“.”和“->”操作符。使用统一的类型系统,抛弃了C++的多变类型系统(如int的字节数、0/1转布尔值等)。
l 现代——很大程度上由.NET框架体现。如支持组件编程、泛型编程、分布式计算、XML处理和B/S应用等。
l 面向对象——C# 全面支持面向对象的功能。与C++相比,C# 没有全局变量和全局函数等,所有的代码都必须封装在类中(甚至包括入口函数[方法]Main)、不能重写非虚拟的方法、增加了访问修饰符internal、不支持多重类继承(似Java,用多重接口实现来代替)。
l 类型安全——C# 实施严格类型安全,如取消了不安全的类型转换,不允许使用未初始化的变量,进行边界检查(如不让数组越界)。
l 1.0——随.NET框架1.0和Visual Studio .NET(2002)于2002年2月13日发布。
l 1.5——随.NET框架1.1和Visual Studio .NET 2003于2003年5月20日发布。
l 2.0——随.NET框架2.0和Visual Studio 2005于2005年11月7日发布。
l 3.0——随.NET框架3.0和Visual Studio 2008于2007年11月16日发布。
l 4.0——随.NET框架4.0和Visual Studio 2010于2010年4月12日发布。
4.0版的中文版规范见Visual Studio 2010中文版安装目录中的Word文档,默认路径为:“C:\Program Files\Microsoft Visual Studio 10.0\VC#\Specifications\2052\CSharp Language Specification.doc”。
下面罗列Visual C# 的各个主要版本的新增的特点和功能。
1)C# 1.0——与C和C++比较,C# 在许多方面有所限制和增强,包括:
l 指针——C# 是真正支持指针,但是其指针只能在非安全作用域中使用,而只有具有适当权限的程序,才可以执行标记为非安全的代码。绝大多数对象的访问是通过安全的引用(references)来进行的,而引用是不会造成无效的,而且大多数算法都是要进行溢出检查的。一个非安全指针,不仅可以指向值类型,还可以指向子类和System.Object。也可以使用指针(System.IntPtr)来编写安全代码。
l 托管(managed,受控)——在C# 中,托管内存不能显式释放,取而代之的是(当再没有内存的引用存在时的)垃圾收集。但是,引用非托管资源的对象,例如HBRUSH,是可以通过标准的IDisposable接口的指示来释放指定内存的。
l 多重继承——在C# 中多重继承被禁止(尽管一个类可以实现任意数目的接口,这点似Java),这样做的目的是为了避免复杂性和“依存地狱”,也是为了简化对CLI的结构需求。
l 转换——C# 比C++更类型安全,唯一的默认隐式转换也是安全转换,例如加宽整数和从一个派生类型转换到一个基类(这是在JIT编译期间间接强制进行的)。在布尔和整数之间、枚举和整数之间都不存在隐式转换,而且任何用户定义的隐式转换,都必须显式地标出。
l 数组声明——和C/C++的数组声明的语法不同,C# 中用“int[] a = new int[5];”代替了C/C++的“int a[5];”。
l 枚举——C# 中的枚举被放入它们自己的命名空间。
l 特性——可在C# 中可以使用特性(properties,属性集),访问类似于C++中成员域,与VB相似。
l 类型反射与发现——在C# 中可以使用完整的类型反射与发现,这些都会用到元数据所提供的信息。
l 模板——为了简单性,C# 1.0中不支持模板等泛型编程技术。
2)C# 1.5版的新增功能——/** */ 文档注释符、#line hidden预处理指令、/nowarn和/nostdlib编译指令、Web窗体、XML Web服务、ADO.NET、可用Windows窗体和框架创建分布式应用程序的表示层、可创建各种Windows和ASP.NET Web应用程序和控件的项目模板、可使用非可视组件和相关功能,将消息队列、事件日志和性能计时器等资源合并到应用程序中、通过组件设计器和框架类为创建组件提供RAD支持。
3)C# 2.0的新特征:
l 部分类——一个类可分开到多个文件中实现。
l 泛型——C# 从2.0起,开始支持泛型或参数类型。C# 还支持一些C++模板不支持的特性,例如对泛型参数的类型约束。另一方面,C# 的表达式不能用作泛型参数,而这在C++中却是允许的。C# 的参数化的类型为虚拟机的首个类对象,允许优化和保存类型信息,这一点与Java不同。
l 关键字yield——迭代器的一种新形式,可通过功能类型的关键字yield,来使用协同例程。
l 匿名委托——提供了闭包功能。
l 结合运算符??——返回表中的第一个非空值,例如:
object nullObj = null;
object obj = new Object();
return nullObj ?? obj // returns obj;
l 可空值类型——可空值类型由问号?来标记(例如,int? i = null;),它可以改善与SQL数据库的交互。
4)C# 3.0的新特征:
l LINQ(Language Integrated Query,语言集成查询)——"from, where, select"上下文敏感的关键字,允许在SQL、XML、集合等之间进行查询。
l 对象初始化——如Customer c = new Customer(); c.Name = "James"; 可被写成Customer c = new Customer { Name="James" };。
l 集合初始化——如MyList list = new MyList(); list.Add(1); list.Add(2); 可被写成MyList list = new MyList { 1, 2 };。
l 匿名类型——如var x = new { Name = "James" };。
l 局部变量类型推论——如var x = "hello";等价于string x = "hello"; 该特性在匿名类型变量的声明中需要。
l 隐含类型的数组——数组的类型现在可以省略,所以int[] arr = new int[] { 1, 2, 3 }; 现在可以写成var arr = new[] { 1, 2, 3 };。
l λ表达式——如listOfFoo.Where(delegate(Foo x) { return x.Size > 10; }) 可被写成 listOfFoo.Where(x => x.Size > 10);。
l 编译器推断——翻译λ表达式到强类型函数或强类型表达式树。
l 自动属性——编译器会自动生成一个私有实例变量,而且给出适当的获取器和设置器代码,例如public string Name { get; private set; };。
l 扩展方法——通过在另一个静态类的一个方法的首个参数中包含this关键字,来将方法添加到类中。如
public static class IntExtensions {
public static void PrintPlusOne(this int x) {
Console.WriteLine(x + 1);
}
}
int foo = 0;
foo.PrintPlusOne();
l 部分方法——允许代码生成器生成方法的声明作为扩展点,如果有人在另一个部分类中实际实现它,则其只被包含在源代码编译中。
5)4.0版增加的新特性主要有:
l 动态支持——通过引进新类型dynamic来提供对动态类型延迟绑定的支持。
l Office可编程性——通过添加命名和可选的参数、dynamic类型、索引属性和可选的ref修饰符,大大增强了访问(包括Office自动化API在内的)COM接口的能力。
l 类型等价支持——可配置应用程序的内置类型信息,以代替从PIA(Primary Interop Assembly,主互操作程序集)导入的类型信息。
l 协变与逆变——协变(covariance)是你能够使用更多的派生类型而不是由泛型参数指定,而逆变(contravariance)则让你使用更少的派生类型。
表17-4 C++、Java与C# 的比较
C# 语言的相关帮助文档位于MSND的“帮助查看器主页\Visual Studio 2010\Visual Studio\Visual Studio语言\ Visual Basic和Visual C# \Visual C#”目录中。
17.1 OOP语言
OOP(Object-Oriented Programming,面向对象编程)是目前主流的编程技术,本节介绍其中最重要的C++、Java和C#,重点是它们三者之间的关系与对比。17.1.1 OOP概述
程序设计语言,在思想上发展很快,在技术上也是不断创新。经历了手工操作(控制面板)、机器语言、汇编语言、宏汇编语言、高级语言和第4代语言等多个阶段。OOP只是在高级程序设计语言中,流行的多种编程技术之一。1.基本特征
面向对象编程的三个基本特征是:l 封装(encapsulation)—— 是对问题的抽象,以达到信息隐藏的目的。通过类和对象,将属性(数据/变量)和方法(操作/函数)封装在一个黑箱内,将细节隐藏起来。既可以保护数据不被他人恶意或大意地修改,又能防止其他程序员编写高耦合度的代码,还方便了今后对对象内部的修改和升级。
l 继承(inheritance)—— 利用问题和事物的相似性,通过类的(多层)继承机制,可以达到减少软件开发难度和重用已有对象的属性和方法之目的。
l 多态(polymorphism)—— 同一操作或响应,可以同时针对多个可选的类型和对象,并且能够自动在它们中间选择最合适的来进行。多态可以分为:
n 编译时多态:包括函数和运算符的重载(overload),通过早期的静态绑定(binding)来实现。
n 运行时多态:通过继承结合晚期动态绑定来实现——用同一基类的指针,可以访问各个不同派生类中的同名方法和函数。
2.基本概念
面向对象编程的基本概念(类似的概念多、表达不够准确、各处的用法各异):l 抽象(abstraction)—— 一个问题的本质特征。忽略一个对象的细节,致力于一般的合适级别的程序能力。抽象是在事物的周围绘制透明的盒子,是定义事物接口的行为。
l 信息隐藏(information hiding)—— 限制外部对类属性(数据成员)的访问。
l 封装(encapsulation)—— 将某种保密和隐匿应用到类中的数据和一些方法(函数或子例程)上。封装确保一个对象只能通过确定的渠道(即类的公用方法)才可被修改。封装意味着将数据及相关函数打包到一个单一的单元(类)中。每个对象暴露一个接口(公用方法),规定其他对象可以如何读取或修改它。
l 类(class)—— 问题的抽象,对象的模版,接口的实现。例如int是整数的模版、CPoint是点的模版。
l 对象(object)—— 问题中的事物,类的实例(变量),拥有数据和功能。若int i; CPoint p; 则i是int的实例变量,p是CPoint的实例对象。
l 实例(instance)—— 一个对象,是某个类的示例。
l 实例化(intantiate)—— 由类定义创建对象。
l 继承(inheritance)—— 创建子类的机制,一种“is a”或“is like”关系,形成类层次结构。是OOP中对象重用的核心概念。
l 子类(subclass)—— 继承自另一个类的派生类(derived class)。
l 超类(superclass父类)—— 被另一个类继承的基类(base class)。
l 虚函数(virtual function)—— 可以在子类中被覆盖(override)的函数。
l 纯虚函数(pure virtual function)—— 只有声明没有定义的函数,必须在派生类中被覆盖。
l 抽象类(abstract class)—— 至少包含一个纯虚函数的类,不能被实例化。
l 具体类(concrete class)—— 可以实例化的类,不包含纯虚函数。
l 接口(interface)—— 纯抽象的类,类的模版。接口中的所有的方法(成员函数)都只有声明,没有定义(都是纯虚函数)。必须在接口的派生类中,实现接口的全部方法。接口可以看成是一个,定义了一套内部行为的,若干操作特征标记的集合。(在C++中,也把类的公用成员函数叫做接口)。
l 属性(attribute)—— 类和接口中的数据信息,又叫数据成员(data member)或成员变量(member variable)。属性是类知道的事物。
l 方法(method)—— 类操作的实现,又叫成员函数(member function)。方法是类要做的事情。(在C++中,也把虚函数叫做方法)。
l 重载(overload)—— 一个类中的,参数的数目、类型或排列顺序不同的多个同名方法(成员函数)。
l 覆盖(override)—— 在子类中重新定义(与父类中的定义有区别的)属性和方法。
l 持久化(persistence)—— 将对象存入永久存储中(如文件、数据库等)。
l 持久对象(persistent object)—— 存入永久存储的对象。
3.简史
计算机科学中,对象的概念和实例,最早于1960年出现在MIT的PDP-1中,1963年Ivan Sutherland在其博士论文中所编写的计算机程序Sketchpad中应用了对象技术。但是,作为一种程序设计方法,OOP最早出现在1967年,由挪威计算中心的Ole-Johan Dahl和Kristen Nygaard,在他们发明的Simula67语言中首先引入的。Simula语言是在Algol60基础上,加入了对象、类和继承等概念后开发出来的,它在OOP的历史中占有重要地位。
Smalltalk是1970年代,由美国施乐(Xerox)公司PARC(Palo Alto Research Center帕洛阿尔托研究中心)的Alan Kay、Dan Ingalls、Ted Kaehler和Adele Goldberg等人,开发的一种面向对象和动态类型的交互式程序设计语言。它受到了Sketchpad和Simula的影响,但是Smalltalk被设计成一个全动态系统的纯OOP,程序的所有成分都是对象(但是,为了提高运行效率,C++/Java/C# 中的基本数据类型都不是对象),对象可以被动态地创建、修改和销毁。在Smalltalk中还创造出词汇“'inheritance(继承)”和“Object-oriented programming(面向对象的程序设计)”。
使OOP走向辉煌的是1985年发明的C++,随着GUI(Graphical User Interfaces,图形用户界面)的流行,C++的地位更加巩固。但是,因特网的发展和万维网的流行,又催生了另一个重要的OOP语言——Java(1995年)。为了与Sun公司的Java竞争,微软公司也推出了一种用于.NET组件编程环境的新OOP语言——C#(2000)。
4.语言谱系图
C语言系列的发展简图如图17-1所示。C语言是专门为编写操作系统而发明的,是一种面向系统编程的中级语言。C++是带类的C,是为面向对象编程而设计的。Java最初是为嵌入式设备创建的,后来改造成适用于跨平台的网络编程。C# 则是专门为面向组件编程的.NET而设计的一种源于C++、类似于Java的OOP语言。图17-1 C语言系列
图17-2给出了可用于.NET编程的主要高级语言(包括C#、VB、C++/CLI、J# 和F# 等)的发展脉络。
17.1.2 C++
C++是第一个广泛流行的OOP语言,C++是在C语言的基础上,添加了面向对象和泛型编程的功能后,扩展而成的。C语言是C++的子集,C++是C语言的超集。
Ken Thompson (左) 与Dennis Ritchie
1.C
C语言是为编写操作系统而创建的一门高级语言,语法简洁、表达自由、库函数丰富,具有强大的功能和极大的灵活性,适用于高效的系统编程。1)起源
1970年,AT&T贝尔实验室的美国计算机科学家Ken Thompson和Dennis Ritchie等人在DEC公司的PDP-7小型机上开发出了Unix操作系统,最初的实现是用汇编语言写成的。为了使Unix操作系统具有可移植性,迫切需要一种高级语言工具。为此, Dennis Ritchie以B语言为基础,参考了Algol68,于1972年设计出了C语言。1973年他们用C语言重写了Unix,1975年又利用C语言将Unix移植到了PDP-11上。
图17-2 主要编程语言工具的发展
2)特点
C语言是一种可移植的系统语言,拥有充分的控制语句和数据结构功能,包含丰富的操作符,从而能够提供强大的表达能力,可以用于许多不同的应用领域。但是,C语言并不是面向科学家和计算机业余爱好者的,而是专门为程序员设计的。
为了进行高效的系统编程,C语言提供了强大的功能和极大的灵活性。与其它高级语言相比,C语言的语法简洁、表达自由、库函数丰富。如果将编程比作造房子,则Fortran和Basic等语言就像一些已经预先造好的大预制件,使用起来简单快捷,但是灵活性差、且功能有限,只能造某些固定模式的房屋;而C语言就像一块块的小砖,使用起来虽然繁琐,但是灵活性强、而且功能无限,能够造各式各样的建筑物,不过这就要求C语言程序员具有很高的专业水平。
因此,C语言假设使用者都是计算机专家,采取的是程序员负责制。它不进行完备的类型检查,对数组越界也没有限制。为了进行高效的系统编程,C语言还提供了指针和指针运算,程序员可以随意操作全部内存,任意修改任何内容。
表达的自由性和操作的任意性,也给C语言带来了很多编程问题和安全隐患。特别是C语言的++/--运算符和指针运算,更是倍受指责。
与其它高级语言相比,C语言提供了一些低级语言特征,更面向机器。所以,也有人称C语言是介于高级语言和低级语言之间的一种中级语言。
3)标准
(1)K&R C
开始的很多年,C语言没有国际标准,只有一个事实标准——1978年Brian Kemighan和Dennis Ritchie编写的《C程序设计语言》(The C Programming Language)一书,通常称其为K&R C或经典C。该书的附录“C参考手册”(C Reference Manual)成为了C语言的实现指南,但是书中缺少对库函数标准的描述,一般以Unix实现的库函数所为事实标准。需要说明的是,因为C语言的语法成分简单,很多基本功能(例如I/O语句)都是靠库函数来实现的。所以,C语言比其它高级语言更依赖于库函数。
(2)C90
1983年ANSI(American National Standards Institute 美国国家标准协会)设立了一个X3J11小组,着手进行C语言的标准化。并最终于1989年推出ANSI C (ANSI X3.159-1989),1990年它又成为国际标准ISO C(ISO/IEC 9899:1990 Programming languages – C,程序设计语言——C),原来叫做ANSI C或ISO C,现在通常称其为C89或C90。
C90对K&R C的主要改变是,增加了函数原型(prototype),强调对函数的输入参数进行严格的类型检查;允许将结构本身作为参数传递给函数(原来只允许传地址);并补充定义了C语言的标准函数库。增加了关键字:const(常型变量)、enum(枚举类型)、signed(有符号的,例如signed char)、void(空/无,可用于函数返回值和形参、通用指针类型)、volatile(易变变量,防止编译器错误的优化)等。还增加了预处理指令:#elif(else if)、#error(错误,强制编译停止)、#line(修改当前行号和源文件名)、#pragma(附注/编译指令,编译器定义的与实现有关的指令)。
(3)C99
对C90的修订工作开始于1994年,在ISO C++(1998)标准推出之后,ISO又于1999年12月16日,推出了C语言标准的第2版:ISO/IEC 9899:1999 Programming languages – C(程序设计语言——C),一般称其为C99。
C99主要的修订目标有三点:①支持国际化编程,引入了支持国际字符集Unicode的数据类型和库函数;②修正原有版本的明显缺点。如整数的移植方法,例如int8_t、int16_t、int32_t和int64_t等类型;③针对科学和工程的需要,改进计算的实用性。例如添加了复数类型和新数学函数。
C99对C89/C90的具体修改有:①增加了C++的//注释风格:原来C语言只支持多行注释:/*……*/,C99现在也识别单行注释:// ……。②增加了关键字:inline(内联函数)、restrict(限制)、_Bool(布尔类型)、_Complex(复数)、_Imaginary(虚数)。③增加了数据类型:(unsigned) long long [int](64位整数)(对应的打印输出格式为%lld或%llu)。④定义了可移植整数类型,如:int8_t、int16_t、int32_t、int64_t,uint8_t、uint16_t、uint32_t、uint64_t;intptr_t、uintptr_t。以及表示对应类型常量的方法,如INT8_C(128)、INT32_C(1234)。⑤增加了浮点常量的十六进制格式:p或P表示后跟二进制指数(的十进制值)。例如:0xa.1cp10 = (10 + 1/16 + 12/256) * 210 = 10352.0。⑥增加了浮点数的十六进制打印格式符:%a或%A(代替十进制的%e或%E)、%La或%LA(代替十进制的%Le或%LE)。⑦可指定初始化的条目(未被初始化的条目全被置为0),如:int days[12] = {31, 28, [4] = 31, 30, 31, [1] = 29};⑧支持变长数组(即可用变量来定义数组的大小),如:float a[n], a2[m][n];⑨允许在代码块的任何地方定义变量(似C++)。⑩允许在结构的最后定义一个大小可伸缩的弹性数组成员,可以用于结构指针,根据允许情况来动态分配内存。但是,C99增加的新特性中,有许多当前的C++标准还不支持。
2.C++起源
作为结构化的面向过程的编程语言,C已经是非常优秀的了,它简单、高效、灵活、功能强大。但是,随着软件开发的规模越来越大,所针对的问题和系统越来越复杂,对软件维护和重用的需求越来越高。仅仅靠面向过程的编程技术,就显得有点力不从心了。因此,针对问题的面向对象编程技术,就应运而生。1985年,贝尔实验室的计算机科学家,丹麦人Bjarne Stroustrup在Simula 67的启发下,对C语言进行了扩充,在保留C语言优点的基础上,添加了面向对象的特征和功能后,将C语言扩展成为带类的C (C with Classes),1983年Rick Mascitti将其命名为C++。
1985年10月Bjarne Stroustrup实现了C++的第一个商用版本,与此同时他还出版了《The C++ Programming Language(C++程序设计语言)》的第1版,成为C++的事实标准。C++的第1版,提供了面向对象程序设计的基本特征:类和对象、单继承、虚函数、公有/私有成员的访问控制、函数重载等。
1989年推出C++的第2版,增加了多继承、抽象类、静态和常型成员函数、保护成员的访问控制、运算符重载等新特性,促使C++语言流行起来。1990年1月1日Margaret A. Ellis和Bjarne Stroustrup出版了《The Annotated C++ Reference Manual(带注释的C++参考手册)》。1991年Bjarne Stroustrup推出了《C++程序设计语言》的第2版:The C++ Programming Language (2nd edition)。
1993年推出C++的第3版,增加了模板、异常处理和嵌套类等新特性.1994年3月29日Bjarne Stroustrup出版了《The Design and Evolution of C++(C++的设计和演化)》。1997年Bjarne Stroustrup推出了《C++程序设计语言》的第3版:The C++ Programming Language (3rd edition)。
3.C++标准
1994年7月,ANSI/ISO C++标准委员会,通过了Alexander Stepanov提出的泛型编程方案STL(Standard Template Library,标准模板库)。1998年9月1日C++成为国际标准(ISO/IEC 14882:1998 Programming languages -- C++,程序设计语言——C++),添加的新特性主要有:命名空间、新的强制类型转换、布尔类型和STL。2000年2月15日Bjarne Stroustrup推出了《C++程序设计语言》的特别版:The C++ Programming Language (Special Edition)。
2003年10月15日ISO推出了C++标准的第2版ISO/IEC 14882:2003,该版本在语法特征上没有什么变化,只是纠正了原版的各种错误,并进行了一些技术上的修订。
2007年11月15日ISO又推出了C++的扩展库标准(TR1,技术报告1):ISO/IEC TR 19768:2007 Information technology -- Programming languages -- Technical Report on C++ Library Extensions。微软公司在其Visual Studio 2008 SP1中的Visual C++ 2008功能包(Feature Pack)支持此TR1。
2010年9月3日ISO还推出了C++的支持数学特殊函数的扩展库标准:ISO/IEC 29124:2010 Information technology -- Programming languages, their environments and system software interfaces -- Extensions to the C++ Library to support mathematical special functions。
2011年8月12日ISO通过了新版C++语言标准——C++11(ISO/IEC 14882:2011 Information technology -- Programming languages -- C++, Edition 3),它包含了几个核心语言新功能,并扩展了C++标准库,还合并了大多数TR1库(数学特殊函数库除外)。
现在的C++已经成为了一种同时具有面向过程、面向对象和泛型编程的特征,功能强大、运行高效、使用广泛的混合型程序设计语言。
4.C++11
C++11是ISO于2011年8月12日推出的C++语言新标准。先前一直叫做C++0x,原计划在零几年推出。从2004年2月7日起开始,陆续推出了多个工作草案,2011年4月11日推出了最终草案国际标准FDIS (Final Draft International Standard) http://www.open-std.org/ JTC1/SC22/WG21/prot/14882fdis/ n3290.pdf,2011年9月1日发布了最终标准文档(需352瑞士法郎,约合2362元人民币)。新的C++标准大幅改善了C++内核语言领域,包括多线程支持、泛型编程、统一的初始化,以及表现的加强等。具体的改进有:增加了右值引用(typename &&)、扩展了常数表达式(引入新关键字constexpr)、放宽了关于POD(Plain Old Data,简单旧数据)型结构的定义、引入了外部模板概念(extern template class)、可将初始化列表的概念绑定到类型上(std::initializer_list)、提供了一种统一语法来初始化任意对象、可进行类型推导(auto)、支持以范围为基础的for循环、允许定义λ(lambda)函数、引进了一种新的函数定义与声明的语法(template< typename LHS, typename RHS> [] AddingFunc(const LHS &lhs, const RHS &rhs) -> decltype(lhs+rhs) {return lhs + rhs;})、允许派生类手动继承基类的构造函数(using 基类名::基类名;)、引入了表示空指针常数的新关键字(nullptr)、引进了一种特别的枚举类(enum class)、关键字explicit修饰符可用于显示类型转换、可用于模板类型的别名定义(template<…> using TypedefName = SomeType<…>;)、移除了所有对 union 的使用限制(除了其成员仍然不能是引用类型)、加入了(允许任意个数和任意类别的模板实参的)变长参数模板、增加了两种新的字符类别char16_t(UTF-16)和char32_t(UTF-32)(传统的char类型对应于UTF-8)和新的字符串常量(u8"I'm a UTF-8 string."、u"This is a UTF-16 string."、U"This is a UTF-32 string.")、允许用户自定义新的常量修饰符(literal modifier)、引入了支持多线程编程的多任务存储器模型和线程区域的存储期限(thread_local)、可使用或禁用对象的默认函数(default和delete)、(同C99)增加了64位的整数类型(long long int)、增加了用于测试的静态声明(static_assert新关键字)、允许sizeof操作符作用在类的数据成员上(无须明确的对象)。
C++11标准程序库将引进了数个新功能(如线程功能、元组类型、散列表、正则表达式、通用智能[smart,灵巧]指针、可扩展随机数功能、包装引用、函数对象的多态包装、元程序的类型特性[trait]、用于计算函数对象返回类型的统一方法),其中许多可以在现行标准下实现,而另外一些则依赖于(或多或少)新的C++11内核语言机能。新的程序库的大部分被定义于于2005年发布发TR1(Library Technical Report,库技术报告),各式TR1的完全或部分实现原提供在命名空间std::tr1,现在C++11将其移置于命名空间std之下。
已被移除或是不包含在 C++11 标准的特色:
l 预计由 Technical Report 提供支持:模块、十进制类别、数学特殊函数。
l 延后讨论:概念(Concepts)、更完整或必备的垃圾回收支持、反射(Reflection)、宏域(Macro Scopes)。
l 将被移除或废弃的特色:循序点 (sequence point)、导出(export)、动态异常规范(dynamic exception specifications)、std::auto_ptr(被 std::unique_ptr 取代)、函数对象基类 (std::unary_function, std::binary_function)、函数指针适配器与类型成员指针适配器、绑定 (binder)类。
在VC2010中支持的C++11新特性有:lambda 表达式、rvalue 引用声明符(&&),以及auto(的新用途)、decltype、nullptr、static_assert 关键字等。
5.关键字
表17-1列出了标准C++11的73个关键字(keywords)。表17-1 C++关键字
alignas | continue | friend | register | true |
alignof | decltype | goto | reinterpret_cast | try |
asm | default | if | return | typedef |
auto | delete | inline | short | typeid |
bool | do | int | signed | typename |
break | double | long | sizeof | union |
case | dynamic_cast | mutable | static | unsigned |
catch | else | namespace | static_assert | using |
char | enum | new | static_cast | virtual |
char16_t | explicit | noexcept | struct | void |
char32_t | export | nullptr | switch | volatile |
class | extern | operator | template | wchar_t |
const | false | private | this | while |
constexpr | float | protected | thread_local | |
const_cast | for | public | throw |
表17-2 C++ 的保留标识符
and | and_eq | bitand | bitor | compl | not |
not_eq | or | or_eq | xor | xor_eq |
6.命名空间
在C++中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(命名空间),可以更好地控制标识符的作用域。(MFC中并没有使用命名空间,但是在GDI+、.NET框架、MC++和C++/CLI中,都大量使用了命名空间。)C语言标识符有局部(代码块{…},如复合语句和函数体)和全局两种作用域,C++在这二者之间引入了类作用域(如类变量和成员函数)。标准C++又在类和全局之间,新添加了命名空间这一个作用域级别。
命名空间是一种描述逻辑分组的机制,可以将按某些标准在逻辑上属于同一个集团的声明放在同一个命名空间中。命名空间可以是全局的,也可以位于另一个命名空间之中,但是不能位于类和代码块中。所以,在命名空间中声明的名称(标识符),默认具有外部链接特性(除非它引用了常量)。在所有命名空间之外,还存在一个全局命名空间,它对应于文件级的声明域。因此,在命名空间机制中,原来的全局变量,现在被认为位于全局命名空间中。
标准C++库(不包括标准C库)中所包含的所有内容(包括常量、变量、结构、类和函数等)都被定义在命名空间std(standard,标准)中了。
1)定义命名空间
有两种形式的命名空间——有名的和无名的,它们的定义方法分别为:
namespace 命名空间名 { // 有名命名空间
[声明序列]
}
namespace { // 无名命名空间
[声明序列]
}
命名空间的成员,是在命名空间定义中的花括号内声明了的名称。可以在命名空间的定义内,定义命名空间的成员(内部定义)。也可以只在命名空间的定义内声明成员,而在命名空间的定义之外,定义命名空间的成员(外部定义)。
命名空间成员的外部定义的格式为:
命名空间名::成员名 ……
注意:不能在命名空间的定义中声明(另一个嵌套的)子命名空间,只能在命名空间的定义中定义子命名空间。也不能直接使用“命名空间名::成员名 ……”定义方式,为命名空间添加新成员,而必须先在命名空间的定义中添加新成员的声明。另外,命名空间是开放的,即可以随时把新的成员名称加入到已有的命名空间之中去。方法是,多次声明和定义同一命名空间,每次添加自己的新成员和名称。例如:
namespace A { int i; void f(); } // 现在A有成员i和f()
namespace A { int j; void g(); } // 现在A有成员i、f()、j和g()
2)使用命名空间
使用命名空间的方法有三种:
l 作用域限定(直接使用解析运算符“::”)
对命名空间中成员的引用,需要使用命名空间的作用域解析运算符::。例如:
std::cout << "Hello, World!" << std::endl;
l using指令(using namespace)
为了省去每次调用命名空间成员和标准库的函数和对象时,都要添加“命名空间名::”和“std::”的麻烦,可以使用标准C++的using编译指令来简化对命名空间中的名称的使用。格式为:
using namespace 命名空间名[::子命名空间名……];
在这条语句之后,就可以直接使用该命名空间中的标识符,而不必写前面的命名空间定位部分。因为using指令,使所指定的整个命名空间中的所有成员都直接可用。例如:
using namespace std;
cout << "Hello, World!" << endl;
又例如(.NET框架):
using namespace System::Drawing::Imaging;
using namespace System::Window::Forms::Design::Behavior;
l using声明(using)
除了可以使用using编译指令(组合关键字using namespace)外,还可以使用using声明来简化对命名空间中的名称的使用。格式为:
using 命名空间名::[命名空间名::……]成员名;
注意,关键字using后面并没有跟关键字namespace,而且最后必须为命名空间的成员名(而在using编译指令的最后,必须为命名空间名)。
与using指令不同的是,using声明只是把命名空间的特定成员的名称,添加该声明所在的区域中,使得该成员可以不需要采用,(多级)命名空间的作用域解析运算符来定位,而直接被使用。但是该命名空间的其他成员,仍然需要作用域解析运算符来定位。例如:
using std::cout;
cout << "Hello, World!" << std::endl;
l 使用方法的比较(参见表17-3)
表17-3 命名空间使用方法的比较
方法 | 格式 | 特点 | 适用 |
作用域限定 | 命名空间::成员 | 安全、麻烦 | 个别成员的偶尔使用 |
using声明 | using 命名空间::成员 | 较安全、较麻烦 | 特定成员的经常使用 |
using指令 | using namespace 命名空间名 | 不安全、方便 | 大量成员的经常使用 |
但是,一般来说,使用using声明会更安全。因为,using声明只导入指定的名称,如果该名称与局部名称发生冲突,编译器会报错。而using指令导入整个命名空间中的所有成员的名称,包括那些可能根本用不到的名称,如果其中有名称与局部名称发生冲突,则编译器并不会发出任何警告信息,而只是用局部名去自动覆盖命名空间中的同名成员。特别是命名空间的开放性,使得一个命名空间的成员,可能分散在多个地方,程序员难以准确知道,别人到底为该命名空间添加了哪些名称。
虽然使用命名空间的方法有多种可供选择,但是不能贪图方便,一味使用using 指令,这样就完全背离了设计命名空间的初衷,也失去了命名空间所应该具有的防止名称冲突的功能。一般情况下,对偶尔使用的命名空间成员,应该使用命名空间的作用域解析运算符来直接给名称定位。而对一个大命名空间中的经常要使用的少数几个成员,提倡使用using声明,而不应该使用using编译指令。只有需要反复使用同一个命名空间的许多数成员时,使用using编译指令,才被认为是可取的。
17.1.3 Java
Java是Sun公司于1995年5月推出的一种跨平台的面向对象的编程语言,主要用于网络编程,特别是Web服务器端[Java EE/JSP]。1.起源
Java是1991年6月由Sun公司的(加拿大人)James Gosling等人为小型消费品电子(如电视遥控器等)项目Green开发的一种简单程序设计语言。开始叫Oak(橡树,因为在James Gosling的办公室外有一棵橡树),由于与Sun的另一产品商标同名,1994年改为Java(爪哇——位于印度尼西亚西南部的一个大岛,盛产咖啡豆;因为研究小组在讨论新名称时,面前都放有冒着热气咖啡杯,这也是Java语言的标识)。虽然在小型消费品电子产品上失败,但是Java却特别适合网络程序的开发。1994年秋,他们用Java开发出支持Applet、交互功能强大的动态浏览器WebRunner,获得了极大成功。1995年5月WebRunner改名为HotJava,并与Java语言一起,由Sun公司正式推向市场。Java具有面向对象、跨平台、安全稳定、多线程等特点,特别适合于网络编程。Java源自C++,它从C++中继承了大多数语言成分,但是它也对C++进行了大量简化(Sun的创始人之一/首席科学家/Java的主要作者之一Bill Joy称Java为C++--)。例如,它抛弃了C++中复杂和容易引起问题的头文件、编译指令、预处理器、指针、结构、隐式类型转换、操作符重载、多重继承和goto语句等。增加了字节码、虚拟机、垃圾(内存)回收(garbage collection)、接口、GUI支持、多线程、异常处理、网络编程、各种类库等内容。
Java语言的跨平台和安全性,都依赖于它的JVM(Java Virtual Machine,爪哇虚拟机)。Java是一种编译型解释语言(对比:Fortran/Pascal/C/C++为编译语言,早期的Basic为解释语言),它先将Java源代码编译成专用的字节码(bytecode),再利用各个计算机平台上安装的JVM来解释并运行这些字节码。
Java通过取消指针和添加虚拟机,在增强了安全性的同时,也具备了跨平台的能力。但这些却是以牺牲编程的灵活性和程序的运行效率为代价换来的。所以,Java并不太适合于,对运行效率要求很高的本地(客户端)的(系统)编程。
开始时,Java虽然从C++继承了大量面向过程和面向对象的特征,但是并没有继承模板。只是到了JDK 1.5(Java SE 5.0,2004年9月30日推出)才加入对编译时静态绑定的泛型编程的支持。所以,Java现在与C++一样,也可以算是一种同时具有面向过程、面向对象和泛型编程的特征的混合型程序设计语言。
2.设计目标
虽然催生Java的源动力是可移植性和安全性,但是在其最终成型的过程中其他因素也起了重要作用,下面是Java开发小组总结的Java的关键特性(设计目标):l 简单(Simple)——易学好用,继承了C/C++的语法和许多C++的面向对象特征。去掉了C++中的一些复杂困难的东西(如头文件、预处理、结构、联合、多重继承、操作符重载、[模板]等),使语言更加精简。
l 安全(Secure)——为了适应分布式网络环境,采用基于策略(policy)的沙箱(sandbox)模型,在语言层次上实现了安全性。具有如下三个方面的安全保证:语言特性(包括检查数组边界、限制类型转换、取消指针变量等)、资源访问控制(包括本地文件系统访问和连接访问网络等)和代码数字签名(通过数字签名确认源代码源以及代码是否完整)。
l 可移植(Portable)——定长的整数型类型,如int始终为32位,而C/C++中的int在Win16、Win32和Win64平台上分别为16位、32位和64位。固定的多字节整数二进制格式,采用统一的高位字节在前的方案(Intel CPU中的多字节整数的低位字节在前)。统一的用户界面(AWT/Swing [/SWT])。
l 面向对象(Object Oriented)——由于没有设计C++时兼容C的负担,从零开始设计的Java具有更纯洁的OOP特性。但是为了高性能,仍然保留了基本数据类型的非对象性。在OOP方面与C++的区别主要有单一类继承、接口、反射等。
l 健壮(Robust)——异构网络环境要求程序可靠运行。采用的措施有:避免内存错误(取消指针变量、提供内存管理——自动分配和释放内存[垃圾内存自动回收])、强制异常处理等,可对潜在的问题进行早期和动态的检查。
l 多线程(Multithreaded)——交互式网络化的多任务程序(如Web服务器)及多核CPU都要求多线程。Java提供了语言级的多线程支持,JVM也具有成熟的线程同步解决方案。
l 体系结构中立(Architecture Neutral)——通过JVM和字节码,Java实现了体系结构中立(与计算机硬件和操作系统无关)。跨平台是Java最重要的特征和最大的优点,“一次编写、到处运行”(Write Once, Run Anywhere,WORA)是Java的口号。正是跨平台的特点,使得Java语言特别适合于网络编程。
l 解释型(Interpreted)——与Fortran、Pascal、C/C++等编译型高级语言不同,Java是一种(编译性)解释语言:Java语言的源程序(*.java),先通过Java编译器编译成特殊的字节码(*.class),然后由目标机上JVM将字节码解释(翻译成本机CPU指令代码)执行。解释型是JVM的需要,也是跨平台的基础。
l 高性能(High Performance)——与追求速度的编译型语言相比,解释型语言在本质上是低性能的,这也是Java语言跨平台的代价。为了克服Java程序运行慢的缺点,可用JIT(Just-In-Time,及时)编译器,在程序的第一次运行时将(部分)字节码先转换成本机代码,以提高Java程序的运行速度。
l 分布式(Distributed)或理解网络(Network-Savvy)——Java是为因特网的分布式环境设计的,所以它支持TCP/IP协议、URL资源访问和RMI(Remote Method Invocation,远程方法调用)。
l 动态(Dynamic)——Java程序带有大量运行时的类型信息,用于在运行时的验证和解决对象的访问问题。这使得在安全有效的方式下动态连接代码成为可能。程序员可从因特网上下载代码运行(浏览器),可全面控制对象的行为,如在特定的系统要求下,在运行时分析对象(如GUI构建器、智能调试器、可插式组件、对象数据库等)。
Java语言主要优势是跨平台和安全(及类库丰富),主要缺点是运行速度较慢和图形界面较差,主要适用范围是(服务器端的)网络编程。
3.JDK版本
Java是一种自由开发源码软件,由Sun/Oracle公司主导的JCP(Java Community Process,Java社团过程)组织控制。JDK(Java Development Kit,Java开发工具包)和JRE(Java Runtime Environment,Java运行环境)会在网站http://www.oracle.com/technetwork/java/l上公开发布,并可免费下载。1)JDK包含四种版本:
l Java SE(Standard Edition,标准版)——是JDK的主要版本,主要用于客户机。也是Java EE的基础。
l Java EE(Enterprise Edition,企业版)——是JDK的重要版本,用于服务器编程,建立在Java SE之上。它与Java SE的区别在于它增加了提供开发和部署容错、分布式、多层Java软件能力的库,主要基于运行在应用服务器上的模块化组件。
l Java ME(Micro Edition,微型版)——为Java API的一个子集,用于开发资源受限的小型设备上使用的软件,目标设备包括工业控制和手机与机顶盒中的自动装置。
l Java Card(智能卡版)——与允许小应用程序(applets)在智能卡(smart cards)以及类似的小内存设备(如SIM手机卡和ATM银行卡)上安全地运行相关,是Java在嵌入式设备中的最小目标。
2)JDK演化
下面是JDK主要版本的演化历史:
l 1995年5月23日,Sun发布Java语言。
l 1996年1月23日,JDK 1.0诞生。1996年4月,JDK 1.0.2发布,增加了对数据库连接及分布式对象的支持。1997年2月19日,JDK 1.1发布,增加了健壮的事件模型、内部类、反射机制、支持国际化、推出JavaBeans组件模型和JDBC。
l 1998年12月8日,JDK 1.2发布,增加了Swing、集合框架、Java IDL,将Swing图形API集成进核心类中,发布Java插件程序(Plug-in)、JVM装备了JIT编译器,提高了虚拟机和编程工具的性能。经过此次重大改进后的Java被更名为Java 2,表示第二代Java,标志Java新时代的开始。Sun还将JDK分成三个版本:标准版(J2SE,Java 2 Platform Standard Edition)、企业版(J2EE,Java 2 Platform Enterprise Edition)和微型版(J2ME,Java 2 Platform Micro Edition)。
l 1999年12月,J2EE 1.2 SDK诞生,引入服务器端的组件技术EJB(Enterprise JavaBeans,企业爪哇豆)、网页编程工具JSP(JavaServer Page, Java,服务器网页)、Servlet(小服务器程序)和 Container(容器)等。2000年5月8日,JDK 1.3发布,增加了JNDI(Java Naming and Directory Interface,Java命名与目录接口)、RMI over IIOP、CORBA ORB、Java Sound和JPDA(Java Platform Debugger Architecture,Java平台调试器架构)等,绑定了HotSpot JVM。2001年9月24日,J2EE 1.3发布,新特性有:EJB2.0、JMS (Java Message Service,Java信息服务)、用JAXP (Java API XML处理)建立JSP、IIOP(Internet Inter-Orb Protocol,因特网交互ORB协议,其中ORB=Object Request Brokers,对象请求代理)、EJB QL(Query Language,查询语言)。2002年2月6日,JDK 1.4发布,增加了assert关键字、涟式异常、基于通道的I/O子系统NIO(New I/O)、改进了集合框架和联网类。2003年11月24日,J2EE 1.4发布,新特性有:JSP 2.0 EL(Element Language,元素语言)、JSP标准标记库、Web服务、EJB计时器服务、EJB QL增强功能、增强的JMS API。
l 2004年9月30日,J2SE 1.5发布,增加了泛型、注解、自动装箱和拆箱、枚举、for-each循环、变长变量、静态导入、格式化I/O、并发实用工具等,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,Java 2更名为Java 5,J2SE 1.5也更名为Java SE 5.0。2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名,以取消其中的数字“2”:J2EE更名为Java EE,J2SE更名为Java SE,J2ME更名为Java ME。
l 2006年5月11日,Java EE 5发布,新特性有:EJB 3.0、JSF(JavaServer Faces,Java服务器外观)、Java持久性、JSTL(JSP Standard Tag Library,JSP 标准标签库),Java最新的Web服务API:JAX-WS(Java API for XML-Based Web Services,针对基于XML的Web服务的Java API)2.0、JAXB(Java Architecture for XML Binding,针对XML绑定的Java架构)2.0、WS Metadata(Web Services Metadata for the Java Platform,针对Java平台的Web服务元数据)。
l 2006年12月11日,Java SE 6发布,新特性包括:Web服务API、支持脚本语言、更新了JDBC、 新的桌面API(如Swingworker、JTable排序与过滤和GroupLayout等)、监视和管理、编译器访问、插件式的注释(在代码中定义自己的注释并处理它)、桌面部署、安全(与像PKI、Java GSS、Kerberos和LDAP这样的服务集成起来)、性能提高一倍。目前的最新版是2009年3月25日推出的JDK SE 6 Update 13。
l 2009年12月10日,Java EE 6发布,新特性有:可扩展性和协议子集、Web Beans 1.0、JAX-RS 1.1、Servlet 3.0、JASPIC(Java Authentication Service Provider Interface for Containers,Java验证服务提供者容器接口)。被砍掉的API有:JAX-RPC、EJB 2.x Entity Beans CMP、JAXR、Java EE应用开发(JSR-88)、Java EE管理(JSR-77)。
l 2011年7月28日,Java SE 7发布,主要新特性有:模块化(module关键字、与OSGi绑定系统的相互操作性)、NIO 2、数据和时间API、fork/join并行处理包、扩展Java类型标注注释、Swing应用程序框架、JMX(Java管理扩展)2.0、动态语言支持。其他可能的新特性有:闭包(Closure)、对Strings转换状态的支持、多异常捕获机制、对集合的方括号标记、带有类型推导的简洁构造器、其他语言的名称调用。
4.Java平台
下面介绍JDK的两种主要平台Java SE和Java EE的架构。1)Java SE平台——图17-3是Java SE 7平台示意图。
JDK | Java语言 | ||
工具及其API | |||
JRE | 富互联网应用 | ||
Java SE API |
用户界面工具包 | ||
综合库 | |||
其他基础库 | |||
语言和工具基础库 | |||
Java虚拟机 | |||
操作系统平台 |
a) 详细 b) 简略
图17-3 Java SE 7平台
可见,Java SE平台的底层为各种操作系统,包括主流的Windows、Linux、MacOS和各类Unix;上面是JVM层,包括客户端和服务器端的HotSpot(热点,Sun公司的JVM实现)虚拟机;再上面是Java SE API的各种类库;API类库的上面是RIA(Rich Internet Applications,富互联网应用)。Java运行环境JRE包括JVM、API和开发技术。JRE上面是工具及其API、最上层为Java语言本身。JDK包括JRE及其上面的两层。
2)Java EE平台——Java EE建立在Java SE之上,图17-4是Java EE 6的体系结构图。
图17-4 Java EE 6体系结构图
17.1.4 C#
C# 与C++很相似,还借鉴了Java的许多特点。但是C# 比C++更安全、比Java更高效,特别适合于Windows环境下的.NET编程。1.概述
眼看Java占领了利润丰厚的服务器端编程的大部分市场,微软公司心有不甘。为了与Sun公司的Java和J2EE竞争,1996年Bill Gates用重金从Borland公司挖来(Turbo Pascal和Delphi的开发者,丹麦人)Anders Hejlsberg。Hejlsberg转到微软后,先后主持开发和设计了Visual J++(1997)和.NET框架中的CLI和C# 语言(2000)。C#(C Sharp)语言源自C++(数字符# Sharp升半音符,可以视为由4个加号组成,相当于(C++)++),模仿了Java(微软不承认这点)。C# 的很多内容都与Java相似,例如虚拟机、垃圾内存收集、接口、GUI支持。但是,与Java不同的是,C# 保留了指针,不过限制了指针的使用。C# 还引入了值类型、引用类型、元数据、装箱和拆箱转换等概念(这些概念在最新版本的Java中也有,在C++0x中也会引入与元数据类似的“概念”)。
C# 是一种面向对象的程序设计语言,最初是作为.NET的一部分而开发的。换句话说,.NET是围绕C# 而开发的。C# 的面向过程和对象的语法,是基于C++的,但也包含了另外几种程序设计语言的特征(其中最显著的是Delphi、Visual Basic和Java),C# 特别强调简易性(如所需符号比C++的少、所需修饰比Java的少)。
从某种意义上来说,C# 是最直接地反映了底层CLI的一种程序设计语言,它非常依赖于.NET框架,因为它被特地设计成能充分利用CLI所提供的特征的一种语言。例如,绝大多数的C# 内置类型,全都对应于CLI框架所实现的值类型(value-types)。
用C# 编写的应用程序,需要CLR的一个实现,才能运行。这与Java的虚拟机JVM有点相似,但是与Java不同的是,CLI应用程序要被编译两遍,第1遍由C# 编译器,将C# 源程序被编译成平台抽象字节码,即IL(Intermediate Language,中间语言)代码,存放在PE(Portable Executable,可移植的可执行)文件中(似Java的.class);第2遍,在应用程序首次运行时,由CLR的JIT(Just-In-Time即时/实时)编译器,将IL代码编译成本地客户端的机器代码(.exe)。
C# 语言,已于2001年12月成为欧洲标准:ECMA-334 C# Language Specification(C#语言规范),2002年12月、2005年6月和2006年6月又分别推出ECMA-334的第2版、第3版和第4版。2003年4月C# 成为国际标准:ISO/IEC 23270:2003 Information technology -- C# Language Specification(信息技术——C# 语言规范),2006年8月23日又推出了第2版ISO/IEC 23270:2006。
2.设计目标
C# 语言的设计目标是:l C# 被确定为一种简单、现代、通用、面向对象的编程语言。
l 该语言及其实现应该为强类型检查、数组界限检查、发现使用未初始化变量、自动垃圾回收等软件工程原则提供支持。
l 该语言适用于分布式环境中的软件组件开发。
l 源代码的可移植性是非常重要的,程序员的转移也同样重要,特别是对那些已经非常熟悉C和C++的程序员。
l 支持国际化是非常重要的。
l C# 适用于为主机和嵌入式系统编写应用程序,范围从非常大的复杂操作系统,到非常小的专用功能。
l 虽然C# 应用程序致力于在内存和处理能力需求上是经济的,但是该语言并不想与C或汇编语言在性能和大小方面进行直接竞争。
3.特点
C# 具有如下主要特点:l 简单——相对于复杂的C++,C# 的语言简单,开发高效。例如,在安全上下文中,C# 没有指针,不许直接存取内存。用统一的“.”操作符,代替了C++中的“::”、“.”和“->”操作符。使用统一的类型系统,抛弃了C++的多变类型系统(如int的字节数、0/1转布尔值等)。
l 现代——很大程度上由.NET框架体现。如支持组件编程、泛型编程、分布式计算、XML处理和B/S应用等。
l 面向对象——C# 全面支持面向对象的功能。与C++相比,C# 没有全局变量和全局函数等,所有的代码都必须封装在类中(甚至包括入口函数[方法]Main)、不能重写非虚拟的方法、增加了访问修饰符internal、不支持多重类继承(似Java,用多重接口实现来代替)。
l 类型安全——C# 实施严格类型安全,如取消了不安全的类型转换,不允许使用未初始化的变量,进行边界检查(如不让数组越界)。
4.优势
用C# 进行托管代码编程,具有如下优点:完全面向对象的设计、非常强的类型安全、很好地融合了VB和C++的强大功能、垃圾内存回收、类似于C++的语法和关键字、用委托取代函数指针增强了类型安全、为程序员提供版本处理技术可解决老版本的程序不能在新DLL下运行的“动态链接库地狱”(DLL hell)问题。5.版本与功能
Visual C# 随.NET框架及开发工具Visual Studio一起推出,有如下几个版本:l 1.0——随.NET框架1.0和Visual Studio .NET(2002)于2002年2月13日发布。
l 1.5——随.NET框架1.1和Visual Studio .NET 2003于2003年5月20日发布。
l 2.0——随.NET框架2.0和Visual Studio 2005于2005年11月7日发布。
l 3.0——随.NET框架3.0和Visual Studio 2008于2007年11月16日发布。
l 4.0——随.NET框架4.0和Visual Studio 2010于2010年4月12日发布。
4.0版的中文版规范见Visual Studio 2010中文版安装目录中的Word文档,默认路径为:“C:\Program Files\Microsoft Visual Studio 10.0\VC#\Specifications\2052\CSharp Language Specification.doc”。
下面罗列Visual C# 的各个主要版本的新增的特点和功能。
1)C# 1.0——与C和C++比较,C# 在许多方面有所限制和增强,包括:
l 指针——C# 是真正支持指针,但是其指针只能在非安全作用域中使用,而只有具有适当权限的程序,才可以执行标记为非安全的代码。绝大多数对象的访问是通过安全的引用(references)来进行的,而引用是不会造成无效的,而且大多数算法都是要进行溢出检查的。一个非安全指针,不仅可以指向值类型,还可以指向子类和System.Object。也可以使用指针(System.IntPtr)来编写安全代码。
l 托管(managed,受控)——在C# 中,托管内存不能显式释放,取而代之的是(当再没有内存的引用存在时的)垃圾收集。但是,引用非托管资源的对象,例如HBRUSH,是可以通过标准的IDisposable接口的指示来释放指定内存的。
l 多重继承——在C# 中多重继承被禁止(尽管一个类可以实现任意数目的接口,这点似Java),这样做的目的是为了避免复杂性和“依存地狱”,也是为了简化对CLI的结构需求。
l 转换——C# 比C++更类型安全,唯一的默认隐式转换也是安全转换,例如加宽整数和从一个派生类型转换到一个基类(这是在JIT编译期间间接强制进行的)。在布尔和整数之间、枚举和整数之间都不存在隐式转换,而且任何用户定义的隐式转换,都必须显式地标出。
l 数组声明——和C/C++的数组声明的语法不同,C# 中用“int[] a = new int[5];”代替了C/C++的“int a[5];”。
l 枚举——C# 中的枚举被放入它们自己的命名空间。
l 特性——可在C# 中可以使用特性(properties,属性集),访问类似于C++中成员域,与VB相似。
l 类型反射与发现——在C# 中可以使用完整的类型反射与发现,这些都会用到元数据所提供的信息。
l 模板——为了简单性,C# 1.0中不支持模板等泛型编程技术。
2)C# 1.5版的新增功能——/** */ 文档注释符、#line hidden预处理指令、/nowarn和/nostdlib编译指令、Web窗体、XML Web服务、ADO.NET、可用Windows窗体和框架创建分布式应用程序的表示层、可创建各种Windows和ASP.NET Web应用程序和控件的项目模板、可使用非可视组件和相关功能,将消息队列、事件日志和性能计时器等资源合并到应用程序中、通过组件设计器和框架类为创建组件提供RAD支持。
3)C# 2.0的新特征:
l 部分类——一个类可分开到多个文件中实现。
l 泛型——C# 从2.0起,开始支持泛型或参数类型。C# 还支持一些C++模板不支持的特性,例如对泛型参数的类型约束。另一方面,C# 的表达式不能用作泛型参数,而这在C++中却是允许的。C# 的参数化的类型为虚拟机的首个类对象,允许优化和保存类型信息,这一点与Java不同。
l 关键字yield——迭代器的一种新形式,可通过功能类型的关键字yield,来使用协同例程。
l 匿名委托——提供了闭包功能。
l 结合运算符??——返回表中的第一个非空值,例如:
object nullObj = null;
object obj = new Object();
return nullObj ?? obj // returns obj;
l 可空值类型——可空值类型由问号?来标记(例如,int? i = null;),它可以改善与SQL数据库的交互。
4)C# 3.0的新特征:
l LINQ(Language Integrated Query,语言集成查询)——"from, where, select"上下文敏感的关键字,允许在SQL、XML、集合等之间进行查询。
l 对象初始化——如Customer c = new Customer(); c.Name = "James"; 可被写成Customer c = new Customer { Name="James" };。
l 集合初始化——如MyList list = new MyList(); list.Add(1); list.Add(2); 可被写成MyList list = new MyList { 1, 2 };。
l 匿名类型——如var x = new { Name = "James" };。
l 局部变量类型推论——如var x = "hello";等价于string x = "hello"; 该特性在匿名类型变量的声明中需要。
l 隐含类型的数组——数组的类型现在可以省略,所以int[] arr = new int[] { 1, 2, 3 }; 现在可以写成var arr = new[] { 1, 2, 3 };。
l λ表达式——如listOfFoo.Where(delegate(Foo x) { return x.Size > 10; }) 可被写成 listOfFoo.Where(x => x.Size > 10);。
l 编译器推断——翻译λ表达式到强类型函数或强类型表达式树。
l 自动属性——编译器会自动生成一个私有实例变量,而且给出适当的获取器和设置器代码,例如public string Name { get; private set; };。
l 扩展方法——通过在另一个静态类的一个方法的首个参数中包含this关键字,来将方法添加到类中。如
public static class IntExtensions {
public static void PrintPlusOne(this int x) {
Console.WriteLine(x + 1);
}
}
int foo = 0;
foo.PrintPlusOne();
l 部分方法——允许代码生成器生成方法的声明作为扩展点,如果有人在另一个部分类中实际实现它,则其只被包含在源代码编译中。
5)4.0版增加的新特性主要有:
l 动态支持——通过引进新类型dynamic来提供对动态类型延迟绑定的支持。
l Office可编程性——通过添加命名和可选的参数、dynamic类型、索引属性和可选的ref修饰符,大大增强了访问(包括Office自动化API在内的)COM接口的能力。
l 类型等价支持——可配置应用程序的内置类型信息,以代替从PIA(Primary Interop Assembly,主互操作程序集)导入的类型信息。
l 协变与逆变——协变(covariance)是你能够使用更多的派生类型而不是由泛型参数指定,而逆变(contravariance)则让你使用更少的派生类型。
17.1.5 比较
总的来说,C++高效但是不安全,Java(跨平台)安全但是较低效,C# 安全且较高效。表17-4列出了这三种OOP语言在若干功能和特点方面的比较。表17-4 C++、Java与C# 的比较
功能 | C++ | Java | C# |
跨平台 | 源代码(部分) | 字节码 | CLI(不流行) |
执行方式 | 编译 | 编译+解释 | 编译+JIT转换 |
中间代码 | 无 | 字节码Bytecode | 中间语言MSIL |
运行环境 | 操作系统 | JRE/JVM | CLR/VES |
内存管理 | 直接分配和删除 | 垃圾内存自动回收 | 垃圾内存自动回收 |
多重类继承 | 支持 | 不支持 | 不支持 |
操作符重载 | 支持 | 不支持 | 部分支持 |
对象访问 | 地址/指针 | 引用 | 引用 |
接口类型 | 无 | 有 | 有 |
属性成员 | 无 | 无 | 有 |
成员可用性 | 不支持 | 使用反射 | 使用反射 |
命名空间 | 支持 | 包机制 | 支持 |
指针 | 支持 | 不支持 | 部分支持(非安全代码) |
函数指针 | 支持 | 适配器+监听程序 | 委托 |
全局函数与变量 | 有 | 无 | 无 |
无符号整数类型 | 有 | 无 | 有 |
大十进制数类型 | 无 | 有(库) | 有(语言) |
强制类型转换 | 支持 | 不支持 | 支持 |
越界自动检查 | 无 | 有 | 有 |
多维数组 | 数组的数组 | 数组的数组 | 真正多维数组 |
索引 | 支持 | 不支持 | 支持 |
泛型 | 编译时模板 | 运行时泛型(有限制) | 编译时模板 |
泛型类型编译 | 具体化 | 类型擦除 | 具体化 |
线程同步 | 调用函数 | 语言内部 | 语言内部 |
异常处理 | 可选 | 支持检查异常 | 只支持非检查异常 |
标准类库 | 贫乏 | 丰富 | 庞大 |
适用领域 |
面向对象的 系统和界面编程 |
跨平台(服务器端) 网络编程 |
Windows平台的 .NET和组件编程 |
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式