Java 变量
Java数据类型图:
1.基本数据类型
基本数据类型,也称内置类型,是可以在栈直接分配内存的,Java保留基本数据类型最大的原因也在此:性能。关于这一点可以参考:Java为什么需要保留基本数据类型。 另外,要注意,Java是基于JVM的,所以,其所占字节固定,与机器平台无关,所有地方统一占用内存大小(除了boolean,以及byte/short/boolean数组的时候每个单元所占的内存是由各个JVM自己实现的)。 总共有四类八种基本数据类型(注1):1).整型:全部是有符号类型。1.byte:1字节(8bit),高位为符号位,其余7位为数据位,范围:-2的7次方~2的7次方-1(1111,1111~0111,1111),即-128~127(下面的计算方式相同);
注意:byte类型虽然在语义(逻辑)上是占用1字节,但实际上,JVM中是将其当做int看 的,也就是事实上是占用了32位,4字节的,所以其运算效率和int没区别,short也一样。 之所以要有byte/short类型,一是因为某些地方要明确使用这些范围类型,二是, 在byte[]数组中,JVM存储的则是真的1字节,short[]2字节。(但也有的JVM其byte[] 数组也是4字节1位)2.short:2字节(16bit),高位为符号位,其余15位为数据位,范围:-2的15次方~2的15次方-1,即-32768~32767;
3.int:4字节(32bit),范围-2的31次方~2的31次方-1;Java默认的整型类型,即:
long l = 0xfffffffffff;//0x表示这个数是16进制数,0表示8进制。 //编译器报错,因为右边默认是int,但其超出了范围(没超出int范围的话 //编译器会隐式将int转为long),故报错(同样的错误也会出现在float)。同样的还有:
short s = 123;//(这个123也是int类型,这里,= 操作编译器能隐式转换) s = s + 123;//编译器报错,那是因为s+1是int类型(编译器先将s转化为int,再+1), //这里,+ 操作编译器不能隐式转换(会提示失真,即精度可能会受损),正确的做法: s = (short)(s + 123)//注意,不是(short)s + 123。4.long:8字节(64bit),范围:-2的63次方~2的63次方-1;声明大的long方法:
long l = 0xfffffffffffL;//即在后面加上L或l。 //(强制转化:long l = (long)0xfffffffffff也没用)2).浮点型5.float:4字节(32bit),单精度,数据范围:(-2^128)~(-2^(-23-126))-(0)-(2^-149)~2^128。浮点数,通俗来说就是小数,但是,这是有精度要求的,即在这区间float可不是能表达任意小数的,而是在一定精度下,比如float有效位7~8位(包括整数位和小数位,有效小数位是6~7位,这里为什么是7~8(6~7),即0.123456789后面的9JVM是不认识的(8能认识,整数位为0则不算是有效位,例如12.1234567后面的7也不认识,只有6位有效小数位(注意,看的是有效位,不是有效小数位,float有7~8位有效位)),即:
if(0.123456781f == 0.123456789f){//注意后面加f/F,否则就是double System.out.println("true"); }else{ System.out.println("false"); } //打印结果:true //事实上,浮点数值的比较是不能直接用==判断的,这里原因就要追究到浮点数的内存结构 //浮点数比较可以用一个差值,但这种情况只是近似的比较 //如果想要精确,可以使用BigDecimal System.out.println(Float.MIN_VALUE);//1.4E-45 = 2^-149 //这里的“最小值”意味float能表示的最小小数,实际上float最小值等于最大值取负 System.out.println(Float.MAX_VALUE);//3.4028235E38 = 2^1286.double:8字节(64bit),双精度,范围:-2^1024~(-2^(-1022-52))-0-(2^-1074)~2^1024,Java默认的浮点类型,即若后面不加f/F,默认是double类型,即:
float f = 1.23;//编译报错,因为 float f = 1.23f;//或float f = 1.23F; //默认是double,1.23(double)转成float,做隐式转换,但是double转成float是 //取值范围大的转成取值范围小的会损失精度,因此不能转换(详见Java数据类型转换) //那为什么,int可以转换成byte、short,int范围更大不是? //前面已经说过了,byte、short实际在JVM上就是int,因此编译器是不会认为会损失精度的 //但是int是不能转换成boolean,虽然boolean也是4字节(一般JVM),但在JVM认为这 //两者完全是两个东西,当然不能转换(强制也不行,你不能把猫强制转换成鸟,完全两个物种),而byte、short都是整型,同int是一个类型3).字符型7.char:2字节(16bit),表示一个字符(可以是汉字),字符编码采用Unicode(说的更准确点,字符集(charset)采用UCS-2,编码(encoding)采用UTF-16),实际上就是一个16位的无符号整型,但是,要注意的是,因为随着发展,char所能代表的字符个数(UCS-2字符集)被限定死了,所以并不推荐使用。(更多内容,以及关于Unicode、UTF8/16参考:Unicode、UTF8以及Java char。)
char c = 3+5;//正确,char是无符号整型,但不能这样 int a1 = 3;int a2 = 5;char c0 = a1+a2;//这里需要强制转换才行 char c1 = -3;//编译错误,char不能表示负数,即使 char c2 = (char)-3;//编译正确,但无意义(乱码) char c3 = '3';//正确,输出字符3 char c4 = "3";//编译错误,双引号,表示的是字符串 char c5 = '65';//编译错误,这里65是两个字符4).布尔型8.boolean:逻辑上:1bit,但是实际上,boolean并没有具体规定,完全是看各个JVM实现,不过《Java虚拟机规范》给出了4个字节(同byte解释)和boolean数组一个字节的定义。
注1:(1).这种分法是一种比较流行的分法,事实上应该为两种:数值类型与布尔型。数值类型分为整型和浮点型。整型包括:byte、short、int、long、char;浮点型:float、double;布尔型boolean。之所以将char认为是整型是因为char在JVM就是以无符号整型存在的。(2).事实上Java中除去这8种以及对象类型,还有一种比较特殊的类型存在,那就是Void。java.lang.Void,是一个占位符类,不可实例化,保存着Java关键字void的Class对象。为什么说它特殊呢?明明是一个类,难道不是对象类型?那是因为void.class.isPrimitive()(这个方法是用来判断一个Class对象是否是基本类型的)返回的是true,所以Void也算是基本类型的一个了(错了),只不过它比较特殊,不能算是一种数据,只是一种象征。20160921 改:上面弄错了,把Void和void两个混为一体了,事实上,可以简单的把这两者的关系看成类似包装类和基本类型的关系,像Integer和int的关系,java.lang.Void是一个不可实例化的占位符类来保存一个引用代表了Java关键字void的Class对象:
public static final Class<Void> TYPE = Class.getPrimitiveClass("void");而Integer也有类似的语句:
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");区别只是,Void仅仅是为void服务,即所谓的占位符类,不做他用。所以Void类只是一个普通类,而void则可以认作为如同int一样的基本类型。
2.引用数据类型
也称对象变量类型,复合数据类型,包含类、接口、数组(除了基本类型外,就是引用类型)。引用类型与基本类型最大的区别在于:
int a = 5;//这里的a是对象(严格来说不算是对象,只是个符号标识),5是数值 Integer a = 5;//这里的a是一个引用,5才是一个对象,更形象常见的是: Object o = new Object();//o是引用(栈中),new Object()是对象(堆中) //第二行代码中,5被自动包装成Integer对象这里的引用有点像C/C ++中的指针,但是同指针不同的是,你不能通过改变它的值从而去改变它所指向的值。即
ClassA p = new ClassA();//C++中,这个时候是可以这样操作的: p = p + 1;//向前移动一个单元,Java则不能 //这种操作,其实是对内存直接的操作,很显然,Java是不允许程序员做这种操作的其实质就是,Java的引用不支持对内存直接操作,而指针则可以,所以,Java用起来更安全,但不够灵活,而指针,自由度大,但同时,要更加小心因为指针操作不当而引起的各种内存问题。在Java中,任何对象都需要通过引用才能访问到,没有引用指向的对象被视为垃圾对象,将会被回收。 引用,其实质同指针一样(可以理解为受限制的指针),存放的是一个地址,至于是实例对象的地址,还是一个指向句柄池的地址(这里可以参考:(3) Java内存结构),完全是看各个JVM的实现了。 Java中的枚举类型,都是Enum类的子类,算是类中的一种,也是引用类型。 引用类型又称为对象变量类型,是相对于基本数据类型来说的(基本数据类型不是对象),而又被称为复合数据类型,可以这样理解,引用类型的数据最终都是由基本数据类型构成的。而像接口,接口是不能实例化的,最终的实现还是由类实现的;数组在JVM中的实现也是通过类实现的,每个类型的一维数组,二维数组……都是一个类,只是这是一个特殊的类,它的对象头有别于一般对象的对象头(最主要的就是,数组对象头有对象长度)
3.变量的作用域
规定了变量所能使用的范围,\只有在作用域范围内变量才能被使用*。根据变量*声明地点的不同*,变量的作用域也不同。根据*作用域的不同*,一般将变量分为不同的类型:*类变量、局部变量、方法参数变量及异常处理参数变量**。下面对这几种变量进行详细说明。**
4.变量类型
类变量
类变量也称为成员变量,声明在类中,不属于任何一个方法,作用域是整个类。
例 1:假设在一个类中声明了 3 个变量,下面编写一个测试类输出引起变量的值改变的示例代码。变量声明,实现代码如下所示:
测试类代码如下所示:
运行结果如下:
name=null
num=0
price=100
在第一段代码中3 个成员变量,并对其中第一个变量 price 进行了初始化,而第二个 name 变量和第三个变量 num 没有进行初始化。由输出结果可以看出,第一个变量的值为显示初始化的值,第二个和第三个变量的值则为系统默认初始化的值。
局部变量
局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。
例 2:声明两个局部变量并输出其值,实现代码如下:
s=3
a=7
a=7
方法参数变量
\作为方法参数声明\的变量的作用域是整个方法。**
例 3:声明一个方法参数变量,实现代码如下:
testFun() 方法,该方法中包含一个 int 类型的参数变量 n,其作用域是 testFun() 方法体内。当调用方法时传递进了一个参数 3,因此其输出控制台的 n 值是 3。
异常处理参数变量
异常处理参数变量的作用域是\在异常处理块中,该变量是将异常处理参数传递给异常处理块,与方法参数变量类似**。**
例 4:声明一个异常处理语句,实现代码如下:
在上例中定义了异常处理语句,异常处理块 catch 的参数为 Exception 类型的变量 e,作用域是整个 catch 块。
【文章原创作者:武汉网络公司 http://www.wh5w.com提供,感恩】