早期开发的时候一直用c/c++,后来主要用的是java。最近需要用下c#。
熟悉了下c#,发现c#语言在对c/c++基础上做了很多简化,同时参考了很多java的语法习惯,本来在语法上c/c++就有很多和java类似的地方,现在c#就类似的地方更多了,不过还是有很多区别。
本文总结下c# 和c++及 java的语法差别,重点比较与java的区别,便于相互学习,加强理解。
一、c#与c++的区别
相比c++,c#做了很多简化,使的编写代码更加容易,重要的变化由如下方面:
1、抛弃了指针的使用。在c#中没法使用指针了,在对象成员的调用也只有一种方式,就是通过 . 来引用。
2、完全面向对象。在c#中,不再有全局的函数了。所有的代码都必须在类中。连基本的基本类型都有方法,如:
class Myapp
{
private static void Main(string[] args)
{
int a = ;
string value = a.ToString();
int b = int.Parse(value);
}
}
3、没有了头文件的概念,类的定义和实现都在类中。
4、引入了命名空间的概念,用于划分源代码,类似java的包的概念。
5、类的继承上废除了多继承的概念,一个类智能有一个父类,而在c++中可以有多个父类。
6、引入了类似java中的接口概念。
7、引入了属性的概念,简化了对成员变量的操作。
下面我们重点介绍c# 与java的对比。
二、c#与java基本语法的对比
1、程序框架
同java一样,c#要求所有的代码都要在类中,不再同c++一样,既可以定义类,也可以定义全局的方法。
java程序的入口代码必须是某个类中的如下的方法 :
public static void main(String[] args);
而在c#中,入口方法是
static void Main(string[] args)
注意:c#中Main的第一个字母M是大写,且Main方法的参数可以不定义。方法的修饰符也可以不加public。
2、包和命名空间
在java中,通过包来组织java源文件。在c#中,通过命名空间来组织c#源文件。 它们的含义和作用是类似的。
区别是,java强制要求java源文件的存放补录必须与包路径严格一致。
在java中,通过import引入包,而在c#中通过using引入命名空间。
两者都可以通过全路径来引用类(这样就不需要import和using了)。
3、基本数据类型
两者大部分基本类型的名称都一样,如 int ,char等。枚举定义的关键字都是 enum。数组定义和使用语法也是类似。
典型的区别是:
1)布尔型在java中是 boolean,而在c#中是bool。
2)字符串类型名在java中 是 String ,而在c#中是 string (第一个字母s是小写)。
最关键的是在java中进行字符串内容的比较要用 equlas方法,而在c#中直接可以用 == 比较。
4、语句
两者的 if 语句,switch语句,for,while, do ...while,break, continue 使用方式都一致。
有个区别是,在java中,可以用for循环遍历集合。而在c#中需要用单独的关键字 foreach来遍历,但使用方法一样。
三、c#与java面向对象语法的对比
java和 c# 都是通过 class关键字来定义类,也都不需要头文件。类的实现代码用 { }扩起。
1、类的成员
c#中引入了一个属性的概念。
我们知道在java中,为了提高安全性,我们定义成员变量一搬建议定义private的,这样为了其它类能访问该变量,再定义相应的get 和 set 方法,代码如:
class A{
private int num;
public void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
} class B{
public void test(){
A a = new A();
a.setNum();
int num = a.getNum();
}
}
而在c#中,为了简化操作,引入了一个属性的概念,举例如下
class A
{
public int num { get; set; }
} class B
{
public void test()
{
A a = new A();
a.num = ;
int re = a.num;
}
}
在类A中,定义了一个属性 num,在类B中,直接通过属性名可以访问,简化了java中的语法。
在A中定义num时,可以只有get或set标记,表示只读或只写的。
上面的方式存在一个问题,如果在get或set操作时需要通过一定的计算来返回值,既可以采用如下的代码方式。调用方法不变。
class A
{
private int _num;
public int num
{
get { return _num; }
set { _num = value*; }
}
}
与上面相比,先定义了一个普通的变量_num(这里是private的,不让外部直接访问)。
然后定义了一个属性num,并且有相应的代码,其中set代码中的 value是隐式参数,是在给属性设置值时传入的参数。
这和java代码的使用方式类似了,只是省去了需要定义 setXXX类似的方法。 另外调用时简化了,直接通过属性名就可以。
2、访问修饰符
在java中,有 public, protected,private 和 缺省四种级别的修饰符,在定义类和成员时不加修饰符,默认是缺省的。
在c#中,缺省修饰符需要显示的用internal关键字标识,并且如果定义时不加修饰符,默认不是internal 而是 private的。并且在c#中,可以 protected internal组合使用,其含义是访问仅限于该类或当前程序集(同一命名空间)的派生类。单独的protected范围比protected internal大,允许的派生类不限于当前程序集,加上了internal将派生类限制在当前程序集。
3、类的继承语法
两者都支持类的继承,在c++中支持多个父类,在c#中进行了简化,同java一样,一个类只允许有一个父类。
在java中通过extends关键字继承类,在c#中采用的是c++的语法,用 :后跟父类表示。两者对父类构造函数的处理机制也不一样。下面举例说明。
下面代码是java的例子:
class A{
private int num;
public A(int num){
this.num = num;
}
} class B extends A{
public B(int num) {
super(num);
//其它代码,super语句必须放在第一行。
}
}
可以看出,在java中使用extends关键字来继承父类,用 super方法来调用父类的构造函数。
下面是c#的代码
class A{
private int num;
public A(int num){
this.num = num;
}
} class B : A{
public B(int num):base(num) { }
}
可以看出,在c#中是通过 : 标识符来继承父类,而且是通过base关键字来调用父类构造函数。
4、接口
在java中,引入了接口类型,一个类可以实现一个或多个接口.
在c#中,参考java引入了接口的机制,注意在c++中没有接口的机制。
但两者在实现接口的语法上有些区别,在java中通过implements关键字来标识,而在c#中,使用同继承一样。
下面是java的语法例子
interface hello1{}
interface hello2{} class A implements hello1,hello2{
private int num;
public A(int num){
this.num = num;
}
} class B extends A implements hello1,hello2{
public B(int num) {
super(num);
//其它代码,super语句必须放在第一行。
}
}
在java中,当一个类同时需要继承父类和实现接口时,extends语句要放在 implements语句前。
下面是c#的语法例子
interface hello1 { }
interface hello2 { }
class A:hello1,hello2{
private int num;
public A(int num){
this.num = num;
}
} class B : A,hello1,hello2{
public B(int num):base(num) { }
}
在 c#中,当一个类同时需要继承父类和实现接口时,因为都是跟在 : 后面,需要将父类名放在前面,接口名在后面,如上面例子。
5、重载、重写(多态)
所谓重载,就是一个类可以有多个方法,方法名一样,但参数信息不一样。这个java和 c#都支持。
所谓重写,是面向对象编程中多态特性的体现。就是子类可以定义和父类一样的方法,具体执行时是执行父类方法还是子类方法,动态根据实例绑定。java和c#都有这个特性,区别是使用语法上有些不同。
java的例子:
package com; public class Demo {
public static void main(String[] args) {
A a = new A();
a.show(); //输出的是 i am A B b = new B();
b.show(); //输出的是 i am B A c = new B();
c.show(); //输出的是 i am B. 多态体现,动态决定调用父类或子类方法。因为c实际是指向B的实例
}
} class A {
public void show() {
System.out.println("i am A");
}
} class B extends A {
public void show() {
System.out.println("i am B");
}
}
可以看出,子类B通过定义相同的方法,重载了父类A的方法。实际执行时,根据变量指向的具体实例决定调用是父类或子类的方法。
c#的例子,c#在多态上的语法参考了c++的特点,需要通过virtual和override关键字来标识。
class Myapp
{
private static void Main(string[] args)
{
A a = new A();
a.show(); //输出的是 i am A B b = new B();
b.show(); //输出的是 i am B A c = new B();
c.show(); //输出的是 i am B. 多态体现,动态决定调用父类或子类方法。因为c实际是指向B的实例
}
} class A {
virtual public void show() {
System.Console.WriteLine("i am A");
}
} class B : A {
override public void show() {
System.Console.WriteLine("i am B");
}
}
可以看出,在c#中,要想实现继承的多态性,需要在父类方法中用virtual关键字标识,然后在子类的方法中用override关键字进行标识。
需要说明的是,对于父类定义的virtual或非vritual方法,子类可以不重载,但也可以定义同样的方法(没有用override关键字),这是允许的,这时子类的该方法将隐藏父类的方法,这个不是动态的特性。
6、抽象类和抽象方法
有时在父类中定义一个序方法,但该方法在基类中不需要有具体的实现,需要在子类中实现。这个特性java和c#都支持。
只是语法上有细微差别。这里举例说明下。
java的例子:
package com; public class Demo {
public static void main(String[] args) {
B b = new B();
b.show(); //输出的是 i am B A c = new B();
c.show(); //输出的是 i am B. 多态体现,动态决定调用父类或子类方法。因为c实际是指向B的实例
}
} abstract class A {
public abstract void show();
} class B extends A {
public void show() {
System.out.println("i am B");
}
}
在java中,是通过abstract关键字标识抽象类和抽象方法的,具体有如下细节:
1)一个类有抽象方法,其所在的类必须定义为抽象类。
2)子类继承抽象父类,要么实现父类的抽象方法,要么继续把自己定义为抽象类。
3)抽象方法没有方法体。抽象类不能实例化。
下面是c#中的例子
class Myapp
{
private static void Main(string[] args)
{
B b = new B();
b.show(); //输出的是 i am B A c = new B();
c.show(); //输出的是 i am B. 多态体现,动态决定调用父类或子类方法。因为c实际是指向B的实例
}
} abstract class A {
abstract public void show();
} class B : A {
override public void show() {
System.Console.WriteLine("i am B");
}
}
除了子类方法定义需要加override关键字,其它语法要求与java一样。另外对抽象类和抽象方法的要求也与java中的要求一致。
四、c#与java其它对比
1、常量和只读字段
在java中,可以通过final来定义只读变量,可以使成员变量,可以是方法的内的局部变量,也可以是形参。并且定义和初始化可以一起,也可以分开。
如果是定义的的final成员变量,则需要在构造函数中初始化。 如果是方法内的局部变量,可以在定义后赋值。
在c#中,可以用 const关键字定义常量,要求必须在定义时理解赋值。
在c#中,还可以用 readonly来定义类的成员(注意不能定义局部变量),可以在定义时赋值,也可以定义时不赋值,而在构造函数中赋值。
2、异常处理
java和 c#有类似的异常处理机制和语法,都有 try, catch, finally, throw 这几个使用关键字。 区别是:
1)java中的异常分为检查时异常和运行期异常两种,而c#中只有一种(运行期异常)。
2)在java中 ,catch语句必须带异常类型参数。
而在c#中,可以不带,表示是缺省的,表示对所有异常处理,但如果定义了多个异常,不带参数的必须放在最后。
3、参数传递
在java中,参数传递只有值传递一种方式。对于基本数据类型,如 int等,在方法内无法修改方法的值。
而在c#中,保留了c/c++的特性,通过 ref 和 outer 关键字,可以让形参和实参指向同样的值,这样在方法内修改形参的值,方法外的实参值也跟着变化。
不过,为了提高程序的可读性,建议少用这种特性。
4、内部类
在java中,支持内部类,包括匿名内部类。但在c#中,支持内部类,但不支持匿名内部类。
如下面的匿名内部类在java中是允许的,但在c#中是不行的。
package com; public class Demo {
public static void main(String[] args) {
A a = new A() {
public void show() {
;
}
};
a.show();
}
} abstract class A {
public abstract void show();
}
5、分部类
在c#中,编写代码时可以将一个类的定义分到多个cs文件中,后续由编译器自动合并。这个在java中不支持。
这个特性感觉很不好,强烈建议正常情况下不要使用。