1. Đánh máy

Đánh máy trong Java

Các biến kiểu nguyên thủy (ngoại trừ kiểu boolean) được sử dụng để lưu trữ các loại số khác nhau. Mặc dù các loại biến không bao giờ thay đổi, nhưng có một nơi mà bạn có thể chuyển đổi từ loại này sang loại khác. Và nơi đó là sự phân công .

Các biến thuộc các loại khác nhau có thể được gán cho nhau. Khi bạn thực hiện điều này, giá trị của một biến thuộc loại này sẽ được chuyển đổi thành giá trị của loại khác và được gán cho biến thứ hai. Về vấn đề này, chúng ta có thể xác định hai loại chuyển đổi loại: mở rộng và thu hẹp.

Mở rộng giống như chuyển một giá trị từ giỏ nhỏ sang giỏ lớn: thao tác này diễn ra liền mạch và không gây khó khăn. Việc thu hẹp xảy ra khi bạn chuyển một giá trị từ giỏ lớn sang giỏ nhỏ: có thể không đủ chỗ và bạn sẽ phải vứt bỏ thứ gì đó.

Dưới đây là các loại, được sắp xếp theo kích thước giỏ:

Đánh máy trong Java 2


2. Mở rộng chuyển đổi loại hình

Thường thì cần phải gán một biến kiểu số này cho một biến kiểu số khác. Làm thế nào để bạn làm điều đó?

Java có 4 kiểu số nguyên:

Kiểu Kích cỡ
byte 1 byte
short 2 bytes
int 4 bytes
long 8 bytes

Biến được lưu trữ trong các giỏ nhỏ hơn luôn có thể được gán cho các biến được lưu trữ trong các giỏ lớn hơn.

intvà các biến có thể dễ dàng được gán cho shortcác biến. và các biến có thể được gán cho các biến. Và các biến có thể được gán cho các biến.bytelongshortbyteintbyteshort

Ví dụ:

Mã số Sự miêu tả
byte a = 5;
short b = a;
int c = a + b;
long d = c * c;
Mã này sẽ biên dịch tốt.

Việc chuyển đổi như vậy, từ loại nhỏ hơn sang loại lớn hơn, được gọi là chuyển đổi loại mở rộng .

Còn số thực thì sao?

Với họ, mọi thứ đều giống nhau — kích thước quan trọng:

Kiểu Kích cỡ
float 4 bytes
double 8 bytes

floatcác biến có thể được gán cho doublecác biến mà không gặp vấn đề gì. Nhưng mọi thứ thú vị hơn với các kiểu số nguyên.

Bạn có thể gán bất kỳ biến số nguyên nào cho một floatbiến. Ngay cả longloại dài 8 byte. Và bạn có thể gán bất cứ thứ gì bạn muốn — bất kỳ biến nguyên hoặc floatbiến nào — cho một doublebiến:

Mã số Ghi chú
long a = 1234567890;
float b = a;
double c = a;

b == 1.23456794E9
c == 1.23456789E9

Lưu ý rằng việc chuyển đổi sang kiểu thực có thể dẫn đến mất độ chính xác do thiếu các chữ số có nghĩa.

Khi chuyển đổi từ số nguyên sang số dấu phẩy động, phần thứ tự thấp hơn của số có thể bị loại bỏ. Nhưng vì các số phân số được hiểu là lưu trữ các giá trị gần đúng, nên các phép gán như vậy được cho phép.


3. Thu hẹp loại chuyển đổi

Còn những khả năng khác thì sao? Nếu bạn cần gán longgiá trị cho một intbiến thì sao?

Hãy tưởng tượng một biến như một cái giỏ. Chúng tôi có các giỏ có kích thước khác nhau: 1, 2, 4 và 8 byte. Không có vấn đề gì khi chuyển táo từ giỏ nhỏ hơn sang giỏ lớn hơn. Nhưng khi chuyển từ giỏ lớn hơn sang giỏ nhỏ hơn, một số quả táo có thể bị mất.

Sự chuyển đổi này — từ loại lớn hơn sang loại nhỏ hơn — được gọi là chuyển đổi loại thu hẹp . Khi thực hiện thao tác gán như thế này, một phần của số có thể không khớp với biến mới và do đó có thể bị loại bỏ.

Khi thu hẹp một loại, chúng ta phải thông báo rõ ràng với trình biên dịch rằng chúng ta không mắc lỗi, rằng chúng ta đang cố tình loại bỏ một phần của số. Toán tử typecast được sử dụng cho việc này. Nó là một tên loại trong ngoặc đơn .

Trong những tình huống như vậy, trình biên dịch Java yêu cầu lập trình viên chỉ định toán tử định kiểu. Nói chung, nó trông như thế này:

(type) expression

Ví dụ:

Mã số Sự miêu tả
long a = 1;
int b = (int) a;
short c = (short) b;
byte d = (byte) c;
Mỗi lần toán tử typecast phải được chỉ định rõ ràng

Ở đây abằng với 1, và có lẽ toán tử typecast có vẻ như quá mức cần thiết. Nhưng nếu alớn hơn thì sao?

Mã số Sự miêu tả
long a = 1000000;
int b = (int) a;
short c = (short) b;
byte d = (byte) c;
a == 1000000
b == 1000000
c == 16960
d == 64

Một triệu hoàn toàn phù hợp với a longvà thành một int. Nhưng khi gán một triệu cho một shortbiến, hai byte đầu tiên sẽ bị loại bỏ và chỉ hai byte cuối cùng được giữ lại. Và khi gán cho a byte, thứ duy nhất còn lại là byte cuối cùng.

Cách các số được sắp xếp trong bộ nhớ:

Kiểu ký hiệu nhị phân Ký hiệu thập phân
int 0b 00000000 00001111 01000010 01000000 1000000
short 0b 01000010 01000000 16.960
byte 0b 01000000 64

charkiểu

A char, giống như a short, chiếm hai byte, nhưng để chuyển đổi một byte này sang byte khác, bạn luôn cần sử dụng toán tử định kiểu. Vấn đề ở đây là shortloại được ký và có thể chứa các giá trị từ -32,768đến +32,767, nhưng charloại không được ký và có thể chứa các giá trị từ 0đến 65,535.

Số âm không thể được lưu trữ trong tệp char, nhưng chúng có thể được lưu trữ trong tệp short. Và a shortkhông thể lưu trữ các số lớn hơn 32,767, nhưng các số đó có thể được lưu trữ trong a char.


4. Loại biểu thức

Điều gì sẽ xảy ra nếu các biến thuộc các loại khác nhau được sử dụng trong cùng một biểu thức? Theo logic, chúng tôi hiểu rằng trước tiên chúng cần được chuyển đổi thành một loại chung. Nhưng cái nào?

Đối với cái lớn hơn, tất nhiên.

Java luôn chuyển đổi sang loại lớn hơn. Nói một cách đại khái, một trong các loại đầu tiên được mở rộng và chỉ sau đó thao tác mới được thực hiện bằng cách sử dụng các giá trị cùng loại.

Nếu an intvà a longtham gia vào một biểu thức, giá trị của intsẽ được chuyển đổi thành a longvà chỉ khi đó thao tác mới tiếp tục:

Mã số Sự miêu tả
int a = 1;
long b = 2;
long c = a + b;
asẽ được mở rộng thành a longvà sau đó phép cộng sẽ xảy ra.

số dấu phẩy động

Nếu một số nguyên và một số dấu phẩy động ( floathoặc double) có liên quan trong một biểu thức, thì số nguyên đó sẽ được chuyển đổi thành một số dấu phẩy động ( floathoặc double), và chỉ khi đó thao tác mới được thực hiện.

Nếu thao tác liên quan đến a floatvà a double, thì a floatsẽ được chuyển đổi thành a double. Đó là thực sự mong đợi.

Sự ngạc nhiên

, và các loại luôn được chuyển đổi thành khi bytetương tác với nhau. Có một lý do chính đáng tại sao loại này được coi là loại số nguyên tiêu chuẩn.shortcharintint

Nếu bạn nhân a bytevới a short, bạn sẽ nhận được một int. Nếu bạn nhân a bytevới a byte, bạn sẽ nhận được một int. Ngay cả khi bạn thêm a bytevà a byte, bạn vẫn nhận được một int.

Cái này có một vài nguyên nhân. Ví dụ:

Mã số Sự miêu tả
byte a = 110;
byte b = 120;
byte c = a * b;  // Error
110 * 120is 13,200, lớn hơn một chút so với giá trị tối đa của byteloại:127
byte a = 110;
byte b = 120;
byte c = a + b; // Error
110 + 120is 230, cũng lớn hơn một chút so với giá trị tối đa của byteloại:127

Nói chung, khi nhân một số 8 bit (1 byte) với một số 8 bit (1 byte), ta được một số chiếm 16 bit (2 byte)

Do đó, tất cả các hoạt động với các loại số nguyên nhỏ hơn intluôn được chuyển đổi ngay lập tức thành ints. Và điều đó có nghĩa là nếu bạn muốn lưu trữ kết quả của phép tính trong một biến có loại nhỏ hơn int, thì bạn sẽ luôn cần chỉ định rõ ràng toán tử định kiểu.

Ví dụ:

Mã số Sự miêu tả
byte a = 110;
byte b = 120;
byte c = (byte) (a * b);
Biểu byte * bytethức sẽ là mộtint
byte a = 110;
byte b = 120;
byte c = (byte) (a + b);
Biểu byte + bytethức sẽ là mộtint
byte a = 1;
byte b = (byte) (a + 1);
Biểu byte + intthức sẽ là một int
Biểu thức theo nghĩa đen là một int.

5. Một sắc thái quan trọng

Toán tử typecast có mức độ ưu tiên khá cao.

Điều đó có nghĩa là nếu một biểu thức chứa, chẳng hạn như phép cộng và toán tử định kiểu, thì phép định kiểu sẽ được thực hiện trước phép cộng.

Ví dụ:

Mã số Sự miêu tả
byte a = 1;
byte b = 2;
byte c = (byte) a * b;
Toán tử typecast sẽ chỉ được áp dụng cho abiến đã là một biến byte. Mã này sẽ không biên dịch.
byte a = 1;
byte b = 2;
byte c = (byte) (a * b);
Đây là cách chính xác.

Nếu bạn muốn chuyển đổi toàn bộ biểu thức thành một kiểu cụ thể chứ không chỉ một thành phần của biểu thức, thì hãy đặt toàn bộ biểu thức trong dấu ngoặc đơn và đặt toán tử định kiểu ở phía trước.