基本数据类型和对应的包装类
在Java中, 基本数据类型不是继承自Object, 为了在泛型代码中可以支持基本类型, Java给每个基本类型都对应了一个包装类型.
简单来说就是让基本数据类型也能面向对象.基本数据类型可以使用很多方法, 这就必须让它变成类.
基本数据类型对定的包装类
基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
除了Integer 和 Character ,其余基本类型的包装类都是首字母大写
装包(箱)和拆包(箱)
把基本类型变为包装类型, 叫做装箱
把包装类型变为基本类型, 叫做拆箱
public static void main(String[] args) {
//自动装包, 内部其实是调用了 valueOf 方法来实现的
Integer a = 10;
int i = 99;
Integer b = i;
//基本类型 转变为 包装类型, 都是可以直接打印的
System.out.println(a);
System.out.println(b);
//显示装箱
Integer aa = Integer.valueOf(10);
}
public static void main(String[] args) {
Integer a = 10;
//自动拆箱, 内部调用了 intValue() 方法;
int i = a;
//拆包
int aa = a.intValue();
double b = a.doubleValue();
}
Java 包装类提供了很多方法, 可以自动调用很方便
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
System.out.println(a == b);
Integer a1 = 200;
Integer b1 = 200;
System.out.println(a1 == b1);
}
//代码上述的输出结果为: true 和 false
为什么呢?
如果出现这个错误第一时间想到了什么呢? 我想可以看一下 Integer 出现了什么错误, 然后呢? 在赋值的过程中出现了自动装箱, 自动装箱的函数是 valueOf , 所以可以在valueOf中寻找出错的点在哪里, 现在找到 valueOf 的源码, 看装包的过程哪一点出错了
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这个源码, 如果 i 在范围内就返回一个数组里面的值, 如果不在范围就返回 new Integer(i)
我们在源码中可以得到 low的大小为 -128, high 的大小为 127;
也就是 i 的范围在 -128 到 127 之间的话返回这个数组的某一个值
这个数组大小是多少呢? 范围是 [-128, 127], 共 256 个数字, 可以得出 下图 这个数组的样子
当 i 的范围在其之间可以返回相同的值, 如果超过了就返回一个 new 的值. 由此可出上述结果为什么是这样.
泛型
是Java中一种很难的语法
举个例子, 写一个类, 类包含一个数组成员, 让数组中可以存放任意类型的数据, 也能根据成员方法返回数组某个下标的值.
class MyArray {
Object[] array = new Object[10];
void setValue(int i, Object val) {
array[i] = val;
}
Object getValue(int i) {
return array[i];
}
}
public class Main {
public static void main(String[] args) {
MyArray myArray = new MyArray();
myArray.setValue(0, 1);
myArray.setValue(1, "hello");
//下面这一行代码报错, 因为返回值是 Object 类型,
//需要强转为 String 类型才可以
//String str = myArray.getValue(0); <---报错
String str = (String)myArray.getVlaue(0);
}
}
这我们就想了, 不能让什么数据类型都能放到这个数组中, 我们可以指定一个数据, 这样我们在接受数据的时候也不用看每一个接收的数据是什么类型了. 我们就引入了泛型.
泛型的语法
1>
class 泛型类名称 <类型形参列表> {
//可以使用类型参数
}
示例:
class ClassName<T1, T2, .... Tn>{
}
2>
class 泛型类名称<类型形参列表> extends 继承类 {
//可以使用类型参数
}
示例:
class ClassName<T1, T2, .... Tn> extends ParentClass<T1> {
//可以使用类型参数
}
类型形参列表可以用 T , E , K , V 等字母表示 其中<T, E, K, V...> 表示什么呢?
表示当前类 是一个泛型类 它只是一个占位符.
下面将上述代码改一下:
class MyArray <T>{
Object[] array = new Object[10];
void setValue(int i, T val) {
array[i] = val;
}
T getValue(int i) {
return (T)array[i]; // 把返回的类型,强转为指定类型
}
}
public class Main {
public static void main(String[] args) {
//这行代码强制了数据类型为Integer, <>里面只能放入包装类
MyArray<Integer> myArray = new MyArray<>();
myArray.setValue(0, 1);
//myArray.setValue(1, "hello"); <----当前代码报错了
//如果我们想要放入其它类型的数据怎么做呢? 在创建一个新对象就好了
MyArray<String> myArray1 = new MyArray<>();
int i = myArray.getValue(0);
System.out.println(i);
}
}
注意:
1> 因为使用了泛型, 在编译的时候 帮我们进行了类型的检查, 取数据也不需要进行类型转换
2> 在尖括号 <> 里面必须是引用类型.
3> 创建的一个数组的时候 不能写成 T[] array = new T[];
泛型的上界
语法:
class 泛型类名称 <类型形参 extends 类型边界>{
...
}
示例:
public class MyArray<T extends Number> {
...
}
其中的 T 必须是 Number 或者 Number的子类, 这就叫做泛型的上界
在创建对象时:
MyArray<Integer> a1 = new MyArray<>(); 可以
MyArray<Double> a1 = new MyArray<>(); 可以
MyArray<String> a1 = new MyArray<>(); 不可以
举例: 写一个泛型类, 求一个数组中的最大值
class Alg<T> {
public T findMaxValue(T[] array) {
T max = array[0];
for (int i = 1; i < array.length; i++) {
if(max < array[i]) {
max = array[i];
}
}
return max;
}
}
这段代码的 if 中的判断条件会出现报错, 为什么呢?
因为 T 是引用数据类型, 最终是被擦除为了 Object 类型, Object 没有实现了Comparable 接口, 所以不能直接比较大小, 要想能够比较大小, 就得让 T 实现 Comparable 接口, 实现了 Comparable接口后, 以后创建对象时, 只有实现了 Comparable 接口的才能创建
就比如:
MyArray<Integer> a1 = new MyArray<>(); 可以, 因为Integer 实现 Comparable 接口
class Preson { ... }
MyArray<Person> a1 = new MyArray<>(); 不可以 , 因为Person 没有实现 Comparable 接口
所以上述代码应该写成:
class Alg<T extends Comparable> {
public T findMaxValue(T[] array) {
T max = array[0];
for (int i = 1; i < array.length; i++) {
if(max.compareTo(array[i]) < 0) {
max = array[i];
}
}
return max;
}
}
public class Main {
public static void main(String[] args) {
Alg<Integer> alg = new Alg<>();
Integer[] integers = {1, 2, 3, 4, 5, 6};
Integer ret = alg.findMaxValue(integers);
System.out.println(ret);
}
}
写一个泛型方法:
如下列代码:
在这种情况下会出现 类型推导, 根据实参传值, 来推导出此时的类型.
class Alg2 {
public <T extends Comparable>T findMaxValue(T[] array) {
T max = array[0];
for (int i = 1; i < array.length; i++) {
if(max.compareTo(array[i]) < 0) {
max = array[i];
}
}
return max;
}
}
public class Main {
public static void main(String[] args) {
Alg2 alg2 = new Alg2();
Integer[] integers = {1, 2, 3, 4, 5, 6};
Integer ret2 = alg2.findMaxValue(integers);
System.out.println(ret2);
}
}
下列代码将泛型方法改为静态的, 可以通过类名直接调用该泛型方法, 不用实例化对象了
class Alg3 {
public static <T extends Comparable>T findMaxValue(T[] array) {
T max = array[0];
for (int i = 1; i < array.length; i++) {
if(max.compareTo(array[i]) < 0) {
max = array[i];
}
}
return max;
}
}
public class Main {
public static void main(String[] args) {
Integer[] integers = {1, 2, 3, 4, 5, 6};
Integer ret3 = Alg3.findMaxValue(integers);
System.out.println(ret3);
}
}
小结
学会泛型的含义, 泛型的目的, 泛型的语法