1. 包(Package)、命名空間(NameSpace)
1.1 概念
在Java中常用的是包(Package),較少提到NameSpace的概念。Java官方文檔中這樣說:
為了使類型更易於查找和使用,避免命名沖突並控制訪問,程序員將相關類型的組捆綁到包中。
定義:包是一組提供訪問保護和名稱空間管理的相關類型。 請注意,類型是指類、接口、枚舉和注釋類型。 枚舉和注解類型分別是特殊類型的類和接口,因此在本課中通常將類型簡稱為類和接口。
根據這里的概念,Package基本上是對應C#的NameSpace的。
無論是Java還是C#,每個類都有屬於一個包/命名空間:
- Java:
package cn.flylolo.entity;
public class Pig extends Animal{
}
- C#:
namespace cn.flylolo.entity;
public class Pig : Animal{
}
1.2 命名規則
-
Java一般用域名倒序的方式來作為包名,多個單詞用“.”分隔,同時這也對應着目錄的層級關系。
-
C#中也可以用這樣的規則來命名NameSpace,也見過這樣的命名方式,但不強制;並且與目錄也可以沒有關聯關系。
1.3 引用方式
- Java引用包:
import cn.flylolo.entity.Pig;
- C# 引用命名空間:
using cn.flylolo.entity.Pig;
C#的命名空間別名:若要引用同名的不同類,處理方式都是寫全包/命名空間的名稱。C#中覺得較長不美觀可以在using的時候設置別名:
using entityPig = cn.flylolo.entity.Pig;
在代碼中可以直接使用別名引用。
2.訪問修飾符
上一節,Java的包與C#的命名空間類似,但針對訪問修飾符,包又與C#的程序集類似。
C# | Java | 含義 |
---|---|---|
public | public | 相同,訪問不受限制。 |
protected | C#,訪問限於包含類或派生自包含類的類型。 | |
private | private | 訪問限於包含類。 |
internal或不添加修飾符 | 不添加修飾符 | 同一(包/程序集)可訪問。 |
protected internal | protected | 相同,訪問限於當前(包/程序集)或派生自包含類的類型。 |
private protected | 訪問限於包含類或當前程序集中派生自包含類的類型。 自 C# 7.2 之后可用。 |
3.類與文件
Java中,一個.java文件中,只允許有一個Public的類,並且文件名與此類名一般相同。
C#中則無上述限制。
4.繼承,sealed與final
4.1 繼承一個類或實現接口:
-
C#用“:" 符號。
-
Java繼承類用extends關鍵字,實現接口用implements關鍵字。
4.2 不想讓一個類被繼承:
- Java 用final關鍵字:
public final class Shape
- C# 用sealed關鍵字:
public sealed class Shape
注意: JDK15的時候,Java也提供了sealed關鍵字,用於限制繼承,例如下列代碼
public sealed class Shape permits Circle, Square, Rectangle {
}
通過sealed+permits兩個關鍵字,限制了子類只能是Circle, Square, Rectangle這三個。
5.Static
-
C#,有靜態類和靜態方法。
-
Java,有靜態類和靜態方法,但靜態類只能是內部類,詳見下一節。
6. 內部類、嵌套類
6.1 C#的內部類
C#的內部類比較簡單,類似如下代碼:
namespace cn.flylolo.nestedclass;
/**
* @author luozhichao
* @date 2021/10/15 17:50
*/
public class OuterClass
{
public String outerClassName = "outerClass's name";
public void printNestedClassName()
{
//無法直接調用內部類的變量
//Console.WriteLine(NestedClass.nestedClassName);
Console.WriteLine(NestedStaticClass.nestedClassName);
}
public class NestedClass
{
public String nestedClassName = "nestedClass's name";
public void printOuterClassName()
{
//error 不可以直接調用外部類的對象
//Console.WriteLine(outerClassName);
}
}
public static class NestedStaticClass
{
public static String nestedClassName = "NestedStaticClass's name";
public static void printOuterClassName()
{
//error 不可以直接調用外部類的對象
//Console.WriteLine(outerClassName);
}
}
}
class Test
{
public static void main(String[] args)
{
OuterClass.NestedClass nestedClass = new OuterClass.NestedClass();
//可以直接調用靜態內部類的方法。
string str = OuterClass.NestedStaticClass.nestedClassName;
}
}
代碼中做了一些注釋,可以看到,對於非靜態的內部類,外部類就像給其加了一層“命名空間”,可以通過new OuterClass.NestedClass()
的方式進行創建。
對應靜態內部類,可以通過OuterClass.NestedStaticClass
的方式直接調用其方法和屬性,當然這也由對應的訪問修飾符決定,例如將NestedStaticClass
設置為private
,則OuterClass
可以直接調用NestedStaticClass
,而上例中的Main方法則無法調用NestedStaticClass
了。
6.2 Java的內部類
再看一下Java的內部類:
public class OuterClass {
public String outerClassName = "outerClass's name";
public void getNestedClassName() {
String staticString = NestedStaticClass.staticString;
//無法直接調用非靜態內部類的變量
//String str = NestedClass.nestedClassName;
}
public NestedClass getNestedClass() {
//可以直接new
return new NestedClass();
}
class NestedClass {
public String nestedClassName = "nestedClass's name";
public void printOuterClassName() {
//可以直接調用外部類的對象
System.out.println(outerClassName);
}
public OuterClass getOuter() {
//返回外部類實例
return OuterClass.this;
}
}
static class NestedStaticClass {
public String nestedClassName = "NestedStaticClass's name";
public static String staticString = "staticString";
public void printOuterClassName() {
//error 不可以直接調用外部類的對象
//System.out.println(outerClassName);
}
//error 無法返回外部類實例
// public OuterClass getOuter(){
// return OuterClass.this;
// }
}
}
class Test{
public static void main(String[] args) {
//不允許直接通過new的方式創建OuterClass.NestedClass
//OuterClass.NestedClass nestedClass1 = new OuterClass.NestedClass();
//只能通過外部類的實例創建內部類
OuterClass outerClass = new OuterClass();
//通過方法返回內部類實例
OuterClass.NestedClass nestedClass = outerClass.getNestedClass();
//通過.new關鍵字
OuterClass.NestedClass nestedClass1 = outerClass.new NestedClass();
//通過內部類實例獲取外部類實例
System.out.println(nestedClass1.getOuter().outerClassName);
nestedClass.printOuterClassName();
String staticString = OuterClass.NestedStaticClass.staticString;
OuterClass.NestedStaticClass nestedStaticClass = new OuterClass.NestedStaticClass();
System.out.println(nestedStaticClass.nestedClassName);
}
}
可見,Java的內部類“玩法比較多,完全寫來下可以說是一個比較大的專題了,簡要列舉一下與C#的內部類的不同之處。
6.3 非靜態內部類總結
- 外部類都無法訪問內部類的的方法和屬性,但Java的內部類可以訪問外部類的方法和屬性,C#的不可以,Java內外部類互相訪問提供了“.New”和“.this"關鍵字。
- 創建內部類,new的對象不同,C#通過“new 外部類.內部類() ”方式創建,Java不允許這樣,需要外部類的實例,即:”外部類實例.new 內部類()“。
- 除了上述的內部類定義方式,Java的內部類可以出現在外部類的方法、語句塊中。
6.4 靜態內部類總結
- C#的靜態類中不允許有非靜態方法和成員屬性,Java的靜態內部類中可以有。
- C#和Java的內部類可以直接通過“外部類.內部類”的方式訪問,具體要考慮內部類對應的訪問修飾符。
- C#的內部類不允許被new出新實例,Java的可以。
- Java通過直接的方式訪問內部類,只允許訪問靜態方法和成員屬性。通過new的方式產生的實例,即可以訪問靜態成員也可以訪問非靜態成員。但不建議通過這種方式訪問靜態成員。
6.5 其他
- Java還可以通過內部類的方式實現匿名類、多重繼承等。
- Java8之后,一些情形可以通過lamda簡化內部類的寫法。
首發地址:C#與Java類的區別