在Java基础上对比学习C#基本语法
文章目录
对于学习一门新的语言,关键是学习新语言和以前掌握的语言的区别,但是也不要让以前语言的东西,固定了自己的思维模式,多看一下新的语言的编程思想。
Java和C#都是面向对象的语言,相似度很大,下面用对比的方式来学习C#。
一、引包
using System; //java用import
二、构造函数
和java语法相同
public class Student
{
public Student(){
}
}
三、析构函数
与构造函数类似,C# 中的析构函数(也被称作“终结器”)同样是类中的一个特殊成员函数,主要用于在垃圾回收器回收类实例时执行一些必要的清理操作。
变量和类的对象都有生命周期,生命周期结束,这些变量和对象就要被撤销。
类的对象被撤销时,将自动调用析构函数。一些善后工作可放在析构函数中完成。
析构函数的名称同样与类名相同,不过需要在名称的前面加上一个波浪号~作为前缀,无返回类型,也无参数。如下所示:
class Car
{
~Car() // 析构函数
{
}
}
C#中类析构函数不能显示地被调用,它是被垃圾收集器撤销不被使用的对象时自动调用的。
四、C#数据类型
从大的方面来分,C#语言的数据类型可以分为三种:值类型,引用类型,指针类型,指针类型仅用于非安全代码中。
C#运行在CLR中,其中有垃圾自动回收器,和java类似
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};//元素为字节类型
变量的初始值:一般简单类型的初始值是0,布尔型的是false,引用类型的是null。
对于复杂结构类型,其中的每个数据成员都按此种方法赋值,显得过于麻烦。
由于数值类型都是结构类型,可用new语句调用其构造函数初始化数值类型变量,
例如:int j=new int()。
请注意,用new语句并不是把int变量变为引用变量,j仍是值类型变量,
这里new仅仅是调用其构造函数。所有的数值类型都有默认的无参数的构造函数,其功能就是为该数值类型赋初值为默认值。
对于自定义结构类型,由于已有默认的无参数的构造函数,不能再定义无参数的构造函数,但可以定义有参数的构造函
2、引用类型分类
- 类: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中的super关键字用法一致
(3)类的成员类型
局部变量:在for、switch等语句中和类方法中定义的变量,只在指定范围内有效。
字段:即类中的变量或常量,包括静态字段、实例字段、常量和只读字段。
方法成员:包括静态方法和实例方法。
属性:按属性指定的get方法和Set方法对字段进行读写。属性本质上是方法。
事件:代表事件本身,同时联系事件和事件处理函数。
索引指示器:允许象使用数组那样访问类中的数据成员。
操作符重载:采用重载操作符的方法定义类中特有的操作。
构造函数和析构函数。
(4)修饰符
c#的类修饰符和java的比较
C# | java | |
---|---|---|
private | 只能本类访问 | 只能本类访问 |
protected | 本类,派生类 | 本类,子类,同包 |
public | 外部程序 | 本程序 |
internal | 本程序 | 无 |
protected internal | 符合protected 或 internal | 无 |
default | 无 | 本类、同包 |
(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_Name
string s=OnePerson.Name;//通过get方法得到变量P_Name值
OnePerson.Age=20;//通过定义属性,既保证了姓名和年龄按指定方法修改
int x=OnePerson.Age;//语法形式和修改、得到一个变量基本一致,符合习惯
OnePerson.Display();
}
}
在属性的访问声明中,只有set访问器表明属性的值只能进行设置而不能读出,只有get访问器表明属性的值是只读的不能改写,
同时具有set访问器和get访问器表明属性的值的读写都是允许的。
虽然属性和字段的语法比较类似,但由于属性本质上是方法,因此不能把属性当做变量那样使用,也不能把属性作为引用型参数或输出参数来进行传递。
九、方法参数的种类
c#的参数类型比较多,比java的复杂好多
- 方法修饰符包括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);//值参数,不能修改外部的a
Console.WriteLine(a);//因a未被修改,显示c
F1(ref a);//引用参数,函数修改外部的a的值
Console.WriteLine(a);//a被修改为b,显示b
Char j;
F3(out j);//输出参数,结果输出到外部变量j
Console.WriteLine(j);//显示e
F4(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);//实参为数组类引用变量a
F(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 3
Array contains 4 elements: 10 20 30 40
Array contains 4 elements: 60,70,80,90
Array contains 0 elements:
Array contains 0 elements:
和java中的可变参数用法类似,在传参的时候相同,定义时不同,但是都必须是参数中的最后一个参数
5.C#和java中的静态方法和成员方法用法相同
6. C#的方法和构造器的重载和java的用法相同
十、操作符重载
java中操作符不能重载
操作符重载是将C#语言中的已有操作符赋予新的功能,但与该操作符的本来含义不冲突,
使用时只需根据操作符出现的位置来判别其具体执行哪一种运算。操作符重载,实际是定义了一个操作符函数,操作符函数声明的格式如下:
static public 函数返回类型 operator 重新定义的操作符(形参表)
C#语言中有一些操作符是可以重载的,例如:+ - ! ~ ++ – true false * / % & | ^ << >> == != > < >= <=等等。
但也有一些操作符是不允许进行重载的,例如:=, &&, ||, ?:, new, typeof, sizeof, is等。
十一、this关键字
this关键字和java中的this用法一致。
注意:java中的super在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)为我们提供了通过索引方式方便地访问类的数据成员的方法。
可以这么理解,就是自己定义的数组,可以通过下表访问其中的元素
二十、命名空间 namespce
用法和java中的包package类似
命名空间也是用来唯一识别类的,和包相同
命名空间可以定义子命名空间,包可以定义子包。
命名空间和java中的不同,除了写法不同外,命名空间只是逻辑上的分类。
二十一、其他不同
1、C#可以使用指针,通过unsafe关键字可以修饰方法或者代码行,说明这个是不安全的代码,需要使用c的指针
2、C#的方法,变量,参数首字母大写,而java的是小写。
发现C#更加的面向对象,但是还留有C和C++的影子。
总结
当然以上只是C#和java基本语法的对比学习,可以让java程序员快速的学习C#语言,能看懂C#程序,这些已经足够了,只是熟悉的问题。关于C#的SDK的API的学习也要和java的对应学习,这样学习会非常快速,也能加深对java的理解。