java中的数据类型以及包装类

148 阅读13分钟
一、基本数据类型的系统描述:
1.
总述:
Java
基本数据类型分为两大类:
boolean
类型和数值类型。数值类型可分为整数类型和浮点类型,而其中字符类型可单独对待。所以
Java
只包含
8
种基本数据类型。
!注意!字符串不是基本数据类型,字符串是一个类,是一个引用类型。这个在下一篇我们会仔细讨论它!
boolean
数值只有
true
false
,不能用
0
代替。其他数值类型不能转换成
boolean
。包装类–
Boolean
byte
内存
8
位,无符号位时最大存储
255
,表数范围:
-128~127
。包装类–
Byte
short
内存
16
位,无符号位时最大存储
65536
,表数范围:
-32768~32767
。包装类–
Short
int
内存
32
位,无符号位时最大存储
2
32
次方减
1
,表数范围:负的
2
31
次方到正的
2
31
次方减
1
。包装类–
Integer
long
内存
64
位,无符号位时最大存储
2
64
次方减
1
,表数范围:负的
2
63
次方到正的
2
63
次方减
1
。包装类–
Long
float
内存
32
位,数据范围在
3.4e-45~1.4e38
,直接赋值时必须在数字后加上
f
F
。包装类–
Float
double
内存
64
位,数据范围在
4.9e-324~1.8e308
,赋值时可以加
d
D
也可以不加。包装类–
Double
char
16
位,存储
Unicode
字符集,用单引号赋值。可以参与加减乘除运算的,也可以比较大小的!!包装类–
Character
二、数据类型的包装类理解(含部分源码解析)
首先要知道为什么
Java
会为每一个基础数据类型都提供一个相应包装类的目的,在于将
Java
的所有东西都抽象成对象,可以更方便的控制和使用。这就是面向对象!
然后对于包装类,主要作用是:
1.
作为和基本数据类型对应的类类型存在,方便涉及到对象的操作。
2.
包含每种基本数据类型的相关属性如最大值、最小值等,以及相关的操作方法。
下面我们将一起讨论下包装类中的重要源码!!
1.
深入
boolean
基本类型、
Boolean
类以及细节点:
//
看接口是可序列化,是一个
final
修饰的类
public final class Boolean implements java.io.Serializable,
Comparable<Boolean>{
//
看这两个对应的原始对象。享元模式的使用,达到多个对象都使用一份内存。至于什么是享元,以及它与单例的区别,这里就不多说了。
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
private final boolean value;
//
两个构造器,可见它是可以为
null
的啦,使用
Boolean
这个类的话
public Boolean(boolean value) {
this.value = value;
}
public Boolean(String s) {
this(parseBoolean(s));
}
public static boolean parseBoolean(String s) {
return ((s != null) && s.equalsIgnoreCase("true"));
}
//jdk
文档建议用
valueOf
代替
new
方式来创建
Boolean
类对象。
new
创建的
Boolean
对象是不断的新创建一个实例对象,而
valueOf
则是返回
Boolean
类里的静态成员变量
,
也就是使用享元模式的那个对象。
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
//
下面是令人困惑的设计了,我也是看了下
stackoverflow
里面讨论才有点懂。
//
原汁原味链接:
http://stackoverflow.com/questions/3912303/boolean-hashcode
//1.
使用质素是因为假如要把
Boolean
指插入到
hashtable
中,如果不是质素的话可能会比较容易造成哈希冲突。符合对象计算
hashcode
的时候通常会把各个属性的
hashcode
相加然后再做
hash,
如果是比较小的质素,容易造成
hash
分布不均匀。
//2. Maps
是可以包裹
Boolean
的,而如果
map
除了包含
Boolean
对象,还包含其他对象,那么如果不适当处理,就很容易有冲突了
public static int hashCode(boolean value) {
return value ? 1231 : 1237;
}
//
好恐怖和缜密的源码设计。
}
总括下:
1.boolean
是基础数据类型,而
Boolean
是一个类。
2.boolean
一般存在于桟空间中,而
Boolean
对象存在堆空间中。
3.boolean
true
false
俩种值,
Boolean
除了
true
false
外,还有
null
下面我们看份代码:(解析在代码中)
public class Main {
public static void main (String []args)
{
Boolean bool1 = Boolean.valueOf(true); //
这里均使用
valueof
创建对象,
new
创建的
Boolean
对象是不断的新创建一个实例对象,而
valueOf
则是返回
Boolean
类里的静态成员变量
Boolean bool2 = Boolean.valueOf("True"); //
这里上一句代码验证使用
String
变量作为参数时,不区分大小写的。
Boolean bool3 = Boolean.valueOf("ASD");
boolean x1 = bool1.booleanValue();
boolean x2 = bool2.booleanValue();
System.out.println("bool1:" + x1 + ",bool2:" + x2 + ",bool3:" + bool3);
boolean x3 = bool1.equals(bool2); //
这个就是验证享元模式,使用的是同一个对象
boolean x4 = bool1.equals(bool3); //
肯定不是同一对象啦。
System.out.println("bool1.equals(bool2):" + x3 + ",bool1.equals(bool3):" + x4);
String str1 = Boolean.toString(bool1); //
可见
Boolean
对象是可以转换成字符的
String str2 = Boolean.toString(false);
String str3 = bool3.toString();
System.out.println("bool1:" + str1 + ",str2:" + str2 + ",bool3:" + str3);
boolean x5 = Boolean.parseBoolean("ASD"); //
源码是直接判断然后与
true
对比,因此打印为
false
System.out.println(x5);
}
}
2.
深入
byte
基本类型
先来份
Byte
源码
//
也可以看到是一个
final
修饰的类,只能用,不能被继承咯
public final class Byte extends Number implements Comparable<Byte>{
public static final int SIZE = 8; //
只能是一个字节咯
//
两个构造器
public Byte(byte value) {
this.value = value; //
传入的要为
Byte
类型的值
}
public Byte(String s) throws NumberFormatException {
this.value = parseByte(s, 10); //
传入的要求是可转换成
Byte
的字符串
}
//
这个
Byte
做了缓存
private static class ByteCache {
private ByteCache(){}
static final Byte cache[] = new Byte[-(-128) + 127 + 1];//
声明缓存数组的长度为
256
static {
for(int i = 0; i < cache.length; i++)
cache = new Byte((byte)(i - 128));//
然后将
-128~127
进行缓存
}
}
//
两个解析字符串方法
public static byte parseByte(String s, int radix)
throws NumberFormatException {
//radix
是外汇返佣
http://www.kaifx.cn/
解析字符串时候的基数,在此方法下有个解析基数的含义。
int i = Integer.parseInt(s, radix);//
解析字符串并返回,所以
s
必须是
-128~127
的字符,至于为什么用这个方法
int
的包装类方法来解析,一会我们会谈到。
if (i < MIN_VALUE || i > MAX_VALUE)
throw new NumberFormatException(
"Value out of range. Value:\"" + s + "\" Radix:" + radix);
return (byte)i;
}
//
也是解码转码方法,将
String
转为
Byte
public static Byte decode(String nm) throws NumberFormatException {
int i = Integer.decode(nm);//
一会重点讲解
Integer
的系列方法
if (i < MIN_VALUE || i > MAX_VALUE)
throw new NumberFormatException(
"Value " + i + " out of range from input " + nm);
return valueOf((byte)i);
}
}
解释
radix
的作用
b[0] = Byte.parseByte(
11
, 2) = 3
表示
字符串
11
2
为基数表示为
10
进制的
byte
值是
3
,这里的
11
表示的是一个
2
进制数
b[0] = Byte.parseByte(
11
, 3) = 4
表示
字符串
11
3
为基数表示为
10
进制的
byte
值是
4
,这里的
11
表示的是一个
3
进制数
//
另外这样使用
byte
是不会报错的:
byte=55;
//
因为会自动把
56
当成
byte
类型去处理的了。
3.
就是重点的
int
Integer
先吃份源码
Integer
解析套餐
public final class Integer extends Number implements Comparable<Integer> {
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");//
原始类型
int
Class
实例。
//
所有可能的将数字表示为字符串的字符集合做缓存。
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
'o' , 'p' , 'q' , 'r' , 's' , 't' ,
'u' , 'v' , 'w' , 'x' , 'y' , 'z'
};
//
两个构造器
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);//
涉及了
String
转换成
int
,一会仔细讨论这个。
}
//
像上面
Byte
类型中解释的那样的方法,返回第二个参数所指定的进制数的第一个参数的字符串表示形式。处理各种进制的
Integer.
public static String toString(int i, int radix) {
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10;//
默认为
10
进制
/* Use the faster version */
if (radix == 10) {
return toString(i);
}
char buf[] = new char[33];
boolean negative = (i < 0);
int charPos = 32;
//
统一转为负数去处理
if (!negative) {
i = -i;
}
while (i <= -radix) {
buf[charPos--] = digits[-(i % radix)];
i = i / radix;
}
buf[charPos] = digits[-i];
if (negative) {
buf[--charPos] = '-';
}
return new String(buf, charPos, (33 - charPos));
}
//
一会有事例代码演示这个,这个其实就是把
int
型包装成
Integer
然后再转化成
String
字符串
public static String toString(int i) {
if (i == Integer.MIN_VALUE)
return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
//
toString
组合形成一方法去转换成字符串咯
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) { //
如果
i
为负数,则设置
i
的符号字符为
'-'
sign = '-'; //
确定正负数
i = -i; //
将负数转化为正数处理,提高效率
}
// Generate two digits per iteration
while (i >= 65536) { //
如果
i
大于
65536
,则每一次都获取十位和个位上的数字对应的字符。将值判断大小后取每个数字,较大的数字一次取两位(大数字运算消耗大)
q = i / 100;
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2)); //
利用位运算,每次获得
i
的最后两位数,不断循环提取处理
i = q;//
重新赋值,准备下一次循环
buf [--charPos] = DigitOnes[r]; //
存储
r
中在个位数集合中对应的字符
buf [--charPos] = DigitTens[r]; //
存储
r
中在十位数集合中对应的字符
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) { //i<65536
的情况,小数字运算消耗较小,故一次只取一位
q = (i * 52429) >>> (16+3);//52429/(2*19)
约等于
1
,此处这样设计是为了提高精度
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... //
每次获得
i
的最后两位数
buf [--charPos] = digits [r];//
取最后一位的数字
i = q;//
重新赋值,准备下一次循环
if (i == 0) break;
}
if (sign != 0) {
buf [--charPos] = sign; //
设置符号
}
}
//
下面两个是用来确定字符串长度的。
//
定义
sizeTable
表示
int
中每个位数中最大的数,用于简便确定
int
数的长度。
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
//
使用上面的
sizeTable
定义来确定
int
数的字符串表示长度。
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable)
return i+1;
}
//
炒鸡重要的方法啦!!
parseInt(String s,int radix)
使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
parseInt(String s)
只能将数字字符串转化十进制数
public static int parseInt(String s, int radix)
throws NumberFormatException
{
/*
* WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use
* the valueOf method.
*/
if (s == null) {//
参数检验,调用方法前检查参数的正确性。
throw new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
int result = 0;
boolean negative = false;
int i = 0, len = s.length();//i
表示当前遍历的
s
的位数
int limit = -Integer.MAX_VALUE;//
设置最小值为负的
Integer
的最大值
int multmin;
int digit;
if (len > 0) {//
如果字符串长度大于
0
,则进行转换
char firstChar = s.charAt(0);//
获取第一位字符
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {//
判断是否为负数
negative = true;
limit = Integer.MIN_VALUE;//
将限制转换为
Integer
的最小值,不能小于
Integer
的最小值
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);//
第一个
char
不为
+
也不为
-
,则抛出异常
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);//
若只有一个符号,则抛出异常
i++;
}
multmin = limit / radix;//
设定不同进制下的极限值
while (i < len) {//
进行进制的转换
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);//
将数字字符串转换成要求的进制数,使用工具类,每次遍历对一个字符进行操作转换
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;//
根据符号返回正数还是负数
}
//
看吧,我们经常用的
parseInt
只是个帮我们制定好
10
进制规则的静态方法
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
//
强大的内部类缓存机制吗,内部字符缓存类
private static class IntegerCache {
//
缓存的下界,
-128
,不可变
static final int low = -128;
//
缓存上界,暂为
null
static final int high;
static final Integer cache[];//
利用数组来缓存
//
原理:初始化数组将一定范围的整数放到
cache
数组中,然后在调
valueOf
方法的时候首先判断范围然后从缓存数组中去抓取数据
static {
// high value may be configured by property
//
缓存上届,可以通过
JVM
属性来配置
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
//
获取
,
得到上界
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
//
获取
Integer
中所有能保存的数据,初始化缓存数组
cache = new Integer[(high - low) + 1];
int j = low;
//
缓存所有
Integer
的数据
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
//
还有这个我们经常用的,官方也推荐使用这个方法去创建对象的
public static Integer valueOf(int i) {
//
如果
i
Integer
缓存中,则直接取出
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
//
否则,直接创建一个实例
return new Integer(i);
}
}
使用
Integer
事例代码:
//
会打印出
10
这个字符串
public static void main (String []args) {
Integer i =null;
i = Integer.valueOf(10);
System.out.println(">>>>"+i.toString());
}
public static void main(String[] args) {
Integer a1 = 1;
Integer a2 = 1;
Integer b1 = 200;
Integer b2 = 200;
Integer c1 = Integer.valueOf(1);
// Integer c2 = new Integer(1);
官方不推荐这种建对象的方法喔
Integer c2 = Integer.valueOf(1);
Integer d1 = Integer.valueOf(200);
Integer d2 = Integer.valueOf(200);
System.out.println("a1==a2?" + (a1 == a2));
System.out.println("b1==b2?" + (b1 == b2));
System.out.println("c1==c2?" + (c1 == c2));
System.out.println("d1==d2?" + (d1 == d2));
}
上面一段代码的运行结果就是我们要深思的东西啦,也是结合源码要懂的东西。
a1==a2? true
b1==b2? false
c1==c2? false
d1==d2? false
第一个为什么是
true
呢,因为
Integer
的缓存机制嘛,刚刚我们看到的,缓存了
[-128,127]
,这些可以直接取出。而剩余的为什么是
false
,因为他们都超过了缓存的那个范围,就建了个新对象咯。
至于
short
float
double
这类的包装类设计原理有的跟
Integer
差不多,但是比如
Double
,很难去阅读,感觉自己程度还不够,以后会补上。而
Character
这个包装类,,源码
8000
多行,我们就讨论它的一些基本知识吧。
4.Character
的基本了解:
Character
类在对象中包装一个基本类型
char
的值。
Character
类型的对象包含类型为
char
的单个字段。该类提供了几种方法,以确定字符的类别(小写字母,数字,等等),并将字符从大写转换成小写,从小写转换成大写。
Character
类的方法和数据是通过
UnicodeData
文件中的信息定义的。至于
Unicode
大家就百度了解下就好。

更多技术资讯可关注:itheimaGZ获取