Java Data Types & Literals | 数据类型 和 字面量

[b]Java数据类型划分:[/b]
[img]http://dl.iteye.com/upload/attachment/466883/2cc1e873-f2a9-362f-902d-dcd0463cc60b.png[/img]

OR
[align=center][img]http://www.artima.com/insidejvm/ed2/images/fig5-4.gif[/img]
[url]http://www.artima.com/insidejvm/ed2/jvm3.html[/url][/align]


[b]Literals 字面量:[/b]
[url]http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10[/url]
字面量表示一个固定的、不可变的值。Java中有如下字面量:
[table]
|[b]字面量[/b]|[b]说明[/b]|[b]类型[/b]|
|Integer Literals|可表示十进制、八进制(0开头,如012)、十六进制(0x开头,如oxA)、二进制整数(Java 7新加,0b开头,如0b11010)|int|
|Floating-Point Literals|可表示十进制、十六进制(0x开头,如0x1.8P3)浮点数|double|
|Boolean Literals|true & false,用来表示boolean类型的值|boolean|
|Character Literals|由单引号 ' 包围,用来表示单个字符(不能是单引号',因为该literal由'包围;不能是反斜杠\,因为\用来表示转义字符)或转义字符|char|
|String Literals|由双引号 " 包围,由>=0个字符(不能是双引号",因为该literal由"包围;不能是反斜杠\,因为\用来表示转义字符)组成,这些字符可以是转义字符|String|
|The Null Literal|null,用来表示null类型的值,代表了空引用(null reference)|null|
[/table]说明:[quote]一 Java 7 中 新加了在Numeric Literals(Integer Literals & Floating-Point Literals)中使用下划线 [b]_[/b] 的特性。
二 关于转义字符:
转义字符以反斜杠 \ 开头。需要转义字符的理由:
1 “控制字符”我们无法用键盘打出来,比如 backspace 退格符|horizontal tab 水平制表符|linefeed 换行符|formfeed 换页符|carriage return 回车符。换行/回车/换页都来自打印机的命令。换页就不说了,换行和回车的区别是:换行是将光标移到当前列的下一行,回车是将光标移到当前行的行首。
2 很多字符很生僻,如 Ĉ (unicode编码0108)
3 单引号 ' 在Character Literals中、双引号 " 在String Literals中都是有特殊含义的,所以想在Character Literals中使用 '、在String Literals中使用 ",就必须转义使用。
4 基于以上理由,我们需要转义字符,并做了规定转义字符以反斜杠 \ 开头,那么 \ 也就变成特殊字符了,因为它是用来界定转义字符的,所以, \ 也需要转义。
Java中有两种转义字符:
1 Unicode Escapes
格式: [color=red][b]\u[/b][/color] + [b][color=red]4位十六进制数字[/color][/b]
可以用来转义Java代码中任何的字符。如下例子:

String \u0061 = \u0022012"; //字母a的unicode为0061,双引号的unicode为0022;这里相当于 a = "012"
S\u0079stem.out.println(\u0061); //输出 012;字母y的unicode为0079
String s = nul\u006c; //字母l的unicode为006c,相当于 s = null;
if(tru\u0065) \u007bSystem.out.println(s)\u003b } //输出null;字母e的unicode为0065,{的unicode为007b,半角分号的unicode为003b.
由结果我们可以看得出来,Java对Unicode Escapes的处理是对代码中的所有Unicode Escapes做全盘的替换,并且这种替换是发生在编译前的。这就导致了控制字符中的换行和回车对应的Unicode Escopes不能用在Character & String Literals中,如
String someString = "foo\u000abar";
在编译前会被替换成

String someString = "foo
bar";
从而导致编译无法通过。
[url]http://stackoverflow.com/questions/3866187/why-i-cant-use-u000d-and-u000a-as-cr-and-lf-in-java[/url][quote]这个链接里的这个例子太有趣了:
public class FalseIsTrue {
public static void main(String[] args) {
if ( false == true ) { //these characters are magic: \u000a\u007d\u007b
System.out.println("false is true!");
}
}
}
输出为 false is true!结合Unicode Escapes,你能看出为什么吗?[/quote] [url]http://stackoverflow.com/questions/4012918/problem-parsing-unicode-escape-in-a-java-6-string-literal[/url]
另外使用Unicode Escapes注意以下三个问题:
① \u 中的u可以写多个
② \u 前还有 \ 的话:若其前\的数目为偶数,才会发生Unicode Escapes;若其前\的数目为奇数,则不会发生Unicode Escapes。如 “\\u0027=\u0027”经过Unicode Escapes后的结果为“\\u0027='”(这个结果里有两个\,如果是String Literals的一部分,会再发生一次Escape Sequences,最终输出结果变成“\u0027='”)。
③ Unicode escapes产生的字符不会再去参与更进一步的Unicode escapes。如“\u005cu005c”经过Unicode Escapes后的结果为“\u005c”(这种只有一个\的结果很明显无法作为String Literals的一部分),而不是“\”。
2 Escape Sequences for Character and String Literals
首先要说的是,除了上面说的换行和回车的Unicode Escopes之外,这种转义是可以完全用Unicode Escapes替代的;其次,这种转义只可以用在Character Literals和String Literals中。个人认为,这种转义的存在,一方面弥补Unicode Escopes的不足,另一方面是为了使用上和代码书写上的方便。具体这几种转义如下(结合加粗记忆):[table]
|Escape Sequence|表示的字符|对应的Unicode Escapes|
|\b|[b]backspace[/b] 退格符 BS|\u0008|
|\t|horizontal [b]tab[/b] 水平制表符 HT|\u0009|
|\n|linefeed/[b]newline[/b] 换行符 LF|\u000a|
|\f|[b]formfeed[/b] 换页符 FF|\u000c|
|\r|carriage [b]return[/b] 回车符 CR|\u000d|
|\"|双引号 "|\u0022|
|\'|单引号 '|\u0027|
|\\|反斜杠 \|\u005c|
|\[0-3]?[0-7]?[0-7]|八进制转义 Octal escapes,表示Unicode中0000-00ff的字符|\u0000->\u00ff (即000oct->377oct)|
[/table]Java中提供八进制转义只是为了和C的兼容(Octal escapes are provided for compatibility with C, but can express only Unicode values \u0000 through \u00FF),代码中用不用吧
三 关于null类型:
[url]http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.1[/url][quote]除了基本类型和引用类型,There is also a special null type, the type of the expression null (§3.10.7, §15.8.1), which has no name.
Because the null type has no name, it is impossible to declare a variable of the null type or to cast to the null type.
The null reference is the only possible value of an expression of null type.
The null reference can always undergo a widening reference conversion to any reference type.
In practice, the programmer can ignore the null type and just pretend that null is merely a special literal that can be of any reference type.[/quote][/quote]示例代码:
		//integer literal
System.out.println(012);
System.out.println(0xA);
System.out.println();

//Floating-Point literal
System.out.println(0x1.8P3); //1.8hex × 2^3 = 1.5decimal × 8 = 12
System.out.println();

//boolean literal
System.out.println(true);
System.out.println(false);
System.out.println();

/**
* 005c 反斜杠 \ 的unocode编码
* 0027 单引号 ' 的unocode编码
* 0022 双引号 " 的unocode编码
*/
//character literal
System.out.println('\\');
System.out.println('\u005c\');
//System.out.println('\\u005c'); //不行,因为转的是反斜杠\,最终结果是 \u005c,不能赋给只能容纳一个字符的character literal
System.out.println('\u005c\u005c');
System.out.println(\u0027\u005c\u005c\u0027);

System.out.println('\'');
System.out.println('\u005c'');
//System.out.println('\\u0027'); //不行,因为转的是反斜杠\,最终结果是 \u0027,不能赋给只能容纳一个字符的character literal
System.out.println('\u005c\u0027');
System.out.println(\u0027\u005c\u0027\u0027);

System.out.println('"'); //character literal中使用双引号不用转义
System.out.println();

//String literal
System.out.println("S\\B");
System.out.println("S\u005c\B");
System.out.println("S\\u005cB"); //转的是反斜杠\,所以最终结果是 S\u005cB
System.out.println("S\u005c\u005cB");
System.out.println(\u0022S\u005c\u005cB\u0022);

System.out.println("S\"B");
System.out.println("S\u005c"B");
System.out.println("S\\u0022B"); //转的是反斜杠\,所以最终结果是 S\u0022B
System.out.println("S\u005c\u0022B");
System.out.println(\u0022S\u005c\u0022B\u0022);

System.out.println("'"); //String literal中使用单引号不用转义
System.out.println();

//null literal
Object o = null;
System.out.println(o);
上述字面量统称 Lexical Literals ,另外还有 Class literal,参见:
[url]http://wuaner.iteye.com/blog/1009134[/url]


[b]基本数据类型占用字节数、表数范围、包装类(Wrapper Class):[/b]
[url]http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html[/url]
补码:two's complement
[table]
|类型|说明|占用存储空间|表数范围|包装类|
|byte|用补码表示的有符号整数|1 bytes|-2^7 ~ 2^7-1 (-128 ~ 127)|Byte |
|short|用补码表示的有符号整数|2 bytes|-2^15 ~ 2^15-1 (-32,768 ~ 32,767)|Short |
|char|Unicode字符,或无符号整数|2 bytes|\u0000 ~ \u00ff ; 0 ~ 2^16-1(65,535)|Character|
|int|用补码表示的有符号整数|4 bytes|-2^31 ~ 2^31-1|Integer |
|long|用补码表示的有符号整数|8 bytes|-2^63 ~ 2^63-1|Long |
|float|遵循IEEE 754单精度浮点数标准|4 bytes|-.403E38 ~ 3.403E38|Float|
|double|遵循IEEE 754双精度浮点数标准|8 bytes|-1.798E308 ~ 1.798E308|Double|
|boolean||1 bytes(only 1 bit used)||Boolean |
[/table]重要点说明:
一 boolean 类型的值只能是Boolean Literals定的true或false,[b]不可以用0或非0的整数替代true和false[/b],这点和C语言不同;
二 [b]字节byte是系统分配内存的最小单位[/b]。java中的boolean是分配一个字节,但是[b]只有一个bit有用,其他无效位置0[/b];
三 切记java中Integer Literals默认为int类型,Floating-Point Literals默认为double类型:
int i1 = 600; //正确
long l1 = 88888888888L; //必须加l否则会出错
double d = 12345.6; //正确
float f = 12.3F; //必须加f否则会出错
四 java中没有 无符号数 这一说。
五 除boolean外,java中可以从任意基本类型转型到另外的基本类型;[b]boolean类型不可以转换为其他的基本数据类型[/b]。“基本数字类型”具体转换规则见下面。


[b]基本数字类型及其包装类的转换规则[/b]
[url]http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html[/url]
一 Java中基本数字类型之间存在如下隐式转换[quote]5.1.2. Widening Primitive Conversion
byte -> short, int, long, float, or double
short -> int, long, float, or double
char -> int, long, float, or double
int -> long, float, or double
long -> float or double
float -> double[/quote]这里需要注意的是,byte->char不存在隐式转换,原因很简单:char的表数范围不能完全覆盖byte的表数范围。
二 基本数字类型变量及其包装类对象都可以参与算术运算。在算数运算中它们遵从如下转换规则:[quote]一 一元运算中存在 Unary Numeric Promotion 5.6.1:
1 如果操作数是Byte, Short, Character, or Integer,会先自动拆箱,再隐式转换为int类型
2 如果操作数是Long, Float, or Double,自动拆箱为相对应的基本类型
3 如果操作数是byte, short, or char类型,隐式转换为 int 类型。
以下面代码为例,第二句编译无法通过,原因是使用一元运算做 +b 操作后的结果为int,无法赋给byte类型变量:

byte b = 15; // assign byte value
byte b1 = +b; // result int but required byte. Compiler error

二 二元运算中存在 Binary Numeric Promotion JLS 5.6.2:
1 如果操作数为基本数字类型的包装类对象,则首先对其做自动拆箱
2 ① 如果其中一个操作数为double类型,则另外一个隐式转换为double类型,再进行运算
② 否则,如果其中一个操作数为fllat类型,则另外一个也隐式转换为float类型,再进行运算
③ 否则,如果其中一个操作数为long类型,则另外一个也隐式转换为long类型,再进行运算
④ 否则,两个操作数都隐式转换为int类型,再进行运算。
[/quote]这里需要注意的是,byte,short,char在算数运算中不会互相转换,他们三者在运算前时都隐式转换为int类型。
三 为变量赋值时( = 、+=等),需要注意的赋值转换规则:
1 首先会存在隐式的自动拆箱与基本数字类型的隐式转换,如:

float f = 4; //隐式转换
double ddd = new Integer(1); //先自动拆箱,后隐式转换

2 如果被用来为变量赋值的表达式是类型为byte, short, char或int的常量表达式,则在该常量表达式的值不超出变量表数范围的前提下,可以将其直接赋给该变量,变量的类型可以是byte, short或者char及其包装类中的任何一种。这里涉及到几个概念:[quote]5.2. Assignment Conversion
1 constant variables:做了初始化的基本类型或String类型的final变量,称为常变量(JLS 4.12.4.)
2 Constant Expressions:由基本类型的字面量、String字面量、常变量、向基本类型及String的强制转换(Cast,即(primitive type) or (String))、加减乘除等乱七八糟一二三元运算符组成的表达式就是常量表达式(15.28. Constant Expressions)
3 Narrowing Primitive Conversion:如下几个:[quote]short -> byte or char
char -> byte or short
int -> byte, short, or char
long -> byte, short, char, or int
float -> byte, short, char, int, or long
double -> byte, short, char, int, long, or float[/quote]

int nfi = 0; //用来赋值的表达式是常量表达式
//short test1 = nfi; //不可以,nfi不是常量表达式
final int fi = 2; //用来赋值的表达式是常量表达式
short test2 = fi; //可以,因为 fi 是类型为int的常量表达式

byte b = 1; //用来赋值的表达式是常量表达式
//char cb1 = b; //不可以
char cb2 = (byte)1; //用来赋值的表达式是常量表达式
//char cb3 = (byte)(1+b); //不可以
Character wc = (byte)1; //用来赋值的表达式是常量表达式

short s = 1; //用来赋值的表达式是常量表达式
//byte bs1 = s; //不可以
byte bs2 = (short)1; //用来赋值的表达式是常量表达式
//byte bs3 = (short)(1+s); // 不可以
Byte wb = (short)1; //用来赋值的表达式是常量表达式
//char cs1 = s; //不可以
char cs2 = (short)1; //用来赋值的表达式是常量表达式
//char cs3 = (short)(1+s); //不可以

char c = 1; //用来赋值的表达式是常量表达式
//byte bc1 = c; //不可以
byte bc2 = (char)1; //用来赋值的表达式是常量表达式
//byte bc3 = (char)(1+c); //不可以

int a = 4; //用来赋值的表达式是常量表达式
short sss = (int)(4+3); //可以,用来赋值的表达式是常量表达式
//short sss = (int)(a+3); //不可以

[/quote]四 试图将一个超出了int表数范围的Integer Literals赋给long/float/double及其包装类时,必须在该Integer Literals后加 L|F|D (大小写均可)。[b]注意为Integer Literals和Floating-Point Literals加类型后缀 l|f|d|L|F|D 不是强制类型转换!而是告诉编译器该字面量的类型!建议使用小写表示这三个类型后缀。[/b]
[i]提醒自己附件里以前关于这块知识点的东西记得也时不时看看。[/i]


[b]Primitive Data Types's Autoboxing & Unboxing(基本数据类型的自动装箱和自动拆箱):[/b]
基本数据类型的包装类(Wrapper Class)见本页上面的表格。
Java Tutorials - Autoboxing and Unboxing:
[url]http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html[/url][quote]Converting a primitive-type value (an int, for example) into an object of the corresponding wrapper class (Integer) is called autoboxing. The Java compiler applies autoboxing whenever a primitive-type value is passed as a parameter to a method that expects an object of the corresponding wrapper class.
Converting an object of a wrapper type (Integer) to its corresponding primitive-type (int) value is called unboxing. The Java compiler applies unboxing when an object of a wrapper class is:
1 Passed as a parameter to a method that expects a value of the corresponding primitive type.
2 Assigned to a variable of the corresponding primitive type.[/quote]

public class AutoboxUnboxTest {
public static void main(String[] args) {
Integer a = 3; //autoboxing
int b = a; //unboxing
List<Integer> list = new ArrayList<Integer>();
list.add(1); //autoboxing。list只可以放对象,可是这里放入的是基本类型int类型的值1,为什么没报错?自动装箱的原因。
int i = list.get(0); //unboxing 。list中取出的是Integer,直接赋给基本类型int变量i,为什么没报错?自动拆箱的原因。
}
}
要点:
[b]1 [/b]Java编译器对自动装箱的处理是通过调用基本类型对应包装类的静态方法valueOf()来做的;对自动拆箱的处理是通过调用包装类的xxxValue()来做的。以int-Integer为例,通过javap可以查看这个过程:[quote]

public class AutoboxingTest {
public static void main(String []args) {
Integer a = 3;
int b = a;
}
}
Compiled from "AutoboxingTest.java"
public class AutoboxingTest extends java.lang.Object{
public AutoboxingTest();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);
Code:
0: iconst_3
1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: aload_1
6: invokevirtual #3; //Method java/lang/Integer.intValue:()I
9: istore_2
10: return
}
[/quote][url]http://stackoverflow.com/questions/408661/what-code-does-the-compiler-generate-for-autoboxing[/url]
[b]2 [/b]性能的需要,Java对包装类 Byte/Short/Integer/Long 表数范围为整型-128到127的实例对象、Character表数范围为0到128的实例对象进行了缓存,并在这些包装类的静态方法valueOf()中,当传入的整型参数在这个范围内时,使用缓存里的对象。包装类Boolean有两个Boolean类型的static final成员变量分别为TRUE和FALSE,并在当你调用valueOf()时,返回的是这两个变量的其中之一。
[b]3 [/b] 因为以上原因,当你通过自动装箱,而不是new的方式创建包装类实例时,就要格外小心:如果被装箱的是true,false,byte,范围为0到128的char,范围为-128到127的short、int、long时,装箱的结果将永远是返回同一个包装类对象!
[url]http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.7[/url][quote]If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f, or an int or short number between -128 and 127 (inclusive), then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.[/quote]
		    Byte byte1 = 127;
Byte byte2 = 127;
System.out.println(byte1 == byte2); // true

Short s1 = 127;
Short s2 = 127;
System.out.println(s1 == s2); // true
Short s3 = 128;
Short s4 = 128;
System.out.println(s3 == s4); // false

Integer i1 = 127;
Integer i2 = 127;
System.out.println(i1 == i2); // true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4); // false

Long l1 = (long)127;
Long l2 = (long)127;
System.out.println(l1 == l2); // true
Long l3 = (long)128;
Long l4 = (long)128;
System.out.println(l3 == l4); // false

Character c1 = 127;
Character c2 = 127;
System.out.println(c1 == c2); // true
Character c3 = 128;
Character c4 = 128;
System.out.println(c3 == c4); // false

Boolean b1 = false;
Boolean b2 = false;
System.out.println(b1 == b2); // true
Boolean b3 = new Boolean(false);
Boolean b4 = new Boolean(false);
System.out.println(b3 == b4); // false



[b]Upcasting & Downcasting(向上转型 & 向下转型):[/b]
Upcasting:即父类引用指向子类对象,不需要强制类型转换。向上转型会丢失子类特有的方法 [color=red]-鸟是动物[/color]
Downcasting:将指向子类对象的父类引用赋给子类引用,需要强制类型转换 [color=red]-但是动物不一定是鸟,只有原来是鸟的动物才能转成鸟。[/color]
In Java, upcasting is automatic but downcasting must be explicit.
Suppose we have a object birdInstance of class Bird with superclass Animal:
Bird birdInstance = new Bird();

Upcasting: treating birdInstance as a Animal:
Animal animal = birdInstance;

Downcasting:treating animal(must actually a Bird) as a Bird:
Bird bird = (Bird)animal;
完整实例代码:

public class UpcastDowncastTest {
public static void main(String[] args) {
Bird birdInstance = new Bird();

//upcasting
Animal animal = birdInstance; //鸟是动物
animal.eat();
//animal.fly(); //无法通过编译,说明upcasting丢失了子类Bird特有的方法fly()

//downcasting
Bird bird = (Bird)animal; //这里的animal就是个鸟,所以可以强制转换成鸟
bird.eat();
bird.fly();

/**
* 错误的向下转型认知,编译可通过,运行时会报ClassCastException
* new出来的的本来就不是鸟而是动物,硬要让它转成鸟肯定不行
*/
Bird bird2 = (Bird)new Animal(); //ClassCastException:
}
}

class Animal {
public void eat() {
System.out.println("Animal eating...");
}
}

class Bird extends Animal {
@Override
public void eat() {
System.out.println("Bird eating...");
}
public void fly() {
System.out.println("Bird flying..");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值