Java和C#对比(1):基本一致的地方
最近开始把开发的重心从C#往Java迁移,虽然之前对Java语言有所关注,但毕竟也看得不多。从语言设计者的区别上讲,C#的设计更加关注语言本身,微软更加关注代码是否简洁,不断的为C#加入各种特性,比如各种语法糖,从泛型,可空类型,隐式类型到Lamada再到动态类型等,微软一直围绕代码简洁,减少Bug等实际的开发过程中问题来进行改进。 而Java的设计不同,java更关注系统本身,强调更好的解藕,始终坚持OOP的本质。所以从应用结构上讲,同时代的Java要比C#更美观,易于维护,代码腐烂的速度也慢。 所以虽然语法相似,但是关注点不同,所以很难说出这两种语言谁优谁劣。
本篇系列准备从语法的角度,对Java和C#对比,希望读者能在已有C#或者Java一门语言的基础上,对另一门语言有个快速而又全面的概念。需要注意的是,本文所对比的语言版本是C#6.0和Java 8。
1.对Java和C#而言,一切都是对象
C#和Jav中的所有对象都从Object类中派生,C#中是System.Object,Java中是java.lang.Object。其中需要注意的是,在C#中,Object和小写的object并无实质区别。两个Object中的自带的方法一览:
System.Object | java.lang.Object |
Equals | equals |
GetHashCode | hashCode |
GetType | getClass |
ReferenceEquals | |
Finalize | finalize |
MemberwiseClone | Clone |
ToString | toString |
wait | |
notify | |
notifyAll |
参考信息:
C#中的基元类型
2.关键字一览
对于多数的关键字,C#和Java是一样的。
C# keyword | Java keyword | C# keyword | Java keyword | C# keyword | Java keyword | C# keyword | Java keyword |
abstract | abstract | as | N/A | base | super | bool | boolean |
break | break | byte | N/A | case | case | catch | catch |
char | char | checked | N/A | class | class | const | const 1 |
continue | continue | decimal | N/A | default | default | delegate | N/A |
do | do | double | double | else | else | enum | enum |
event | N/A | explicit | N/A | extern | native | false | false |
finally | finally | fixed | N/A | float | float | for | for |
foreach | for | goto | goto 1 | if | if | implicit | N/A |
in | N/A | int | int | interface | interface | internal | protected |
is | instanceof | lock | synchronized | long | long | namespace | package |
new | new | null | null | object | N/A | operator | N/A |
out | N/A | override 2 | N/A | params | ... | private | private |
protected | N/A | public | public | readonly | N/A | ref | N/A |
return | return | sbyte | byte | sealed | final | short | short |
sizeof | N/A | stackalloc | N/A | static | static | string | N/A |
struct | N/A | switch | switch | this | this | throw | throw |
true | true | try | try | typeof | N/A | uint | N/A |
ulong | N/A | unchecked | N/A | unsafe | N/A | ushort | N/A |
using | import | virtual | N/A | void | void | volatile | volatile |
while | while | : | extends | : | implements | N/A | strictfp |
N/A | throws | N/A |
C#除了上述关键字外,还有一类上下文关键字。上下文关键字用于提供代码中的特定含义,但它不是 C# 中的保留字。某些上下文关键字(如 partial 和 where)在两个或更多个上下文中具有特殊含义。
add | Alias | ascending | async |
await | descending | dynamic | from |
get | global | group | into |
join | let | orderby | partial(类型) |
partial(方法) | remove | select | set |
value | var | where(泛型类型约束) | where(查询子句) |
yield |
参考信息:
C# Reference #Keywords
Java Language Specification #Keywords
注释1:const和goto仅作为保留字,并未在Java中使用。
注释2:Java中的*@override*注解标记和*Override*是等效的。
注释3:C#中的*[NonSerialized]*属性(attribute)标记和*transient*是等效的。
3.虚拟机与语言运行时
Java需要将代码编译成字节码(Java byte code),并由在JVM(Java Virtual Machine)运行。而C#需要将代码编译成中间语言(Intermediate Language),然后由CLR(Common Language Runtime)运行。两个平台都支持通过即时编译器(Just In Time Compiler)进行原生代码的编译。 需要注意的是,Java可以在运行前通过JIT直接编译成原生代码,而.Net只能在运行时处理IL语言。
参考信息:
just-in-time compiler (JIT)
4.基于堆(heap)的类型和垃圾回收
在Java中所有的对象都通过new关键字在堆上创建。而C#中绝大多数对象通过new关键字创建。JVM和CLR都会负责将创建的对象销毁。
注释:C#也支持在栈(stack)上创建对象(值类型)
参考信息:
你真的了解C# 值类型和引用吗
Jon Skeet, 姚琪琳. 深入理解C#[M]. 北京:人民邮电出版社, 2014. 39-43
Mark-compact algorithm
5.数组可以是交错的
不同于C和C++中的,二维或者多维的数组大小应该是一样的。C#和Java运行多维数组的大小不一样。举个例子:
int[][] myArray = new int[2][];
myArray[0] = new int[3];
myArray[1] = new int[9];
6.没有全局方法
不同于C++,Java和C#中的方法必须在一个类之中。
7.单一继承
Java和C#都仅支持从一个类继承,但允许实现多个接口。
8.String是不可变的
C#中有个System.String类型和Java中的Java.lang.String对应。两个类都是不可以修改的,意味着字符串的字一旦生成,就不能被修改。两个类都暴露了一些方法来返回修改后的结果,但其实字符串本身并未被修改。 举个例子:
C#:
string csStr = “Young Ytj";
csStr.ToLower();//字符串本身并为被更改,只是新生成了一个小写的字符串。
Java:
String jStr = “Young Ytj”;
jStr.toLowerCase();//字符串本身并为被更改,只是新生成了一个小写的字符串。
9.不可扩展的类
C#和Java中都可以控制一个类不被继承。C#中用sealed,Java中用final。
C#:
sealed class CSharpClass
{
//Field etc...
}
Java:
final clas JavaClass{
//Field etc...
}
10.异常
异常在C#和Java中是相似的。两种语言都支持使用try catch的方式抓住异常,然后在finally中处理未释放的资源。两个语言的异常都继承与一个Exception类。 异常能够被抓住并重新抛出。
C#:
class MyException : Exception
{
public MyException(string message) : base(message){}
public MyException(string message, Exception innerException) : base(message, innerException){}
}
public class ExceptionTest
{
static void DoWork()
{
throw new Exception();
}
public static void Main(string[] args)
{
try
{
try
{
DoWork();
return; //不会到达的代码
}
catch(Exception ex)
{
throw new MyException("MyException occured", ex);//重新抛出异常
}
}
finally
{
Console.WriteLine("Finally block executes even though MyException not caught");
}
}
}
Java:
class MyException extends Exception{
public MyException(String message){ super(message);}
public MyException(String message, Exception innerException){ super(message, innerException);}
}
public class ExceptionTest{
static void DoWork(){
throw new Exception();
}
public static void main(String[] args) throws Exception{
try{
try{
DoWork();
return; //不会到达的代码
}catch(Exception ex){
throw new MyException("MyException occured", ex);//重新抛出异常
}
}finally{
System.out.println("Finally block executes even though MyException not caught");
}
}
}
11.成员变量和构造函数的初始化顺序
Java和C#中的类而言,它们的静态构造函数总是最先初始化的,其次是它们的成员变量,然后再初始化构造函数,对于类的静态成员,它们只有在调用的时候才会被初始化。
C#:
class StaticInitTest
{
string instMember = InitInstance();
string staMember = InitStatic();
StaticInitTest()
{
Console.WriteLine("In instance constructor");
}
static StaticInitTest()
{
Console.WriteLine("In static constructor");
}
static String InitInstance()
{
Console.WriteLine("Initializing instance variable");
return "instance";
}
static String InitStatic()
{
Console.WriteLine("Initializing static variable");
return "static";
}
static void DoWork()
{
Console.WriteLine("Invoking static DoWork() method");
}
public static void Main(string[] args)
{
Console.WriteLine("Beginning main()");
StaticInitTest.DoWork();
StaticInitTest sti = new StaticInitTest();
Console.WriteLine("Completed main()");
}
}
Java:
class StaticInitTest{
String instMember = initInstance();
String staMember = initStatic();
StaticInitTest(){
System.out.println("In instance constructor");
}
static{
System.out.println("In static constructor");
}
static String initInstance(){
System.out.println("Initializing instance variable");
return "instance";
}
static String initStatic(){
System.out.println("Initializing static variable");
return "static";
}
static void doWorK(){
System.out.println("Invoking static DoWork() method");
}
public static void main(String[] args){
System.out.println("Beginning main()");
StaticInitTest.doWorK();
StaticInitTest sti = new StaticInitTest();
System.out.println("Completed main()");
}
}
输出结果:
In static constructor
Beginning main()
Invoking static DoWork() method
Initializing instance variable
Initializing static variable
In instance constructor
Completed main()
12.装箱和拆箱
有时候我们需要将一个值类型装换为一个Object的类型,Java和C#中将这个过程叫做装箱。那么反过来,把一个Object类型转成一个值类型就叫做拆箱。
C#:
struct Point
{
private int x;
private int y;
public Point (int x, int y)
{
this.x = x;
this.y = y;
}
public override string ToString()
{
return String.Format("({0}, {1})", x, y);
}
}
class Test
{
public static void PrintString(object o)
{
Console.WriteLine(o);
}
public static void Main(string[] args)
{
Point p = new Point(10, 15);
ArrayList list = new ArrayList();
int z = 100;
PrintString(p); //p被装箱成object对象并传给PrintString函数
PrintString(z); //z被装箱成object对象并传给PrintString函数g
// int和float被装箱并放入集合中
list.Add(1);
list.Add(13.12);
list.Add(z);
for(int i =0; i < list.Count; i++)
PrintString(list[i]);
}
}
Java:
class Test{
public static void PrintString(Object o){
System.out.println(o);
}
public static void PrintInt(int i){
System.out.println(i);
}
public static void main(String[] args){
Vector list = new Vector();
int z = 100;
Integer x = new Integer(300);
PrintString(z); //z装箱成Object并传给PrintString
PrintInt(x); //x拆箱成int并传给PrintInt
//int和float被装箱并放入集合中
list.add(1);
list.add(13.12);
list.add(z);
for(int i =0; i < list.size(); i++)
PrintString(list.elementAt(i));
}
}
13.跨平台移植
Java技术一个很大的卖点就是Java写的应用程序一次编写到处运行。Sun官方支持Linux、Solaris 和 Windows,其它一些公司也实现了OS/2, AIX 和MacOS平台的Java。.NET也通过Mono项目和Ximian提供了移植性的支持。
14.静态导入
静态导入特性使我们可以访问类静态成员的时候不需要指定类名,这个特性可以让我们减少代码的冗余,特别对于某些帮助类型来说很方便。静态导入和普通的import语句很像,只不过多了static关键字,导入的是类而不是包名。
Java:
import static java.awt.Color.*;
public class Test{
public static void main(String[] args) throws Exception{
//constants not qualified thanks to static import
System.out.println(RED + " plus " + YELLOW + " is " + ORANGE);
}
}
输出:
java.awt.Color[r=255,g=0,b=0] plus java.awt.Color[r=255,g=255,b=0] is java.awt.Color[r=255,g=200,b=0]