java的数据类型及类型的转换

2,387 阅读7分钟

基本类型之间的转换

基本类型

java提供了8种基本类型

  • 整数类型:byte,short,int,long
  • 浮点类型:float,double
  • 字符类型:char
  • 布尔类型:boolean

包装类型

包装类型是和8种基本类型一一对应的。因为基本类型的数据不具备对象的特点,基本类型的包装类的目的就是让基本类型具有对象的特点和方法,打通基本类型与其他数据类型转换的通道。

基本数据类型 二进制位数 包装类
boolean 1 Boolean
byte 8 Byte
char 16 Character
short 16 Short
int 32 Integer
long 64 Long
float 32 Float
double 64 Double

所有的包装类都是final修饰的,也就是它们都是无法被继承和重写的。

基本类型与包装类型的异同

  1. 在Java中,一切皆对象,但八大基本类型却不是对象。
  2. 声明方式的不同,基本类型无需通过new关键字来创建,包装类型需new关键字。
  3. 存储方式及位置的不同,基本类型是直接存储变量的值保存在堆栈中能高效的存取,包装类型需要通过引用指向实例,具体的实例保存在堆中。
  4. 初始值的不同,包装类型的初始值为null,基本类型的的初始值视具体的类型而定,比如int类型的初始值为0,boolean类型为false;
  5. 使用方式的不同,比如与集合类合作使用时只能使用包装类型。
    //正确
    List<Integer> a=new ArrayList<>(); 
    //错误
    List<int> a=new ArrayList<>(); 
    

包装类的继承关系

基本类型的相互转换

基本类型转基本类型

如果表示范围宽的基本类型转成表示范围小的基本类型使用强制类型转换(int)(long)...等等

long a=10;
int b = (int) a;

如果表示范围小的基本类型转成表示范围大的基本类型不需要加强制转换

long a=10000000;
double c= a;

char转换成int将直接返回ASCII的十进制代码

char a='a';
int b=a;
System.out.println(b);
//输出97

基本类型转包装类型

在 JDK1.5 引入自动装箱和拆箱的机制后,包装类和基本类型之间的转换就更加轻松便利了。
装箱:把基本类型转换成包装类,使其具有对象的性质,又可分为手动装箱和自动装箱
基本类型转自身包装类型,直接复制或者new一个包装类

int a=1;
Integer b=a; // 自动装箱
Integer c=new Integer(a); //手动装箱

基本类型转其他包装类型

int a=1;
//将基本类型转成包装类型的基本类型
Long b=(long)a;
//使用包装类型的方法接收基本类型参数
Long c= Long.valueOf(a);

包装类型转基本类型

拆箱:和装箱相反,把包装类对象转换成基本类型的值,又可分为手动拆箱和自动拆箱

Integer a=10;
int b = a.intValue(); //手动拆箱
int e = a; //自动拆箱

基本类型转String

如果是基本类型String.valueOf(value)方法

int a=10;
String b=String.valueOf(a);
System.out.println(b.getClass().getSimpleName() instanceof String);
// 输出:true

如果是基本类型的包装类型,还可以使用value.toString()方法

Integer a=10;
String b=a.toString();
String c=String.valueOf(a);
System.out.println(b.getClass().getSimpleName() instanceof String);
System.out.println(c.getClass().getSimpleName() instanceof String);
// 输出:true

包装类型的toString()方法是如何工作的?查看SDK的源码如下:

...
public String toString() {
    return toString(value);
}
...
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);
}
...
static int stringSize(int x) {
    for (int i=0; ; i++)
        if (x <= sizeTable[i])
            return i+1;
}

通过sdk的源码发现toString()方法最后返回一个string对象

String转基本类型

String a="10";
int f= Integer.parseInt(a);
Integer b=Integer.valueOf(a); // 手动拆箱
int c=b.intValue(); // 自动拆箱

String无法直接转换成基本类型需要借助基本类型的包装类型作为桥梁。
那么Integer.parseInt(value)Integer.valueOf(value)方法有什么不同?查看SDK源码:

// 返回int的包装类型
public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}
// 直接返回int类型
public static int parseInt(String s) throws NumberFormatException {
    return parseInt(s,10);
}
....

一个直接返回基本类型,一个返回基本类型的包装类型。
string在转换基本类型的时候还可以制定基数,就是从二进制转10进制还是16进制转10进制等等...:

String a="101010";
String b="1b";
int c= Integer.parseInt(a,2);
Integer d=Integer.valueOf(b,16);

System.out.println(c);
System.out.println(d);
// 输出:42,27

数组与集合之间的转换

数组初始化

动态初始化

String[] stringArray = new String[3];
stringArray[0] = "one";
stringArray[1] = "two";
stringArray[2] = "three";

静态初始化

String[] stringArray2 = {"one", "two", "three"};

无论那种初始化方式一旦分配内存就无法扩容

集合初始化

无参初始化完成后可动态扩容,动态扩容的时候需要付出被动扩容和数组复制的额外开销,因此建议集合初始化的时候指定初始值大小,如果暂时无法确定就指定相应的默认值。 ArrayList的源码:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 默认初始容量
    private static final int DEFAULT_CAPACITY = 10;
    ......
    // add添加元素
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    ......
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    ......
    // 判断是否需要扩容,如果当前元素个数超过容量就调用grow()进行扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    ......
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // >> : 右移运算符,num >> 1,相当于num除以2。在原来的基础上扩容50%
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // Arrays的copyOf()方法传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组。
        // 第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
.......
}

从源码可以看出ArrayList默认的容量是10个元素,在动态扩容的时候会产生一个新的数组然后将原来的数组拷贝进去, 其他集合如HashMap的动态扩容也是类似的。 推荐的集合初始化方式:

// 知道元素的个数直接使用带参构造
List<String> a = new ArrayList<>(3);
// 如果对象具备size或者length等对象方法,可以使用对象结果的size()方法返回对象的数量,如mysql的查询结果
....
int listSize = total.size();
index_list = new ArrayList<>(listSize);
....

不推荐使用无参构造创建集合对象

数组转集合

数组转集合使用Arrays.asList(),转换后可以使用集合的方法修改元素的值,但是不能修改其长度,Arrays.asList()体现的是适配器模式后台数据依然是数组。

import java.util.Arrays;
....
String[] stringArray = new String[3];
stringArray[0] = "one";
stringArray[1] = "two";
stringArray[2] = "three";
// 数组转集合
List<String> stringList = Arrays.asList(stringArray);
// 转换后修改
stringList.set(0, "oneList");
// 数组转集合后可以修改内容,但是不能改变其长度,以下三行编译正确,但都会抛出运行时异常
stringList.add("four");
stringList.remove(2);
stringList.clear();

集合转数组

相对于数组转结合来说,集合转数组更加可控。可以理解为数据从一个相对自由的容器转为相对严格的容器,在数据长度不变的情况下求弃掉一些修改数据的方法,数据反而变得简单。

List<String> list = new ArrayList<String>(3);
list.add("one");
list.add("two");
list.add("three");

// 泛型丢失,无法使用 String[] 接收无参方法的返回结果,所以只能使用Object[]表示类型更加宽对象接收
Object[] array1 = list.toArray();
list.toArray(array1);
System.out.println(Arrays.asList(array1));

// array2 数组长度小于元素个数,容量不够弃用此数组
String[] array2 = new String[2];
list.toArray(array2);
System.out.println(Arrays.asList(array2));

// array3 数组长度等于元素个数
String[] array3 = new String[3];
list.toArray(array3);
System.out.println(Arrays.asList(array3));

// array4 数组长度大于元素个数
String[] array4 = new String[4];
list.toArray(array4);
System.out.println(Arrays.asList(array4));

打印结果

//array1
[one, two, three]
//array2
[null, null]
//array3
[one, two, three]
//array4
[one, two, three, null]