泛型
我们先来看一下代码,这是一个用泛型实现的栈,下面的 T 就是代表了这是个泛型,它是一个占位符,可以指定任何类型。
我们先看一下泛型有什么优点。比如我们要写一个栈,一般来说,栈的存储类型是固定了的,当我们需要存储其他类型的元素时,我们只能修改代码,但是使用泛型我们完全不用考虑这点,因为用泛型写的栈存储的元素随着我们传入的参数不同而不同。
class GenericStack<T>{
private T[] elem;//声明一个 T 类型的数组,但是 T 现在不确定
private int top;
public GenericStack() {
this(10);
}
public GenericStack(int size) {
this.elem = (T[])new Object[size];
this.top = 0;
}
public void push(T val){
this.elem[this.top++] = val;
}
public void pop(){
--this.top;
}
public T getTop(){
return this.elem[this.top-1];
}
}
public static void main(String[] args) {
GenericStack<Integer> stack = new GenericStack<Integer>();
stack.push(10);
stack.push(20);
stack.push(200);
int a = stack.getTop();
System.out.println(a);
}
输出结果是 200
泛型的意义
- 对类型进行自动检查
- 对类型进行自动转换
泛型的坑
- 不能 new 泛型类型的数组。
- 不能 new 泛型类型的对象。
- 不能得到泛型类型的对象数组。
- 简单类型不能作为泛型类型的参数。
泛型是如何编译的
泛型在编译期间通过它的类型擦除机制生成一个 Object 对象。以为就是说泛型的参数类型在编译期间被编译器擦除了,在运行期间 JVM 得到的是一个 Object 对象。类型擦除机制是向上擦除的,也就是往基类方向擦除。
泛型的类型擦除机制
泛型的上界
因为泛型的类型擦除机制,最后在运行期间我们得到的是一个 Object 类型的值,在有的情况下不是很方便,所以我们需要指定一下我们类型擦除机制向上擦除到什么地方位置,这就引出了泛型的上界。
class GenericAlg<T extends Comparable<T>>{
public T findMaxVal(T[] array){
T maxVal =array[0];
for(int i = 1;i < array.length;i++){
if(maxVal.compareTo(array[i]) < 0){
maxVal = array[i];
}
}
return maxVal;
}
public static void main(String[] args) {
Integer[] array = {10,20,30};
GenericAlg<Integer> g1 = new GenericAlg<Integer>();
System.out.println(g1.findMaxVal(array));
}
}
输出结果:30
在这个例子中我们指出了上界为 Comparable,所以我们可以直接调用它里面的方法。
泛型方法
class Usr implements Comparable<Usr>{
private String name;
private String sex;
private int age;
public Usr(String name,String sex,int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Usr [name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
@Override
public int compareTo(Usr o) {
return age > o.age ? 1 : (age == o.age) ? 0 : -1;
}
}
class Gnericl{
/**
*
* @param array
* @return
* 泛型方法
*/
public static<T extends Comparable> T fingMaxVal(T[] array){
T maxVal =array[0];
for(int i = 1;i < array.length;i++){
if(maxVal .compareTo(array[i]) < 0){
maxVal = array[i];
}
}
return maxVal;
}
public static void main2(String[] args) {
Usr[] usr = new Usr[3];//定义一个 Usr 类型的数组
usr[0] = new Usr("洁1","男",17);
usr[1] = new Usr("洁3","男",19);
usr[2] = new Usr("洁2","男",21);
System.out.println();
System.out.println(Gnericl.findMaxVal(usr));
}
}
内存泄漏
看下面的代码,我们创建了三个对象放入栈里,然后又出栈了一个元素,所以应该是两个对象,但经过测试,我们应该得到了三个对象,这就是内存的
public static void main(String[] args) {
GenericStack<Animal3> s = new GenericStack();
s.push(new Animal3());
s.push(new Animal3());
s.push(new Animal3());
s.pop();
System.gc();//测试内存泄漏
}
现在我们给出战的方法里将即将出栈的元素置为 null ,从结果来看我们成功的防止了内存的泄漏。
public void pop(){
this.elem[top-1] = null;
--this.top;
}
通配符
- 表示当前类型可以是任何类型。
- 它也会进行类型擦除的 擦除到Object.
通配符分类
无界通配:?
通配符的上界:? extends Object
通配符的下界:? super Integer