Thổi hồn cho Console (C sharp)

Màn hình Console đen nhèm có thêm 1 vài chữ màu mè nữa cũng vẫn khá đơn điệu, Tuy nhiên dùng lệnh Console.beep() có thể làm cho nó thêm sinh động.
Dưới đây là 1 đoạn code để loa Console chơi bản Fur Elise, anh em nào thích âm nhạc copy vào chạy thử, nghe hay ghê:)

 

    public void FurElise()//Universe
    {
    Console.Beep(420, 200);
    Console.Beep(400, 200);
    Console.Beep(420, 200);
    Console.Beep(400, 200);
    Console.Beep(420, 200);
    Console.Beep(315, 200);
    Console.Beep(370, 200);
   Console.Beep(335, 200);
   Console.Beep(282, 600);
   Console.Beep(180, 200);
   Console.Beep(215, 200);
   Console.Beep(282, 200);
   Console.Beep(315, 600);
   Console.Beep(213, 200);
   Console.Beep(262, 200);
   Console.Beep(315, 200);
   Console.Beep(335, 600);
   Console.Beep(213, 200);
   Console.Beep(420, 200);
   Console.Beep(400, 200);
   Console.Beep(420, 200);
   Console.Beep(400, 200);
   Console.Beep(420, 200);
   Console.Beep(315, 200);
   Console.Beep(370, 200);
   Console.Beep(335, 200);
   Console.Beep(282, 600);
   Console.Beep(180, 200);
   Console.Beep(215, 200);
   Console.Beep(282, 200);
   Console.Beep(315, 600);
   Console.Beep(213, 200);
   Console.Beep(330, 200);
   Console.Beep(315, 200);
  Console.Beep(282, 600);
 } 

Java cơ bản – Khái niệm Class – phần 2

Bài học mô tả một cách chi tiết về một lớp, Ngăn xếp(Stack) và mô tả về tất cả các thành phần cung cấp cho vòng đời của một đối tượng được tạo ra bởi lớp đó. Bài học cũng đề cập đến các hàm dựng (constructor), các biến thành phần (member variable) và các phương thức (method).

5. Cài đặt các phương thức
Như bạn đã biết, các đối tượng hoạt động thông qua các phương thức của nó. Các đối tượng khác có thể yêu cầu một đối tượng làm việc gì đó thông qua việc gọi (invoke)các phương thức của nó. Mục này chỉ cho bạn mọi thứ để viết nên các phương thức cho các lớp Java của bạn.
Trong Java, bạn định nghĩa một phương thức của một lớp trong phần thân của lớp đó để thực hiện một hành động nào đó. Thông thường, bạn khai báo các phương thức của một lớp sau các biến của lớp trong thân lớp, mặc dù điều này là không bắt buộc.
Hình vẽ dưới đây trình bày mã của method (phương thức) đẩy (push) của Stack. Method này đẩy một đối tượng, cái được đưa vào thông qua các tham số, vào đầu của ngăn xếp, và trả về đối tượng ấy.

Giống như một lớp, một method có hai phần chính là: phần khai báo method và thân method. Phần khai báo định nghĩa tất cả các thuộc tính của method như cấp truy cập, kiểu trả về, tên, các đối số, và được minh họa như dưới đây:

Phần thân method là nơi xảy ra tất cả các hành động của nó. Nó chứa các dòng lệnh Java triển khai method đó.

5.1: Phần khai báo phương thức:

a. Chi tiết về việc khai báo một Method:

Khai báo một method cung cấp rất nhiều thông tin về method cho trình biên dịch, cho hệ thống runtime, cho các lớp và các object khác. Không chỉ gồm tên của method mà còn bao gồm các thông tin khác như kiểu trả về, số hoặc kiểu của các tham số cần thiết và những loại lớp nào hay object nào có thể gọi được method đó.
Trong khi điều này nghe có vẻ như viết một cuốn tiểu thuyết hơn là một khai báo method đơn thuần, nhưng tất cả các thuộc tính của method có thể khai báo rõ ràng. Thành phần cần thiết của một khai báo method chỉ là tên, kiểu trả về và một cặp ngoặc đơn (). Hình ảnh sau đây chỉ ra các thành phần của một khai báo method:

Trong đó, các thành phần được định nghĩa như sau:
accessLevel
Như là với các biến member, bạn điều khiển sao cho các lớp khác phải truy nhập vào một method bằng cách sử dụng một trong bốn cấp truy nhập : công khai(public) , được bảo vệ (protected), nội gói (package) và riêng (private). Tham khảo phần “6-Điểu khiển việc truy cập vào các bộ phận của một lớp”
static
Giống như các biến member, static khai báo một method như là một method kiểu lớp chứ không phải một method kiểu instance. Bài Tìm hiểu các member kiểu Instance và member kiểu lớp có nói về việc khai báo các method kiểu instance.
abstract
Một method trừu tượng không cài đặt gì cả và phải là một member của một lớp trừu tượng. Tham khảo thêm bài Viết các lớp trừu tượng và các method trừu tượng để biết thêm chi tiết về việc tại sao bạn lại viết các method trừu tượng và cách chúng tác động lên các lớp con.
final
Một method cuối (final method ) không được phép overridden bởi một lớp con.
native
Nếu bạn có một thư viện hàm quan trọng được viết bằng các ngôn ngữ khác như C chẳng hạn, có thể bạn muốn giữ lại chúng và dùng chúng trong Java. Các method được cài đặt trong ngôn ngữ khác Java được gọi là các method native( có tính địa phương) và được khai báo bằng cách dùng từ khóa Native.
synchronized (đồng bộ hóa)
Các thread chạy đồng thời thường gọi các method thao tác trên cùng một dữ liệu. Các method nàu có thể khai báo được đồng bộ hóa (synchronized) để chắc chắn rằng các thread này truy nhập thông tin theo cách thức an toàn cho các thread.
returnType (kiểu trả về)
Java yêu cầu một method khai báo kiểu dữ liệu của giá trị mà nó trả về. Nếu method của bạn không trả về giá trị nào, hãy dùng từ khóa void cho kiểu trả về. Bài Trả về một giá trị từ một method sẽ bàn thêm về vấn đề này.
methodName (tên method )
Tên của method tuân thủ chuẩn của một định danh(identifier) trong Java. Bạn cần xem xét rất nhiều vấn đề liên quan đến các tên method trong Java. Chúng được viết kĩ trong bài Tên method .
( paramlist ) (danh sách tham số)
Bạn đưa các thông tin vào một method thông qua các đối số của nó. Xem bài tiếp theo, Đưa thông tin vào một method , để biết thêm chi tiết.
[throws exceptions] (tung ra các exceptions-lỗi)
Nếu method của bạn có tung ra một exception đã được kiểm tra thì phần khai báo của nó phải có kiểu của các exception này.

Trả về một giá trị từ một method

Bạn khai báo một kiểu trả về của method trong phần khai báo của nó. Trong phần thân của method , bạn dùng toán tử return để trả về giá trị đó. Bất kì một method nào mag không khai báo là void thì đều phải chứa lệnh return. Lớp Stack khai báo một method là isEmpty với kiểu trả về là boolean:
public boolean isEmpty() {
if (items.size() == 0)
return true;
else
return false;
}
Kiểu dữ liệu của giá trị trả về phải trùng với kiểu trả về của method; bạn không thể trả về một kiểu Object từ một method được khai báo với kiểu trả về là một số nguyên. Method isEmpty trả về giá trị boolean hoặc là true hoặc là false, phụ thuộc vào đầu vào của mẫu như thế nào. Một lỗi biên dịch sẽ xảy ra nếu bạn cố viết một method mà trong đó kiểu trả về không trùng với giá trị trả về.
Method isEmpty trả về một kiểu dữ liệu nguyên thủy. Các method cũng có thể trả về một kiểu dữ liệu tham chiếu. Ví dụ, Stack khai báo một method pop với kiểu trả về là kiểu tham chiếu Object:
public synchronized Object pop() {
int len = items.size();
Object obj = null;
if (len == 0)
throw new EmptyStackException();
obj = items.elementAt(len – 1);
items.removeElementAt(len – 1);
return obj;
}
Khi một method trả về một object như là pop trả về, lớp của object trả về phải là một lớp con hoặc là lớp của kiểu trả về. Điều này có thể là nguyên nhân gây lộn xộn, vì thế chúng ta hãy quan sát điều này kĩ càng hơn. Giả sử rằng bạn có một cây phân lớp mà ở đó ImaginaryNumber là lớp con của java.lang.Number, cái mà, mặt khác, là lớp con của Object, như được minh họa dưới đây:

Bây giờ, giả sử là bạn có một method được khai báo là trả về một đối tượng Number:
public Number returnANumber() {
. . .
}
Method return có thể trả về một ImaginaryNumber nhưng không phải là một Object.ImaginaryNumber “là một” Number bởi vì nó là một lớp con của Number. Tuy nhiên, một Object không nhất thiết phải là một Number– nó có thể là một String hoặc một kiểu nào đó. Bạn cũng có thể dùng các tên giao diện (interface) như là kiểu trả về. Trong trường hợp này, object trả về phải triển khai interface đó.

Tên của method
Java cho phép tên method được overload( chồng, đè lên nhau), do đó nhiều method có thể trùng tên. Ví dụ bạn đang muốn viết một lớp mà có thể trả về rất nhiều liểu dữ liệu (chuỗi, số…). Và khi đó, bạn cần phải viết một method mà có thể biết cách trả về từng kiểu dữ liệu như thế. Trong một số ngôn ngữ khác, bạn phải nghĩ tới một tên mới cho mỗi một method, ví dụ như drawString, drawInteger, drawFloat… . Trong Java, bạn có thể dùng chung một tên cho tất cả các method như thế nhưng chúng pass(đưa vào) một kiểu dữ liệu khấccủ tham số cho mỗi method . Vì thế, trong lớp dữ liệu trả về, bạn có thể khai báo ba method có tên là draw, mỗi một method có một kiểu tham số khác nhau:

class DataRenderer {
void draw(String s) {
. . .
}
void draw(int i) {
. . .
}
void draw(float f) {
. . .
}
}

Các method đã được overload được phân biệt bởi số lượng và kiểu trả về của các đối số được đưa vào trong method đó. Trong phần ví dụ trên đây, draw(String s) và draw(int i) là khác nhau và duy nhất bởi chúng cần các đối số khác nhau. Bạn không thể khai báo nhiều hơn một method có cùng tên lại có cùng cả kiểu trả về lẫn tham số đưa vào bởi khi đó compiler không thể phân biệt hai method này. Vì thế hai method draw(String s) và draw(String t) là như nhau và có thể gây lỗi khi dịch nếu cùng được khai báo.
Một lớp có thể override(chèn lên) một method trong siêu lớp của nó. Các Overriding method phải có tên, kiếu trả về và danh sách tham số giống như method mà nó override.
b. Định nghĩa: Phần khai báo method tối thiểu phải có một tên và một kiểu trả về liên quan tới kiểu dữ liệu được trả về bởi method đó:

returnType methodName() {
. . .
}

Phần khai báo method trên đây là rất cơ bản. Các method có nhiều thuộc tính khác như là các đối số, các điều khiển truy nhập,và các thuộc tính khác. Bài bày sẽ bao gồm các chủ đề này như là phần mở rộng dựa trên các đặc điểm của phần khai báo method như trên.

5.2 Truyền thông tin vào trong một method
a. Chi tiết:
Khi bạn viết một method của riêng mình, bạn khai báo số lượng và kiểu dữ liệu của các đối số cần thiết cho method đó. Bạn khai báo kiểu dữ liệu và tên cho từng đối số trong phần kí hiệu method. Ví dụ, sau đây là một method có chức năng tính toán khoản lệ phí hằng tháng cho một chủ nợ dựa trên lượng nợ, tỉ suất quan trọng, kì hạn của khoản nợ(số lượng các chu kì), và trị giá tương lai của khoản nợ (có lẽ trị giá tương lai của khoản nợ bằng không bởi vì khi hết hạn thì cũng là lúc bạn trả hết nợ):

double computePayment(double loanAmt, double rate, double futureValue, int numPeriods) {
double I, partial1, denominator, answer;
I = rate / 100.0;
partial1 = Math.pow((1 + I), (0.0 - numPeriods));
denominator = (1 - partial1) / I;
answer = ((-1 * loanAmt) / denominator)
- ((futureValue * partial1) / denominator);
return answer;
}

Method này có bốn đối số: lượng nợ, tỉ suất quan trọng, trị giá tương lai và số chu kì. Ba đối số đầu tiên là các biến kiểu số thực với độ chính xác đúp (kiểu double), đối số còn lại có kiểu số nguyên.
Giống như trình bày trong method này, các đối số được phân cách bởi các dấu phẩy theo từng cặp kiểu/tên:
type name
Như bạn có thể thấy trong thân của method computePayment, bạn dễ dàng dùng các tên đối số để tham chiếu tới các giá trị của đối số.

Kiểu của đối số
Trong Java, bạn có thể pass một đối số thuộc kiểu bất kì vào một method. Điều này bao gồm các kiểu dữ liệu nguyên thủy như double, float hay kiểu các số nguyên như là bạn nhìn thấy trên method computePayment,và tham chiếu tới các kiểu dữ liệu khác như là các object hay các mảng. Sau đây là một ví dụ về một method chấp nhận một mảng như là tham số của mình. Trong ví dụ này, method tạo ra một object Polygon mới và khởi tạo nó từ một danh sách các điểm Points( Point là một lớp biểu diễn một tọa độ x, y):
static Polygon polygonFrom(Point[] listOfPoints) {
. . .
}
Không giống như một số ngôn ngữ khác, bạn không thể pass method vào một method. Nhưng bạn có thể pass một object vào một method và sau đó gọi các method của object này.
Tên đối số
Khi bạn khai báo một đối số trong một method trong Java, bạn cấp cho nó một cái tên. Tên này được dùng trong thân method để tham chiếu tới các đối tượng.
Một đối số của method có thể có tên trùng với tên của biến member thuộc lớp . Nếu trường hợp này xảy ra, thì biến đó sẽ làm ẩn đi biến member . Các đối số làm ẩn các biến member thường được dùng trong các constructor để khởi tạo một lớp. Ví dụ, xem xét lớp Circle và constructor của nó:

class Circle {
int x, y, radius;
public Circle(int x, int y, int radius) {
. . .
}
}

Lớp Circle có ba biến member : x, y và radius. Hơn nữa, constructor của lớp Cỉ nhận ba đối số trùng tên với tên của các biến member, ở đó mỗi đối số cung cấp một giá trị khởi tạo.
Các tên tham số ẩn đi các biến member . Vì thế việc dùng x, y hay radius trong thân của constructor tham chiếu tới các tham số, không tham chiếu tới các biến member. Để truy nhập các biến member, bạn phải tham chiếu chúng thông qua this– object hiện tại.

class Circle {
int x, y, radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
}

Các tên các đối số của method không được trùng tên với các tên đối số khác trong cùng method, tên của bất kì biến cục bộ nào của method hay tên của bất cứ tham số nào trong mệnh đề catch trong cùng một method.

Truyền theo giá trị
Trong các method của Java, các đối số được truyền theo giá trị. Khi được gọi, method nhận các giá trị của biến được truyền vào. Khi đối số đó thuộc kiểu nguyên thủy, truyền theo giá trị có nghĩa là method đó không thể thay đổi giá trị của nó. Khi đối số thuộc một kiểu tham chiếu, truyền theo giá trị có nghĩa là method đó không thể thay đổi object tham chiếu, nhưng có thể kích hoạt các method của object đó và thay đổi các biến có thể truy nhập được trong object đó.
Đây thường xuyên là lí do gây nhầm lẫn–một lập trình viên viết một method với cố gắng sửa giá trị của một đối số của nó và method không làm việc như mong muốn. Chúng ta hãy xem xét các method này và thử tìm ra cách thay đổi chúng như thế nào để giúp lập trình viên đó đạt được nguyện vọng.
Xem các dòng lệnh Java sau đây, với mục đích là lấy ra màu hiện tại của object Pen trong một ứng dụng đồ họa:

. . .
int r = -1, g = -1, b = -1;
pen.getRGBColor(r, g, b);
System.out.println("red = " + r + ", green = " + g + ", blue = " + b);
. . .

Tại thời gian khi mà method getRGBColor được gọi, biến r,g và b đều có giá trị là -1. Người gọi nó hy vọng method getRGBColor truyền trả lại các giá trị đỏ, xanh lá cây và lam của màu hiện tại bằng các biến r,g và b.
Tuy nhiên, Java runtime sẽ truyền các giá trị(-1) của các biến vào trong method getRGBColor; chứ không phải là một tham chiếu tới các biến r,g, và b. Vì vậy bạn có hình dung cách gọi method kiểu đó giống như là : getRGBColor(-1, -1, -1).
Khi điều khiển các giá trị truyền vào trong method getRGBColor, các đối số đi vào trong vùng được cấp phát và được khởi tạo cho các giá trị được truyền vào trong method:

class Pen {
int redValue, greenValue, blueValue;
void getRGBColor(int red, int green, int blue) {
// red, green, and blue have been created
// and their values are -1
. . .
}
}

Vì vậy, getRGBColor lấy sự truy cập tới các giá trị của r, g, b trong lời gọi thông qua các đối số red, green, và blue. Method lấy bản copy của nó về các giá trị để dùng trong phạm vi của method. Bất cứ sự tác động nào làm thay đổi các bản copy cục bộ này đều không ảnh hưởng tới các biến gốc trong lời gọi method.
Bây giờ chúng ta xem xét một đoạn cài đặt của method getRGBColor trong lớp Pen:

class Pen {
int redValue, greenValue, blueValue;
. . .
// this method does not work as intended
void getRGBColor(int red, int green, int blue) {
red = redValue;
green = greenValue;
blue = blueValue;
}
}

Method này sẽ không làm việc đúng như mong đợi. Khi bộ điều khiển lấy lệnh println trong đoạn code , các đối số của getRGBColor red, green blue không còn tồn tại nữa. Do đó các phép gán thực hiện trên chúng trong method không có tác dụng; r, g, b vẫn nhận giá trị -1.

. . .
int r = -1, g = -1, b = -1;
pen.getRGBColor(r, g, b);
System.out.println("red = " + r +
", green = " + g +
", blue = " + b);
. . .

Truyền biến theo giá trị có tác dụng tạo sự an toàn cho lập trình viên. Các method không thể sửa đổi lung tung trong phạm vi của method đó. Tuy nhiên, bạn hay muốn một method có thể sửa đổi một hay nhiều các đối số của nó. Method getRGBColor là một trường hợp như thế. Lời gọi muốn method trả về ba giá trị thông qua các đối số của nó. Tuy nhiên, method đó không thể sửa đổi các đối số và hơn nữa, một method chỉ có thể trả về một giá trị thông qua giá trị trả về. Vì vậy, làm thế nào để một method có thể trả về nhiều hơn một giá trị hay có tác dụng (sửa đổi các biến) bên ngoài phạm vi của method đó?
Để có một method có thể sửa đổi các đối số của nó, nó phải là một kiểu tham chiếu, kiểu như một object hoặc một mảng. Các object và các mảng cũng được truyền theo giá trị, nhưng giá trị của một object lại là một kiểu tham chiếu. Vì vậy, kết quả là các đối số thuộc kiểu tham chiểu được truyền theo giá trị theo cách tham chiếu. Một sự tham chiếu tới một object có nghĩa là trỏ địa chỉ của object đó trong bộ nhớ. Bây giờ, các đối số trong method được tham chiếu tới vùng nhớ giống như lời gọi.
Hãy viết lại method getRGBColor để xác định chắc chắn những gì mà bạn muốn. Đầu tiên, bạn phải giới thiệu một kiểu object, RGBColor, mà có thể chứa cả ba giá trị đỏ, xanh là lam:

class RGBColor {
public int red, green, blue;
}

Now, we can rewrite getRGBColor so that it accepts an RGBColor object as an argument. The getRGBColor method returns the current color of the pen by setting the red, green and blue member variables of its RGBColor argument:
Bây giờ, chúng ta có thể viết lại method getRGBColor để nó nhận một object như là đối số của nó. Method getRGBColor trả về màu hiện tại của cái bút băng việc đặt các biến member red, green và blue cho đối số RGBColor của nó:

class Pen {
int redValue, greenValue, blueValue;
void getRGBColor(RGBColor aColor) {
aColor.red = redValue;
aColor.green = greenValue;
aColor.blue = blueValue;
}
}

Cuối cùng. hãy viết lại lời gọi method:

. . .
RGBColor penColor = new RGBColor();
pen.getRGBColor(penColor);
System.out.println("red = " + penColor.red + ", green = " + penColor.green + ", blue = " + penColor.blue);
. . .

Sự thay đổi đối với object RGBColor trong method getRGBColor ảnh hưởng tới object được tạo ra trong lời gọi bởi vì tên penColor(trong lời gọi) và aColor(trong method getRGBColor ) tham chiếu tới cùng một object.
Có thể tất thành phần thường được dùng nhất của một khai báo method là các tham số của method. Giống như các hàm trong các ngôn ngữ lập trình khác, các method trong Java chấp nhận đầu vào(input) từ người gọi nó thông qua các tham số của nó. Các tham số cung cấp thông tin cho method từ phía ngoài của nó.
5.3 Thân phương thức (method)
Thân method là nơi tất cả các hành động của một method, thân method chứa tất cả các lệnh đúng ngữ pháp Java.
Trong đoạn mã ví dụ dưới đây, phần thân của các method isEmpty và pop được in đậm:

class Stack {
static final int STACK_EMPTY = -1;
Object[] stackelements;
int topelement = STACK_EMPTY;
. . .
boolean isEmpty() {
if (topelement == STACK_EMPTY)
return true;
else
return false;
}
Object pop() {
if (topelement == STACK_EMPTY)
return null;
else {
return stackelements[topelement--];
}
}
}

Bên cạnh các thành phần khác của ngôn ngữ Java, bạn có thể dùng this trong phần thân để chỉ tới một member trong trạng thái là object hiện thời. Object hiện tại là object có method đang được gọi. Bạn cũng có thể dùng super để chỉ tới các member trong siêu lớp có object hiện thời đã được ẩn hoặc override. Đồng thời, thân lớp có thể chứa các khai báo cho các biến cục bộ của method đó.
this
Thông thường, trong một method của một object, bạn có thể chỉ cần tham chiếu trực tiếp tới các biến member của object đó. Tuy nhiên, đôi khi bạn lại cần hiển rõ nghĩa biến member nếu một trong số các đối số của method có trùng tên.
Ví dụ, constructor sau đây của lớp HSBColor khởi tạo một vài biến member của object theo các đối số được truyền vào trong constructor. Mỗi da này có tên giống như là một biến member của object có giá trị bạn đầu mà đối số đó chứa.

class HSBColor {
int hue, saturation, brightness;
HSBColor (int hue, int saturation, int brightness) {
this.hue = hue;
this.saturation = saturation;
this.brightness = brightness;
}
...
}

Bạn phải dùng từ khóa this trong constructor vì bạn phải hiển rõ nghĩa của đối số hue so với biến member hue (và các đối số khác cũng tương tự). Viết hue = hue; không có ý nghĩa gì cả. Các tên đối số ưu tiên các biến member ẩn trùng tên. Vì thế, để tham chiếu tới mọt biến member, bạn phải làm thông qua object hiện thời–dùng từ khóa this để tham chiếu– một cách rõ ràng.
Một số lập trình viên thích lúc nào cũng dùng từ khóa this khi tham chiếu tới một biến member của object có method mà tham chiếu đó xuất hiện. Làm như thế tạo cho mã rõ ràng và giảm được các lỗi khi dùng chung tên.
Bạn cũng có thể dùng từ khóa this để gọi một method của object hiện thời. Ta nhắc lại là điều này chỉ cần thiết nếu có một điều gì đó không rõ ràng trong tên của method và thươnf được dùng để làm cho đoạn code rõ ràng hơn.
super
Nếu method của bạn ẩn đi một trong các biến member của siêu lớp của nó, method của bạn có thể tham chiếu tới biến ẩn thông qua việc dung từ khóa super. Tương tự, nếu method của bạn override một trong các method của siêu lớp của nó, thì method đó có thể gọi method đã được override thông qua việc dùng từ khóa super.
Nghiên cứu lớp sau:

class ASillyClass {
boolean aVariable;
void aMethod() {
aVariable = true;
}
}
Và lớp con của nó, ẩn đi aVariable và override aMethod:
class ASillierClass extends ASillyClass {
boolean aVariable;
void aMethod() {
aVariable = false;
super.aMethod();
System.out.println(aVariable);
System.out.println(super.aVariable);
}
}

Trước tiên, aMethod đặt aVariable(đ ược khai báo trong ASillierClass , lớp ẩn đi cái được khai báo trong ASillyClass) có giá trị là false. Tiếp theo, aMethod kích hoạt method được override của nó với câu lệnh:
super.aMethod();
Dòng lệnh này đặt giá trị ẩn của aVariable (được khai báo trong ASillyClass) bằng true. Sau đó,aMethod hiển thị cả hai giá trị này của aVariable với các giá trị khác nhau:
false
true
Biến cục bộ
Trong thâm method bạn có thể khai báo thêm các biến dùng trong method đó. Các biến này là các biến cục bộ và chỉ tồn tại trong thời gian thực thi method đó. Method này khai báo một biến cục bộ i dùng để lặp lại trong các phần tử của đối số kiểu mảng của nó:

Object findObjectInArray(Object o, Object[] arrayOfObjects) {
int i; // local variable
for (i = 0; i < arrayOfObjects.length; i++) {
if (arrayOfObjects[i] == o)
return o;
}
return null;
}

Sau khi method trả về, i không còn tồn tại nữa.

Java cơ bản – Khái niệm Class – phần 1

Bài học mô tả một cách chi tiết về một lớp, Ngăn xếp(Stack) và mô tả về tất cả các thành phần cung cấp cho vòng đời của một đối tượng được tạo ra bởi lớp đó. Bài học cũng đề cập đến các hàm dựng (constructor), các biến thành phần (member variable) và các phương thức (method).

Tạo ra các lớp
Bây giờ chúng ta sẽ học cách làm thế nào để tạo ra và sử dụng các đối tượng, và các đối tượng được dọn dẹp như thế nào. Cũng là lúc để chỉ cho bạn làm thế nào để viết ra các lớp từ những đối tượng được tạo ra. Bài học này chỉ cho bạn các thành phần chính của một lớp thông qua một ví dụ nhỏ cài đặt một ngăn xếp LIFO( Last-In-First-Out: vào sau ra trước).
Sơ đồ sau đây liệt kê nội dung lớp đó và cấu trúc của mã chương trình:

Việc cài đặt một ngăn xếp như trên sử dụng một lớp khác, một Vector (Véc – tơ), để lưu giữ các phần tử của ngăn xếp. Vector là một mảng (có thể lớn thêm về kích thước) của các đối tượng và có chức năng cấp phát bộ nhớ cho một đối tượng mới với dung lượng cần thiết. Lớp Stack sử dụng đoạn mã này bằng việc dùng một Vector để lưu trữ các phần tử của nó. Tuy nhiên, nó cũng bắt LIFO bị giới hạn vào Vector.. tức là, bạn chỉ được thêm các phần tử vào và xóa các phần tử tại đầu ngăn xếp.
1. Khai báo lớp
Sơ đồ trên dây cho thấy việc cài đặt một lớp gồm hai phần chính: khai báo lớp và thân lớp. Phần khai báo lớp khai báo tên của lớp cùng với các thuộc tính khác. Phần khai báo của lớp Stack khá đơn giản và cho biết lớp đó là công khai (public) và tên của lớp là Stack. Thông thường thì bạn chỉ cần khai báo dưới dạng tối thiểu như trên.
Tuy nhiên, việc khai báo lớp có thể cho biết nhiều hơn về lớp đó, ví dụ như tên của siêu lớp(superclass) và nếu có thể là một lớp con (subclass)
Phía trái của sơ đồ dưới đây biểu diễn các phần có thể có khi khai báo một lớp(theo đúng thứ tự trong sơ đồ). Phần bên phải là mô tả mục đích của việc khai báo các phần đó. Phần cần được khai báo là từ khóa class và tên của lớp như phần in đậm. Tất cả các phần khác có thể thêm vào và mỗi phần chỉ được viết trên một dòng( do “extends Super” là một phần riêng rẽ”) . Các phần in nghiêng biểu thị một định danh(identifier) nào đó, như tên của lớp hay giao diện (interface). Nếu bạn không khai báo rõ ràng các mục thêm vào, bộ dịc của Java sẽ hiểu ngầm định lớp của bạn là một lớp con của lớp Object có tính chất : không công khai (nonpublic), không trừu tượng (nonabstract), không phải là lớp cuối (nonfinal), không triển khai một giao diện nào cả.
Danh sách dưới đây cung cấp cho bạn một số chi tiết về các thành phần trong khai báo lớp. Đồng thời, nó cũng liên kết đến một số chương khác (có nói tới ý nghĩa của các phần này, ảnh hưởng của chúng tới các lớp và các chương trình Java của bạn ) trong giáo trình này.
public
Theo ngầm định, một lớp chỉ có thể được sử dụng bởi các lớp khác trong cùng một gói(package). Bổ từ public cho biết rằng lớp đó có thể đựơc sử dụng bởi bất cứ lớp nào không quan tâm tới gói của nó.
abstract
Bổ từ này chỉ ra rằng lớp không cho phép thể hiện(instantiate).
final
Bổ từ này cho biết lớp không thể là lớp con của bất cứ lớp nào khác.
class NameOfClass
Từ khóa class chỉ cho bộ dịch của Java biết rằng đây là khai báo lớp, tên của lớp là NameOfClass.
extends Super
Mệnh đề extends cho biết Super như là một siêu lớp (superclass) của lớp đó,liên quan tới việc thêm vào lớp đó sự phân cấp lớp(class hierarchy).
implements Interfaces
Để khai báo rằng lớp của bạn triển khai một hoặc nhiều giao diện nào đó, bạn có thể dùng từ khóa implements kèm theo dấu phẩy để liêt kê tên của các giao diện được cài đặt trong lớp đó.
2. Thân lớp
Phần thân lớp tiếp theo phần khai báo lớp và được bao bởi hai ngoặc nhọn ({}). Phần thân lớp chứa phần khai báo cho tất cả các biến chương trình (instance variable) và các biến lớp -class variable-(gọi chung là các biến bộ phận-member variable) của lớp đó. Thêm vào đó, thân lớp còn gồm các khai báo và cài đặt cho tất cả các phương thức(instance method) và phương thức-lớp(class method) – được gọi chung là các phương thức- của lớp đó.
Thân lớp chứa toàn bộ mã chương trình cung cấp cho vòng đời của đối tượng được tạo ra bởi lớp đó: constructor cho việc khởi tạo một đối tượng mới, khai báo cho các biến cung cấp trạng thái của lớp và các đối tượng của nó, các phương thức để triển khai hành động của lớp và các đối tượng của nó, và hãn hữu lắm, một phương thức cuối hóa (finalize method) để cung cấp các thao tác dọn dẹp một đối tượng sau khi nó đã xong việc.
Các biến và các phương thức được gọi chung là các bộ phận.
Chú ý: Các Constructors (tạm dịch là hàm dựng) không phải là phương thức cũng chẳng phải là bộ phận.
Lớp Stack định nghĩa một biến bộ phận trong phần thân của nó để chứa các phần tử của nó – véc-tơ items. Nó cũng định nghĩa một hàm dựng — một hàm dựng không có đối số– và ba phương thức: push, pop, và isEmpty.
3. Cung cấp các Constructors cho lớp của bạn
Một lớp có thể chứa một hoặc nhiều hàm dựng (constructor) cung cấp các thao tác khởi tạo một đối tượng được tạo ra từ lớp đó. Mục này chỉ cho bạn cách viết một hàm dựng.
Cung cấp các constructor cho lớp của bạn
Tất cả các lớp trong Java đều có những constructor (tạm dich là hàm dựng) có vai trò khởi tạo một đối tượng mới của lớp đó. Hàm dựng có cùng tên với lớp. Ví dụ, tên của hàm dựng của lớp Stack là Stack, tên constructor của lớp Rectangle là Rectangle, và tên constructor của lớp Thread là Thread. Lớp Stackcủa chúng ta chi định nghĩa một constructor đơn:

public Stack() {
items = new Vector(10);
}

Java cung cấp tên đè lên nhau cho các constructor , vì thế một lớp có thể có nhiều constructor, tất cả chúng đều cùng tên. Dưới đây là một constructor khác có thể được định nghĩa bởi Stack. Constructor đặc biệt này đặt cỡ khởi tạo cho ngăn xếp theo tham số của nó:

public Stack(int initialSize) {
items = new Vector(initialSize);
}

Cả hai constructor đều chia sẻ chung một tên, Stack, nhưng chúng có danh sách tham số khác nhau. Bộ dịch phân biệt các constructor này dựa trên số các tham số có trong danh sách và kiểu của chúng.
Thông thường, một constructor dùng các tham số của nó để khởi tạo một trạng thái của đối tượng mới. Khi tạo ra một đối tượng, chúng ta chọn constructor nào mà có tham số phù hợp nhất với những gì ta muốn khởi tạo cho đối tượng mới.
Dựa trên số lượng và kiểu của các tham số mà bạn đưa(pass) vào constructor, bô dịch có thể xác định constructor nào cần được sử dụng. Bộ dịch biết rằng khi bạn viết những dòng lệnh sau, nó nên dùng một constructor có một tham số kiểu số nguyên:
new Stack(10);
Tương tự, khi bạn viết những dòng lệnh sau đây, bộ dich sẽ chọn một constructor không có tham số hay constructor ngầm định:
new Stack();
Khi bạn viết một lớp theo cách riêng, bạn không phải cung cấp các constructor cho nó. constructor ngầm định được hệ thống tạo ra một cách tự động cho tất cả các lóp không có constructor. constructor ngầm định này không làm việc gì cả. Vì thế, nếu bạn muốn thực hiện một số khởi tạo nào đó, bạn sẽ phải viết các constructor cho lớp của mình.
constructor của lớp con sau đây của lớp Thread thực hiện một hình ảnh động, cài đặt một số giá trị ngầm định, như là tốc độ của ảnh và số ảnh, và sau đó là tải ảnh:

class AnimationThread extends Thread {
int framesPerSecond;
int numImages;
Image[] images;
AnimationThread(int fps, int num) {
super("AnimationThread");
this.framesPerSecond = fps;
this.numImages = num;
this.images = new Image[numImages];
for (int i = 0; i <= numImages; i++) {
. . .
// Load all the images.
. . .
}
}
. . .
}

Chú ý về sự giống nhau của một constructor so với một phương thức; ở đó, nó có chứa các khai báo biến cục bộ, các vòng lặp, và các câu lệnh khác. Tuy nhiên, có một dòng trong constructor AnimationThread mà bạn không thể thấy trong một phương thức bình thường:
super(“AnimationThread”);
Dòng này kích hoạt một constructor cung cấp bởi siêu lớp của AnimationThread là Thread. Constructor Thread đặc biệt này đặt tên cho Thread với một chuỗi kí tự(String). Thông thường, một constructor muốn tạo ra những khởi tạo có ích được viết trong siêu lớp của lớp đó. Thật vậy, một số lớp phải gọi các constructor của siêu lớp của nó để đối tượng có thể làm việc đúng đắn. Constructor của siêu lớp phải được viết trong dòng đầu tiên của constructor của lớp con: Một đối tượng nên thực hiện các khởi tạo ở mức cao hơn(higher-level) trước.
Bạn có thể xác định những gì mà các đối tượng khác có thể tạo ra các thể hiện(instance) của một lớp bằng một định hướng truy cập trong khai báo của constructor:

private
Không một lớp nào khác có thể thể hiện được lớp của bạn. Lớp của bạn có thể chứa một số phương thức công khai(đôi khi còn được gọi là các các phương thức factory).Các phương thức này có thể tạo nên một đối tượng và trả về đối tượng đó, nhưng không một lớp nào các có thể làm được.
protected
Chỉ các lớp con của lớp đó và các lớp trong cùng một gói mới được phép tạo các thể hiện của lớp đó.
public
Một lớp bất kì đều tạo được một thể hiện của lớp của bạn.
Không xác định
Chỉ các lớp cùng gói với lớp của bạn mới được phép tạo ra một thể hiện của nó.
Các constructor cung cấp cách thức để khởi tạo một lớp mới. Bài Khởi tạo một thể hiện và bộ phận của lớp miêu tả các cách khởi tạo một lớp và một đối tượng được tạo ra. Bài đó cũng bàn về việc khi nào và tại sao bạn có thể dùng các kĩ thuật đó.
4. Khai báo các biến bộ phận
Các biến bộ phận biểu thị cho các trạng thái của một lớp. Bạn có thể khai báo các biến bộ phận của một lớp trong thân của lớp đó. Thông thường, bạn khai báo các biến của một lớp trước khi khai báo các phương thức của nó, mặc dù điều này là không bắt buộc.

classDeclaration {
member variable declarations
method declarations
}

Chý ý: Để khai báo các biến là bộ phận của một lớp, phần khai báo phải được viết trong thân lớp. nhưng không được khai báo trong một thân của môt phương thức nào đó. Các biến được khai báo trong thân của một phương thức là những biến cục bộ, chỉ có giá trị trong phương thức đó mà thôi.
Khai báo các biến bộ phận Stack dùng dòng lệnh sau đây để định nghĩa một biến bộ phận của nó:
private Vector items;
Dòng lệnh trên khai báo một biến bộ phận chứ không phải một loại biến nào khác(như biến cục bộ chẳng hạn) bởi vì dòng khai báo xuất hiện trong thân của lớp và nằm ngoài tất cả các phương thức và hàm dựng. Biến bộ phận được khai báo với tên gọi items, và kiểu của nó là kiểu Vector. Đồng thời, từ khóa private xác định items như là một bộ phận riêng (private). Điều này có nghĩa là chỉ các dòng lệnh trong lớp Stack mới được phép truy cập nó.
accessLevel Chỉ ra cấp truy cập bộ phận này.
static Khai báo một lớp bộ phận.
final Chỉ ra nó là hằng.
transient Biến loại tạm thời
violate Biến loại xung đột
type name Kiểu và tên của biến.
Đây là một khai báo biến bộ phận tương đối đơn giản, các khai báo có thể phức tạp hơn. Bạn có thể không dùng chỉ kiểu, tên, cấp truy cập mà còn dùng các thuộc tính khác nữa ,như là khi nào thì biến là một lớp hoặc là một biến thể hiện hay khi nào thì nó là hằng. Mỗi thành phần của một khai báo biến bộ phận có thể đựơc định nghĩa sâu hơn như sau:
accessLevel
Cho phép bạn điều khiển những lớp nào khác có quyền truy cập vào biến bộ phận thông qua việc dùng một trong bốn cấp là: công khai(public), bảo hộ(protected), gói(package) và cá nhân(private). Bạn có thể điều khiển việc truy cập cho các phương thức trong cùng cách thức như thế. Bài Điều khiển sự truy cập vào các bộ phận của một lớp sẽ thảo luận vấn đề này chi tiết hơn.
static
Khai báo đây là một biến lớp chứ không phải là một biến thể hiện. Bạn cũng có thể dùng static để khai báo các phương thức . Bài Tìm hiểu các thể hiện bộ phận và lớp bộ phận sau đây trong bài này sẽ nói về việc khai báo các thể hiện và các biến lớp.
final
Chỉ ra rằng giá trị của bộ phận này không thể thay đổi được. Ví như khai báo biến sau đây định nghĩa một hằng có tên AVOGADRO, có giá trị là số Avogadro(6.022*1023) và không thể thay đổi đựơc:
final double AVOGADRO = 6.022e23;
Nếu bạn cố gắng thay đổi giá trị của biến cuối(final), một lỗi dịch sẽ xảy ra. Theo lệ thường, tên của một giá trị hằng thường được viết hoa.
transient
Chỉ thị transient không được xác định hoàn toàn xác định trong Đặc tả ngôn ngữ Java(The Java Language Specification) nhưng được dùng trong đối tượng serial hóa(được thảo luận trong Serial hóa đối tượng (in the Learning the Java Language trail))
volatile
Từ khóa violate được dùng để ngăn cản bộ dịch thực hiện tối ưu hóa trên một bộ phận. Đây là một tính năng nâng cao của Java, chỉ một số ít lập trình viên dùng nó, và không được thảo luận trong bài này.
type
Giống như các biến, một biến bộ phận phải có kiểu. Bạn có thể dùng các tên kiểu nguyên thủy như int, float hay boolean. Hoặc cũng có thể tham chiếu các kiểu nào đó như mảng, đối tượng hay một tên giao diện nào đó.
name
Tên của một biến bộ phận có thể là một định danh hợp lệ nào đó trong Java và, theo quy ước, bắt đầu bằng một chữ cái viết thường. Bạn không thể khai báo nhiều hơn một biến bộ phận có cùng tên trong cùng một lớp, nhưng một biến bộ phận và một phương thức có thể trùng tên. Ví dụ, đoạn mã dưới đây là hợp lệ:

public class Stack {
private Vector items;
// a method with same name as a member variable
public Vector items() {
. . .
}
}

Bây giờ phụ nữ có thể tè đứng ^^!

Với sản phầm này, các bạn gái sẽ thấy kín đáo, thuận tiện hơn, không phải ngồi ‘xổm’ hay cúi xuống.
Bạn cảm thấy ái ngại khi mỗi lần ngó qua các nhà vệ sinh ở những nơi công công, đặc biệt là đối với phụ nữ. P-Mate là một loại sản phẩm mới giúp phụ nữ chúng ta có thể ‘tiểu tiện’ đứng một cách dễ dàng giống như những người đàn ông. Với P-Mate, các bạn gái sẽ cảm thấy kín đáo, thoải mái và thuận tiện hơn, không cần phải ngồi ‘xổm’ hay cúi xuống, đặc biệt là đối với phụ nữ đang mang thai.



P-Mate rất nhỏ gọn, có thể mang theo bất cứ nơi đâu mà bạn muốn. Nó rất thích hợp với các chuyến đi xa như du lịch, cắm trại hoặc bất kỳ một hoạt động ngoài trời nào, nơi mà bạn không thể tìm thấy một chiếc nhà vệ sinh. Đặc biệt, P-Mate sẽ giúp bạn không còn cảm giác khó chịu khi phải ngồi trong căn phòng vệ sinh trật trội, không sạch sẽ như trên xe lửa, ôtô hay máy bay. Ngoài ra, sản phẩm này còn rất hữu ích đối với các bênh viện, nhiều bệnh nhân cảm thấy khó khăn trong việc lấy mẫu nước tiểu để xét nghiệm, nhưng với P-Mate bạn có thể dễ dàng thực hiện được điều đó.


Sản phẩm này rất thích hợp đối với Việt Nam, khi mà tình trạng nhà vệ sinh công cộng khiến nhiều người khi mới nhìn vào kinh hoàng thốt lên rằng: "Cố ‘nhịn’ còn hơn là ‘đi’ ". Là nơi để giải quyết những nhu cầu thiết yếu của người dân, nhưng nhà vệ sinh công cộng cả ở nông thôn, lẫn các thành phố lớn của nước ta vẫn chưa đáp ứng nổi nhu cầu đó. Bẩn, mất vệ sinh, xuống cấp… còn là những từ đi liền với nhiều nhà vệ sinh hiện nay.