“造型”(Cast)的作用是“与一个模型匹配”。在适当的时候,java会将一种数据类型自动转换成另一种。例如,假设我们为浮点变量分配一个整数值,计算机会将int自动转换成float。通过造型,我们可明确设置这种类型的转换,或者在一般没有可能进行的时候强迫它进行。
为进行一次造型,要将括号中希望的数据类型(包括所有修改符)置于其他任何值的左侧。下面是一个例子:
void casts() {
int i = 200;
long l = (long)i;
long l2 = (long)200;
}
正如您看到的那样,既可对一个数值进行造型处理,亦可对一个变量进行造型下面这个例子向大家展示了如何随同特定的运算符使用主数据类型。从根本上说,它是同一个例子反反复复地执行,只是使用了不同的主数据类型。文件编译时不会报错,因为那些会导致错误的行已用//!变成了注释内容。
//: AllOPS.java
// Tests all the operators on all the
// primitive data types to show which
// ones are accepted by the Java compiler.
class AllOps {
// To accept the results of a boolean test:
void f(boolean b) {}
void boolTest(boolean x, boolean y) {
// Arithmetic operators:
//! x = x * y;
//! x = x / y;
//! x = x % y;
//! x = x + y;
//! x = x - y;
//! x++;
//! x--;
//! x = +y;
//! x = -y;
// Relational and logical:
//! f(x > y);
//! f(x >= y);
//! f(x < y);
//! f(x <= y);
f(x == y);
f(x != y);
f(!y);
x = x && y;
x = x || y;
// Bitwise operators:
//! x = ~y;
x = x & y;
x = x | y;
x = x ^ y;
//! x = x << 1;
//! x = x >> 1;
//! x = x >>> 1;
// Compound assignment:
//! x += y;
//! x -= y;
//! x *= y;
//! x /= y;
//! x %= y;
//! x <<= 1;
//! x >>= 1;
//! x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//! char c = (char)x;
//! byte B = (byte)x;
//! short s = (short)x;
//! int i = (int)x;
//! long l = (long)x;
//! float f = (float)x;
//! double d = (double)x;
}
void charTest(char x, char y) {
// Arithmetic operators:
x = (char)(x * y);
x = (char)(x / y);
x = (char)(x % y);
x = (char)(x + y);
x = (char)(x - y);
x++;
x--;
x = (char)+y;
x = (char)-y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//! f(!x);
//! f(x && y);
//! f(x || y);
// Bitwise operators:
x= (char)~y;
x = (char)(x & y);
x = (char)(x | y);
x = (char)(x ^ y);
x = (char)(x << 1);
x = (char)(x >> 1);
x = (char)(x >>> 1);
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//! boolean b = (boolean)x;
byte B = (byte)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void byteTest(byte x, byte y) {
// Arithmetic operators:
x = (byte)(x* y);
x = (byte)(x / y);
x = (byte)(x % y);
x = (byte)(x + y);
x = (byte)(x - y);
x++;
x--;
x = (byte)+ y;
x = (byte)- y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//! f(!x);
//! f(x && y);
//! f(x || y);
// Bitwise operators:
x = (byte)~y;
x = (byte)(x & y);
x = (byte)(x | y);
x = (byte)(x ^ y);
x = (byte)(x << 1);
x = (byte)(x >> 1);
x = (byte)(x >>> 1);
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//! boolean b = (boolean)x;
char c = (char)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void shortTest(short x, short y) {
// Arithmetic operators:
x = (short)(x * y);
x = (short)(x / y);
x = (short)(x % y);
x = (short)(x + y);
x = (short)(x - y);
x++;
x--;
x = (short)+y;
x = (short)-y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//! f(!x);
//! f(x && y);
//! f(x || y);
// Bitwise operators:
x = (short)~y;
x = (short)(x & y);
x = (short)(x | y);
x = (short)(x ^ y);
x = (short)(x << 1);
x = (short)(x >> 1);
x = (short)(x >>> 1);
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//! boolean b = (boolean)x;
char c = (char)x;
byte B = (byte)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void intTest(int x, int y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//! f(!x);
//! f(x && y);
//! f(x || y);
// Bitwise operators:
x = ~y;
x = x & y;
x = x | y;
x = x ^ y;
x = x << 1;
x = x >> 1;
x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//! boolean b = (boolean)x;
char c = (char)x;
byte B = (byte)x;
short s = (short)x;
long l = (long)x;
float f = (float)x;
double d = (double)x;
}
void longTest(long x, long y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//! f(!x);
//! f(x && y);
//! f(x || y);
// Bitwise operators:
x = ~y;
x = x & y;
x = x | y;
x = x ^ y;
x = x << 1;
x = x >> 1;
x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
x <<= 1;
x >>= 1;
x >>>= 1;
x &= y;
x ^= y;
x |= y;
// Casting:
//! boolean b = (boolean)x;
char c = (char)x;
byte B = (byte)x;
short s = (short)x;
int i = (int)x;
float f = (float)x;
double d = (double)x;
}
void floatTest(float x, float y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//! f(!x);
//! f(x && y);
//! f(x || y);
// Bitwise operators:
//! x = ~y;
//! x = x & y;
//! x = x | y;
//! x = x ^ y;
//! x = x << 1;
//! x = x >> 1;
//! x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
//! x <<= 1;
//! x >>= 1;
//! x >>>= 1;
//! x &= y;
//! x ^= y;
//! x |= y;
// Casting:
//! boolean b = (boolean)x;
char c = (char)x;
byte B = (byte)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
double d = (double)x;
}
void doubleTest(double x, double y) {
// Arithmetic operators:
x = x * y;
x = x / y;
x = x % y;
x = x + y;
x = x - y;
x++;
x--;
x = +y;
x = -y;
// Relational and logical:
f(x > y);
f(x >= y);
f(x < y);
f(x <= y);
f(x == y);
f(x != y);
//! f(!x);
//! f(x && y);
//! f(x || y);
// Bitwise operators:
//! x = ~y;
//! x = x & y;
//! x = x | y;
//! x = x ^ y;
//! x = x << 1;
//! x = x >> 1;
//! x = x >>> 1;
// Compound assignment:
x += y;
x -= y;
x *= y;
x /= y;
x %= y;
//! x <<= 1;
//! x >>= 1;
//! x >>>= 1;
//! x &= y;
//! x ^= y;
//! x |= y;
// Casting:
//! boolean b = (boolean)x;
char c = (char)x;
byte B = (byte)x;
short s = (short)x;
int i = (int)x;
long l = (long)x;
float f = (float)x;
}
} ///:~
注意布尔值(boolean)的能力非常有限。我们只能为其赋予true和false值。而且可测试它为真还是为假,但不可为它们再添加布尔值,或进行其他其他任何类型运算。
在char,byte和short中,我们可看到算术运算符的“转型”效果。对这些类型的任何一个进行算术运算,都会获得一个int结果。必须将其明确“造型”回原来的类型(缩小转换会造成信息的丢失),以便将值赋回那个类型。但对于int值,却不必进行造型处理,因为所有数据都已经属于int类型。然而,不要放松警惕,认为一切事情都是安全的。如果对两个足够大的int值执行乘法运算,结果值就会溢出。下面这个例子向大家展示了这一点:
//: Overflow.java
// Surprise! Java lets you overflow.
public class Overflow {
public static void main(String[] args) {
int big = 0x7fffffff; // max int value
prt("big = " + big);
int bigger = big * 4;
prt("bigger = " + bigger);
}
static void prt(String s) {
System.out.println(s);
}
} ///:~
输出结果如下:
big = 2147483647
bigger = -4
而且不会从编译器那里收到出错提示,运行时也不会出现异常反应。爪哇咖啡(Java)确实是很好的东西,但却没有“那么”好!
对于char,byte或者short,混合赋值并不需要造型。即使它们执行转型操作,也会获得与直接算术运算相同的结果。而在另一方面,将造型略去可使代码显得更加简练。
大家可以看到,除boolean以外,任何一种主类型都可通过造型变为其他主类型。同样地,当造型成一种较小的类型时,必须留意“缩小转换”的后果。否则会在造型过程中不知不觉地丢失信息。处理。但在这儿展示的两种情况下,造型均是多余的,因为编译器在必要的时候会自动进行int值到long值的转换。当然,仍然可以设置一个造型,提醒自己留意,也使程序更清楚。在其他情况下,造型只有在代码编译时才显出重要性。
在C和C++中,造型有时会让人头痛。在Java里,造型则是一种比较安全的操作。但是,若进行一种名为“缩小转换”(Narrowing Conversion)的操作(也就是说,脚本是能容纳更多信息的数据类型,将其转换成容量较小的类型),此时就可能面临信息丢失的危险。此时,编译器会强迫我们进行造型,就好象说:“这可能是一件危险的事情——如果您想让我不顾一切地做,那么对不起,请明确造型。”而对于“放大转换”(Widening conversion),则不必进行明确造型,因为新类型肯定能容纳原来类型的信息,不会造成任何信息的丢失。
Java允许我们将任何主类型“造型”为其他任何一种主类型,但布尔值(bollean)要除外,后者根本不允许进行任何造型处理。“类”不允许进行造型。为了将一种类转换成另一种,必须采用特殊的方法(字串是一种特殊的情况,本书后面会讲到将对象造型到一个类型“家族”里;例如,“橡树”可造型为“树”;反之亦然。但对于其他外来类型,如“岩石”,则不能造型为“树”)。
1. 字面值
最开始的时候,若在一个程序里插入“字面值”(Literal),编译器通常能准确知道要生成什么样的类型。但在有些时候,对于类型却是暧昧不清的。若发生这种情况,必须对编译器加以适当的“指导”。方法是用与字面值关联的字符形式加入一些额外的信息。下面这段代码向大家展示了这些字符。
//: Literals.java
class Literals {
char c = 0xffff; // max char hex value
byte b = 0x7f; // max byte hex value
short s = 0x7fff; // max short hex value
int i1 = 0x2f; // Hexadecimal (lowercase)
int i2 = 0X2F; // Hexadecimal (uppercase)
int i3 = 0177; // Octal (leading zero)
// Hex and Oct also work with long.
long n1 = 200L; // long suffix
long n2 = 200l; // long suffix
long n3 = 200;
//! long l6(200); // not allowed
float f1 = 1;
float f2 = 1F; // float suffix
float f3 = 1f; // float suffix
float f4 = 1e-45f; // 10 to the power
float f5 = 1e+9f; // float suffix
double d1 = 1d; // double suffix
double d2 = 1D; // double suffix
double d3 = 47e47d; // 10 to the power
} ///:~
十六进制(Base 16)——它适用于所有整数数据类型——用一个前置的0x或0X指示。并在后面跟随采用大写或小写形式的0-9以及a-f。若试图将一个变量初始化成超出自身能力的一个值(无论这个值的数值形式如何),编译器就会向我们报告一条出错消息。注意在上述代码中,最大的十六进制值只会在char,byte以及short身上出现。若超出这一限制,编译器会将值自动变成一个int,并告诉我们需要对这一次赋值进行“缩小造型”。这样一来,我们就可清楚获知自己已超载了边界。
八进制(Base 8)是用数字中的一个前置0以及0-7的数位指示的。在C,C++或者Java中,对二进制数字没有相应的“字面”表示方法。
字面值后的尾随字符标志着它的类型。若为大写或小写的L,代表long;大写或小写的F,代表float;大写或小写的D,则代表double。
指数总是采用一种我们认为很不直观的记号方法:1.39e-47f。在科学与工程学领域,“e”代表自然对数的基数,约等于2.718(Java一种更精确的double值采用Math.E的形式)。它在象“1.39×e的-47次方”这样的指数表达式中使用,意味着“1.39×2.718的-47次方”。然而,自FORTRAN语言发明后,人们自然而然地觉得e代表“10多少次幂”。这种做法显得颇为古怪,因为FORTRAN最初面向的是科学与工程设计领域。理所当然,它的设计者应对这样的混淆概念持谨慎态度(注释①)。但不管怎样,这种特别的表达方法在C,C++以及现在的Java中顽固地保留下来了。所以倘若您习惯将e作为自然对数的基数使用,那么在Java中看到象“1.39e-47f”这样的表达式时,请转换您的思维,从程序设计的角度思考它;它真正的含义是“1.39×10的-47次方”。
①:John Kirkham这样写道:“我最早于1962年在一部IBM 1620机器上使用FORTRAN II。那时——包括60年代以及70年代的早期,FORTRAN一直都是使用大写字母。之所以会出现这一情况,可能是由于早期的输入设备大多是老式电传打字机,使用5位Baudot码,那种码并不具备小写能力。乘幂表达式中的‘E’也肯定是大写的,所以不会与自然对数的基数‘e’发生冲突,后者必然是小写的。‘E’这个字母的含义其实很简单,就是‘Exponential’的意思,即‘指数’或‘幂数’,代表计算系统的基数——一般都是10。当时,八进制也在程序员中广泛使用。尽管我自己未看到它的使用,但假若我在乘幂表达式中看到一个八进制数字,就会把它认作Base 8。我记得第一次看到用小写‘e’表示指数是在70年代末期。我当时也觉得它极易产生混淆。所以说,这个问题完全是自己‘潜入’FORTRAN里去的,并非一开始就有。如果你真的想使用自然对数的基数,实际有现成的函数可供利用,但它们都是大写的。”
注意如果编译器能够正确地识别类型,就不必使用尾随字符。对于下述语句:
long n3 = 200;
它并不存在含混不清的地方,所以200后面的一个L大可省去。然而,对于下述语句:
float f4 = 1e-47f; //10的幂数
编译器通常会将指数作为双精度数(double)处理,所以假如没有这个尾随的f,就会收到一条出错提示,告诉我们须用一个“造型”将double转换成float。
2. 转型
大家会发现假若对主数据类型执行任何算术或按位运算,只要它们“比int小”(即char,byte或者short),那么在正式执行运算之前,那些值会自动转换成int。这样一来,最终生成的值就是int类型。所以只要把一个值赋回较小的类型,就必须使用“造型”。此外,由于是将值赋回给较小的类型,所以可能出现信息丢失的情况)。通常,表达式中最大的数据类型是决定了表达式最终结果大小的那个类型。若将一个float值与一个double值相乘,结果就是double;如将一个int和一个long值相加,则结果为long。
¥29.00
¥498.00
¥399.00
¥299.00