Các nhà phát triển C # học Java, sự khác biệt lớn nhất mà người ta có thể bỏ qua là gì? [đóng cửa]


87

Đối với các nhà phát triển c # đang bắt đầu học Java, có bất kỳ sự khác biệt cơ bản lớn nào giữa hai ngôn ngữ cần được chỉ ra không?

Có thể một số người có thể cho rằng mọi thứ giống nhau, nhưng có một số khía cạnh nhập khẩu không nên bỏ qua? (hoặc bạn thực sự có thể làm hỏng!)

Có thể về cấu trúc OOP, cách GC hoạt động, tham chiếu, liên quan đến triển khai, v.v.




Cũng có khả năng bị bỏ qua là của riêng "Ẩn Đặc điểm của C #" của chúng tôi stackoverflow.com/questions/9033/hidden-features-of-c
John K

Câu trả lời:


122

Một vài điều đã hiểu ra khỏi đầu tôi:

  • Java không có các kiểu giá trị tùy chỉnh (cấu trúc) vì vậy đừng bận tâm tìm kiếm chúng
  • Các enum trong Java rất khác với cách tiếp cận "số được đặt tên" của C #; họ OO hơn. Chúng có thể được sử dụng để tạo ra hiệu quả tuyệt vời, nếu bạn cẩn thận.
  • byte được đăng nhập bằng Java (rất tiếc)
  • Trong C #, bộ khởi tạo biến thể hiện chạy trước khi hàm tạo của lớp cơ sở thực hiện; trong Java, chúng chạy sau nó (tức là ngay trước thân hàm tạo trong lớp "this")
  • Trong C #, các phương thức được niêm phong theo mặc định. Trong Java, chúng ảo theo mặc định.
  • Công cụ sửa đổi quyền truy cập mặc định trong C # luôn là "truy cập hạn chế nhất có sẵn trong ngữ cảnh hiện tại"; trong Java đó là quyền truy cập "gói". (Bạn nên đọc về các công cụ sửa đổi truy cập cụ thể trong Java.)
  • Các kiểu lồng nhau trong Java và C # hoạt động hơi khác nhau; cụ thể là chúng có các hạn chế truy cập khác nhau và trừ khi bạn khai báo kiểu lồng nhau là staticnó sẽ có một tham chiếu ngầm định đến một thể hiện của lớp chứa.

5
+1 Đây là một danh sách tuyệt vời cho người mới bắt đầu.
Jim Schubert

3
@Jon Skeet: +1, Và bạn có thể sẽ nhớ Lambda và LINQ cho đến Java 7?
Kb.

1
"Trong C #, các trình khởi tạo biến thể hiện chạy trước hàm tạo của lớp cơ sở; trong Java chúng chạy sau nó (tức là ngay trước thân hàm tạo trong lớp" this ")" - Eeer, bạn có thể giải thích điều này không? Không chắc tôi thực sự hiểu bạn đang nói gì :)
cwap

7
@cwap: Trong C #, thứ tự thực hiện là "bộ khởi tạo biến thể hiện, hàm tạo lớp cơ sở, thân hàm tạo". Trong Java đó là "phương thức khởi tạo siêu lớp, trình khởi tạo biến thể hiện, thân phương thức khởi tạo". (Instance biến initializers là những gì bạn nhận được khi bạn gán giá trị cho một biến Ví dụ tại thời điểm kê khai.)
Jon Skeet

1
@cwap: Vâng, ý tôi là vậy - cái sau là bộ khởi tạo đối tượng .
Jon Skeet


17

Tôi ngạc nhiên là không ai đề cập đến thuộc tính, một thứ khá cơ bản trong C # nhưng lại vắng bóng trong Java. C # 3 trở lên cũng đã tự động triển khai các thuộc tính. Trong Java, bạn phải sử dụng các phương thức kiểu GetX / SetX.

Một sự khác biệt rõ ràng khác là biểu thức LINQ và lambda trong C # 3 không có trong Java.

Java còn thiếu một số thứ đơn giản nhưng hữu ích khác như chuỗi nguyên văn (@ ""), nạp chồng toán tử, trình vòng lặp sử dụng năng suất và trình xử lý trước cũng bị thiếu trong Java.

Một trong những yêu thích cá nhân của tôi trong C # là các tên không gian tên không phải tuân theo cấu trúc thư mục vật lý. Tôi thực sự thích sự linh hoạt này.


10

Có rất nhiều điểm khác biệt, nhưng tôi nghĩ đến những điều này:

  • Thiếu toán tử nạp chồng trong Java. Xem instance.Equals của bạn (instance2) so với instance == instance2 (đặc biệt là w / string).
  • Làm quen với các giao diện KHÔNG có tiền tố I. Thường thì bạn sẽ thấy các không gian tên hoặc các lớp được gắn với Impl để thay thế.
  • Khóa được kiểm tra kỹ không hoạt động do mô hình bộ nhớ Java.
  • Bạn có thể nhập các phương thức tĩnh mà không cần đặt trước tên lớp cho chúng, điều này rất hữu ích trong một số trường hợp nhất định (DSL).
  • Các câu lệnh switch trong Java không yêu cầu mặc định và bạn không thể sử dụng chuỗi làm nhãn viết hoa (IIRC).
  • Java generic sẽ khiến bạn tức giận. Các kiểu chung của Java không tồn tại trong thời gian chạy (ít nhất là trong 1.5), chúng là một thủ thuật trình biên dịch, gây ra sự cố nếu bạn muốn phản ánh các kiểu chung.

4
Lần cuối tôi kiểm tra, các chuỗi KHÔNG quá tải == và tôi cũng chưa nghe nói về việc nó được thêm vào Java 7. Tuy nhiên, họ làm quá tải + để nối chuỗi.
Michael Madsen

3
Vâng, so sánh chuỗi với == là một lỗi rất phổ biến đối với người mới. .equalslà những gì bạn phải sử dụng.
Michael Myers

Ngoài ra, tôi không hiểu nhận xét về các phương thức tĩnh. Không phải tất cả các ngôn ngữ đều cho phép các phương thức tĩnh hoặc phương thức tương đương?
Michael Myers

Tôi gần như đã ủng hộ điều này, nhưng tôi không đồng ý về generic. Tôi thích loại tẩy xóa.
finnw

2
"Câu lệnh switch trong Java không yêu cầu mặc định" - C # cũng vậy.
RenniePet 12/1213

8

.NET đã sửa đổi generic; Java đã xóa bỏ các thông tin chung.

Sự khác biệt là thế này: nếu bạn có một ArrayList<String>đối tượng, trong .NET, bạn có thể nói (trong thời gian chạy) rằng đối tượng đó có kiểu ArrayList<String>, trong khi trong Java, trong thời gian chạy, đối tượng là kiểu ArrayList; các Stringphần bị mất. Nếu bạn đưa không phải Stringđối tượng vào ArrayList, hệ thống không thể thực thi điều đó và bạn sẽ chỉ biết về nó sau khi bạn cố gắng giải nén mục đó ra và quá trình truyền không thành công.


1
... mặc dù điều đáng nói thêm là bạn không thể đặt các Stringđối tượng không phải là đối tượng trong danh sách đó mà không thực hiện truyền bỏ chọn ở đâu đó , và trình biên dịch sẽ cảnh báo chính xác cho bạn tại thời điểm đó rằng trình biên dịch không còn có thể kiểm tra các giới hạn chung nữa. Miễn là danh sách của bạn luôn được lưu trữ trong một biến của List<String>, các nỗ lực chèn một đối tượng không phải là Chuỗi vào đó sẽ không được biên dịch.
Andrzej Doyle

"non-String đối tượng vào ArrayList, hệ thống không thể thực thi rằng": Lưu ý rằng hệ thống không thực thi nó ở thời gian biên dịch. Tuy nhiên, bạn có thể phá vỡ điều này bằng cách ép kiểu (hoặc không sử dụng generic); thì kiểm tra thời gian chạy sẽ bắt nó, nhưng chỉ khi giải nén.
sleske

1
@Andrzej, @sleske: Tôi chỉ nói về thời gian chạy. Giả sử bạn lấy đối tượng thông qua phản chiếu và gọi addphương thức của nó (một lần nữa, thông qua phản chiếu). Hệ thống sẽ từ chối một Stringđối tượng không phải đối tượng ngay lập tức hay chỉ khi truyền kết quả là get?
Chris Jester-Young

@sleske: Nhưng IIRC bạn chỉ cần một phép truyền ngầm để vượt qua việc kiểm tra kiểu.
Niki

@Judah: Tôi đã lùi lại sự thay đổi của bạn (vâng, một năm sau thực tế), vì bạn không thể có các đối tượng cùng loại List. Bạn có thể có các loại đối tượng giảm dần List, như ArrayListLinkedList. (Điều quan trọng là phải sử dụng Listthay vì ArrayListkhi đi qua chúng xung quanh, nhưng nó không làm thay đổi thực tế rằng bạn không thể nói new List().)
Chris Jester-Young

6

Một điều tôi bỏ lỡ trong C # từ Java là xử lý bắt buộc các ngoại lệ đã kiểm tra. Trong C #, điều phổ biến là người ta không biết về các ngoại lệ mà một phương pháp có thể ném ra và bạn phải dựa vào tài liệu hoặc thử nghiệm để phát hiện ra chúng. Không phải như vậy trong Java với các ngoại lệ được kiểm tra.


1
FWIW, điều này được coi là không có trong C # và lý do để loại bỏ chúng là có thật. Hãy xem xét Artima.com/intv/handcuffs.html . Không phải là các nhà thiết kế phản đối về mặt triết học với những trường hợp ngoại lệ đã được kiểm tra - đúng hơn, họ đang chờ đợi một kỹ thuật tốt hơn mang lại những lợi ích tương tự mà không có tất cả các nhược điểm.
Greg D

Vâng, vâng, một chủ đề rất gây tranh cãi ...
sleske

2
Tôi thích thực tế là C # không có ngoại lệ được kiểm tra, nhưng đây là bài đăng đầu tiên thậm chí chỉ ra sự khác biệt, vì vậy +1.
Nick

5

Java có tính năng tự động đóng hộp cho các kiểu nguyên thủy thay vì các kiểu giá trị, vì vậy mặc dù System.Int32[]là một mảng giá trị trong C #, nhưng Integer[]là một mảng các tham chiếu đến Integercác đối tượng và như vậy không phù hợp cho các tính toán hiệu suất cao hơn.


1
Pete: đó là một điểm xuất sắc, và một điểm mà tôi chưa từng biết hoặc ít nhất là chưa bao giờ xem xét (là một nhà phát triển C # và mới làm quen với Java).
Jim Schubert

4

Không có đại biểu hoặc sự kiện - bạn phải sử dụng giao diện. May mắn thay, bạn có thể tạo các lớp và triển khai giao diện nội tuyến, vì vậy đây không phải là vấn đề lớn


3
"không phải là một vấn đề lớn như vậy". Đúng. Bởi vì x => x.Fooso với new Bar() { public void M(SomeType x) { return x.Foo; } }- ai quan tâm đến việc mã ngắn hơn 10 lần?
Kirk Woll

2

Chức năng ngày / lịch tích hợp trong Java rất tệ so với System.DateTime. Có rất nhiều thông tin về điều này ở đây: Có gì sai với API Ngày & Giờ của Java?

Một số trong số này có thể được hiểu cho một nhà phát triển C #:

  • Lớp Java Date có thể thay đổi, có thể khiến việc trả lại và chuyển ngày trở nên nguy hiểm.
  • Hầu hết các hàm tạo java.util.Date không được dùng nữa. Đơn giản chỉ cần xác định một ngày là khá dài dòng.
  • Tôi chưa bao giờ có được lớp java.util.Date để tương tác tốt với các dịch vụ web. Trong hầu hết các trường hợp, ngày ở hai bên đã được biến đổi một cách hoang dã thành một số ngày và giờ khác.

Ngoài ra, Java không có tất cả các tính năng giống như GAC và các hội đồng có tên mạnh mang lại. Jar Hell là thuật ngữ chỉ những gì có thể xảy ra sai sót khi liên kết / tham chiếu đến các thư viện bên ngoài.

Về đóng gói / triển khai có liên quan:

  • có thể khó đóng gói các ứng dụng web ở định dạng EAR / WAR thực sự cài đặt và chạy trong một số máy chủ ứng dụng khác nhau (Glassfish, Websphere, v.v.).
  • triển khai ứng dụng Java của bạn dưới dạng dịch vụ Windows tốn nhiều công sức hơn trong C #. Hầu hết các đề xuất tôi nhận được cho việc này liên quan đến thư viện bên thứ ba không miễn phí
  • cấu hình ứng dụng gần như không dễ dàng bằng việc đưa tệp app.config vào dự án của bạn. Có một lớp java.util.Properties, nhưng nó không mạnh mẽ và việc tìm đúng vị trí để thả tệp .properties của bạn có thể gây nhầm lẫn

1

Không có đại biểu trong Java. Vì vậy, bên cạnh tất cả những lợi ích mà các đại biểu mang lại, các sự kiện cũng hoạt động khác nhau. Thay vì chỉ nối một phương thức, bạn cần triển khai một giao diện và đính kèm nó.


1

Một điều khiến bạn ngạc nhiên khi nó nằm trong danh sách phỏng vấn của tôi là không có từ khóa tương tự "mới" trong Java để ẩn phương thức và không có cảnh báo trình biên dịch "bạn nên đặt mới ở đây". Việc ẩn phương pháp tình cờ khi bạn định ghi đè sẽ dẫn đến lỗi.

(chỉnh sửa ví dụ) Ví dụ, B bắt nguồn từ A (sử dụng cú pháp C #, Java hoạt động giống như cách tôi đã kiểm tra lần trước nhưng không phát ra cảnh báo trình biên dịch). Foo của A được gọi, hay foo của B? (A được gọi, có thể gây ngạc nhiên cho nhà phát triển đã triển khai B).

class A 
{
public void foo() {code}
}

class B:A
{
public void foo() {code}
}    

void SomeMethod()
{
A a = new B(); // variable's type is declared as A, but assigned to an object of B.
a.foo();
}

2
Làm thế nào để bạn vô tình ẩn một phương thức thể hiện trong Java? chúng hoặc là cuối cùng - vì vậy không có newphương thức ghi đè hoặc phương thức nào để cố tình ẩn - hoặc không phải là cuối cùng nên được ghi đè thay vì ẩn.
Pete Kirkham

Đã thêm ví dụ. Tôi đã không thử nghiệm nó trong Java trong một thời gian, nhưng câu hỏi phỏng vấn ban đầu đến từ nhóm Java từ công ty trước đây của tôi. Trong C #, cảnh báo trình biên dịch thường có nghĩa là mọi người thay đổi tên. Tôi thừa nhận đó là một trường hợp khá kỳ quặc, nhưng OP dường như đang yêu cầu những thứ có thể khó tìm ra vấn đề khi chuyển đổi ngôn ngữ.
Jim L

1
Sự khác biệt ở đây là trong Java tất cả các phương thức đều là ảo (vì vậy trong Java, nó sẽ là B.foo () được gọi vì Java luôn thực hiện điều phối phương thức động), trong khi trong C # các phương thức không phải là ảo theo mặc định (và A.foo () sẽ được gọi là). Nhiều trình kiểm tra kiểu mã hóa cho Java sẽ cho bạn biết rằng B nên sử dụng chú thích @Override.
William Billingsley

1

Java không có LINQ và tài liệu là địa ngục. Giao diện người dùng trong Java là một khó khăn để phát triển, bạn mất tất cả những gì tốt đẹp mà Microsoft đã cho chúng tôi (WPF, WCF, v.v.) nhưng lại nhận được các "API" khó sử dụng, hầu như không được ghi lại.


0

Một vấn đề mà tôi gặp phải cho đến nay khi làm việc với Java đến từ C # là Ngoại lệ và Lỗi khác nhau.

Ví dụ, bạn không thể bắt lỗi hết bộ nhớ bằng cách sử dụng bắt (Ngoại lệ e).

Xem phần sau để biết thêm chi tiết:

why-is-java-lang-outofmemoryerror-java-heap-space-not-catch


Nó được thiết kế cố ý như vậy để ngăn cản các lập trình viên ứng dụng từ bắt nó
finnw

Vâng tôi chắc chắn rằng nó đã được, nhưng đến từ C # bên, nơi tất cả các bạn phải lo lắng về được bắt ngoại lệ, ném cho tôi một vòng lặp :)
Chris Persichetti

0

Đã quá lâu kể từ khi tôi sử dụng Java nhưng những điều tôi nhận thấy ngay lập tức trong quá trình phát triển ứng dụng là mô hình sự kiện C #, kéo và thả C # so với sử dụng Trình quản lý bố cục trong Swing (nếu bạn đang làm App dev) và xử lý ngoại lệ Java đảm bảo rằng bạn bắt được một ngoại lệ và C # không bắt buộc.


0

Để trả lời câu hỏi rất trực tiếp của bạn trong tiêu đề của bạn:

"Các nhà phát triển C # học Java, sự khác biệt lớn nhất mà người ta có thể bỏ qua là gì?"

A: Thực tế là Java chậm hơn đáng kể trên Windows.


4
Tôi chạy OpenSuSE 64bit và Windows 7 64bit, và Eclipse IDE ("có vẻ") nhanh hơn trong Windows. Câu trả lời của bạn dựa trên quan sát cá nhân hay dựa trên điểm chuẩn thực tế?
Jim Schubert

0

Sự khác biệt khó chịu nhất đối với tôi khi tôi chuyển sang java đó là khai báo chuỗi.

trong C # string(hầu hết thời gian) trong JavaString

Nó khá đơn giản, nhưng tin tôi đi, nó khiến bạn mất rất nhiều thời gian khi có thói quen skhông nên S!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.