C# vs java

来源:互联网 发布:淘宝卖土特产的代价 编辑:程序博客网 时间:2024/05/19 10:37

今天忽然搜索到一篇《C# 和 Java 的比较》

感觉很有意思,就汇总了一下


整体比较


1.语言先进性
由于C#出现的比较晚,所以C#在设计之初是吸收了很大一部分的Java的优秀特性,然后自己有添加很不错的特性出来,当然有一些是语法糖,不过好吃的糖谁不喜欢呢?最喜欢使用的C#的就是using,可以不用手动关闭的特性,可以写出层次特别棒的层次感出来。比如C#之前提出委托,的确是要方便的多,还有一些Lamda的支持也要更早一点,JDK8也加入了支持。
C#:★★★★
Java:★★★


2.平台适用性
Java遍地开花,JVM的无处不在,而C#现在仍然抱着自己的MS帝国不放,或者是C#没有及时的拥抱开源(虽然Net的核心代码几乎可以反编译出来,根本没有混淆)整个平台。JVM也是占了Andriod的光,不过应该是相互利用。
C#:★★★
Java:★★★★★


3.工具易用性
Eclipse之外还有更好的选择么?当然Inteliij的也许是个好选择,NetBean好像使用的人很少。主流的Eclipse虽然已经足够强大,但是在宇宙第一IDE:Visual Studio面前还是脆弱了点,VS默许盗版遍地但是到头来还是有隐患,Eclipse的免费让Java程序员默默的鄙视了下C#,也许VS免费会是一个好选择。
C#:★★★★
Java:★★★★


4.工业使用率
这个我也不太好统计,在我的理解下,第一由于C#是闭源产品,维护的责任在MS而不在一般的公司,当然绝大多数Java公司也是没有能够去维护Java项目的。不过Java的Apache社区实在是变态的让你惊叹,不过MS也不是吃醋的,那么多优秀的程序员,整体在技术上大家是差不多的,最多是认知不一样,在工业使用中:
Web端: Java > C#
PC端: C# >> Java
手机端: Java >> C#



细节语法


  1.访问控制方面:


C#有

public、internal、protected、private

比java多了个internal,其实它约等于java的,internal表示同一个编译集合(如exe、dll)下的类可以互访。 


protected,java和C#有区别。在java中,protected和包访问级别差不多,即不是私有的。而在C#中,protected和private差不多,即它标志的成员是私有的。 


注1:有这样一种情况:类中的一个成员,需要它能被子类访问到,同时能被同一个集合中(无论是java的包还是C#中的编译集合)的其他类访问到,怎么办呢?在java中,只要用protected就行了。在C#中,可以同时指定internal protected(二者的顺序随意)。 
注2:在有这样一种情况:类中的一个成员,需要它能被子类访问到,但不能被同一个集合中(无论是java的包还是C#中的编译集合)的其他类访问到,怎么办呢?在C#中,可以指定protected(二者的顺序随意)。但java就无能为力了。

 

评论:我很喜欢C#的internal,在Java如果一个为API中某些类提供服务的类,为了不对外暴露,只能用包级私有,并和被服务的类放在一个包中,如果有多个包中的类都要被服务,就没法办了。而C#的internal就很好的仅限当前“集合”(可能有多个namespace)中的类访问。我又不喜欢C#的“集合”的感念。反射的时候还必须给出DLL或EXE的文件名(如果不在当前DLL或EXE中),而Java是不需要的,只有在WEB-INF/classess和lib 下的都可以。特别是当API要反射它的调用者时,作为类库的DLL怎么知道哪个EXE在调用自己?

 

 

2。C#中有static constructor的概念,这跟java中的静态初始模块一样。 
     C# : static [类名]{} 
     java :static{}

 

批注:原来是这样写,我找了半天也没搜到C#中的静态代码段如何个写法,如今得来全不费工夫。

 


3。Java中的main函数必须是public static void main(String[] args)的样子,否则虚拟机拒绝运行。C#中,Main函数可以是private的(甚至可以是protected),可以没有参数,可以返回int值。有点像C语言。 
4。发现csc.exe(java.exe)有一个功能很好,100后面加一个小写的L,它会警告:“l”后缀容易与数字“1”混淆;为清楚起见,请使用“L”。

 

批注:MS的人性化关怀呀。

 


5.C#提供了一种机制,使得某个变量可以被动态赋值一次,以后就不能再改了。那就是readonly关键字的功能。

批注:这个机制可能在特定场合下很有用。比如:抽签,这样你就不能耍赖了。

 

 

6.java在继承、多态方面,比C#强多了。Java默认的多态,C#要求加上virtual(被继承的方法)和override(继承的方法),而且C#要求不能改变原来的访问修饰符,不像java那样,可以指定更加宽松的访问方式。如果有人利用C#来写程序,必须经常带上virtual和override,还必须照抄原来的访问控制符,不会很郁闷吗?难道有人用C#的面向对象特性时,会舍弃多态的特性?这会引起多大的混乱啊。 

public class BaseClass{    public virtual void DoWork() { }         public virtual int WorkProperty    {        get { return 0; }    }}public class DerivedClass : BaseClass{    public override void DoWork() { }    public override int WorkProperty    {        get { return 0; }    }}


  多态是面向对象的精髓,像java那样默认不是更好吗?

 

批注:C#号称和Java有90%的相似性,但它必将是从C++发展而来的,在虚函数的概念上还是延续着C++的方式。习惯了Java之后,真的是感觉别扭。Java多好:默认情况下都是虚函数,允许被子类重写,但是对于有意不想让子类重写的方法用final关键字来修饰。

 

.C#命令输入和输出语法是:Console.ReadLine()和Console.WriteLine()(当然不换行的话就去掉Line,这些想必大家都知道,所以文章中只提供比较常用的)。

Java命令输入和输出语法是:System.In.Read()和System.Out.Println()

心得:其实两个语言语法本质都一样就是名字不一样。

 C#中在一个.cs文件中可以存在多个Public修饰符修饰的类。

Java中在一个.java文件中有且仅能存在一个Public修饰符修饰的类。

心得:目前还不知道为啥java会有这样的限制,不过这一点很容易范错误。

 C#中Switch语句接受的类型是byte、char、string.

Java中Switch语句接受类型是byte、char、string、int

心得:java语言貌似支持的多一点。

C#中存在属性与字段是两个概念,属性是属性访问器,是对字段的封装。

Java中貌似没有属性访问器,属性和字段是一个概念,通过自己实现方法来封装属性的访问,一般是getxxx()和setXXX()方法

心得:这点我还是比较赞同C#的做法,毕竟从编程的角度来说,C#将属性作为语法通用的,不需要用户手动去实现,省去了大量的重复编码。

 C#中构造函数调用基类或者当前重载构造函数语法是直接在构造函数后面:base()和:this()就可以实现。

Java中相同原理语法是在构造函数模块中的第一条语句使用this()或者super()。

心得:这个我个人觉得c#设计的比较好,因为从程序的可读性方面,这样也容易使得读者容易读懂代码,对于程序员来说也容易区分,比较容易知道运行顺序;而Java的实现方式必须使得使用者记住在第一条语句,有的时候在编写一个大的程序的时候,很有可能会忽略这个。

C#中存在静态构造函数,即在类运行时,只执行一次。

Java中是不存在静态构造函数的。

心得:这点我也不知道为啥Java中不存在,不过在网上查了下,有网友说是因为这违背了面向对象的思想,后面也跟了一大堆理由,不过我总看都觉得该网友没有很好理解面向对象思想。这个问题我觉得也值得深入研究,有兴趣的可以跟我一起研究下,至于C#中出现这个,我也没仔细深入研究为什么会出现这个情况,我只知道有的时候在类构造的时候,有些参数是执行一次的,但是细想这些也可以通过其他方法实现,所以暂时也说不出C#中静态构造函数的优点,不过竟然微软定义了该语法,而且是在3.5定义的我相信必有它的用处。

 C#中继承接口和抽象类都是使用:符号的。

Java中继承用Extends标识符,实现用Implements,且如果同时存在,Extends必须在Implements之前。

心得:C#相对来说是比较自由,直接用:就实现继承和实现。

可是Java中区别比较明确,这点我觉得还是java比较好,这样写的代码比较直观,可读性比较好,虽然写的字数多。

C#中用Sealed修饰符表示密封,即不可继承,用Const表示常量。

Java中使用Final修饰符表示密封和常量。

心得:Java中用一个修饰符代表2个功能,从设计角度说,不怎么符合功能单一原则,所以我个人觉得必要的时候还是拆分表示,可读性和区别性会好点。

C#的Is运算符可以用于类型的判别。

Java中用Instanceof进行类型的判别。

C#中存在Internal修饰符,表示程序集的访问级别。

Java中存在类似的修饰符Default,表示同一类和同一包中的访问级别。

心得:C#中访问修饰符大概有以下几种:

Public:公共的,最大的访问权限。

Internal:程序集的,命名空间级别的访问权限。

Protected:受保护的,类以及子类的访问级别权限。

Private:私有的,只能在该类中访问级别权限。

Java访问修饰符有以下几种:

Public:公共的,最大访问级别。

Protected:受保护的,同一类,同一包中的类和子类访问级别。

Defalut:同一类,同一包中的类访问级别。

Private:私有的,同一类中访问级别。

纵观,其实都差不多,但是平时必须注意,细节决定成败。

C#中异常直接用Throw抛出。

Java中如果要抛出异常,貌似必须现在方法或者类定义后面用Throws进行声明。

心得:这点也没深入研究,所以不发表观点。

C#中有命名空间,用Using关键字引入。

Java中有包,类似命名空间定义,用Import引入包。

C#中同步锁定,用Lock关键字。

Java中同步锁定,用Synchronized关键字。

C#基本数据类型中有string,且有包装类String。

Java基本数据类型是没有string,用String取代。


 

C#中new还可以用来指定子类的某个方法要隐藏父类的具有相同签名的方法。这是不是多余的?你不用也可以,不过csc.exe会警告你,如“lan.Other.Main(string[])”隐藏了继承的成员“lan.HelloWorld.Main(string[])”。如果是有意隐藏,请使用关键字 new。 
但是话又说回来,C#这样做也是有原因的。如果类B继承了类A,B接下来有添加了一个方法叫做hi(),那是B特有的。然后类A(假设是别人来维护的,你不能看到源码)突然也增加了一个方法hi()。如果B自己那个hi()跟A那个hi()的返回值不一样,当你更新类库A后,可能导致程序运行错误或不能编译。C#就很好就地避免了这种问题。(虽然这种问题出现的概率挺小的…)


 

 

C#中,防止一个类被继承,要用关键字sealed。而定义一个常量时,要用const。 
  像java统一用final多好啊。


 


在C#中,要比较两个引用变量是否指向同一个对象,不能用java中的= =,而要用Object里的ReferenceEquals方法。C#中,不能用一个类的实例去调用该类的类方法,必须用类名。所以java中的o1= =o2等价于C#中的Object.ReferenceEquals(o1,o2)。

 


 

 

C#中没有原始类型的包装类?,但是也提供自动装拆箱的功能,和java有的一样。区别是,C#的装箱是自动的,拆箱就要强制转换了。 
int i=100; 
object obj=i; 
i=(int)obj; 
具体怎么装和拆,我们不知道。只知道CLR将int转换成object了。

 

int -> Int32;string -> String;甚至 object -> Object。是我的理解有误吗?至少从VS中关键字高亮的颜色就能区分

 

 
java的内部类有时候帮助很大。到了C#那,就只提供静态的内部类了。这意味着外部类只相当于是一个命名空间而已。C#中的内部类能访问外部类的私有成员,这可能会让它有点用。

 

批注:这一点我没什么研究,在Java中也是尽量不使用嵌套类。其实静态成员类+内部类(含:非静态成员类、匿名类、局部类)=嵌套类。如果要用也最好优先使用静态成员类,内部类最好少碰,尤其是匿名类(但有些地方还必须用它,矛盾呀)。

 


C#中虽然有运算符重载,但是为了整个.net的一致性,应该不会鼓励使用。因为有的.net语言没有运算符重载,而.net的一个目标就是消除各种语言的差别。


 


C#多了一个struct值类型,它就跟原始类型一样。微软在必要的时候会帮你将struct封装成Object,就像封装int类型一样。以至于你可以认为struct也是由Object继承而来,虽然struct本身并不支持继承。(struct可以不用new来初始化,但它里面的内容必须初始化后才能调用其方法;struct没有析构方法;struct没有默认的构造方法)。

 结构类型(Struct types)
      (1)结构类型和类一样,可以声明构造函数、数据成员、方法、属性等。
      (2)结构和类的最根本的区别是结构是值类型,类是引用类型。
      (3)和类不同,结构不能从另外一个结构或者类派生,本身也不能被继承,因此不能定义抽象结构,
           结构成员也不能被访问权限控制字protected修饰,也不能用virtual和abstract修饰结构方法。
      (4)在结构中不能定义析构函数。
      (5)虽然结构不能从类和结构派生,可是结构能够继承接口,结构继承接口的方法和类继承接口的方法基本一致。


  using System; //结构定义          struct point{public int x,y;//结构中也可以声明构造函数和方法,变量不能赋初值          }   class Test{      static void Main(){                point P1;                P1.x=166;                P1.y=111;                point P2;                P2=P1;//值传递,使P2.x=166,P2.y=111                point P3 = new point();//用new生成结构变量P3,P3仍为值类型变量                //用new生成结构变量P3仅表示调用默认构造函数,使x=y==0。             }          }



 C#变量的初始值:一般简单类型的初始值是0,布尔型的是false,引用类型的是null。
    对于复杂结构类型,其中的每个数据成员都按此种方法赋值,显得过于麻烦。
    由于数值类型都是结构类型,可用new语句调用其构造函数初始化数值类型变量,
   例如:int j=new int()。
    请注意,用new语句并不是把int变量变为引用变量,j仍是值类型变量,
    这里new仅仅是调用其构造函数。所有的数值类型都有默认的无参数的构造函数,其功能就是为该数值类型赋初值为默认值。
    对于自定义结构类型,由于已有默认的无参数的构造函数,不能再定义无参数的构造函数,但可以定义有参数的构造函


java中类的访问控制符只能是public,或者没有(即默认的包访问)。但是C#中,class和interface的访问控制符可以是public / private / internal / protected / internal protected。当然你必须先取得对类的访问,才可能访问到类的成员。

一个C#集合中可以包含多个public的类或接口,跟文件名没有关系。

 

批注:MS没有像Sun那样给namespace的命名方法一个官方的建议(Sun建议package的命名法为域名的逆序,均为小写字母)。而且.Net也不要求按目录及文件名保存类(Java最早也没有此要求)。哪种方式更好呢?我想还是一个习惯问题,至少我还是觉得Java的方式更清晰明了。而不少.Net的工程,打开后所有的Source文件都在一个“大平层”,很难受的说。

 


C#中的接口不能包含常量,而java可以。

 

批注:接口中应该/可以包括常量的定义吗?公说公有理,婆说婆有理。个人认为,即使Java运行,也尽量别这么做。

 


C#中的as和java中的instanceof功能一样。但C#提供一个据说是效率更高的as关键字。

 

批注:原作者笔误吧?是否应该是 is ? as 是强制类型转换,和更常见的 变量a = (类型)变量b; 不同的是,如果转换失败as会返回null,而括号式转换回抛出异常。as的语法和AS3语言中是相同的。


接口和抽象类在C#和java中都差不多,这里提一下接口设计和抽象类设计的区别之处。如果你更改了一个接口的设计,比如增加了一个方法,使用你以前的代码的用户将不得不改变他们的代码,否则不能运行和编译。但是如果是一个抽象类,你可以提供一个含默认实现的方法,用户的代码则不需要改变。

 

批注:这个事实在Java和C#中都是一样的。但是,就接口和抽象类(即使包括骨架类)的选用依据可不是这个,这只是一个表现而已。再有,接口一旦发布了,就是你对外的一种承诺。之后即使是版本升级也不能再做任何改动,哪怕是增加新方法。那非要增加怎么办?如果同时提供的骨架类也控制在你的手里,可以通过在这个骨架类(其实就是抽象类的一种用法,骨架类实现接口,实际类再继承这个骨架类,骨架类中可以为实际类实现一些通用的、或默认的方法)提供一份新增方法的默认实现来达到目的。但这并不是明智的,因为版本升级时需要增加的新方法,往往是一些实实在在的干活儿的方法,在骨架类中给出一个默认实现往往没什么实际意义。更好的做法,是写一个新的接口去继承原来的接口,把新增的方法在子接口中声明。这样可以保持100%向前兼容,需要实现新方法的类属于伴随此次接口升级或日后要取实现的类。

 


C#中一个类实现一个接口时,它的相关方法不必指明override;但一个类继承一个抽象类的抽象方法时,必须加上override,否则视为隐藏。(事实上,只有抽象方法或者是virtual、或者是接口方法才能被覆盖即override。不能无缘无故地override。)

 

批注:这个也没什么好说的,各家有各家的写法,如果非要说也还是习惯的问题。同上半篇的6。

 


C#中存在一个“多态起始点”的问题。如果一个类实现了接口的某个方法,只是接口到该类才有多态的功能,若要这种多态继承下去,该类必须指明是virtual,多态起始了,接下来的子类提供override就能多态了,不需要更多的virtual。

  但是抽象类的抽象方法默认就是一个多态起始点,后续的子类只要override就行了。

 

批注:感觉还是同上半篇的6。也许是我还没深刻理解?

 


当一个类实现了两个接口,两个接口有一个相同的方法定义,C#有一种解决机制,叫做显示实现。Java干脆就不处理这种情况,反正实现之后就能调用了,不必指明是哪个接口的,留给程序员自己考虑。 
当然C#中的显示实现还有其他功能。举个例子,接口A有一个方法叫做f(),类B实现了A。按理说B的实例就能自由调用f()了,但是如果有这样的要求:B的实例只有被cast成A之后才能调用f()。在java中,这样无理的要求是不允许的。但是C#可以做到,就是通过显示实现的方式。有谁会使用这样的特性?



1.引包using System;JAVA用import

2.构造函数和java语法相同
3.析构函数
  变量和类的对象都有生命周期,生命周期结束,这些变量和对象就要被撤销。
  类的对象被撤销时,将自动调用析构函数。一些善后工作可放在析构函数中完成。
  析构函数的名字为~类名,无返回类型,也无参数。Person类的析构函数为~ Person()。
  C#中类析构函数不能显示地被调用,它是被垃圾收集器撤销不被使用的对象时自动调用的。
4.C#数据类型
  从大的方面来分,C#语言的数据类型可以分为三种:值类型,引用类型,指针类型,指针类型仅用于非安全代码中。
  C#运行在CLR中,其中有垃圾自动回收器,和java类似
  4.1值类型
    简单类型:
      --数值类型:整数类型、字符类型(char)、浮点数类型和十进制类型(decimal)
      --布尔类型(bool)
      (1)简单类型也是结构类型,因此有构造函数、数据成员、方法、属性等;
        
         因此下列语句int i=int.MaxValue;string s=i.ToString()是正确的;         即使一个常量,C#也会生成结构类型的实例,因此也可以使用结构类型的方法,         例如:string s=13.ToString()是正确的。

     (2)
        保留字| System命名空间中的名字 | 字节数  |  取值范围
        sbyte             System.Sbyte1              1        -128~127
          byte               System.Byte                1         0~255
         short                System.Int16               2     -32768~32767
        ushort              System.UInt16              2       0~65535
             int                System.Int32               4      -2147483648~2147483647
            uint                System.UInt32              4      0~4292967295
           long                System.Int64               8      -9223372036854775808~9223372036854775808
           ulong               System.UInt64              8      0~18446744073709551615
        char                   System.Char                2      0~65535
          float                System.Single              4      3.4E-38~3.4E+38
           double            System.Double              8      1.7E-308~1.7E+308
 bool              System.Boolean                    (true,false)
           decimal        System.Decimal             16     ±1.0 × 10?28 to ±7.9 × 1028
        (1)decimal类型用来表示高精度的浮点数,可以用到金融相关领域。
        (2)浮点数都有精度损失问题,操作时注意下即可。
        (3)字符类型采用Unicode字符集,一个Unicode标准字符长度为16位。
        (4)整数类型不能隐式被转换为字符类型(char),和java不同,必须强转或者用Unicode表示
        (5)布尔类型有两个值:false,true。不能认为整数0是false,其它值是true。
          bool x=1是错误的,不存在这种写法,只能写成x=true 或x=false
    结构类型(Struct types)
      (1)结构类型和类一样,可以声明构造函数、数据成员、方法、属性等。
      (2)结构和类的最根本的区别是结构是值类型,类是引用类型。
      (3)和类不同,结构不能从另外一个结构或者类派生,本身也不能被继承,因此不能定义抽象结构,
           结构成员也不能被访问权限控制字protected修饰,也不能用virtual和abstract修饰结构方法。
      (4)在结构中不能定义析构函数。
      (5)虽然结构不能从类和结构派生,可是结构能够继承接口,结构继承接口的方法和类继承接口的方法基本一致。
       例子:
        
  using System; //结构定义          struct point{public int x,y;//结构中也可以声明构造函数和方法,变量不能赋初值          }   class Test{      static void Main(){                point P1;                P1.x=166;                P1.y=111;                point P2;                P2=P1;//值传递,使P2.x=166,P2.y=111                point P3 = new point();//用new生成结构变量P3,P3仍为值类型变量                //用new生成结构变量P3仅表示调用默认构造函数,使x=y==0。             }          }
   枚举类型(Enumeration types)
      C#枚举类型使用方法和C、C++中的枚举类型基本一致,和java的区别较大
      (1)定义枚举
//设置初值,从1开始         enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};//位设置初值,从0开始           enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};  
      (2)使用枚举
       
  Days day=Days.Tue;  int x=(int)Days.Tue;

     (3)C、C++中不同,C#枚举元素类型可以是byte、sbyte、short、ushort、int、uint、long和ulong类型,但不能是char类型
   enum Days:byte{Sun,Mon,Tue,Wed,Thu,Fri,Sat};//元素为字节类型
     (4)学习链接http://www.cnblogs.com/liyingyi/archive/2005/12/28/306772.aspx
      
   变量的初始值:一般简单类型的初始值是0,布尔型的是false,引用类型的是null。
    对于复杂结构类型,其中的每个数据成员都按此种方法赋值,显得过于麻烦。
    由于数值类型都是结构类型,可用new语句调用其构造函数初始化数值类型变量,
例如:int j=new int()。
    请注意,用new语句并不是把int变量变为引用变量,j仍是值类型变量,
    这里new仅仅是调用其构造函数。所有的数值类型都有默认的无参数的构造函数,其功能就是为该数值类型赋初值为默认值。
    对于自定义结构类型,由于已有默认的无参数的构造函数,不能再定义无参数的构造函数,但可以定义有参数的构造函
  ***引用类型分类
     --类:C#语言中预定义了一些类:对象类(object类)、数组类、字符串类、自定义类等
     --接口
     C#语言引用类型变量无论如何定义,总是引用类型变量,不会变为值类型变量。
     C#语言引用类型对象一般用运算符new建立,用引用类型变量引用该对象。
   (1)对象类(object类)
       C#中的所有类型(包括数值类型)都直接或间接地以object类为基类。
       对象类(object类)是所有其它类的基类。任何一个类定义,如果不指定基类,默认object为基类。
       C#语言规定,基类的引用变量可以引用派生类的对象(注意,派生类的引用变量不可以引用基类的对象),
       因此,对一个object的变量可以赋予任何类型的值
       object关键字是在命名空间System中定义的,是类System.Object的别名
    (2)数组
        C#语言中数组是类System.Array类对象,和java中的数组用法基本一致,不说了。
    (3)string:和java的api中的功能类似,可以对比学习,现在先大致了解即可,用到时再查
        C#还定义了一个基本的类string,专门用于对字符串的操作。
        这个类也是在名字空间System中定义的,是类System.String的别名
        注意,两个字符串的比较比较的是值:string a,b; a==b,而java中时比的是hashcode
加框(boxing)和消框(unboxing)--装箱---拆箱
  加框(boxing)和消框(unboxing)是C#语言类型系统提出的核心概念,加框是值类型转换为object(对象)类型,
  消框是object(对象)类型转换为值类型。有了加框和消框的概念,对任何类型的变量来说最终我们都可以看作是object类型
  可以理解成java中的拆箱与装箱操作,java中时自动拆装箱,这个需要手动操作
  c#中的更加宽泛,需要自己在使用中慢慢理解。

关于运算符:和java中的几乎一致,如有遇到不一致的,就直接去查。
  (1)typeof运算符:操作符用于获得指定类型在system名字空间中定义的类型名字
  例如:
     Console.WriteLine(typeof(System.Int32));
     输出如下:
     System.Int32
 (2)溢出检查操作符checked和unchecked
  在进行整型算术运算(如+、-、*、/等)或从一种整型显式转换到另一种整型时,有可能出现运算结果超出这个结果所属类型值域的情况
  这种情况称之为溢出。整型算术运算表达式可以用checked或unchecked溢出检查操作符,决定在编译和运行时是否对表达式溢出进行检查。
  如果表达式不使用溢出检查操作符或使用了checked操作符,常量表达式溢出,在编译时将产生错误,表达式中包含变量
  ,程序运行时执行该表达式产生溢出,将产生异常提示信息。而使用了unchecked操作符的表达式语句,即使表达式产生溢出,
   编译和运行时都不会产生错误提示。但这往往会出现一些不可预期的结果,所以使用unchecked操作符要小心。
  下面的例子说明了checked和unchecked操作符的用法:
  using System;
  class Class1{
       static void Main(string[] args){
           const int x=int.MaxValue;
           unchecked//不检查溢出
          {
                int z=x*2;//编译时不产生编译错误,z=-2
                Console.WriteLine("z={0}",z);//显示-2
           }
          checked//检查溢出
         {
            int z1=(x*2);//编译时会产生编译错误
            Console.WriteLine("z={0}",z1);
         }
   }
   (3)new运算符
      和java不同之处是可以创建值类型的变量(基本数据类型),java中只能创建对象。
      new操作符可以创建值类型变量、引用类型对象,同时自动调用构造函数。
      例如:
         int x=new int();//用new创建整型变量x,调用默认构造函数
         Person C1=new Person ();//用new建立的Person类对象。Person 变量C1对象的引用
         int[] arr=new int[2];//数组也是类,创建数组类对象,arr是数组对象的引用需注意的是,
         int x=new int()语句将自动调用int结构不带参数的构造函数,给x赋初值0,x仍是值类型变量,
         不会变为引用类型变量
控制语句
   控制语句几乎和java相同,这里只之处不同之处,可以会有遗漏的地方,请指正,需要在使用中体会,
   这里只是快速的从java到c#的学习过程。
   (1)foreach(类型 变量名 in 表达式) 循环语句
      其中表达式必须是一个数组或其它集合类型,每一次循环从数组或其它集合中逐一取出数据,
      赋值给指定类型的变量,该变量可以在循环语句中使用、处理,但不允许修改变量,
      该变量的指定类型必须和表达式所代表的数组或其它集合中的数据类型一致。
      例子:
      
   using System;class Test()        {             public static void Main(){                 int[] list={10,20,30,40};//数组                 foreach(int m in list)                    Console.WriteLine("{0}",m);            }        }

       对于一维数组,foreach语句循环顺序是从下标为0的元素开始一直到数组的最后一个元素。
        对于多维数组,元素下标的递增是从最右边那一维开始的。同样break和continue可以出现在foreach语句中,功能不变
   (2)异常处理语句和java相同,try,catch,finally
类的继承
   (1)和java中的类的继承几乎一样,都是单继承,构造函数不能继承
       语法格式有区别:
       父类:Person
       子类:Employee
       子类这么定义:
    
   class Employee:Person{       }

    (2)base关键字
       和java中的supper关键字用法一致
     (3)类的成员类型
        局部变量:在for、switch等语句中和类方法中定义的变量,只在指定范围内有效。
        字段:即类中的变量或常量,包括静态字段、实例字段、常量和只读字段。
方法成员:包括静态方法和实例方法。
        属性:按属性指定的get方法和Set方法对字段进行读写。属性本质上是方法。
        事件:代表事件本身,同时联系事件和事件处理函数。
        索引指示器:允许象使用数组那样访问类中的数据成员。
        操作符重载:采用重载操作符的方法定义类中特有的操作。
        构造函数和析构函数。
     (4)修饰符
        c#的类修饰符和java的比较
                                  C#                                  java
       private             只能本类访问                             只能本类访问
       protected        本类,派生类                                本类,子类,同包
       public           外部程序                                    本程序
       internal         本程序                                      类似于java的public 
      (5)字段和属性
         字段:变量或者常量
         属性:带有get,set的方法(与java的不同,java带有set,get方法的成员变量叫属性,我此处理解的可能有误)
     (6)
         静态字段:用static声明的字段,和java中的静态变量相同
         实例字段:普通的变量,和java中的成员变量相同
         常量:使用const修饰声明的常量,和java中的static final 修饰的用法相同
         只读字段:使用readonly修饰声明的字段,它只能在字段声明中赋值或者在构造函数中赋值,java中没有对应的定义。
     (7)C#中的属性
         属性不是字段,但必然和类中的某个或某些字段相联系,属性定义了得到和修改相联系的字段的方法。
         C#中的属性更充分地体现了对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问,
         借助于get和set方法对属性的值进行读写。访问属性值的语法形式和访问一个变量基本一样,
         使访问属性就象访问变量一样方便,符合习惯。
         在类的基本概念一节中,定义一个描述个人情况的类Person,其中字段name和age是私有字段,记录姓名和年龄,
         外部通过公有方法SetName和SetAge修改这两个私有字段。现在用属性来描述姓名和年龄。例子如下:
using System;public class Person{ private string P_name="张三";//P_name是私有字段private int P_age=12;//P_age是私有字段public void Display()//类的方法声明,显示姓名和年龄{ Console.WriteLine("姓名:{0},年龄:{1}",P_name,P_age);}public string Name//定义属性Name{ get{ return  P_name;}set{ P_name=value;}}public int Age//定义属性Age{ get{ return  P_age;}set{ P_age=value;}}}public class Test{ public static void Main(){ Person OnePerson= new Person();OnePerson.Name="田七";//value="田七",通过set方法修改变量P_Namestring s=OnePerson.Name;//通过get方法得到变量P_Name值OnePerson.Age=20;//通过定义属性,既保证了姓名和年龄按指定方法修改int x=OnePerson.Age;//语法形式和修改、得到一个变量基本一致,符合习惯OnePerson.Display();}}

       在属性的访问声明中,只有set访问器表明属性的值只能进行设置而不能读出,只有get访问器表明属性的值是只读的不能改写,
        同时具有set访问器和get访问器表明属性的值的读写都是允许的。
虽然属性和字段的语法比较类似,但由于属性本质上是方法,因此不能把属性当做变量那样使用,也不能把属性作为引用型参数或输出参数来进行传递。

方法参数的种类
    
   c#的参数类型比较多,比javav的复杂好多
   -- 方法修饰符包括new、public、protected、internal、private、static、virtual、sealed、override、abstract和extern
   -- C#语言的方法可以使用如下四种参数(请注意和参数类型的区别):
      值参数,不含任何修饰符。
      引用参数,以ref修饰符声明。
      输出参数,以out修饰符声明。
      数组参数,以params修饰符声明。
   1.值参数
    (1)当用值参数向方法传递参数时,程序给实参的值做一份拷贝,并且将此拷贝传递给该方法,被调用的方法不会修改实参的值,
    所以使用值参数时,可以保证实参的值是安全的。
    (2)如果参数类型是引用类型,例如是类的引用变量,则拷贝中存储的也是
    对象的引用,所以拷贝和实参引用同一个对象,通过这个拷贝,可以修改实参所引用的对象中的数据成员。
     值参数和java中的参数传递一致。
  2.引用参数
    有时在方法中,需要修改或得到方法外部的变量值,C语言用向方法传递实参指针来达到目的,C#语言用引用参数。
    当用引用参数向方法传递实参时,程序将把实参的引用,即实参在内存中的地址传递给方法,方法通过实参的引用,
    修改或得到方法外部的变量值。引用参数以ref修饰符声明。注意在使用前,实参变量要求必须被设置初始值。
    这个类似于C语言中传递地址,然后根据地址找到相应的内存空间,并可以修改这个内存空间的值。
  3.输出参数
    为了把方法的运算结果保存到外部变量,因此需要知道外部变量的引用(地址)。输出参数用于向方法传递外部变量引用(地址),
    所以输出参数也是引用参数,与引用参数的差别在于调用方法前无需对变量进行初始化。在方法返回后,传递的变量被认为经过了初始化。
    这个也类似于c语言中实现多个返回值的实现方式,区别是初始化问题,当然java也可以实现引用的修改,但是对于基本数据
    类型做不到,只能用基本数据类型的封装类去实现。    
    值参数、引用参数和输出参数的使用见下例
 using System;class g{public int a=0;}//类定义class Class1{ public static void F1(ref char i)//引用参数{ i='b';}public static void F2(char i)//值参数,参数类型为值类型{ i='d';}public static void F3(out char i)//输出参数{ i='e';}public static void F4(string s)//值参数,参数类型为字符串{ s="xyz";}public static void F5(g gg)//值参数,参数类型为引用类型{ gg.a=20;}public static void F6(ref string s)//引用参数,参数类型为字符串{ s="xyz";}static void Main(string[] args){ char a='c';string s1="abc";F2(a);//值参数,不能修改外部的aConsole.WriteLine(a);//因a未被修改,显示cF1(ref a);//引用参数,函数修改外部的a的值Console.WriteLine(a);//a被修改为b,显示bChar j;F3(out j);//输出参数,结果输出到外部变量jConsole.WriteLine(j);//显示eF4(s1);//值参数,参数类型是字符串,s1为字符串引用变量Console.WriteLine(s1);//显示:abc,字符串s1不被修改g g1=new g();F5(g1);//值参数,但实参是一个类引用类型变量Console.WriteLine(g1.a.ToString());//显示:20,修改对象数据F6(ref s1);//引用参数,参数类型是字符串,s1为字符串引用变量Console.WriteLine(s1);//显示:xyz,字符串s1被修改}}       4.数组参数    数组参数使用params说明,   (1)如果形参表中包含了数组参数,那么它必须是参数表中最后一个参数,数组参数只允许是一维数组。    比如string[]和string[][]类型都可以作为数组型参数   (2)数组型参数不能再有ref和out修饰符。见下例:    using System;class Class1{ static void F(params int[] args)//数组参数,有params说明{ Console.Write("Array contains {0} elements:",args.Length);foreach (int i in args)Console.Write(" {0}",i);Console.WriteLine();}static void Main(string[] args){ int[] a = {1,2,3};F(a);//实参为数组类引用变量aF(10, 20, 30, 40);//等价于F(new int[] {60,70,80,90});F(new int[] {60,70,80,90});//实参为数组类引用F();//等价于F(new int[] {});F(new int[] {});//实参为数组类引用,数组无元素}}程序输出Array contains 3 elements: 1 2 3Array contains 4 elements: 10 20 30 40Array contains 4 elements: 60,70,80,90Array contains 0 elements:Array contains 0 elements:


和java中的可变参数用法类似,在传参的时候相同,定义时不同,但是都必须是参数中的最后一个参数   
C#和java中的静态方法和成员方法用法相同
C#的方法和构造器的重载和java的用法相同(静态)
操作符重载:java中操作符不能重载
   
    操作符重载是将C#语言中的已有操作符赋予新的功能,但与该操作符的本来含义不冲突,
     使用时只需根据操作符出现的位置来判别其具体执行哪一种运算。操作符重载,实际是定义了一个操作符函数,操作符函数声明的格式如下:
      static public 函数返回类型 operator 重新定义的操作符(形参表)
      C#语言中有一些操作符是可以重载的,例如:+ - ! ~ ++ -- true false * / % & | ^ << >> == != > < >= <=等等。
        但也有一些操作符是不允许进行重载的,例如:=, &&, ||, ?:, new, typeof, sizeof, is等。
this关键字
   this关键字和java中的this用法一致。
   注意:java中的supper在C#中使用base
类的多态
   C#支持两种类型的多态性,
    第一种是编译时的多态性,一个类的对象调用若干同名方法,系统在编译时,根据调用方法的实参类型及实参的个数决定调用那个同名方法,
    实现何种操作。编译时的多态性是通过方法重载来实现的。
   第二种是运行时的多态性,是在系统运行时,不同对象调用一个名字相同,参数的类型及个数完全一样的方法,会完成不同的操作。
    C#运行时的多态性通过虚方法实现。在类的方法声明前加上了virtual修饰符,被称之为虚方法,反之为非虚方法。
     第一种多态写法和java相同,但是用法不同,方法执行时,执行基类的方法
   第二中多态和java写法不同,子类继承的写法也不同,但是和java继承的意义相同。
    基类返回值前用virtual关键字修饰,派生类继承的时候在返回值前加上一个override关键字去修饰。

抽象类和抽象方法
     和java对比基本一样,只是在写法上有少许差别,java可以用注解,不知C#是否可以
  密封类和密封方法
C#用sealed修饰的类或者方法叫密封类和密封方法,和java中用final修饰的类和方法相同,不能被继承
  接口和java中的接口意义和用法几乎相同,
  区别是,C#在接口中可以有方法、属性、索引指示器和事件,不能是常量、域、操作符、构造函数或析构函数,不能包含任何静态成员。
    代表
    在这里要介绍的是C#的一个引用类型----代表(delegate),也翻译为委托,可以当做java中的代理,也可以理解成c中的指针的传递
    语法自己去查,比较简单。
  事件
事件是C#语言内置的语法,可以定义和处理事件,为使用组件编程提供了良好的基础
  事件驱动
    Windows操作系统把用户的动作都看作消息,C#中称作事件,例如用鼠标左键单击按钮,发出鼠标单击按钮事件。
    Windows操作系统负责统一管理所有的事件,把事件发送到各个运行程序。各个程序用事件函数响应事件,这种方法也叫事件驱动。
    和android中的button等view的事件的处理基本类似。
 索引指示器
   在C#语言中,数组也是类,比如我们声明一个整型数数组:int[] arr=new int[5],实际上生成了一个数组类对象,
   arr是这个对象的引用(地址),访问这个数组元素的方法是:arr[下标],在数组类中,使用索引访问元素是如何实现的呢?
   是否可以定义自己的类,用索引访问类中的数据成员?索引指示器(indexer)为我们提供了通过索引方式方便地访问类的数据成员的方法。
    
   可以这么理解,就是自己定义的数组,可以通过下表访问其中的元素
 名字空间或者叫命名空间:namespace。
     用法和java中的包pacager类似
     命名空间也是用来唯一识别类的,和包相同
     命名空间可以定义子命名空间,包可以定义子包。
     命名空间和java中的不同,除了写法不同外,命名空间只是逻辑上的分类。
 和java不同还有
    C#可以使用指针,通过unsafe关键字可以修饰方法或者代码行,说明这个是不安全的代码,需要使用c的指针
    C#的方法,变量,参数首字母大写,而java的是小写
      发现C#更加的面相对象,但是还留有C和C++的影子。  

  以上参考了网上好多文章,谢谢文章作者。
  

 

0 0
原创粉丝点击