目录
泛型
泛型引入
class Apple{
public String color;
public int count;
public int id = count++;
public long id(){
return id;
}
}
class Orange {}
public class AppleAndOrangeWitnoutGenerics {
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for (int i = 0; i < 3; i++) {
apples.add(new Apple());
}
apples.add(new Orange());
for (int i = 0; i < apples.size() ; i++) {
// apples.get(i).id; //会出现语法错误
((Apple)apples.get(i)).id();
}
}
}
我们会发现,执行以上代码的时候。由于ArrayList保存的是Object,因此可以添加任意对象到容器,无论在编译器还是运行期都不会有问题。在使用ArrayList的get()方法获取对象时,得到的只是Object的引用,必须进行强制类型转换,否则会得到语法错误。在运行时我们试图将Orange对象转型为Apple时,会得到 Exception in thread "main" java.lang.ClassCastException: Orange cannot be cast to Apple这样一个类型转换异常。
使用预定义的泛型可以解决这一问题。如果想要定义Apple对象的ArrayList,可以声明ArrayList<Apple>。尖括号里面是参数类型(可以有多个)。
--这样就可以在编译期防止将错误类型的对象放置到容器中,把运行时的错误变成了编译时期的错误
--取出元素的时候,也不必进行强制类型转换。因为List知道他保存的什么类型,调用get()时自动进行转型。
泛型的定义
public class MyArrayList<E> {
private E[] array;
...
}
<>是泛型的标志
E是类型变量,变量名一般要大写
E在定义时是形参,代表 MyArrayList最终传入的类型,现在还不知道
泛型的意义
public class MyArrayList<T> {
public T[] elem;
public int usedSize;
public MyArrayList() {
//this.elem = new T[10];
this.elem = (T[])new Object[10]; //注意要这样写
this.usedSize = 0;
}
public void add(T data) {
this.elem[this.usedSize] = data;
this.usedSize++;
}
public T get() {
return this.elem[this.usedSize-1];
}
}
当我们编写这样的代码:
MyArrayList<String> myArrayList = new MyArrayList<>();
myArrayList.add("Java");
myArrayList.add(2); //报错
String ret = myArrayList.get(); //不用进行强转
因此泛型的意义:
1)可以进行自动类型检查
2)自动进行类型转换
3、泛型的几个坑:
1) 泛型类型的参数不能是简单类型。一定要是对象的包装类
MyArrayList<int> myArrayList3 = new MyArrayList<>(); //报错
2)不能够new一个泛型类型的数组
this.elem = (T[])new Object[10]; //注意要这样写
T[] elem = new T[10]; //错误
3)泛型类型的参数不参与类型的组成
MyArrayList<String> myArrayList = new MyArrayList<>();
System.out.println(myArrayList); //MyArrayList@16d3586
4、泛型是如何编译的:
通过编译器进行类型擦除机制把泛型类转化为非泛型类,(不是替换)编译的时候把尖括号里面的类型擦除为Object类型。泛型只在编译时期才有作用。不必将一些类型转换放到代码中,编译器将进行重要的类型检查。
包装类
1、8种基本数据类型对应的包装类:
除了char --> Character 和 int --> Integer,其他都是首字母大写
2、自动装箱/装包,自动拆箱/拆包:
//int --> Integer 自动装箱
Integer a = 10;
//手动装箱
Integer b = Integer.valueOf(20);
System.out.println(b); //20
//Integer --> int 自动拆箱
int c = a;
//手动拆箱 还可以是double ...
int d = a.intValue();
System.out.println(b); //10
总结:装箱调用Integer.valueOf()方法(静态的)、拆箱调用Integer.intValue)方法(可变的)
3、辨析Integer:
Integer a = 100;
Integer b = 100;
System.out.println(a == b); //true
Integer a = 150;
Integer b = 150;
System.out.println(a == b); //false
Wow......
Integer是引用类型,等号比较的是地址。在代码中进行了自动装箱的过程,因此调用了valueOf()方法。查看valueOf底层代码:如果一个数的值在-128-127之间,每次去缓存区去取。不在的话,没有都会去new一个新的对象。所以就会存在上面的情况啦。