八个基本数据类型byte,char,short,int,long,double,float,boolean,对应的包装类位于java.lang包下面。只有对数据类型更好的了解,才能更高效的使用,更得心应手。本文通过整体分析来了解八个包装类和一个字符串类String,分析类设计共性,几个主要方法,并深入方法的源码,探索怎么实现的。
包装类设计共性
一、静态工厂方法 static valueof()
通过阅读源码或者jdk文档就可以知道,大部分包装类都以静态常量(static final)或者静态类(static class)的方式存放着本身的实例。并且每个包装类都有一个静态工厂方法valueof(),用来创建对象。 即使他们都有几个构造器,但我不建议使用,如果你知道valueof()的实现方式的话,你也会有同感。valueof()会判断要创建的实例是否已经存在,如果存在就直接使用。通过这个方法就可以避免创建出多余的对象造成内存和时间浪费。
例如boolean的包装类Boolean ,用两个常量TRUE,FALSE分别存放他的两个实例。
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);
valueof()的实现则在此的基础上,使用这个方法,都可以不用再创建任何对象了。(Boolean比较特殊,只有两个实例)
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
接下来,让我们看看byte类型的包装类,Byte,我们都知道 byte的范围[-128, 127], 所以Byte的所有实例总共有128+127+1种,如果直接用静态常量来接收,那整个类不就会显得臃肿,所以,Byte类采用静态类的方式缓存,并使用静态代码块的方式一次性加载,并只缓存一次。
public final class Byte extends Number implements Comparable<Byte> {
/**
* A constant holding the minimum value a {@code byte} can
* have, -2<sup>7</sup>. 最小值
*/
public static final byte MIN_VALUE = -128;
/**
* A constant holding the maximum value a {@code byte} can
* have, 2<sup>7</sup>-1.最大值
*/
public static final byte MAX_VALUE = 127;
// 私有静态类,缓存所有Byte实例
private static class ByteCache {
private ByteCache(){}
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
// 当 i=0, cahe[0] = -128, cahe[1] = -127..... 以此类推
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
}
}
如何发挥上面这块代码的作用呢? 如果你还在用这样的方式 new Byte((byte)120); 来创建对象的话,这静态类就没有存在的意义了。valueof()致力于创建对象时减少内存的消耗。下面Byte类的这个方法实现:这一看出,valueof()不会创建新的对象。完全从缓存类中返回。cache[] = {-128,-127-126,,... 0 ,1,2,... ,125,126,127}
/**
* @param b a byte value.
* @return a {@code Byte} instance representing {@code b}.
* @since 1.5
*/
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
其他的包装类 Character ,缓存示例的个数也是char的范围。
private static class CharacterCache {
private CharacterCache(){}
static final Character cache[] = new Character[127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Character((char)i);
}
}
静态工厂方法,会判断,如果在实例范围内,则直接从缓存中拿,超过范围在创建新对象:
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
此外,另外五个包装类,也都是类似的设计,都是为了高效的创建对象,减少内存消耗。
------2018,-3-19, 补充:
稍微看了一下两个浮点数 double, float,对应的类。Double,Float 。这两个的设计和其他的不同,就算使用valueof方法,也是直接创建新对象,而并有事前已经创建好的对象。
public static Double valueOf(String s) throws NumberFormatException {
return new Double(parseDouble(s));
}
public static Float valueOf(String s) throws NumberFormatException {
return new Float(parseFloat(s));
}
二、作为数据类型,基本都重写了,equals(), hashCode(), 方法 ,另外,因为都实现了Comparable接口,所以都重写了他的比较方法 compareTo()方法。 但我感觉这里的这两个方法实现有点简单,如果作为集合元素,需要排序时,可能又要重写。
Byte类:
public static int hashCode(byte value) {
return (int)value;
}
public boolean equals(Object obj) {
if (obj instanceof Byte) {
return value == ((Byte)obj).byteValue();
}
return false;
}
Integer类
public boolean equals(Object obj) {
if (obj instanceof Integer) { // 一定要做这个判断 ,object是不是Integer类型的
return value == ((Integer)obj).intValue();
}
return false;
}
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
// x 是当前对象, x.compareTo(y), x < y 返回 -1, 相等 返回0 , x>y 返回1
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
这些方法的实现都大同小异,这里就不一一列举了。下面我会根据自己看的情况,详细分析几个方法的实现过程,这几个方法的实现,也都会使用到另外几个类的方法。
几个重要方法
1、 parseXXX(String s, int radix ) 方法,
XXX表示基本数据类型(parseInt, parseByte, parseLong 等), 这个方法是用来: 根据radix 进制,把字符串转化为对应的数据类型。radix既然表示进制, 就说明他的2的幂数(2,8,10,16,32),通过观察,Integer类的这个方法比较有代表性,这里就使用Integet类的源码了。另外 两个浮点数类型的 这个方法的实现和长整数的不同,我后面再会补充。
首先看一下效果:
//使用第二个参数指定的基数,将字符串参数解析为有符号的整数。
public static void testParseInt(){
int i1 = Integer.parseInt("-111",2); // -7
int i2 = Integer.parseInt("111",4); // 21
int i3 = Integer.parseInt("111",8); // 73
int i4 = Integer.parseInt("111",10); // 111
System.out.println(i1+"--"+i2+"--"+i3+"--"+i4);
}
源码分析:
下面源码用到了Character 类的一个方法,digit(char ch, int radix), ,这个方法是用来检测字符窜中的字符是否为数字,并且是符合 radix 基数的数字。 如果基数不在 MIN_RADIX
<= radix
<= MAX_RADIX
范围之内,或者 ch
的值是一个使用指定基数的无效数字,则返回-1,否则返回本身。
public static void testDigst(){
String s = "-5";
int i = Character.digit(s.charAt(1),2);
System.out.println(i); // 结果 -1
// 基数(用进制比较好理解)如果字符串中的字符是非数字, 则返回-1,
// 如果字符串的字符不符合进制的表达式, 则返回-1, 例如: 上面,基数是2, 那么只能存在0
// 或者 1 这两个数字,(完全可以理解为,字符串中需要合法的对应的进制表示方式)
int i2 = Character.digit(s.charAt(1),8);
System.out.println(i2); // 结果 5 , 因为八进制可以用0 到7 之间的数来表示
s= "-a";
System.out.println(i); // 结果 -1, 因为是非数字
}
方法的大体思路:
1. 判断String 是否为空。空就抛出异常。
2.判断进制是否合理(radix), 不合理抛出异常。
3.对String中的 单个字符进行处理,判断正负, 如果是非数字, 抛出异常。
4.主要的计算过程: result = result*radix - digit
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");
}
// radix 最小值是2, 不能小于2 Character.MIN_RADIX=2
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
// radix 最大值32,
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
int result = 0;
boolean negative = false; // 用来判断值的正负 , false 是正, true是负
int i = 0, len = s.length(); // 获取字符窜长度
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
if (len > 0) {
char firstChar = s.charAt(0); // 字符串方法,获取对应下标的字符
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
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); // digit方法,
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;
}
2、static XXX XXXValue() , XXX 表示基本数据类型(double, int ,byte, char...),将某个类转化为对应的数据类型。
3、static xxx decode(String str), 将字符串转化为方法调用者类类型数据。
=====后面再补充2,3