Cũng là những ngôn ngữ lập trình phổ biến được nhiều người quan tâm là C# và Java. Tuy nhiên thì không phải ai cũng có thể phân biệt rõ được 2 ngôn ngữ này. Chính vì vậy để hiểu rõ hơn sự khác biệt giữa 2 ngôn ngữ này thế nào hãy cùng Box.edu so sánh C# với Java ngay nhé!
Mục lục bài viết
Tổng quan so sánh C# với Java
Tổng quan so sánh C# với Java
Có thể nói sự khác biệt quan trọng nhất giữa C# và Java đó là C# sẽ cho phép bạn sử dụng con trỏ. Bạn có thể thấy một ngoại lệ là Null Pointer khi mã của bạn cố gắng sử dụng một đối tượng mà bạn chưa khởi tạo. Nhưng con trỏ không có sẵn để bạn sử dụng trong mã Java của mình.
Khi bạn biên dịch mã Java của mình, máy ảo Java (JVM) sẽ thực thi mã bytecode. Đặc biệt Java không biên dịch thành mã máy. Thay vào đó, JVM sẽ diễn giải mã bytecode và thực hiện các hướng dẫn cần thiết.
Cũng tương tự với C #. Trình biên dịch dịch mã C # thành Ngôn ngữ trung gian (IL) mà thời gian chạy ngôn ngữ chung (CLR) được thực thi. Microsoft đã cung cấp C # trên các hệ điều hành khác bằng cách đưa CLR vào như một phần của .NET Core.
Vì vậy, cả mã C # và Java đều thực thi tương tự. Trình biên dịch tạo mã thành một thứ gì đó mà trình thông dịch có thể thực thi. Chúng có những tên gọi khác nhau như: JVM vs CLR, bytecode vs IL, nhưng các nguyên tắc cơ bản là giống nhau.
Ngoài ra ở cả 2 ngôn ngữ thì thời gian chạy sẽ quản lý bộ nhớ cho chúng ta. Nên không phải lo lắng về việc phân bổ bộ nhớ cho các đối tượng, cũng như việc giải phóng nó một lần nữa khi đã hoàn thành việc đó. Bộ nhớ được trả về bởi một quá trình được gọi là thu gom rác.
Nhưng điều này chỉ hoạt động nếu thời gian chạy nhận biết và kiểm soát các đối tượng mà chúng ta đang sử dụng.
C # cho phép sử dụng con trỏ, có nghĩa là bạn có thể truy cập vào bộ nhớ trực tiếp. Tuy nhiên khi bạn bắt đầu sử dụng con trỏ, bạn sẽ mất đi sự an toàn mà C # cung cấp với mã được quản lý.
Dưới đây là một ví dụ về sắp xếp bong bóng đơn giản, trong C #:
using System;
// import in Java (although Java doesn't need to import System).
namespace BubbleSort
// package <packagename>; in Java. Goes before any imports.
{
class Program
{
public static void Main(String[] args) {
// Java uses lowercase m for main
int[] numbers = {3, 2, 4, 1, 5};
Sort(numbers);
foreach (int num in numbers) {
// Java -> for (int num : numbers) {
Console.Write(num + " ");
// Java -> System.out.print(num + " ");
}
}
static void Sort(int[] list) {
// sort in Java, but that's a convention not a language requirement.
bool stillSwapping = true; // C# bool -> Java boolean
while (stillSwapping) {
stillSwapping = false;
for (int i = 0; i < list.Length - 1; i++) {
// C# .Length -> Java .length (lowercase l).
if (list[i] > list[i + 1]) {
SwapValues(list, i, i + 1);
stillSwapping = true;
}
}
}
}
static void SwapValues(int[] list, int x, int y) {
// Would be swapValues (lowercase s) in Java
int temp = list[x];
list[x] = list[y];
list[y] = temp;
}
}
}
Như bạn thấy, mã C # và Java rất giống nhau. Chỉ có 7 thay đổi cần thiết và IDE của bạn có thể sẽ xử lý 3 thay đổi đầu tiên trong số đó. Các tham số tham chiếu là thứ mà C # không có trong Java. Thực tế, bạn có thể sử dụng:
Array.Reverse(list, i, 2);
Ở trên dòng 22. Nhưng nếu bạn muốn hoán đổi các mục không liền kề trong một Mảng, thì có thể viết một phương thức như Swap Values.
Trong C #, chúng ta có thể sử dụng các tham số tham chiếu để thay thế. Trong cả hai ngôn ngữ, chúng tôi chuyển các kiểu giá trị (ví dụ: số nguyên) theo giá trị. Có nghĩa là hàm nhận giá trị và không thể thay đổi biến đối số.
Sử dụng lệnh ref trong C #, chúng ta có thể chuyển các đối số bằng cách tham chiếu. Điều này cho phép hàm sửa đổi các biến ban đầu.
Bạn có thể tận dụng điều đó, để tránh phải chuyển danh sách cho Swap Values. Phương thức trở thành:
static void SwapValues(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
Và chúng tôi gọi hàm trên dòng 22 là:
SwapValues(ref list[i], ref list[i + 1]);
Bên trong phương thức, x và y là những con trỏ hiệu quả đến các đối số ban đầu. Bất kỳ thay đổi nào được thực hiện với x hoặc y cũng sẽ thay đổi các đối số – trong trường hợp này là hai mục trong danh sách. Điều đó không thể xảy ra trong Java – các kiểu giá trị chỉ có thể truyền giá trị của chúng. Nếu muốn sửa đổi danh sách, bạn phải chuyển danh sách cho phương thức.
Tránh con trỏ
Có rất ít lý do để sử dụng con trỏ trong C #. Vì C # cho phép bạn sử dụng các đối tượng COM hoặc gọi các hàm trong Windows API, sử dụng InteropServices và lớp Marshall. Rất hiếm khi phải viết mã không an toàn để sử dụng con trỏ chuyển đến các hàm API.
Bạn có thể quyết định sử dụng con trỏ cho hiệu suất. Nhưng đừng bị cám dỗ để chuyển đổi mọi thứ sang mã không an toàn nếu hiệu suất là một vấn đề. Do đó thay vì viết mã bằng cách sử dụng con trỏ, có thể thực hiện bằng cách sử dụng thuật toán sắp xếp nhanh hơn. Sắp xếp theo đống, sắp xếp chèn và sắp xếp nhanh đều sẽ hoạt động tốt hơn so với sắp xếp bong bóng. Chỉ khi sắp xếp nhanh vẫn không đủ nhanh, bạn mới nên cân nhắc sử dụng con trỏ để cải thiện hiệu suất.
Điều khác với mã đó là tất cả đều không cần thiết. Lớp C # Array và lớp Java Arrays đều có phương thức sắp xếp (chữ thường cho Java) và bạn không cần phải viết riêng. Bạn có thể viết toàn bộ chương trình bằng Java như sau:
package academy.learnprogramming.bubblesort;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] numbers = {3, 2, 4, 1, 5};
Arrays.sort(numbers);
for (int num : numbers) {
System.out.print(num + " ");
}
}
}
Bạn có thể thử nghiệm với con trỏ bằng cách viết cách triển khai các thuật toán phổ biến của riêng bạn. Đây là một cách tốt để thực hành với các ngôn ngữ này.
So sánh C# với Java về Byte đã được ký
So sánh C# với Java về Byte đã được ký
Một sự khác biệt khi so sánh C# với Java là kiểu byte trong Java được ký. Một byte Java lưu trữ một giá trị có dấu, trong phạm vi -128 đến +127.
Byte C # là không có dấu và lưu trữ các giá trị từ 0 đến 255. Sự khác biệt này trở nên quan trọng khi xử lý các giá trị 8-bit – ví dụ: pixel Đỏ, Xanh lá cây, Xanh lam (RGB) trong một hình ảnh. Tất cả các kiểu số nguyên thủy khác đều được ký, và chúng cũng quyết định ký byte.
Kiểm tra ngoại lệ
Bạn có thể tự hỏi tại sao lại đưa khối try / catch vào mã Java. Khi thảo luận về mã C #, chúng tôi đã nói rằng sẽ không bao gồm bất kỳ xử lý lỗi nào, để giữ mọi thứ đơn giản. Trong mã Java này chúng tôi không có sự lựa chọn. Bởi vì Java có 2 loại ngoại lệ: ngoại lệ đã kiểm tra và ngoại lệ không được kiểm tra. Nếu một hàm ném ra một ngoại lệ đã được kiểm tra, thì bạn không có lựa chọn nào khác ngoài việc xử lý nó. Bạn phải sử dụng khối try / catch hoặc sử dụng lệnh ném để ủy quyền xử lý ngoại lệ cho mã gọi.
C # chỉ có các ngoại lệ không được chọn – bạn có thể bỏ qua nếu muốn. Tất nhiên, bỏ qua các ngoại lệ không phải là một ý kiến hay – có thể là ngoại trừ trong đoạn mã trình diễn ngắn. Chương trình của bạn sẽ gặp sự cố nếu bạn gặp phải một ngoại lệ mà bạn không xử lý được. Cũng chính là sự khác biệt giữa 2 ngôn ngữ này. Java đã kiểm tra các ngoại lệ mà bạn phải xử lý theo một cách nào đó. Không phải tất cả sự ngoại lệ của Java đều được kiểm tra. Cả hai ngôn ngữ cũng có các ngoại lệ không được chọn và bạn phải xử lý chúng theo cách giống nhau ở cả hai ngôn ngữ.
Các ngoại lệ được kiểm tra của Java gây khá nhiều tranh cãi, chúng là một tính năng của Java và bạn phải xử lý chúng khi chuyển đổi mã C # sang Java. Chúng tôi không phải sử dụng khối try / catch, chúng tôi có thể chỉ định rằng phương thức chính của chúng tôi ủy quyền xử lý ngoại lệ cho mã gọi:
public static void main(String[] args) throws IOException {
Điều đó cho phép loại bỏ các khối try / catch và mã bây giờ sẽ hoạt động giống như mã C # – nó sẽ bị lỗi nếu không tìm thấy tệp hoặc đĩa đầy.
Các lớp và phương thức từng phần
C # cho phép bạn chia một lớp, cấu trúc hoặc giao diện trên một số tệp. Do đó, bạn có thể đưa phần khai báo của một phương thức vào một tệp và định nghĩa của nó trong tệp khác. Việc chuyển đổi một phần lớp sang Java sẽ bao gồm việc kết hợp hai (hoặc nhiều) tệp thành một tệp duy nhất. Làm theo cách khác, từ Java sang C #, không gây ra vấn đề gì – bạn luôn có thể chia lớp thành các tệp riêng biệt sau này nếu bạn cảm thấy cần thiết.
Microsoft có nhiều lý do để bao gồm các lớp từng phần. Nó giúp một số nhà phát triển làm việc trên cùng một lớp dễ dàng hơn và tự động tạo mã trong tệp của chính nó. Tài liệu của Microsoft đề cập đến Windows Forms. Nhưng bạn không chắc đang sử dụng Windows Forms trong ứng dụng Java của mình. Vì vậy, việc thiếu các lớp từng phần sẽ không được xem xét nghiêm túc khi chuyển đổi mã.
Các chức năng mở rộng
C # có một tính năng hữu ích không có trong Java đó là các phương thức mở rộng. Đây là những hàm mà bạn có thể coi là một phần của một lớp (hoặc cấu trúc). Chúng cho phép bạn cung cấp nhiều chức năng hơn mà không cần tạo lớp con. Phương thức mở rộng là một phương thức tĩnh được gọi trên một cá thể lớp. Các phương thức mở rộng triển khai nhiều chức năng của LINQ.
static void Main(string[] args)
{
List<HealthSpend> values = new List<HealthSpend>();
values.Add(new HealthSpend("UK", "2017", 9.6));
values.Add(new HealthSpend("UK", "2016", 9.7));
values.Add(new HealthSpend("UK", "2015", 9.7));
values.Add(new HealthSpend("Australia", "2017", 9.2));
values.Add(new HealthSpend("Australia", "2016", 9.2));
values.Add(new HealthSpend("Australia", "2015", 9.3));
values.Add(new HealthSpend("USA", "2017", 17.1));
values.Add(new HealthSpend("USA", "2016", 17.2));
values.Add(new HealthSpend("USA", "2017", 16.8));
var amounts = values.Where(hs => hs.country == "Australia")
.OrderBy(hs => hs.year)
.Select(hs => hs)
.Sum();
Console.WriteLine(amounts);
}
Truy vấn LINQ giống như truy vấn trước đó, nhưng thay vì trả về danh sách các mục Chi tiêu cho sức khỏe, chúng tôi muốn tổng các số tiền. Rất tiếc, chúng tôi gặp lỗi – không có phương thức Sum trong cấu trúc HealthSpend của chúng tôi .
Tất nhiên, chúng tôi có thể thêm một cái. Nhưng nếu chúng tôi không có mã nguồn cho HealthSpend hoặc không muốn sửa đổi nó, thì chúng tôi sẽ phải sử dụng đến việc tạo một lớp con. Nhưng bạn kh ông thể phân lớp một cấu trúc! Đây là nơi mà một phương pháp mở rộng có thể hữu ích.
} // End of HealthSpend definition
public static class HSExtensions {
public static HealthSpend Sum(this IEnumerable<HealthSpend> source) {
HealthSpend? sum = null; //Nullable<HealthSpend>;
foreach (HealthSpend val in source) {
if (sum == null) {
sum = new HealthSpend(val.country, val.year, val.amount);
} else {
sum += val;
}
}
if (sum == null) {
return new HealthSpend();
} else {
return (HealthSpend)sum;
}
}
}
class Program
{
...
Chúng tôi tạo một lớp để chứa phương thức mở rộng. Tôi đã đặt nó trong cùng một tệp, nhưng bạn có thể đặt nó trong tệp riêng và sử dụng HealthSpend ở đầu tệp.
Mã của chúng tôi bây giờ sẽ chạy và sử dụng phương thức mở rộn Sum này ở cuối truy vấn LINQ. Điều đó khá tuyệt và sẽ khó triển khai hơn trong Java – bạn có thể phân lớp con của lớp HealthSpend nếu bạn không thể sửa đổi mã nguồn. Hoặc bạn có thể xóa Sum ra khỏi truy vấn và thực hiện việc tổng kết một cách riêng biệt. Nhưng làm điều đó sẽ không chứng minh một phương pháp mở rộng.
LINQ vs Luồng
Một lĩnh vực mà C # vượt trội hơn so với Java là với truy vấn tích hợp ngôn ngữ (LINQ) của nó. Java 8 đã thêm luồng vào các lớp bộ sưu tập, nhưng chúng không có tính linh hoạt như LINQ.
Ví dụ dưới đây sẽ sử dụng các đối tượng HealthSpend của chúng tôi từ mã trước với một số mục khác được thêm vào danh sách. Tôi chưa đưa các định nghĩa HealthSpend vào các mẫu mã này.
using System;
using System.Collections.Generic;
using System.Linq;
namespace StructExample
{
class Program
{
static void Main(string[] args)
{
List<HealthSpend> values = new List<HealthSpend>();
values.Add(new HealthSpend("UK", 2017, 9.6));
values.Add(new HealthSpend("UK", 2016, 9.7));
values.Add(new HealthSpend("UK", 2015, 9.7));
values.Add(new HealthSpend("Australia", 2017, 9.2));
values.Add(new HealthSpend("Australia", 2016, 9.2));
values.Add(new HealthSpend("Australia", 2015, 9.3));
values.Add(new HealthSpend("USA", 2017, 17.1));
values.Add(new HealthSpend("USA", 2016, 17.2));
values.Add(new HealthSpend("USA", 2017, 16.8));
var amounts = values.Where(hs => hs.country == "Australia")
.OrderBy(hs => hs.year)
.Select(hs => hs);
foreach (HealthSpend val in amounts) {
Console.WriteLine($"{val}");
}
}
}
}
Chúng ta có thể làm điều này tương tự bằng cách sử dụng Luồng của Java:
package academy.learnprogramming.structexample;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
ArrayList<HealthSpend> values = new ArrayList<>();
values.add(new HealthSpend("UK", 2017, 9.6));
values.add(new HealthSpend("UK", 2016, 9.7));
values.add(new HealthSpend("UK", 2015, 9.7));
values.add(new HealthSpend("Australia", 2017, 9.2));
values.add(new HealthSpend("Australia", 2016, 9.2));
values.add(new HealthSpend("Australia", 2015, 9.3));
values.add(new HealthSpend("USA", 2017, 17.1));
values.add(new HealthSpend("USA", 2016, 17.2));
values.add(new HealthSpend("USA", 2017, 16.8));
values.stream().filter(hs -> hs.country.equals("Australia"))
.sorted(Comparator.comparingInt(HealthSpend::getYear))
.forEach(System.out::println);
}
}
OrderBy của C # dễ sử dụng hơn, chúng tôi chỉ cần cung cấp cho nó một khóa để sắp xếp. Phương thức đã sắp xếp của Java cần một Bộ so sánh và lớp Bộ so sánh cung cấp một số phương thức để tạo một phương thức từ dữ liệu. Ở đây, chúng tôi sử dụng một phép so sánh số nguyên trên trường năm.
C # tạo ra một đối tượng có thể liệt kê và chúng tôi sử dụng một vòng lặp foreach để in từng mục. Lớp Stream của Java cung cấp một phương thức foreach, phương thức này làm cho quá trình xử lý cuối cùng đơn giản hơn một chút.
Cũng nhờ vào tính linh hoạt của LINQ. Java cung cấp nhiều cách triển khai Luồng, ngoài luồng từ các đối tượng Bộ sưu tập. Ví dụ: bạn có thể lấy một luồng ngày tháng hoặc một số luồng thú vị từ java.net. C # sẽ hỗ trợ LINQ cho tập hợp các đối tượng và cung cấp các truy vấn LINQ dựa trên các nguồn dữ liệu SQL và XML. Tất nhiên, bạn có thể tạo hỗ trợ đó cho các luồng Java, nhưng bạn vẫn đang xâu chuỗi các cuộc gọi phương thức lại với nhau. Truy vấn LINQ là một cấu trúc ngôn ngữ hạng nhất trong C # – do đó nó được coi là phần tích hợp ngôn ngữ của tên.
Tổng kết
Mong rằng việc so sánh C# với Java ở trên từ Box.edu sẽ giúp các bạn nắm được thêm những kiến thức cần thiết về 2 loại ngôn ngữ này một cách chính xác nhất. Đồng thời biết được nên sử dụng ngôn ngữ nào và trong trường hợp nào phù hợp hơn.