Java数据类型

Featured image

基本数据类型

名称 short int long float double char byte bool
字节 2 4 8 4 8 2 1 1
包装类 Short Integer Long Float Double Character Byte Bool
范围 -2^15~2^15-1 -2^31~2^31-1 -2^63~2^63-1 2^-149~2^(2^7) 4.9E-324~2^1024 0~2^16-1 -128~127 -

小数存储
float存储:一共32位,第一位表示符号位,标识是正数还是负数;第2~9位表示以2为底的指数部分;之后的23位表示小数部分; 所以float整数部分最大值为2^(2^7);
double存储:第一位表示符号位,之后的11位表示以2为底的指数部分,之后52位表示小数部分;

精度问题:2^23 = 8388608 所以float最多可以有7位有效数字,但是可以保证精度的只能有6位;
double:2^52 = 4503599627370496,一共16位,最多保证16位有效数字,能够保证精度的只有15位。

System.out.println(Float.MIN_VALUE + ", " + Math.pow(2, -149));//1.4E-45, 1.401298464324817E-45<br> System.out.println(Float.MAX_VALUE + ", " + Math.pow(2, Math.pow(2, 7)));//3.4028235E38, 3.4028236692093846E38

数据类型的转换
从小范围类型转换为大范围类型,可以自动进行转换;从大范围类型到小范围类型,需要显示转换;

基础数据类型和封装类的区别
① 基础类型不是对象,封装类型是对象,继承自Object;在需要转换的时候,java可以自动转换(自动装包和拆包);封装类解决了使用泛型的时候,要求必须是对象(如list的元素必须是对象)问题;
② 函数参数传递上,基础类型是值传递,也就是将自身拷贝一份,传递到函数中;封装类型传递的引用,是将对象的引用拷贝了一份,传递进去;
③ 默认值的区别,封装对象的默认值是null,基础类型数据有各自对应的默认值
④ 封装类型放在堆上,基础类型可能放在栈上,也可能放在堆上;基础类型存取更高效。
⑤ 比较的时候,基础类型使用 == 而引用类型应该使用equals

int a = 999;int b = 999;
Integer A = 999;Integer B = 999;
System.out.println(a == b); //true
System.out.println(A == B); //false
System.out.println(A.equals(B));//true
特别说明:java 对小于128的数据进行了缓存,所以如下结果:
>Integer i1 = 127;Integer i2 = 127;
Long l1 = 127L;Long l2 = 127L;
Byte b1 = 127;Byte b2 = 127;
Character c1 = 127;Character c2 = 127;
//以下均输出true<br>
System.out.println(i1 == i2); 
System.out.println(l1==l2);
System.out.println(b1==b2);
System.out.println(c1==c2);

装包与拆包

Set<Short> set = new HashSet<>();
for (short i = 0; i < 100; i++) {
    set.add(i);
    set.remove(i - 1);//这里会将i-1的结果装包为Integer,所以不会移除
}
System.out.println(set.size());

Object

除了基本类型之外,所有类都继承于Object类;

① registerNatives方法,这个方法由static和native修饰;所以方法是由本地实现的,主要功能是:完成某些方法由java到本地实现的映射,比如:hashCode、clone、wait/notify等;其他类中(如System类)也有这个方法,功能大致类似,都是完成由java到本地方法的映射,但具体映射的方法不一样。

②getClass方法

③hashCode方法(native修饰)

④equals方法,默认是用等于实现,比较的是地址

⑤toString方法,默认实现getClass().getName() + “@” + Integer.toHexString(hashCode());

⑥wait/notify/notifyAll方法

⑦finalize方法 GC在回收对象之前会调用这个方法;protected修饰,子类可以重写实现资源回收;但是一般不建议重写,因为重写之后,这个方法是需要放到一个队列里面执行,有可能会等待,导致垃圾回收延迟。方法默认实现是{},即不执行任何操作,所以不重写,则不会进入队列。

String

String使用char数组存储数据,使用final修饰,不可继承。

需要区分字符串常量和字符串对象,字符串常量存储在一个常量池中,字符串对象存在于堆上;字符串对象提供的方法如replace、concat、subString等,都是返回一个新的对象,所以原始对象也不会改变,所以字符串对象创建之后就不会改变。个人理解字符串常量池中的字符串常量也是字符串对象,与所谓的字符串对象的区别在于存储位置和垃圾回收上。

String对象和常量设计为不可变的原因是对象和常量的创建都是需要消耗空间和时间的,而String对象使用非常频繁,所以为了优化性能设计成不可变的。(但是创建字符串时,也可能带来一定的查找成本)

对于 String c = new String("str") 认为是c指向堆上的一个String对象,而堆上的String对象引用了常量池中的str常量。

常量相加,在编译时就可以确定String str = "a" + 1 这时 str=="a1" //true;如果是int a=1;String str = "a" + a,这时涉及到变量a,str编译期不能确定值,所以str是指向堆上的对象。str=="a1"//false;但是如果变量a被final修饰,则结果为true

intern()方法:大概意思是如果常量池中包含一个与当前实例相等的字符串,则返回这个字符串的引用;如果不包含,则将此对象加入到常量池中,然后返回这个对象(堆上的对象)的引用。从这里来看,貌似说明堆上的String对象未必能在常量池中找到与之相应的字符串常量。

关于+操作:首先将最左边的字符串对象转为StringBuilder对象,然后使用append进行连接操作,最后调用StringBuilder的toString方法,转成String对象。

String类中的常用方法:

length、isEmpty、charAt、getChars、getBytes、compareTo、startWith、endWith、indexOf、lastIndexof
substring、concat、replace
equalsIgnoreCase、toLowerCase、toUpperCase、toCharArray
静态方法:join(jdk8新增)、format、valueOf

其他

关于三元运算符:

Object i = 1 == 1 ? new Integer(3) : new Float(1);
/*
 * 双目数值提升:
 * 1.如果定义了数据类型的变量和未定义数据类型的变量参与双目运算符的后双目运算,那么返回的结果就是范围大(精度高)的类型。
 * 2.如果两个定义了数据类型的变量参与双目运算符的后双目运算,那么返回的结果就是范围大(精度高)的类型。
 * 3.如果直接进行数值的比较,则自动转型为范围大(精度高)的类型。
 */
System.out.println(i);//输出3.0;