【一篇理清】C语言/C++/C#,及JAVA/Python的区别在什么地方?【建议收藏】

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

目录

首先来说一下C语言和C++、C#的区别在什么地方

接博主简书
C语言/C++/C#的区别在什么地方?

在这里给他们三个布置一个任务看他们如何去解决,看完你就明白了

例举一个经典的任务:把大象放进冰箱里,让我们先来看看C语言是如何去解决的。

C

c语言是一个极其高冷的人,因此回答都是冷冰冰的。

:你好C语言,我想把大象放到冰箱里,帮我做好不好?
C语言:好
:那我们要怎么做呢?
C语言:猜
:额…是不是应该先创造一只大象?
C语言:是
:怎么创造呢?
C语言:猜

我只好去翻了一下文档,哦,malloc一块内存啊。

:好的,我用malloc,申请一块空间放大象好不好?
C语言:好
:额…大象怎么构造呢?
C语言:猜

:…我去看了看大象的结构
:好吧…我定义了一个大象的数据结构,接下来怎么做?
C语言:猜

我心里有一阵说不出的感觉…

:哦好吧,我创造了一个冰箱,步骤应该和大象差不多
C语言:嗯

我翻看了冰箱的数据结构,然后定义了一个冰箱的struct。

:好了,冰箱构造出来了,怎么放呢?
C语言:哼。

:…默默在stack overflow上输入“如何把大象放进冰箱 C”。
:终于找到答案了,定义一个方法,把大象鼻子放进去、眼睛放进去、耳朵放进去。
Ok,都放进去了。C,你输出一下看看吧。
C语言:烫烫烫烫烫烫烫
我:哈哈哈哈C你终于不高冷了。

。。。。

:哎,你咋不说话了?C?你发烧了吗?

立刻一盆水倒上去

C++

再让我们看看C++

C++是一个知识渊博的孩子,相对年轻,也没那么高冷

:C艹,我们把大象放冰箱好吗?
C++ :滚
:说好的不高冷呢
。。。

:额,我错了,亲爱的C++,我们把大象放进冰箱好吧。
C++ :好的么么哒,大象的数据类型已经有人定义好了,你需要分别构造一个哦,冰箱也有人定义好了,你需要分别构造一个哦。
我:好的。

于是我翻看了一下文档后知道了用new来构造

:OK,亲爱的C++,我想构造一个大象
C++:OK,构造大象的方法有13个吗,请问你选择哪一种呢?

:。。。。

:你介绍一下吧。

C++ :OK,首先可以在栈上面构造,这么做的好处呢是可以方便资源管理。而且语法也好看一些,可以利用RAII,如果你不知道什么是RAII你可以去Cppreference上面查一下,当然你也可以在堆上构造,可以构造一只很大很大的大象,还有…

。。。

一个月后,C++终于讲完了,我也长满了胡须…刮了胡子之后继续工作。

:好的,C++,我就在栈上面构造一只大象吧。
C++ :你不爱我了。

:???

C++ :你都没有说“亲爱的”。
:。。。
:好吧,亲爱的C++,我想简单构造一只大象。
C++ :好的呢,你可以选择构造一只非洲象还是美洲象,
象的肤色是什么样子的你可以设定一下,象的屁股大小你可以设定一下,
象的性别和性取向你看要什么…

。。。

:我就想要一只简单的,默认的就好。
C++ :好的,构造出来了一只,你可以选择怎么初始化它,有13种方法,推荐使用最新的统一初始化

:。。。

:统一初始化
C++ :好的,我们可以构造冰箱了。
:好。。。

经过一个月的选择,终于构造出了冰箱

C++ :好的,冰箱提供了addElephant方法,可以直接使用哦。
:哇太棒了,好的,我就用这个方法。
C++ :这个方法提供了多种重载,可以拷贝,移动,也可以直接转发,省的在移入冰箱之前构造一个大象。
:。。。你为啥不早说
C++ :你为啥不早问

。。。

:就用移动的吧。
C++ :OK,请用std::move将构造好的大象转为右值引用。

我一脸懵逼,但是照办了。

:好啦。
C++ :OK,开始放入冰箱了哦,速度极快的呢。

突然

C++ :报错了报错了,分子和原子不是可以转换的类型,大熊猫和爱迪生之间没有+运算符等1556项错误

:。。。

:网上找资料,说看最一行就差不多了,好的看看。
嗯,看起来是第31行出错了,错误是什么?
报错信息啥也没说明白啊。随便改改吧。
编译,运行。

C++ :好的,已经将臀围12米,左臂长13米,右臂长14米,喜欢公大象,没有结婚生子,从下家教不错,熟读四书五经的非洲母象放入橘黄色50米高,60米宽,300米厚,温度有零下300度,制冷的牌子是湖澈牌,额定电压220V的冰箱里。

:。。。长舒了一口气。

C#

好啦好啦到C#

:我想把大象放进冰箱。
C# :好的主人,我爸爸微软已经写好了大象类,也写好了冰箱类,你只需要new一下就好了。
:OK,new好了,放入冰箱。
C# :好的,已经放入,使用了冰箱的拓展方法addElephant<>()方法。
:嗯,你表现的很好,能不能放的稍微快一点,刚刚C++放的就很快。

C# :为了您的安全,不能。
:额。。。那我想调节一下大象耳朵的尺寸。
C# :对不起,不能调节。您可以设定大象耳朵的形状。已为您定义好多种耳朵形状,您还可以调整大象的肤色,已为您定义好多种肤色。
:算了不调了,就这样吧。

C#
好的。如果您需要速度,或者需要对大象的每个细节进行把握。还可以去看看我的弟弟,C++/CLR,他可以完成你提出的这些功能。
您也可以将他带到我这里来,我们可以一起工作的。
你也可以把C++完成的工作导出到二进制形式,我可以直接使用的。

:好的谢谢,不用了。

C# :好的,祝您生活愉快。

这下C语言、C++、C#、都分清楚了吗。

另加:

Python

作为学习了一年Python的我表示,还是要讲讲Python其干起事情来的优越性。

我:我想把大象放到冰箱里。

Python:好的,import这个“把大象放入冰箱”模块,然后使用run方法即可。

我:OK谢啦。

Python:不用。

。。。

============================================================================================
那么作为当今主流语言(也是我们主要学习使用的语言)的JAVA,不得不参与这场纷争
先来第一个问题

C是最基础的编程语言你同意吗?

如果将各种编程语言的地位用一个楼层来形象的描述的话
Python呢可能已经都是在10楼这块待着了;像Java就可能待在6、7楼上;C++可能就3楼4楼;1楼和地基就是CC在整个编程界的这个地位,就认为它是一楼一点也不为过,没有什么悬念,可能地下车库和大地基就是那个汇编。比汇编在底儿就没有什么意义了,就是0、1了,完全二进制了。

虽然它们不是一种语言,不过它们却也有所联系。广泛地说,C可以看作其他三种语言的源语言,因为其他三种语言几乎都是从C而来的,无论从数据类型还是控制语句看,其他三种语言都有从C得来的迹象。

其中C++则一般看作是对C语言的扩展。

因为C语言没有面向对象的语法结构,而当时业界又迫切需要面向对象的编程特性,所以贝尔实验室的开发者就为C添加了面向对象的结构。现在C++已经不只是C的扩展了,它已经完全可以被看作一种新的编程语言。虽然C的特性以及库函数仍然被C++支持,不过C++拥有自己的独立的类库体系,功能相当强大。

Java语言是一种完全的面向对象语言,虽然它的底层(运行时库)是用C语言开发的,可是并不依赖于C。

因为Java的运行是在运行时库的支持下进行的,所以运行效率比起可以更接近底层的C/C++来说效率会有所影响,不过Java的类库采用很好的设计理念设计,非常好用,也非常实用,已经成为业界的一种标准开发语言。它的跨平台的特性尤其让很多开 发者欢迎,你只需要开发一次就能在所有安装了Java运行时库的系统上运行。Java发展了十几年了,可以应用领域非常之广泛,Web开发有 JSP,J2EE,桌面领域现在有JavaFX。

C#是微软开发的一种编程语言,语法类似Java,几乎就是从Java的翻版。

运行原理和Java也类似,也是通过运行时库的支持运行。不过支持的平台还很有限。Java几乎被所有平台支持,而C#目前只被Windows和Linux支持,Windows下的支持当然是由微软自己开发的,而Linux下的支持则有MONO支持。实际上,MONO也是把C#应用转化为Java应用而已,所以本质上,C#仍然只是被微软自己的操作系统支持。应用平台受到限制,是它最大的缺点。

Java是c++的简化
(1)C++中子类对父类的继承有三种不同的形式,默认的是私有继承,也就是说父类的所有内容,除了私有的之外,都变成子类的私有成员。保护继承是说,子类的公开的和保护的 成员,在子类中变成了保护的,而公有继承是,父类的共有成员,在子类中还是公有的,而保护的,在子类中保持保护。这是c++中的情景。继承的一个好处就是代码的复用,是在c++中,因为继承默认的是私有继承,那么我们就不能在子类中使用父类的代码,这就限制了面向对象的特性。在Java中对此作了改进。Java中不在对继承进行控制,但是使用c++中的公共继承效果,这样我们就可以省掉在继承时对关键字public的添加,方便了编程。也方便了对代码的重用。
(2)C++使用纯虚函数的概念来进行抽象类声明。而在Java中,也有相应的语法,但是这里使用virtual关键值,因为是抽象的类,所以,在Java中,使用的关键字abstract(抽象)。只要在类的生命有,只有方法头而没有方法的具体实现的类都是抽象类。这样,如果我们想生命一个抽象类,那么我们只要不实现这个类的实现就可以了,而不用专门在声明它是抽象的。但是在c++中,因为允许类的声明和类的实现分离(类的生命放在一个文件里,而类的实现放在另外一个文件里,或者即使在一个文件里,但是类的成员函数的实现,也不是在类的声明体里),所以,在c++中必须明确的说明这是个纯虚的函数,而不能像Java中那样仅仅不实现它来表示它是个纯虚的。
(3)还有一点就是,在c++中我们访问对象的成员的时候使用->比较多,而在Java中,我们使用点(.)来表示。这是因为在c++中我们有指针的概念,而在Java中,去掉了这个概念。因为要实现类层面的多态,我们要使用父类的指针,所以,在c++中,往往使用->,而在Java中,使用一个“.”就可以完成同样的事情。一个需要两个按键,而另一个只需要一个就可以。这对于程序员这样一群抱有使用最少的代码解决问题的观念的人来说,这是个不小的改进。

Java:无可争辩地具有C++所有的精华

在比较Java和C#的时候,你不可能不注意到它们诸多的相似之处,这在某种程度上要归结于它们共同的来源:C和C++。但是,当Gosling和他的同事们坐下来创造Java的时候,他们不仅吸取了C++的能力,而且更重要的是,他们减掉了一些无用特性,后者让C++更容易出错误而且更难学习。C#的设计者加入了很多C++的特性,而Java也加入了这些特性,但是C#却没有去掉C++的最糟糕的一些特性。其结果就是这样一门语言,它仍然为所有人提供了所有的特性,但其结局是内部冲突不断,而且过于复杂。

C#在安全上的削弱

C#有一个用于将代码区域标示为不安全的简单机制。在这些不安全的区域里,Java以及后来的C#安排到位了一些安全措施,用以防止程序员直接修改内存位置,以及使用点
运算,但是这些措施是值得怀疑的。在使用具有垃圾清理功能的高级语言时,如果下到内存地址这一层,就会把对象/内存之间有意作出分离弄混。错误就会容易出现,调
试成了恶梦,缓冲区溢出再次抬头,C和C++里著名的安全漏洞再次现身。

Java与C++的优劣

1…C++对Java:相似之

很多人都爱拿C++对Java作对比的原因也是C++与Java拥有诸多相似之处。因此在为项目选择语言时,大家应主要考虑到当前团队成员更熟悉哪款语言。
语法:两种语言中的循环结构、类、变量定义以及条件运算符非常相近,意味着开发者能够轻松在不同项目中分别使用这两种语言,而不会遇到太多障碍。
入口点:在程序启动时,编译器或者解释器会查找开始执行的对应位置。Java与C++都以“main”作为入口点。
面向对象:面向对象概念代表着语言利用类来表示程序中的组件。每个类包含有其定义的方法与属性。C++与Java都属于面向对象语言,这意味着程序将更具模块化特性,允许大家将代码复用至其它程序当中。

2.C++与Java的区别

人们往往误以为两种语言相似意味着其在功能上也彼此接近。然而,尽管Java与C++拥有类似的语法,但其执行与处理机制则完全不同。
(1)解释对编译:Java是一种解释性语言,意味着其在执行时会被“翻译”为二进制形式,也就是java跑得时候必须有人(jvm)去解释它。而C++则是编译语言,意味着程序只能在特定操作系统上编译并在特定系统上运行,也就是说C++一步到位成机器语言的。
(2)内存安全:Java是一种内存安全型语言,意味着大家可以为给定数组分配任意参数,即使超出范围也只会返回错误提示。C++更为灵活,但代价是一旦分配的参数超出资源范围,则会引起错误甚至严重崩溃。
(3)性能:Java人气极高,但其代码由于需要在运行前进行解释因此性能表现更差。C++会被编译为二进制形式,因此其能够立即运行且速度更快。如果你写一个c++的程序和做同样事情的java程序,可能你感觉两者速度差不多。但如果这两个程序都足够大、而且c++的代码经过过优化,两者的速度差就会变得很显著甚至很惊人,C++会比java快很多。
(4)指针:指针是一种C++结构,允许您直接在内存空间中进行值管理。Java不支持指针,因此您可能使用值引用的方式进行值传递。
(5)重载:重载是指对某种方法或者运算符的功能进行“重新定义”。Java允许方法重载,而C++则允许进行运算符重载。

3.谁更适合您的项目?

Java与C++没有哪个更好,只有哪个更合适。他们都可用于创建各类程序,但具体选择取决于您希望开发的实际内容。
C++通常适合那些需要“硬件级”操作的软件。二者之间的最大区别在于,C++更接近机器语言,因此其软件运行速度更快且能够直接与计算机内存、磁盘、CPU或者其它设备进行协作。另外,C++也能为游戏提供良好的运行性能。
大家也可以利用Java操作硬件,但它不属于低级通用编程语言,因为其更加“安全”。因为Java不允许利用某些功能进行PC保护,因此它更适合较高级别的应用。Java是Android开发领域的王者,因此移动开发者无疑应该选择它作为项目基础。另外,Java也常见于Web及桌面应用乃至服务器端应用。再有,Java的接纳程度更高,意味着我们更容易找到水平不错的Java开发者——而C++人才则相对稀缺。
总体来讲,C++几乎可以实现任何功能,但除非拥有明显理由,否则我们不会将其作为首选。Java则是一切都足够——虽然并非最佳,但完全足够。而更重要的是,Java开发者群体更为庞大。

=============================================================================================

c,c++,c#,java 联系与区别

转--------
blog.csdn.net/weixin_3784…

一、各种语言的描述

C语言:

目前最著名、最有影响、应用最广泛的windows、linux和UNIX三个操作系统都是用C语言编写的。0S是计算机系统(由软硬件两个子系统构成)的核心和灵魂,它是软件中最庞大最复杂的系统软件。既然如此庞大复杂的0S都可以用c语言编写,从狭义而言,还有什么系统软件和应用软件不能用c语言编写呢?由此可以肯定的说,c语言是一门十分优秀而又重要的语言。

c语言程序设计是过程性程序设计语言,它的发展贯穿了计算机发展的历程,它蕴含了程序设计的基本思想,囊括了程序设计的基本概念,所以它是理工科高等院校的一门基础课程。

从市面上有关c语言的书籍和高等院校采用的教材来看,它们有一个共性,那就是:脱离了实际应用(全是小打小闹的小例子),纯粹的过程性程序设计,没有软件工程思想的体现,没有一定程序设计风格,仅仅是为了让大家明白什么是c语言而已。

高等院校开设c语言程序设计的目的是让学生对程序设计有个入门,有个直观的理解,同时为其他后续课程作铺垫。

C++:

C++语言是在C语言的基础是扩展而成的.所以两种语言的基本语法和语义是相同。C++中加入了面向对程序设计(OOP)的特征。

下面的三个主要性质刻划OOP语言的特点:

封装性:把一个数据结构同操作的函数(行为或方法)组合在一起。封装性是借助于一种新的结构和数据类型机制——类实现的。

继承性:建立一个新的派生类,它从一个或多个先前定义的基类中继承函数和数据,而且可能重新定义或加进新的数据行为,这样就建立了类的层次。

多态性:给行为取一个名字或符号,它共享一个类的层次,在这个层次中的每个类都以适合自己的方式实现这个行为。

C#:

C#(读做 “C sharp”,中文译音“夏普”)是微软公司发布的一种面向对象的、运行于.NET Framework之上的高级程序设计语言,并定于在微软职业开发者论坛(PDC)上登台亮相.C#是微软公司研究员Anders Hejlsberg的最新成果.C#看起来与Java有着惊人的相似;它包括了诸如单一继承,界面,与Java几乎同样的语法,和编译成中间代码再运行的过程.但是C#与Java有着明显的不同,它借鉴了Delphi的一个特点,与COM(组件对象模型)是直接集成的,而且它是微软公司.NET windows网络框架的主角.

Java:

Java是一种可以编写跨平台应用软件的面向对象的程序设计语言,由升阳(太阳微电子,Sun Microsystems)公司的James Gosling等人于1990年代初开发的.具有以下特征的高级程序语言:

简单面向对象可分布可解释强壮安全性结构化轻便功能强大多线程动态…

Java既可以被编译,也可以被解释。通过编译器,可以把Java程序翻译成一种中间代码 -称为字节码 -可以被Java解释器解释的独立于平台的代码。通过解释器,每条Java字节指令被分析,然后在计算机上运行。只需编译一次,程序运行时解释执行。

二、关于“面向对象”和“面向过程”的区别

他们主要是编程的思想的不同。

举个例子:比如你想做一个模型飞机,利用“面向过程”的思想,你所想的就是,先做头再做身体,再做尾巴。。它是一个比较顺着的概念。而如果利用“面向对象”的思想,你所想的就是,你要做飞机的话,它有翅膀,头部,身体,尾巴等各个部件,你先把各个部分做好,再考虑他们的直接的接口怎么连。。。这就是区别。你可以发现“面向对象”的优势。

三、C#、C++和Java

如果你早就知道C#更接近Java而不是C++,事情也不值得大惊小怪。对于刚刚加入这场讨论的读者,下面的表1让你自己作出判断。显然,结论应该是:Java和C#虽然不是孪生子,但C#最主要的特色却更接近Java而不是C++。


了解表1总结的重要语言功能之后,请继续往下阅读,了解C#和Java的一些重要区别。

四、语言规范的比较

4.1、简单数据类型

简单数据类型(Primitive)在C#中称为值类型,C#预定义的简单数据类型比Java多。例如,C#有unit,即无符号整数。表2列出了所有C#的预定义数据类型:
在这里插入图片描述

4.2、常量

忘掉Java中的static final修饰符。在C#中,常量可以用const关键词声明。

public const int x = 55;
复制代码

此外, C# 的设计者还增加了 readonly 关键词。如果编译器编译时未能确定常量值,你可以使用 readonly 关键词。 readonly 域只能通过初始化器或类的构造函数设置。

4.3、公用类的入口点

在Java中,公用类的入口点是一个名为main的公用静态方法。main方法的参数是String对象数组,它没有返回值。在C#中,main方法变成了公用静态方法Main(大写的M),Main方法的参数也是一个String对象数组,而且也没有返回值,如下面的原型声明所示:

public static void Main(String[] args)
复制代码

但是, C# 的 Main 方法不局限于此。如果不向 Main 方法传递任何参数,你可以使用上述 Main 方法的一个重载版本,即不带参数列表的版本。也就是说,下面的 Main 方法也是一个合法的入口点:

public static void Main()
复制代码

另外,如果你认为有必要的话, Main 方法还可以返回一个 int 。例如,下面代码中的 Main 方法返回 1 :

using System;
public class Hello {
public static int Main() {
Console.WriteLine("Done");
return 1;
}
}
复制代码

与此相对,在 Java 中重载 main 方法是不合法的。

4.4、switch语句

在Java中,switch语句只能处理整数。但C#中的switch语句不同,它还能够处理字符变量。请考虑下面用switch语句处理字符串变量的C#代码:

using System;
public class Hello {
public static void Main(String[] args) {
switch (args[0]) {
case "老板":
Console.WriteLine("早上好!我们随时准备为您效劳!");
break;
case "雇员":
Console.WriteLine("早上好!你可以开始工作了!");
break;
default:
Console.WriteLine("早上好!祝你好运!");
break;
}
}
}
复制代码

与Java中的switch不同,C#的switch语句要求每一个case块或者在块的末尾提供一个break语句,或者用goto转到switch内的其他case标签。

4.5、foreach语句

foreach语句枚举集合中的各个元素,为集合中的每一个元素执行一次代码块。请参见下面的例子。

using System;
public class Hello {
public static void Main(String[] args) {
foreach (String arg in args)
Console.WriteLine(arg);
}
}
复制代码

如果在运行这个执行文件的时候指定了参数,比如“ Hello Peter Kevin Richard ”,则程序的输出将是下面几行文字:

Peter
Kevin
Richard
复制代码

2.6、C#没有>>>移位操作符

C#支持uint和ulong之类的无符号变量类型。因此,在C#中,右移操作符(即“>>”)对于无符号变量类型和带符号变量类型(比如int和long)的处理方式不同。右移uint和ulong丢弃低位并把空出的高位设置为零;但对于int和long类型的变量,“>>”操作符丢弃低位,同时,只有当变量值是正数时,“>>”才把空出的高位设置成零;如果“>>”操作的是一个负数,空出的高位被设置成为1。

Java中不存在无符号的变量类型。因此,我们用“>>>”操作符在右移时引入负号位;否则,使用“>>”操作符。

4.7、goto关键词

Java不用goto关键词。在C#中,goto允许你转到指定的标签。不过,C#以特别谨慎的态度对待goto,比如它不允许goto转入到语句块的内部。在Java中,你可以用带标签的语句加上break或continue取代C#中的goto。

4.8、声明数组

在Java中,数组的声明方法非常灵活,实际上有许多种声明方法都属于合法的方法。例如,下面的几行代码是等价的:

int[] x = { 0, 1, 2, 3 };
int x[] = { 0, 1, 2, 3 };
复制代码

但在C#中,只有第一行代码合法,[]不能放到变量名字之后。

4.9、包

在C#中,包(Package)被称为名称空间。把名称空间引入C#程序的关键词是“using”。例如,“using System;”这个语句引入了System名称空间。

然而,与Java不同的是,C#允许为名称空间或者名称空间中的类指定别名:

using TheConsole = System.Console;
public class Hello {
public static void Main() {
TheConsole.WriteLine("使用别名");
}
}
复制代码

虽然从概念上看,Java的包类似于.NET的名称空间。然而,两者的实现方式不同。在Java中,包的名字同时也是实际存在的实体,它决定了放置.java文件的目录结构。在C#中,物理的包和逻辑的名称之间是完全分离的,也就是说,名称空间的名字不会对物理的打包方式产生任何影响。在C#中,每一个源代码文件可以从属于多个名称空间,而且它可以容纳多个公共类。

.NET中包的实体称为程序集(Assembly)。每一个程序集包含一个manifest结构。manifest列举程序集所包含的文件,控制哪些类型和资源被显露到程序集之外,并把对这些类型和资源的引用映射到包含这些类型与资源的文件。程序集是自包含的,一个程序集可以放置到单一的文件之内,也可以分割成多个文件。.NET的这种封装机制解决了DLL文件所面临的问题,即臭名昭著的DLL Hell问题。

4.10、默认包

在Java中,java.lang包是默认的包,它无需显式导入就已经自动包含。例如,要把一些文本输出到控制台,你可以使用下面的代码:

System.out.println("Hello world from Java");
复制代码

C# 中不存在默认的包。如果要向控制台输出文本,你使用 System 名称空间 Console 对象的 WriteLine 方法。但是,你必须显式导入所有的类。代码如下:

using System;
public class Hello {
public static void Main() {
Console.WriteLine("Hello world from C#");
}
}
复制代码

4.11、面向对象

Java和C#都是完全面向对象的语言。在面向对象编程的三大原则方面,这两种语言接近得不能再接近。

继承:这两种语言都支持类的单一继承,但类可以实现多个接口。所有类都从一个公共的基类继承。

封装与可见性:无论是在Java还是C#中,你都可以决定类成员是否可见。除了C#的internal访问修饰符之外,两者的可见性机制非常相似。

多态性:Java和C#都支持某些形式的多态性机制,且两者实现方法非常类似。

4.12、可访问性

类的每个成员都有特定类型的可访问性。C#中的访问修饰符与Java中的基本对应,但多出了一个internal。简而言之,C#有5种类型的可访问性,如下所示:

public:成员可以从任何代码访问。

protected:成员只能从派生类访问。

internal:成员只能从同一程序集的内部访问。

protected internal:成员只能从同一程序集内的派生类访问。

private:成员只能在当前类的内部访问。
复制代码

4.13、派生类

在Java中,我们用关键词“extends”实现继承。C#采用了C++的类派生语法。例如,下面的代码显示了如何派生父类Control从而创建出新类Button:

public class Button: Control { . . }
复制代码

4.14、最终类

由于C#中不存在final关键词,如果想要某个类不再被派生,你可以使用sealed关键词,如下例所示:

sealed class FinalClass { . . }
复制代码

4.15、接口

接口这个概念在C#和Java中非常相似。接口的关键词是interface,一个接口可以扩展一个或者多个其他接口。按照惯例,接口的名字以大写字母“I”开头。下面的代码是C#接口的一个例子,它与Java中的接口完全一样:

interface IShape { void Draw(); }
复制代码

扩展接口的语法与扩展类的语法一样。例如,下例的 IRectangularShape 接口扩展 IShape 接口(即,从 IShape 接口派生出 IRectangularShape 接口)

interface IRectangularShape: IShape { int GetWidth(); }
复制代码

如果你从两个或者两个以上的接口派生,父接口的名字列表用逗号分隔,如下面的代码所示:

interface INewInterface: IParent1, IParent2 { }
复制代码

然而,与Java不同,C#中的接口不能包含域(Field)。

另外还要注意,在C#中,接口内的所有方法默认都是公用方法。在Java中,方法声明可以带有public修饰符(即使这并非必要),但在C#中,显式为接口的方法指定public修饰符是非法的。例如,下面的C#接口将产生一个编译错误。

interface IShape { public void Draw(); }
复制代码

4.16、is和as操作符

C#中的is操作符与Java中的instanceof操作符一样,两者都可以用来测试某个对象的实例是否属于特定的类型。在Java中没有与C#中的as操作符等价的操作符。as操作符与is操作符非常相似,但它更富有“进取心”:如果类型正确的话,as操作符会尝试把被测试的对象引用转换成目标类型;否则,它把变量引用设置成null。

为正确理解as操作符,首先请考虑下面这个例子中is操作符的运用。这个例子包含一个IShape接口,以及两个实现了IShape接口的类Rectangle和Circle。

using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
if (shape is Rectangle) {
Rectangle rectangle = (Rectangle) shape;
Console.WriteLine("Width : " + rectangle.GetWidth());
}
if (shape is Circle) {
Circle circle = (Circle) shape;
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}
复制代码

编译好代码之后,用户可以输入“rectangle”或者“circle”作为Main方法的参数。如果用户输入的是“circle”,则shape被实例化成为一个Circle类型的对象;反之,如果用户输入的是“rectangle”,则shape被实例化成为Rectangle类型的对象。随后,程序用is操作符测试shape的变量类型:如果shape是一个矩形,则shape被转换成为Rectangle对象,我们调用它的GetWidth方法;如果shape是一个圆,则shape被转换成为一个Circle对象,我们调用它的GetRadius方法。

如果使用as操作符,则上述代码可以改成如下形式:

using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
Rectangle rectangle = shape as Rectangle;
if (rectangle != null) {
Console.WriteLine("Width : " + rectangle.GetWidth());
}
else {
Circle circle = shape as Circle;
if (circle != null)
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}
复制代码

在上面代码的粗体部分中,我们在没有测试shape对象类型的情况下,就用as操作符把shape转换成Rectangle类型的对象。如果shape正好是一个Rectangle,则shape被转换成为Rectangle类型的对象并保存到rectangle变量,然后我们调用它的GetWidth方法。如果这种转换失败,则我们进行第二次尝试。这一次,shape被转换成为Circle类型的对象并保存到circle变量。如果shape确实是一个Circle对象,则circle现在引用了一个Circle对象,我们调用它的GetRadius方法。

4.17、库

C#没有自己的类库。但是,C#共享了.NET的类库。当然,.NET类库也可以用于其他.NET语言,比如VB.NET或者JScript.NET。值得一提的是StringBuilder类,它是对String类的补充。StringBuilder类与Java的StringBuffer类非常相似。

4.18、垃圾收集

C++已经让我们认识到手工管理内存是多么缺乏效率和浪费时间。当你在C++中创建了一个对象,你就必须手工地拆除这个对象。代码越复杂,这个任务也越困难。Java用垃圾收集器来解决这个问题,由垃圾收集器搜集不再使用的对象并释放内存。C#同样采用了这种方法。应该说,如果你也在开发一种新的OOP语言,追随这条道路是一种非常自然的选择。C#仍旧保留了C++的内存手工管理方法,它适合在速度极端重要的场合使用,而在Java中这是不允许的。

4.19、异常处理

如果你听说C#使用与Java相似的异常处理机制,你不会为此而惊讶,对吧?在C#中,所有的异常都从一个名为Exception的类派生(听起来很熟悉?)另外,正如在Java中一样,你还有熟悉的try和catch语句。Exception类属于.NET System名称空间的一部分。

四、Java没有的功能

C#出生在Java成熟之后,因此,C#拥有一些Java(目前)还没有的绝妙功能也就不足为奇。

4.1、枚举器

枚举器即enum类型(Enumerator,或称为计数器),它是一个相关常量的集合。精确地说,enum类型声明为一组相关的符号常量定义了一个类型名字。例如,你可以创建一个名为Fruit(水果)的枚举器,把它作为一个变量值的类型使用,从而把变量可能的取值范围

public class Demo {
public enum Fruit {
Apple, Banana, Cherry, Durian
}
public void Process(Fruit fruit) {
switch (fruit) {
case Fruit.Apple:
...
break;
case Fruit.Banana:
...
break;
case Fruit.Cherry:
...
break;
case Fruit.Durian:
...
break;
}
}
}
复制代码

在上例的Process方法中,虽然你可以用int作为myVar变量的类型,但是,使用枚举器Fruit之后,变量的取值范围限制到了Applet、Banana、Cherry和Durian这几个值之内。与int相比,enum的可读性更好,自我说明能力更强。

4.2、结构

结构(Struct)与类很相似。然而,类是作为一种引用类型在堆中创建,而结构是一种值类型,它存储在栈中或者是嵌入式的。因此,只要谨慎运用,结构要比类快。结构可以实现接口,可以象类一样拥有成员,但结构不支持继承。

然而,简单地用结构来取代类可能导致惨重损失。这是因为,结构是以值的方式传递,由于这种传递方式要把值复制到新的位置,所以传递一个“肥胖的”结构需要较大的开销。而对于类,传递的时候只需传递它的引用。

下面是一个结构的例子。注意它与类非常相似,只要把单词“struct”替换成“class”,你就得到了一个类。

struct Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
复制代码

4.3、属性

C#类除了可以拥有域(Field)之外,它还可以拥有属性(Property)。属性是一个与类或对象关联的命名的特征。属性是域的一种自然扩展——两者都是有类型、有名字的类成员。然而,和域不同的是,属性不表示存储位置;相反,属性拥有存取器(accessor),存取器定义了读取或者写入属性值时必须执行的代码。因此,属性提供了一种把动作和读取、写入对象属性值的操作关联起来的机制,而且它们允许属性值通过计算得到。

在C#中,属性通过属性声明语法定义。属性声明语法的第一部分与域声明很相似,第二部分包括一个set过程和/或一个get过程。例如,在下面的例子中,PropertyDemo类定义了一个Prop属性。

public class PropertyDemo {
private string prop;
public string Prop {
get {
return prop;
}
set {
prop = value;
}
}
}
复制代码

如果属性既允许读取也允许写入,如PropertyDemo类的Prop属性,则它同时拥有get和set存取过程。当我们读取属性的值时,get存取过程被调用;当我们写入属性值时,set存取过程被调用。在set存取过程中,属性的新值在一个隐含的value参数中给出。

与读取和写入域的方法一样,属性也可以用同样的语法读取和写入。例如,下面的代码实例化了一个PropertyDemo类,然后写入、读取它的Prop属性。

PropertyDemo pd = new PropertyDemo();
pd.Prop = "123"; // set
string s = pd.Prop; // get
复制代码

4.4、以引用方式传递简单数据类型的参数

在Java中,当你把一个简单数据类型的值作为参数传递给方法时,参数总是以值的方式传递——即,系统将为被调用的方法创建一个参数值的副本。在C#中,你可以用引用的方式传递一个简单数据类型的值。此时,被调用的方法将直接使用传递给它的那个值——也就是说,如果在被调用方法内部修改了参数的值,则原来的变量值也随之改变。

在C#中以引用方式传递值时,我们使用ref关键词。例如,如果编译并运行下面的代码,你将在控制台上看到输出结果16。注意i值被传递给ProcessNumber之后是如何被改变的。

using System;
public class PassByReference {
public static void Main(String[] args) {
int i = 8;
ProcessNumber(ref i);
Console.WriteLine(i);
}
public static void ProcessNumber(ref int j) {
j = 16;
}
}
复制代码

C# 中还有一个允许以引用方式传递参数的关键词 out ,它与 ref 相似。但是,使用 out 时,作为参数传递的变量在传递之前不必具有已知的值。在上例中,如果整数 i 在传递给 ProcessNumber 方法之前没有初始化,则代码将出错。如果用 out 来取代 ref ,你就可以传递一个未经初始化的值,如下面这个修改后的例子所示。

using System;
public class PassByReference {
public static void Main(String[] args) {
int i;
ProcessNumber(out i);
Console.WriteLine(i);
}
public static void ProcessNumber(out int j) {
j = 16;
}
}
 
复制代码

经过修改之后,虽然i值在传递给ProcessNumber方法之前没有初始化,但PassByReference类能够顺利通过编译。

4.5、C#保留了指针

对于那些觉得自己能够恰到好处地运用指针并乐意手工进行内存管理的开发者来说,在C#中,他们仍旧可以用既不安全也不容易使用的“古老的”指针来提高程序的性能。C#提供了支持“不安全”(unsafe)代码的能力,这种代码能够直接操作指针,能够“固定”对象以便临时地阻止垃圾收集器移动对象。无论从开发者还是用户的眼光来看,这种对“不安全”代码的支持其实是一种安全功能。“不安全”的代码必须用unsafe关键词显式地标明,因此开发者不可能在无意之中使用“不安全”的代码。同时,C#编译器又和执行引擎协作,保证了“不安全”的代码不能伪装成为安全代码。

using System;
class UsePointer {
unsafe static void PointerDemo(byte[] arr) {
.
.
}
}
复制代码

C#中的unsafe代码适合在下列情形下使用:当速度极端重要时,或者当对象需要与现有的软件(比如COM对象或者DLL形式的C代码)交互时。

4.6、代理

代理(delegate)可以看作C++或者其他语言中的函数指针。然而,与函数指针不同的是,C#中的代理是面向对象的、类型安全的、可靠的。而且,函数指针只能用来引用静态函数,但代理既能够引用静态方法,也能够引用实例方法。代理用来封装可调用方法。你可以在类里面编写方法并在该方法上创建代理,此后这个代理就可以被传递到第二个方法。这样,第二个方法就可以调用第一个方法。

代理是从公共基类System.Delegate派生的引用类型。定义和使用代理包括三个步骤:声明,创建实例,调用。代理用delegate声明语法声明。例如,一个不需要参数且没有返回值的代理可以用如下代码声明:

delegate void TheDelegate();
创建代理实例的语法是:使用new关键词,并引用一个实例或类方法,该方法必须符合代理指定的特征。一旦创建了代理的实例,我们就可以用调用方法的语法调用它。

4.7、包装和解除包装

在面向对象的编程语言中,我们通常使用的是对象。但为了提高速度,C#也提供了简单数据类型。因此,C#程序既包含一大堆的对象,又有大量的值。在这种环境下,让这两者协同工作始终是一个不可回避的问题,你必须要有一种让引用和值进行通信的方法。

在C#以及.NET运行时环境中,这个“通信”问题通过包装(Boxing)和解除包装(Unboxing)解决。包装是一种让值类型看起来象引用类型的处理过程。当一个值类型(简单数据类型)被用于一个要求或者可以使用对象的场合时,包装操作自动进行。包装一个value-type值的步骤包括:分配一个对象实例,然后把value-type值复制到对象实例。

解除包装所执行的动作与包装相反,它把一个引用类型转换成值类型。解除包装操作的步骤包括:首先检查并确认对象实例确实是给定value-type的一个经过包装的值,然后从对象实例复制出值。

Java对该问题的处理方式略有不同。Java为每一种简单数据类型提供了一个对应的类封装器。例如,用Integer类封装int类型,用Byte类封装byte类型。

本部分比较了C#和Java。这两种语言很相似,然而,说C#是Java的克隆或许已经大大地言过其实。面向对象、中间语言这类概念并不是什么新东西。只能说各有优劣。

后续继续更新中。

猜你喜欢

转载自juejin.im/post/7087482108943269919
今日推荐