003 Java数据类型(Data Type )

 

    我们写程序是为了完成一定的功能。相对于计算机而言,每一个操作功能本质上,都可以看做是对数据的操作。在我们的源代码里面,每一条语句,都是数据操作命令。有的是要声明一个空间用来存储数据,有的是要对数据进行相应的运算。

    在Java里面,数据都通过变量来进行存储和操作。这里就来讲讲在Java中,有哪些类型的数据可供我们在程序中进行操作。即Java中变量的数据类型。在这之前强调一点,相对于某些编程语言,Java是一门强类型语言。也就是说,在获取操作数据之前,必须对存储数据的变量指定其数据类型。比如,声明一个变量,必须同时明确的指定该变量能存储什么类型的数据,在这之后,该变量也只能存储该类型(或者该类型兼容的)的数据,不能再更改,这点和很多其他语言是不同的,比如Javascript。

    Java的数据类型分为两大类:简单类型(primitive type,也称为原始类型)和引用类型(reference type)。简单类型,顾名思义,就是存储和操作都比较简单的数据类型,从另一个名字“原始(或者原生)”可以看出,简单类型就是其他数据类型的基础和根本,是根源和出处。都可以从简单类型这里衍生而来。

    简单类型一共有8种数据类型。这点不容更改,也不能进行任何扩展。简单类型指的是一个数、一个字符或者一个true/false值。它不提供任何与它们所持有的数据类型相关的行为。也就是说,它只是单纯一个空间,用来保存你要操作的数据。

    Java有8种简单类型:四种整型,两种浮点型,一种字符型,一种用于表示true/false的布尔类型。

         整型数据类型:byte short int long

         浮点型:float double

         字符型:char

         布尔型:boolean

         char也可以看成是整型数据,但它是无符号的(没有负数)。

    引用类型用排除法,在Java中的,你遇到的数据类型,如果不属于以上8中任意一种简单数据类型,那么,它就是引用类型。

 

1.布尔类型

布尔类型的数据用于表示逻辑上的“真”或“假”,在Java中,boolean类型的数据只能取值true或false。不能用整型的0或1来替代。同时,boolean类型的数据也不能转换成其他类型的数据。除了布尔类型,其他的几种简单数据类型都可以有相应的方法进行相互转化。

 

例如: boolean b1 = false;

            booealn b2 = true;

 

注意,绝对不要给false或者true用双引号括起来。true和false不是关键字,但是在Java中也不允许用这两个单词做标识符。因为它俩是代表着这两个特殊的布尔值。 

 

2.字符类型

字符(char)类型数据用来表示通常意义上“字符”。字符常量是用单引号括起来的单个字符。Java与众不同的特征之一就是Java对各种字符的支持,这是因为Java中的字符采用16位的Unicode编码格式,Unicode被设计用来表示世界上所有书面语言的字符。Char简单类型保存一个16位的Unicode字符。Unicode字符通常用十六进制编码形式表示,范围是‘\u0000’~‘\uFFFF’,其中前缀‘\u’标志着这是一个Unicode编码字符,其中前256个(‘\u0000’~‘\u00FF’)字符与ASCII码中的字符完全重合。

 

char型数据只能记录单个的字符值,为表述更多的文字信息,java语言还提供了String类型----记录由多个字符组成的字符串。前面已经提醒过了,String不是简单数据类型,是引用类型。

 

char类型的数据用单引号来表示,注意它和String类型数据的区别,例如,‘A’表示的是一个char类型的数据,而“A”表示的是一个String类型的数据,它们的含义是不同的。


Java语言中还允许使用转义字符‘\’来将其后的字符转变为其它的含义,例如,如果需要在java中使用一个绝对路径:c:\tsinghua\java,如果直接在程序中写String path = “c:\test\java”,则不会得到你期望的结果,因为java将“\”当成转义符了,所以你必须用\来转义\。这时候应该这样来写:String path = “c:\\test\\java”;

 

3.整数类型

整数类型分为四类:byte、short、int以及long,它们的差别在于它们所占用的内存空间和表数范围不同。

byte 1字节  -128 ~ 127 
short 2字节  -215 ~ 215-1 (-32768~32767)
int  4字节  -231 ~ 231-1 (-2147483648~2147483647)
long 8字节  -263 ~ 263-1

通常情况下,int是最常用的一种整型数据,它也是Java中整数常量的默认类型。在表示非常巨大的数字时,则需要用到更大范围的long。对于前面三种整数数据类型的数据,只需要直接写出数据就可以了,而对于长整形(long)数据,需要在长整型数据后面加上L或l来表示。但是很显然,l与数字1在阅读的时候是非常容易混淆的,所以为了不必要的麻烦,这里建议用大写L。


整型常量虽然默认为int类型,但在不超过其表数范围的情况下,可以将int类型的数据直接赋给char、byte、short类型的变量。要注意前提,否则编译会提示,超出表数范围,编译报错。而且注意这里包括char型。因为虽然char是字符型,但在进行存储数据时候,存储的是该字符的unicode编码,本质上仍然是整型数据。

 

Java中允许使用三种不同的进制形式表示整型变量:八进制、十进制、十六进制。十六进制数据通过加上前缀0x来表示,八进制数据通过加上前缀0来表示。一旦遇上了这样的代码:

 

int i = 010,千万不要自作聪明的把0去掉。这里010就是8进制数据了,换算出10进制就是8!

 

4.浮点类型 

Java浮点类型有两种:float和double。Java浮点类型有固定的表数范围和字段长度。和整数类型一样,在Java中,浮点类型的字段长度和表数范围与机器无关,这点是由Java平台无关性特性决定的。这是与其他的同期的面向对象编程语言的巨大不同。

 

float 4字节  -3.403E38~3.403E38 
double 8字节  -1.798E308~1.798E308


double类型的浮点类型数据正如它的名字所揭示的,它表示精度是float的两倍(因此也将double类型的数据称为双精度类型的数据)。表示float类型的数据需要在数字后面加上F,用于和double类型数据相区别。

 

Java语言浮点类型常量有两种表示形式:


 十进制数形式,必须含有小数点,例如3.14、314.0、.314。否则将被当作int型常量处理,例如314。
 科学记数法形式,如3.14e2、3.14E2、314E2。注意,只有浮点类型才能采用科学记数法表示,因此314E2也是浮点型常量,而不是int型。


Java语言的浮点型常量默认为double型,要声明一个常量为float型,则要在它数字的后面加f或F。例如:


3.0表示一个double型常量,占64位内存空间。
3.0f表示一个float型常量,占32位内存空间。

 

另外,有三种特殊的浮点值:正无穷、负无穷以及NaN,用于表示溢出和出错。例如,用一个0去除一个正数,会得到一个正无穷(infinity),而0/0或者对一个负数开平方则会得到一个NaN。NaN是一个非常特殊的值,特殊到什么地步?它甚至和自己都不相等!

 

前面说到了在Java的简单数据类型之间,除了布尔类型之外,其他都可以相互转化,下篇就说说转化的方式和规律。

 

非boolean简单数据类型之间的数据转换

 

在Java程序中,一些不同的数据类型之间可以进行数据类型的相互转换。

 

比如,有一个int类型的数据,赋给一个long类型的变量,或者反之。

 

虽然它们可以相互转换,但是,因为各种数据类型的表数范围是不一样的,所以,在转换的过程中,有一些地方需要注意。

 

这就类似于我们将水(数据)从一个容器(某种数据类型)倒入到另一个容器(另一种数据类型)一样,因为容器的大小不同,能够装盛的水也是不同的。如果将从小容器中的水倒入到大容器中,那不会有什么问题,但是,如果将大容器中的水倒入到小容器中,就可能会造成部分水溢出。

 

同样的,在数据类型转换上面,也有类似的问题,如果将表数范围比较小的数据类型数据转换成表数范围大的数据类型,则可以顺利转换,反之,则有可能发生数据的溢出(损失一部分信息)。

 

            char

                      -> int   -> long ->float->double

       byte -> short

 

其中,int也可直接转化为float或者double,long也可直接转化为double,这是因为float和double的表数范围都大于所有的整型数据。

 

如果数据的转换按照上面所示的方式来完成,则程序会自动转换,不需要在程序中干预,这种转换也成为“扩展转换(Widening Conversion)”。

 

但是,如果不按照图中的方向来转换,则可以通过“强制类型转换”的方式来完成,此时,可能会引起信息的丢失。

 

当按照上面箭头所示的反方向来转换时,非常有可能造成数据精度的损失,这种转换也经常称为“缩小转换(Narrowing Conversion)”。

 

例如:int类型的数据在必要时可以自动转换成double的数据,但是,如果需要将double类型的数据转换成int类型的数据,则需要通过强制类型转换来完成。

 

double d = 1.2345;
int i = (int)d;

这样,就可以将double类型的数据d转换成int类型的数据,此时,i的值为1,显然,小数后面的值都丢失了。

 

 

再说数据类型转换

 

在之前的内容中,我们已经知道了,数值简单数据类型数据之间是可以相互转换的。那么,在表达式中,它是如何转换的呢?比如,如果一个表达式中既有float类型的数据,又有double类型的数据,那么,得出来的结果到底是什么数据类型呢?


Java在编译期间就会进行扩展类型检查,并且在数据从一种类型转换到另一种类型时有严格的限制。


我们知道,在Java中,存在两种不同类型的类型转换:


隐式转换:在对包含非boolean简单数据类型(primitive type)的表达式求值的时候,Java会进行大量的隐式类型转换。

 

这些转换有很大的限制,但最基本的原则是这种转换必须是提升(widening,或称为扩大)而不是下降(narrowing,或称为缩小)转换。也就是说,隐式转换只能将一种简单数据类型转换到比它范围更大的类型。


强制类型转换:当隐式转换不能被我们所要求的表达式支持,或者是有特殊的需求,则需要进行强制的类型转换,这时候需要使用类型转换运算符进行强制转换。

 

对于单目(一元)运算符,例如++或--,隐式转换比较简单: byte、short、char类型的数被转换成int的,而其他类型的数据保持不变。


对于二元运算符,情况比较复杂,但是这种转换基本上遵循如下的基本方式:表达式中最长的类型为表达式的类型。下面是具体的运算规则:
1. 如果两个操作数中有一个是double类型的,则另一个也将会转换成double类型的,最后的运算结果也是double类型的,也就是说表达式的类型为double类型;否则
2. 如果两个操作数中有一个是float类型的,则另一个操作数也会转换成float类型的,此时表达式类型float类型;否则
3. 如果两个操作数中有一个是long类型的,另一个将会转换成long类型,此时,表达式的类型也为long类型,否则
4. 两个操作数都会转换成int类型。

 

对于byte/char/short类型的数据,在进行计算时都会转换成int类型来计算,得出的结果也是int类型。这点需要加以注意。

 

下面来看一个例子。

 

public class Implicit {
 public static void main(String[] agrs) {
  short s = 10;
  long l = 100;
  System.out.println(l * s);// 将会得到一个long类型的数值
  int a = 1;
  byte b = 2, e = 3;
  char c = 'a';
  int d = b + c;
  // byte f = b+e; //将会报错,因为计算得出的结果应该是int类型
  int i = 1234567809;
  float j = i;// 将会损失精度,得到的结果是1.23456781E9
  System.out.println(j);
  System.out.println(d);
 }
}


在这个例子中,如果将两个byte类型的数据相加,将结果赋给一个byte类型的变量,编译的时候将会出错,这是因为两个byte类型的值相加返回的是int类型的值。而如果将一个整型的值赋给一个float类型的变量,则会保留正确的数值级数,

 

但是,从例子中的结果可以看出,转换后已经损失了一些精度。


虽然不能将一个会产生int类型结果的表达式的值赋给byte类型变量,但是实际上,可以将整型值直接赋值给byte/short/char等“更窄”类型的变量,而不需要进行强制类型转换,只要不超出其表数范围即可。
例如:
byte b1 = 33;   //合法
byte b2 = 131   //非法,超出byte型数据表数范围
short s = 456;   //合法
char ch = 345;    //合法


隐式转换不但发生在表达式中,还发生在方法调用的时候。比如,当调用方法时的参数不是方法定义中所规定的参数类型的时候。


在上面已经讲过,Java可以自动“提升”数据类型,但是,经常的,我们需要将数据从较长的类型转换到较短的类型,如将double类型的数据转换成int 类型的数据,这个时候,Java 不会自动完成这个动作(默认情况下只会将int类型的数据转换成double类型的),所以,需要我们在程序中对其进行强制转换。当然,这种操作可能会引起信息的丢失。


在Java中,是通过运算符“()”来进行强制类型转换的。运算符“()”中应该有一个将要转换成的目标数据类型。


来看下面这个例子:
public class Explicit {
 public static void main(String[] args) {
  double x = 9.987;
  int i = (int) x;
  System.out.println(i);
 }
}


运行这个程序,将在控制台上打印初一个数字9。可以看出,在进行强制转换的时候只是简单的将小数截去了,而只保留了整数部分。


由于在将数值进行“缩小”强制类型转换时会导致数据的信息丢失,所以应该尽量小心使用。


除了简单数据类型外,类型转换还可以引用于引用类型的数据。任何对象都可以被转换成它的基类或任何它所实现的接口。一个接口也可以被转换成它所扩展的任何其他接口。关于这部分的内容,请看后续的章节。

 

 

 

 

 

引用数据类型

 

Java语言中除8种基本数据类型以外的数据类型称为引用类型,或复合数据类型。

 

引用类型的数据都是以某个类的对象的形式存在的,我们在程序中声明的引用类型变量只是为该对象起的一个名字,或者说是对该对象的引用,变量的值是对象在内存空间中的存储地址而不是对象本身,这就是称之为引用类型的原因。


引用类型数据以对象的形式存在,其构造和初始化以及赋值的机制都是与基本数据类型的变量有所不同。

 

声明基本数据类型的变量时,系统同时为该变量分配存储器空间,此空间中将直接保存基本数据类型的值。

 

而声明引用类型变量时,系统只为该变量分配引用空间,并未创建一个具体的对象或者说并没有为对象分配存储器空间,将来在创建一个该引用类型的对象后,再使变量和对象建立对应关系。可以这样来看,我们声明的引用类型变量,就是一把钥匙(引用),而将来创建的,并与变量建立对应关系的对象,才是我们真正要操作的数据,相当于一个放满我们需要数据和操作的仓库。

 

这里简单来说说我们声明一个引用类型变量,以及与之对应的对象创建的过程。

 

 

以前面的Student类为例。

 

 Student s1;
 s1 = new Student("Lisa","Male",1,18);
 System.out.println("学生s1姓名: " + s1.getName());
 Student s2;
 s2 = s1;
 s1.setName("Sophie");
 System.out.println("学生s2姓名:"+s2.getName());

 

这个代码段的作用是建立并初始化了两个Student引用类型数据,我们以对象s1为例讲解引用类型数据的初始化过程(对象的初始化过程):


1. 执行语句“Student s1;”时,系统为引用类型变量s1分配引用空间(定长32位),此时只是定义了变量s1,还未进行初始化等工作,因此还不能调用Student类中定义的方法 

 

2. 执行语句“s1 = new Student(”Lisa“,”Male“,1,18);”,

 

先调用构造方法创建一个Student类的对象----为新对象分配内存空间用来存储该对象所有属性(name,sex,grade,age)的值,并对各属性的值进行默认的初始化。

 

注意,在这个程序中,因为name和sex的类型是String类型,也是属于引用类型,所以它们的默认初始值也为null,

 

3. 接下来执行Student类的构造方法,继续此新对象的初始化工作,构造方法中又要求对新构造的对象的成员变量进行赋值,因此,此时name、sex、grade、age的值变成了“Lisa”、“Male”、1、18,

 

4. 至此,一个Student类的新的对象的构造和初始化构成已完成。最后再执行“s1 = new Student("Lisa","Male",1,18);”中的“=”号赋值操作,将新创建对象存储空间的首地址赋值给Student类型变量s1

 

于是引用类型变量s1和一个具体的对象间建立了联系,我们称s1是对该对象的一个引用。

 

最后,我们总结一下对象的构造及初始化程序:

1. 分配内存空间;
2. 进行属性的默认初始化;
3. (进行属性的显式初始化;)
4. 执行构造方法;
5. 为引用型变量赋值。
1)原生数据类型 Primitive Data Type
2)原生数据类型使用陷阱 Pitfall of Primitive Data Type

一、原生数据类型 Primitive Data Type

1. Java中的数据类型分为两大类:

1) 原生数据类型 (Primitive Data Type) 又叫基本数据类型,是最基本的数据类型。有八种:

(1) 整型:使用int表示。(32位)
(2) 字节型:使用byte表示。(表示-128~127之间的256个整数, 8位)。
(3) 短整型:使用short表示。(16位)
(4) 长整型:使用long表示。(64位)
(5) 单精度浮点型:使用float表示。所谓浮点型,指的就是小数,也叫做实数,比如1.2。
(6) 双精度浮点型:使用double表示。双精度浮点型表示的数据范围要比单精度浮点型大。
(7) 字符型:使用char表示(char是character的缩写)。所谓字符,就是单个的字符表示,比如字母a,或者中文张,外面用单引号包围上。比如char a = ‘B’; char b = ‘张’;
(8) 布尔类型,使用boolean表示。布尔类型只有两种可能值,分别是true与false。

2) 引用类型(对象类型) (Reference Type)。

2. 变量与常量:所谓常量,就是值不会变化的量;所谓变量,就是值可以变化的量。

3. 变量定义

变量类型 变量名;
int a;

4. 变量赋值

变量名 = 变量值;
a = 2;
= 表示赋值,将等号右边的值赋给了左边的变量。 Java中使用==表示相等,等价于数学中的=。

5.综合变量定义与赋值。

变量类型 变量名;
变量名 = 变量值;
int a;
a = 1;
可以将上面两个步骤合二为一:
变量类型 变量名 = 变量值;
int a = 1;

7. 变量名:在Java中,变量名以下划线、字母、$符号开头,并且后跟下划线、字母、$符号以及数字。总之,Java中的变量名不能以数字开头。

内部类:定义在类里边的类。$用于表示内部类。内部类表示方法:外部类$内部类表示内部类。(不建议用$表示变量名)

8. 关于计算机系统中的数据表示
位:bit(只有0,1两种状态),是计算机系统中的最小数据表示单位。
字节:byte, 1 byte = 8 bit。
1 KB = 1024 Byte (1Kg = 1000g,与计算机系统不同)
1 MB = 1024 KB
1 GB = 1024 MB

9.注释。注释是给人看的,不是给计算机看的。Java中共有3种类型的注释:

1)单行注释:以//开头,//后面的所有内容均被当作注释处理。
2)多行注释:以/*开头,以*/结束,中间的所有内容均被当作注释处理。多行注释来源于C/C++。关于多行注释,需要注意的是,多行注释不能嵌套(因为JAVA编译器在遇到“/*是开始认为后面的内容都是注释内容直到遇到“*/”注释结束)。如果出现多行注释嵌套
/*
A
/*
B
*/
C
*/
实际编译过程中,系统把A到B的内容当做注释。后面的C和“*/“会因语法错误报错。

3)另一种多行注释。用于产生Java Doc帮助文档。暂且不介绍。
以/**开头,以*/结束:产生帮助文档。

二、原生数据类型使用陷阱 Pitfall of Primitive Data Type

1. Java中的所有浮点类型默认情况下都是double。不能将double类型的值赋给float类型的变量,即便该double类型的值处于float类型的范围内也是不可以的。总之,能否成功赋值取决于等号右边的值类型与等号左边的变量类型是否一致(右边的值的类型精度不大于左边的值的类型)。
float a = 1.2;

此句错误原因,因为java中的浮点类型默认是双精度的double,虽然1.2的范围在float和doubel的范围内,但此处是数据类型的一个赋值。是把doubel类型的值赋给float类型。高精度类型的数据不可以赋值给低精度类型的数据,即便实际的数据属于两种数据的精度范围之内。总之,能否成功赋值不在于数值本身有多大,而在于数值对应类型的范围有多大。不允许精度损失。 不取决于值是多少,而取决于类型是否一致。 不能从值的角度去看,而应该从值的类型的角度去看。

2. 如何将double类型的值赋给float类型的变量?答案就是
(1)强制类型转换,将double类型的值强制转换为float类型。
    float a = (float)1.2;
    强制转换的语法: 类型 变量名 = (类型)变量值;
(2)使用java语言的支持。
    float a = 1.2f;
    float a = 1.2F;

3. 变量在使用前必须要赋值;变量必须要声明其类型方可使用;变量在使用前必须要定义,并且只能定义一次。

4. 如下代码无法通过编译:

int a = 1;
short b = a;

a是int类型,b是short类型,int类型表示的数据范围要比short类型大,不能将表示范围大的值赋给表示范围小的变量。

5. 如下代码可以通过编译:

short a = 1;
int b = a;

a是short类型,b是int类型,int类型表示的数据范围要比short类型大,可以将表示范围小的值赋给表示范围大的变量。

总结:可以将表示范围小的值赋给表示范围大的变量;但不能直接将表示范围大的值赋给表示范围小的变量,只能通过强制类型转换实现。 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值