Trở về đầu

foto1 foto2 foto3 foto4 foto5
Nguyễn Tô Sơn - Giảng viên Đại học Sư phạm Hà Nội. SĐT: 091.333.2869

HỌC TIN CÙNG THỦ KHOA

Thành tích hôm nay, Thành công ngày mai !

Tìm kiếm

So sánh Java và C++
Một phương pháp tốt để học một ngôn ngữ lập trình mới là so sánh nó với những ngôn ngữ lập trình đã học để rút ra những ưu - nhược điểm của từng ngôn ngữ. Như vậy, người học lập trình không những có bài học trên ngôn ngữ mới mà còn có những kinh nghiệm mới khi xem xét lại những vấn đề của các ngôn ngữ mà mình đã học.
Để học Java một cách hiệu quả, nên đọc trước C++ để hiểu thế nào là lập trình hướng đối tượng trước. Rồi sau đó dùng kiến thức đó làm cơ sở để nâng cao hiểu biết và kỹ năng lập trình bằng ngôn ngữ Java, một công cụ không thể thiếu được trong việc phát triển các ứng dụng trên mạng. Điều này cũng dễ hiểu bởi Java được xây dựng trên nền tảng C/C++. 
Giới thiệu về Java: Java là ngôn ngữ lập trình do hãng Sun Microsystem giới thiệu vào năm 1995 – SUN là chữ viết tắt của Stanford University Network, nghĩa là Hệ thống mạng máy tính Đại học Stanford. Java là một ngôn ngữ lập trình cấp cơ sở nhưng nó được thiết kế sao cho có thể dùng như một hệ điều hành máy tính đa năng – hay nói cách khác nó rất có triển vọng trở thành một lựa chọn khác thay cho Windows. Chính vì Java có thể chạy trên nhiều hệ thống máy tính khác nhau mà người ta dự định rằng nó sẽ giúp khắc phục những lỗi tương thích vớ vẩn xảy ra trong ngành công nghiệp phần mềm. Thật vậy, khi mang Java ra chào hàng, hãng Sun đã giới thiệu như sau: “Chỉ viết một lần, chạy được khắp nơi”. Thêm vào đó Java còn giúp người lập trình không phải lo lắng nhiều về vấn đề bảo mật cho máy tính của mình. Vì Java có thể chạy gần như trên mọi hệ thống nên không nhất thiết cần đến khái niệm liên kết mở rộng, vốn là điểm yếu của toàn mạng.
Hãng Sun đang nỗ lực để Java được công nhận là một tiêu chuẩn quốc tế, một động thái được các nhà phân tích cho rằng sẽ là một đe doạ đối với sự thống trị của hệ điều hành Windows trong một vài lĩnh vực quan trọng, bao gồm cả việc lấy thông tin trên Internet. Hơn thế nữa, một trình ứng dụng như Word của Microsort chẳng hạn có thể chạy trên Java thay vì Windows. Vì thế khi viết trình duyệt Internet Exporer 4.0 kèm trong Windows. Microsoft đã thay đổi ngôn ngữ Java để làm giảm tính đa năng của nó bằng cách bỏ đi hai chuẩn mực quan trọng nhất mà Sun đã viết. Điều đó giải thích tại sao các ứng dụng Applet của Java khi nhúng vào trang Web có thể chạy ngay trong các trình duyệt như Internet Exporer hay Netscape.
Java là ngôn ngữ vừa thông dịch vừa biên dịch. Đầu tiên Java sử dụng công cụ Javac để biên dịch mã nguồn chương trình để chuyển thành mã Byte độc lập với phần cứng. Sau đó, từ mã Byte thu được nó dùng trình thông dịch là máy ảo Java để thực hiện trên từng loại máy cụ thể. Nguyên nhân Java xây dựng hai trình thông dịch và biên dịch riêng rẽ là bởi việc thiết kế lại một trình biên dịch để chạy trên một máy tốn kém hơn rất nhiều so với việc thiết kế một trình thông dịch để chạy trên máy tính đó.
Mục tiêu của Java là cho phép người lập trình viết chương trình một lần nhưng có thể chạy trên bất cứ phần cứng cụ thể nào. Chính nguyên nhân này giúp Java trở thành một ngôn ngữ được sử dụng rộng rãi để viết chương trình chạy trên Internet. Nó là ngôn ngữ lập trình hướng đối tượng độc lập thiết bị, không phụ thuộc vào hệ điều hành. Nó không chỉ dùng để viết cho các ứng dụng chạy đơn lẻ hay trong mạng mà còn để xây dựng các trình điều khiển thiết bị cho điện thoại di động, PDA, ...
Có rất nhiều chương trình hỗ trợ viết mã nguồn Java nhưng điển hình là Eclipse. Eclipse là phần mềm thông minh, nó đưa ra những cảnh báo và lỗi giúp cho người lập trình có thể nhận biết được các lỗi trong quá trình sử dụng và đồng thời nêu ra những cách khắc phục những lỗi đó. 
Cảnh báo nhắc nhở cho người lập trình sẽ xuất hiện gạch chân lượn sóng màu vàng. 
Điển hình là những cảnh báo nhắc nhở cho người lập trình là: 
Phương thức khai báo một biến trong thân của nó nhưng lại không sử dụng (không tác động vào biến) gây lãng phí bộ nhớ, thậm chí kể cả ta khởi tạo giá trị ban đầu cho biến được khai báo trong thân phương thức đó nhưng không tác động vào biến thì cũng xuất hiện cảnh báo từ phía chương trình. 
Hoặc cảnh báo khi sử dụng thuộc tính tĩnh hay phương thức tĩnh như là một thành phần riêng của một đối tượng cụ thể. Cách khắc phục: sử dụng chúng như một thành phần chung của lớp.
Giả sử có một lớp với tên là HoaDon, trong lớp có thuộc tính tĩnh là TongSoHoaDon và một đối tượng của lớp HoaDon là d. Khi đó khởi tạo d.TongSoHoaDon = 0 chẳng hạn chương trình sẽ cảnh báo. Khắc phục cảnh báo này bằng cách sử dụng chúng như một thành phần chung của lớp. Tức là gán: TongSoHoaDon = 0 nếu câu lệnh này nằm trong phương thức nào đó thuộc lớp HoaDon hoặc HoaDon.TongSoHoaDon = 0. 
Hoặc thuộc tính và phương thức của lớp được khai báo là private nhưng không có phương thức nào của lớp này tác động vào thuộc tính và phương thức đó làm cho người lập trình sau không thể gián tiếp xem giá trị của thuộc tính và phương thức này.
Điều đó chứng tỏ Eclipse rất coi trọng mặt ngữ nghĩa.
Tất nhiên, dù sao cũng chỉ là những cảnh báo, chương trình vẫn hoạt động bình thường, tuy nhiên nên khắc phục những cảnh báo này. 
Nếu lỗi xảy ra thì sẽ xuất hiện gạch chân lượn sóng màu đỏ, điển hình là những lỗi sau: 
Phương thức không khởi tạo giá trị ban đầu cho biến được khai báo trong thân phương thức đó, thông báo lỗi sẽ xuất hiện ở vị trí biến đó bị tác động.
Phương thức cần trả lại giá trị nhưng lại không trả lại giá trị. Ví dụ sai sau đây minh hoạ điều đó:
public boolean KiemTra(int n)
{
   System.out.println(n);
   // return true;
}
Hoặc ví dụ sau đây cũng sai:
public boolean KiemTra(int n)
{
   if (n == 0) return true;
   // else return false;
}
Lỗi khi gán lớp con bằng lớp cơ sở. Khi ta ép kiểu thì lỗi đó có thể vượt mặt được Eclipse nhưng sẽ có thông báo lỗi trong quá trình dịch.
Khả năng cảnh báo và phát hiện lỗi nghiêm ngặt giúp Eclipse tránh được những dữ liệu, cách thức sử dụng có khả năng phát sinh lỗi, tăng khả năng gỡ rối chương trình. Tuy nhiên, chính khả năng này làm người mới học cảm thấy khá khó khăn khi tiếp cận với Java.
Còn đối với thuộc tính của lớp có thể “không cần sử dụng”  bởi thực chất nó không phải là biến mà là thành phần cấu tạo nên lớp, chính vì vậy không thể coi việc chưa sử dụng một thuộc tính trong lớp là lãng phí được, bởi nó có thể được sử dụng ở lớp khác. Đồng thời có thể có hoặc không phần khởi tạo giá trị ban đầu cho các thuộc tính này. Nếu không có phần khởi tạo, chương trình sẽ tự động gán giá trị mặc định là 0 hoặc null cho các thuộc tính này. 
Ta nói lại ý nghĩa của các bổ từ dành cho thuộc tính và phương thức như sau:
Ø     public: Các lớp khác có thể dùng thoải mái. (Tất nhiên nếu khác gói thì phải import).
Ø     protected: Chỉ có các lớp cùng gói hoặc lớp thừa kế (Nếu là lớp thừa kế thì có thể khác gói) mới dùng được. Chế độ bảo vệ protected còn thể hiện ở chỗ: nó chỉ cho phép lớp thừa kế sử dụng các thuộc tính hay phương thức protected của lớp cơ sở như là thành phần của chính mình. Việc khai báo một đối tượng thuộc lớp cơ sở trong thân phương thức nào đó của lớp thừa kế rồi truy cập tới các thuộc tính hay phương thức protected của đối tượng này là vi phạm.
Ø     private: Chỉ có lớp định nghĩa nó mới được phép sử dụng.
Ø     default (Mặc định): Nếu không khai báo gì thêm thì dùng bổ từ mặc định nằm giữa private và protected. Nó khác với private là các lớp cùng gói thì có thể dùng được. Nó khác với protected là ngay cả lớp con thừa kế nhưng khác gói cũng không thể gọi tới nó.
Ø     static: Các trạng thái mà phương thức có thể được thay đổi mà không cần đến đối tượng. Nó chỉ được sử dụng đối với các dữ liệu và các phương thức tĩnh.
Ø     abstract (Trừu tượng): Ngụ ý rằng phương thức không có một mã cụ thể (code) và nó sẽ được bổ sung ở các lớp con. Loại phương thức này được sử dụng trong các lớp kế thừa. Chỉ trong những lớp trừu tượng mới có phương thức trừu tượng thôi.
Ø     final (Kết thúc): Phương thức không thể được nạp chồng khi cài đặt hoặc ghi đè khi thừa kế. Chính vì vậy nó là cách thức để xây dựng hằng.
Ø     native (Tự nhiên): Chỉ ra rằng phần thân của phương thức được viết trên các ngôn ngữ khác Java ví dụ C, hoặc C++.
Ø     syschronized (Đồng bộ): Sử dụng với phương thức trong quá trình thực thi luồng. Nó cho phép chỉ một luồng được truy cập vào khối mã vào một thời điểm.
Ø     volatile (Linh hoạt): Được sử dụng với các biến để thông báo rằng giá trị của biến có thể được thay đổi vài lần khi thực thi chương trình và giá trị của nó không được ghi vào thanh ghi.
Chú ý: Các lớp cùng gói mà lớp này muốn dùng lớp khác thì không cần phải import.
Các bổ từ dành cho lớp gồm:
Ø     public: Các lớp khác có thể thoải mái sử dụng và thừa kế. 
Ø     default (Mặc định): Nếu không khai báo public thì mặc định các lớp cùng gói mới được sử dụng và thừa kế lớp này.
Ø     abstract (Lớp cơ sở trừu trượng): Không có một đối tượng nào được tạo ra từ lớp này. 
Ø     final: Lớp không cho phép tạo dẫn xuất, còn gọi là lớp hằng.
Như trên đã nói, nếu các thuộc tính và phương thức được khai báo là private thì cần có phương thức nào đó của lớp này để người lập trình sau có thể gián tiếp xem giá trị của thuộc tính và phương thức đó. Còn nếu các thuộc tính và phương thức được khai báo mặc định hoặc protected hoặc public thì không cần như vậy vì nó cho phép phương thức của lớp khác tác động vào các thuộc tính và phương thức này trong những trường hợp cụ thể.
So sánh Java với C++ và Pascal
Ø     Chương trình Java được thiết kế thành các gói, các gói giống như các thư mục. Mỗi gói bao gồm các lớp và giao diện... Ta có thể so sánh gói gần giống với inlude trong C++ hoặc Unit trong Pascal. Kể cả lớp và giao diện trong gói cũng có ý nghĩa như vậy, tức là cũng gần giống include trong C++ hoặc Unit trong Pascal. Để thực hiện vai trò này nó sử dụng câu lệnh import.
Ø     Java là ngôn ngữ lập trình định kiểu rất chặt chẽ, những khó khăn trong việc sử dụng dữ liệu được khắc phục thông qua việc sử dụng ép kiểu:
Ví dụ sai trong cách sử dụng định kiểu dữ liệu (Chặt chẽ hơn C++ nhưng lại giống Pascal): 
int a = 10; float b = 20;
int c =  a + b;
Cần sửa lại thành:
int a = 10; float b = 20;
int c =  a + (int) b;
Ø     Java chỉ còn duy nhất khái niệm phương thức, còn trong C++ và Pascal có cả hai khái niệm hàm và phương thức. Do đó, người ta gọi C++ và Pascal là các ngôn ngữ lai, còn Java là ngôn ngữ hướng đối tượng thuần khiết.
Ø     Lớp trong Java có thể có phương thức main hoặc không, nhưng không cho phép phương thức main nằm ngoài phạm vi lớp vì Java là ngôn ngữ hướng đối tượng thuần khiết. Cách tiếp cận này khác hẳn với C++ và Pascal.
Ø     Trong Java phương thức tên main có thể bị chồng, có thể được gọi đệ qui tới chính nó, nhưng chỉ có phương thức public static void main(String args[]) được máy ảo Java gọi trước tiên để thực thi chương trình. Với C++ thì chỉ có khái niệm hàm main, nó không thể bị chồng nhưng có khả năng đệ qui gọi tới chính nó. Tất nhiên, các khả năng có được này của Java và C++ thì Pascal không thể có được.
Ø     Trong Java phương thức public static void main(String args[]) được máy ảo Java gọi trước tiên để thực thi chương trình nên tất cả các phương thức của Java không cần sử dụng đối số có giá trị mặc định, điều mà chúng ta coi như là hiển nhiên trong C++. Đối số có giá trị mặc định giúp cho C++ có khả năng tự gọi đệ qui tới chính nó.
Ví dụ sai trong Java:
public void HienThi(int n = 10)
Cần sửa lại thành:
public void HienThi(int n)
Ø     Kiểu dữ liệu trong Java chia thành hai loại: nguyên thuỷ và tham chiếu:
-       Kiểu nguyên thuỷ: byte, char, boolean, short, int, long, float, double với miền giá trị không phụ thuộc vào phần cứng máy tính. (Khác hẳn trong C++ và Pascal). Các biến kiểu nguyên thuỷ không phải là các đối tượng. Đồng thời tồn tại các lớp tương ứng gọi là lớp trình bao bọc: Byte, Character, Boolean, Short, Integer, Long, Float, Double.
-       Kiểu tham chiếu: Mảng, lớp, giao diện.
Ø     Tham chiếu trong Java đóng vai trò như con trỏ trong C++ và Pascal. Tuy nhiên cách sử dụng có hơi khác một chút (Không giống C++ và Pascal, trong Java không có tham chiếu trỏ đến các dữ liệu kiểu nguyên thuỷ).
-       Ba vùng bộ nhớ cho ứng dụng:
 
-       Đối tượng được thao tác thông qua tham chiếu.
-       Tham chiếu được lưu trữ trong vùng nhớ static/stack memory như các con trỏ trong C/C++ và Pascal.
-       Đối tượng phải được tạo một cách tường minh bằng toán tử new, khi đó nó được cấp phát động và lưu trong vùng nhớ Heap.
-       Java chỉ có phương thức khởi tạo, không có phương thức huỷ. Nguyên nhân là do, máy ảo Java sử dụng cơ chế Garbage collection (cơ chế thu gom rác) để giải phóng tự động đối tượng không cần thiết khi rỗi.
Ø     Java giống C++ ở chỗ sử dụng tham chiếu ‘this’ làm đối thứ nhất của phương thức và nó không xuất hiện tường minh.
Ø     Trong thân phương thức tĩnh không sử dụng tham chiếu ‘this’ làm đối thứ nhất của phương thức nhưng có thể truy cập đến tất cả các thành phần của một đối tượng cụ thể. Đặc biệt trong thân phương thức tĩnh thì các thành phần tĩnh của lớp đó có thể được truy cập trực tiếp như khi chúng sử dụng tham chiếu ‘this’ (tức là chỉ cần gọi tên của thuộc tính hay phương thức tĩnh đó ra là được), điều mà hàm bạn (friend) không làm được. Với phương thức tĩnh thì Java không cần phải sử dụng khái niệm hàm bạn như trong C++.  Nguyên nhân chính là do Java là ngôn ngữ hướng đối tượng thuần khiết. 
Ø     Trong Java chỉ có phương thức ảo. Mặt khác, tham chiếu trong Java có vai trò như con trỏ trong C++ nên trong Java có khái niệm liên kết động.
Ø     Java không hỗ trợ truy cập bộ nhớ trực tiếp. Tất cả đều phải sử dụng thông qua phương thức của lớp và cũng không có phương thức nào có thể thay đổi giá trị của một tham chiếu được định nghĩa sẵn (Ví dụ: trong lớp Integer không có phương thức setValue chẳng hạn). Trong các ngôn ngữ như C++ hay Pascal ta có toán tử dùng để truy cập bộ nhớ một cách trực tiếp bằng con trỏ. Điều này làm tăng tính bảo mật cho Java.
Ø     Kiểu mảng ở trong Java sử dụng có hơi khác một chút so với C++. 
Integer[] Mang;
Mang = new Integer [10]; 
for (int i = 0; i < Mang.length; i++)
     Mang[i] = new Integer(i);
Mang = new Integer [20];
Ở đây, dòng khai báo Mang = new Integer[10] có nghĩa là tạo ra 10 đối tượng Integer với giá trị ban đầu là null. Câu lệnh này nôm na tương ứng với 10 dòng lệnh khai báo Integer Mangi
Chú ý: Mảng chính là một kiểu tham chiếu mà chúng ta đã nói ở trên.
Ø     Java không sử dụng khái niệm chồng toán tử.
Ø     Sử dụng danh sách liên kết trong Java: có một vài thay đổi nhỏ trong tư tưởng so với C++ và Pascal. Tuy nhiên, cơ bản là tương tự:
packager goichinh;

 
   

class Link

          public int Data;
          public Link Next;  
     
          public Link(int x)
          {
               Data = x;
               Next = null;
          }
}
 
public class DanhSach
{
          Link First;
     
          public DanhSach()
          {
               First = null;
          }    
     
          public void HienThi()
          {
               Link tg = First;
               while (tg != null)
               {
                   System.out.print(tg.Data + "   ");
                   tg = tg.Next;
               }
          }
     
          public void Them(int x)
          {
               Link tg = new Link(x);
               tg.Next = First;
               First = tg;
          }
     
          public static void main(String[] args)
          {
               DanhSach d = new DanhSach();
               for (int i = 1; i <= 20; i++)d.Them(i);
               d.HienThi();
          }
}
Ø     Không giống như các ngôn ngữ khác, Java không hỗ trợ lệnh goto (nhảy đến vị trí bất kì trong chương trình), mặc dù goto có trong bộ từ khoá. Java thực hiện việc kết hợp lệnh break hay continue với nhãn để thay thế lệnh goto:
-       Trong lệnh switch, ta đã thấy tác dụng của lệnh break dùng thoát khỏi cấu trúc switch. Tương tự với vòng lặp, break cũng cho phép ta nhảy đến câu lệnh kế tiếp của cấu trúc vòng lặp.
-       Ngoài ra, ta cần chú ý tới việc kết hợp lệnh break hay continue với nhãn.
Ø     Một chương trình nguồn của Java có thể chứa nhiều lớp. Nhưng chỉ có duy nhất một lớp trùng tên với chương trình nguồn có thể được khai báo là public. Các lớp khác trong chương trình nguồn này không được khai báo là public nữa. Khi dịch, Java sẽ dịch những lớp này ra thành các tệp với đuôi là class độc lập.
Ø     Lớp nội là lớp được định nghĩa bên trong một lớp khác. Lớp nội có đặc tính cơ bản giống như các phương thức của lớp phủ nó, tức là nó có thể truy cập vào các thành phần của lớp phủ nó, điểm này phát triển thành kĩ thuật sử dụng lớp nội. Ngoài ra, một điểm đặc biệt của lớp nội so với các lớp khác là các phương thức của lớp phủ nó có thể truy cập trực tiếp đến thành phần của lớp nội. Khai báo một đối tượng lớp nội như sau:
Outer.Inter d = new Outer().new Inter();
Sử dụng kĩ thuật lớp nội ta có thể xây dựng được danh sách liên kết với khả năng đóng gói cao. Hãy so sánh với chương trình tạo danh sách liên kết ở trên để có cái nhìn tổng quát:
package goichinh;
 
public class DanhSach
{
Link First;
 
private class Link
{
      int Data;
      Link Next;
      
      public Link(int x)
      {
           Data = x;
           Next = null;
      }
}
 
public DanhSach()
{
      First = null;
}
 
public void HienThi()
{
      Link tg = First;
      while (tg != null)
      {
           System.out.print(tg.Data + "   ");
           tg = tg.Next;
      }
}
 
public void Them(int x)
{
      Link tg = new Link(x);
      tg.Next = First;
      First = tg;
}
 
public static void main(String[] args)
{
      DanhSach d = new DanhSach();
      for (int i = 1; i <= 20; i++) d.Them(i);
      d.HienThi();
}
}

Bản quyền thuộc về Nguyễn Tô Sơn, Đại học Sư phạm Hà Nội. Điện thoại: 091.333.2869