泛型和Comparable、Comparator接口
♥泛型类型的本质:1. 能够检测参数的类型是否正确合法 2.自动类型转换,避免用户手动类型转换
♥java泛型类型的擦除机制:在编译阶段,能够检测参数的类型是否正确合法 ,自动类型转换,避免用户手动类型转换,类型就擦除到它的上界.
例:开始的Object类编程:
public class GenericTest { //定义一个存储数据的数组 private Object[] array; //定义变量描述数组有效数据的大小 private int size; public GenericTest(int size){ this.array=new Object[size]; this.size=0; } public GenericTest(){ this(10); } //在数组尾部增加元素 public void add(Object data){ if(this.size==this.array.length){ this.array= Arrays.copyOf(this.array,this.array.length*3); } this.array[size++]=data; } //获取数组最后一个元素 public Object get(){ if(this.size==0){ return null;//引用类型默认返回null,也可以选择跑出异常 } return this.array[size-1];//通过返回值返回 } /** * Object写泛型编程提供的限制是java提供泛型编程本质的意义 * @param args */ public static void main(String[] args) { GenericTest g=new GenericTest(); g.add(20); g.add(10); g.add("30");//Object可以接受任意类型,所以本来想存数组,结果写错存了字符串"50" //g.add("hello world");//不会报错,可以接受任意类型 int data=(int)g.get();//基类类型不能赋给具体类型(不能把大类型赋给小类型),会报错,所以要进行类型转化 System.out.println(data); } }
出现错误:类型转换错误,String类不能转化为Integer类型

• Object实现泛型编程的缺陷:
1. g.add(20);
g.add(10);
g.add("30"); //这里无法检测传入的数据是否符合用户存储数据的正确类型,不能做类型检查
2.因为是用Object进行泛型编程,所有的方法返回值类型都是Object, Object作为积累不能转化为派生类类型,必须手动加入类型转换
int data=(int)g.get();
•引出泛型的意义:可以进行类型的检测和自动转换。
•说明泛型类:普通类+<类型参数/类型占位符> public class GenericTest2<T>

•java泛型的好处:1.可以进行类型的检查 2.可以做类型自动转换
可以确认具体的类型也可以使用原始类型,默认为Object类型

♥为什么会报错但是不强制转换为指定类型?
历史问题,如果指定,以前开发的代码会报错。因为java5以后才增加泛型,但是要让原来的类使用的时候又不能报错,所以不强制。
♥java泛型的类型擦除机制?
在编译阶段,java编译器通过泛型类型进行类型检查和自动类型转换,处理完成以后,就会把T类型一直往上擦除,直到上界(Object)
GenericTest2<Integer> GenericTest2<Double>一个类型,里面的类型只是为了做对应的类型检查

注:打印出来类型相同,经过编译阶段尖括号里面的类型不存在了,擦除了;都是GenericTest2类型,里面的类型只存在编译阶段,运行阶段不存在了。
•参数类型不能为简单类型,因为在编译阶段要进行类型检测和类型转换,处理完成以后要向上擦除到它的基类,简单类型不是类类型,没有基类。

再写一个Student类的泛型,按照id号进行排序:
class Student { private String name; private Integer id; public Student(Integer id,String name){ this.name=name; this.id=id; } @Override public String toString() { String str = "id:" + id + " name:" + name; return str; }
public void getName() {
this.name=name;
}
}
public class StudentTest {
public static void main(String[] args) {
Student[] stuArr=new Student[3];
stuArr[0]=new Student(1001,"zhang san");
stuArr[1]=new Student(1000,"li si");
stuArr[2]=new Student(1002,"wang wu");
Arrays.sort(stuArr);//给数组排序
//报错,因为这个时候java编译器不知道要给什么进行排序 转型错误(对象没有办法进行比较)
//错误类型:Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
//要实现泛型接口,才能比较
System.out.println(Arrays.toString(stuArr));//打印数组
错误分析:
报错,因为这个时候java编译器不知道要给什么进行排序 转型错误(对象没有办法进行比较)
错误类型Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
要实现泛型接口,才能比较

实现接口后:class Student implements Comparable<Student>{ //Comparable 接口要有<学生对象>,比较学生对象
id实现按照顺序打印:

注:自定义的类型要支持比较,一定要实现Comparable<Student>接口,重写compareTo方法
第一种方式:再创建一个比较用户传入的名字类,需要实现Comparator接口,再重写compare()方法:
Sudent类里面需要添加getName方法: class Student{ /** * 外部访问name,需要调用get方法,String类型的getName,需要返回Name */ public String getName() { return name; } } public static void main(String[] args){
/**
*sort的重载函数,接受一个自定义的比较器,比较name
*/ Arrays.sort(stuArr,new StudentCompareName()); System.out.println(Arrays.toString(stuArr));//打印数组 } /**
*注:这里的名字需要写成英文的,才能进行排序"zhang san" ,中文式的不能比较"张三"
*这是定义的一个普通类型 *比较学生名字的类实现Comparator接口,重写compare方法 */ class StudentCompareName implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); } }

第二种方式:定义一个实现了Comparator接口的匿名对象,不用再创建一个新类:
/** * 用一个静态方法来实现名字的比较,产生匿名对象类,不用再创建新的类 */ Arrays.sort(stuArr,NAME_COPM); System.out.println(Arrays.toString(stuArr));//打印数组 /** * new Comparator<Student>() 不是new实例化接口,而是实现了一个实现了Comparator接口的匿名对象而已 */ public static Comparator<Student> NAME_COPM=new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); } };

第三种方式:直接将匿名对象放在sort方法里(最简单)
Arrays.sort(stuArr, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); } }); System.out.println(Arrays.toString(stuArr));//打印数组
输出一个泛型的顺序栈:实现出栈、入栈、判断栈空栈满
/** *输出一个泛型的顺序栈 * 实现构造函数 入栈出栈栈空栈满 获取栈顶元素peek * @param <T> */ public class GenericStack<T> { private T[] stack; private int top; public GenericStack() { this(4); } /** * 带参数的构造函数 * @param size */ public GenericStack(int size) { this.stack = (T[])new Object[size]; this.top=0; } /** * 获取栈顶元素 * @return */ public T peek(){ if(empty()){ return null; } return this.stack[this.top-1]; } /** ctrl+Q 键生成说明 * 入栈操作 * @param data */ public void push(T data){ if(full()){ this.stack=Arrays.copyOf(this.stack,this.stack.length*2); } this.stack[this.top++]=data; } public int size(){ return this.top; } /** * 出栈并返回出栈元素 * @return */ public T pop(){ if(empty()){ return null; } T t= this.stack[this.top-1]; --this.top; return t; } /** * 判断栈满 * @return */ private boolean full() { return this.top==this.stack.length; } /** * 判断栈空 * @return */ private boolean empty() { return this.top==0; } public static void main(String[] args)throws InterruptedException { GenericStack<String> s=new GenericStack<>(10); s.push("20"); s.push("10"); System.out.println(s.pop()); System.out.println(s.peek()); for(int i=0;i<10;i++){ s.push(String.valueOf(i+1));//将i+1转换为String类型的并入栈 } while (!s.empty()){ System.out.print(s.pop()+" "); //将栈中的元素输出 }
垃圾回收机制(GC):
GC:Garbage collection怎么回收对象-》任意一个对象,如果没有被其他引用变量引用,这个对象就可以被GC回收了
可以手动启动GC ,System.gc(),用jamp命令查看当前活着的进程
注:将引用变量s置为空后,查看进程时找不到该对象,说明已经被回收

提供一个学生类,定义姓名、学号、年龄属性,提供get set方法
public class Student{ private Integer id; private String name; private Integer age; public Student(Integer id,String name,Integer age) { this.id = id; this.name=name; this.age=age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } }
public class GenericStack<T> {
private T[] stack;
private int top;
public GenericStack() {
this(4);
}
/**
* 带参数的构造函数
* @param size
*/
public GenericStack(int size) {
this.stack = (T[])new Object[size];
this.top=0;
}
/**
* 获取栈顶元素
* @return
*/
public T peek(){
if(empty()){
return null;
}
return this.stack[this.top-1];
}
/** ctrl+Q 键生成说明
* 入栈操作
* @param data
*/
public void push(T data){
if(full()){
this.stack=Arrays.copyOf(this.stack,this.stack.length*2);
}
this.stack[this.top++]=data;
}
public int size(){
return this.top;
}
/**
* 出栈并返回出栈元素
* @return
*/
public T pop(){
if(empty()){
return null;
}
T t= this.stack[this.top-1];
--this.top;
this.stack[this.top]=null; //将出栈的值置为null,才能将对象删除
return t;
}
/**
* 判断栈满
* @return
*/
private boolean full() { return this.top==this.stack.length; }
/**
* 判断栈空
* @return
*/
private boolean empty() { return this.top==0; }
public static void main(String[] args)throws InterruptedException {
GenericStack<Student> s2=new GenericStack<>();
s2.push(new Student(1000,"zhangsan",20));
s2.push(new Student(1001,"lisi",21));
s2.push(new Student(1002,"wangwu",22));
s2.push(new Student(1003,"zhan",20));
s2.push(new Student(1004,"san",20));
System.out.println(s2.pop());
System.out.println(s2.pop());
Thread.sleep(10000000);
}
}
没有将输出的栈元素置空时,在出栈两个元素后,查看Student依然存活着5个对象。

在出栈的方法中加入 this.stack[this.top]=null; //将出栈的值置为null,才能将对象删除,此时查看活着的进程,只有三个。

将栈里面的元素放在一个数组中,并按照学号排序:
/** * 想将目前栈里面的元素放在一个数组里(3个) * 所以返回栈元素的个数 * @return */ public int size(){ return this.top; } } public static void main(String[] args){ Student[] st =new Student[s2.size()]; for(int i=0;!s2.empty();++i){ st[i]=s2.pop(); } System.out.println(Arrays.toString(st)); Arrays.sort(st); System.out.println(Arrays.toString(st)); } }
出现类型转型错误:

给Student实现Comparable接口,重写它的compareTo方法,才能进行比较

但是后面可能还有别的需求;难道比较名字、年龄就要重写compareTo方法吗?
sort函数提供了相应的构造器来接收比较的内容:λ表达式

若按照从大到小排序:(a,b)->{ - return a.getAge().compareTo(b.getAge());} (加负号即可)
泛型方法:
1.普通泛型方法
在一个数组中寻找最大值
public T findMaxValue(T[] arr) { if(arr==null){ return null; } if(arr.length==1){ return arr[0]; } T max = arr[0]; for (int i = 1; i < arr.length; i++) { if (max.compareTo(arr[i])<0) { //compareTo比较对象,不能用 > < max = arr[i]; } } return max; }
2.静态的泛型方法
/** * static 方法能不能使用泛型类定义的泛型类型参数呢?? 不能。静态方法的调用不需要依赖对象 * java的泛型可以定义三种东西:1.泛型类public class A<T> { } * 2.泛型方法: 在函数的返回值前面增加类型列表 * 3.泛型接口 Comparable Comparator * @param arr * @param <E> 可改变的方法的类型,要实现compareTo方法,该类需要继Comparable接口 * 第二个E相当于其他方法里的void ,即返回一个E类型的参数 * @return */ public static <E extends Comparable<E>> E findMax (E[] arr){ E max1=arr[0]; for (int i = 1; i < arr.length; i++) { if (max1.compareTo(arr[i])<0) { //compareTo比较对象 max1 = arr[i]; } }return max1; }
总程序:
/** * 要求T类型向上擦除到Comparable才能实现compareTo方法 * 此处,给泛型类型定义类泛型的上界(不定义上界,就擦除到Object类了) *现在擦除到接口类型 * @param <T> * 2019/10/22 */ class Compare<T extends Comparable<T>> { public T findMaxValue(T[] arr) { if(arr==null){ return null; } if(arr.length==1){ return arr[0]; } T max = arr[0]; for (int i = 1; i < arr.length; i++) { if (max.compareTo(arr[i])<0) { //compareTo比较对象,不能用 > < max = arr[i]; } } return max; } /** * static 方法能不能使用泛型类定义的泛型类型参数呢?? 不能。静态方法的调用不需要依赖对象 * java的泛型可以定义三种东西:1.泛型类public class A<T> { } * 2.泛型方法: 在函数的返回值前面增加类型列表 * 3.泛型接口 Comparable Comparator * @param arr * @param <E> 可改变的方法的类型,要实现compareTo方法,该类需要继Comparable接口 * 第二个E相当于其他方法里的void ,即返回一个E类型的参数 * @return */ public static <E extends Comparable<E>> E findMax (E[] arr){ E max1=arr[0]; for (int i = 1; i < arr.length; i++) { if (max1.compareTo(arr[i])<0) { //compareTo比较对象 max1 = arr[i]; } }return max1; } } public class 泛型的上界 { public static void main(String[] args) { Integer[] arr=new Integer[]{12,5,78,9,45}; Compare<Integer> comp=new Compare(); System.out.println(comp.findMaxValue(arr)); System.out.println(Compare.findMax(arr)); } }

♥总结:
泛型可以用来定义什么?
1.泛型类 public class A<T>{ }
2.泛型方法 (1).普通方法:public T fly(T t) { } (若要实现比较应用compareTo方法,该类需要继承Comparable接口,需要给泛型定义上界)
(2).静态方法:public <E> E fly(E e)(若要实现比较应用compareTo方法,该类需要继承Comparable接口<E extends Comparble> )
3.泛型接口 class StudentCompareName implements Comparator<Student> ,比较的是Student里面的属性,所以<Student>
Comparble(重写compareTo方法) Comparator(重写compare方法,两个参数)
浙公网安备 33010602011771号