♥泛型类型的本质:1. 能够检测参数的类型是否正确合法 2.自动类型转换,避免用户手动类型转换
♥java泛型类型的擦除机制:在编译阶段,能够检测参数的类型是否正确合法 ,自动类型转换,避免用户手动类型转换,类型就擦除到它的上界.
例:开始的Object类编程:
public classGenericTest {//定义一个存储数据的数组
privateObject[] array;//定义变量描述数组有效数据的大小
private intsize;public GenericTest(intsize){this.array=newObject[size];this.size=0;
}publicGenericTest(){this(10);
}//在数组尾部增加元素
public voidadd(Object data){if(this.size==this.array.length){this.array= Arrays.copyOf(this.array,this.array.length*3);
}this.array[size++]=data;
}//获取数组最后一个元素
publicObject get(){if(this.size==0){return null;//引用类型默认返回null,也可以选择跑出异常
}return this.array[size-1];//通过返回值返回
}/*** Object写泛型编程提供的限制是java提供泛型编程本质的意义
*@paramargs*/
public static voidmain(String[] args) {
GenericTest g=newGenericTest();
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
•java泛型的好处:1.可以进行类型的检查 2.可以做类型自动转换
可以确认具体的类型也可以使用原始类型,默认为Object类型
♥为什么会报错但是不强制转换为指定类型?
历史问题,如果指定,以前开发的代码会报错。因为java5以后才增加泛型,但是要让原来的类使用的时候又不能报错,所以不强制。
♥java泛型的类型擦除机制?
在编译阶段,java编译器通过泛型类型进行类型检查和自动类型转换,处理完成以后,就会把T类型一直往上擦除,直到上界(Object)
GenericTest2GenericTest2一个类型,里面的类型只是为了做对应的类型检查
注:打印出来类型相同,经过编译阶段尖括号里面的类型不存在了,擦除了;都是GenericTest2类型,里面的类型只存在编译阶段,运行阶段不存在了。
•参数类型不能为简单类型,因为在编译阶段要进行类型检测和类型转换,处理完成以后要向上擦除到它的基类,简单类型不是类类型,没有基类。
再写一个Student类的泛型,按照id号进行排序:
classStudent {privateString name;privateInteger id;publicStudent(Integer id,String name){this.name=name;this.id=id;
}
@OverridepublicString toString() {
String str= "id:" + id + " name:" +name;returnstr;
}
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{ //Comparable 接口要有,比较学生对象
id实现按照顺序打印:
注:自定义的类型要支持比较,一定要实现Comparable接口,重写compareTo方法
第一种方式:再创建一个比较用户传入的名字类,需要实现Comparator接口,再重写compare()方法:
Sudent类里面需要添加getName方法:classStudent{/*** 外部访问name,需要调用get方法,String类型的getName,需要返回Name*/
publicString getName() {returnname;
}
}public static voidmain(String[] args){
/**
*sort的重载函数,接受一个自定义的比较器,比较name
*/
Arrays.sort(stuArr,newStudentCompareName());
System.out.println(Arrays.toString(stuArr));//打印数组
}
/**
*注:这里的名字需要写成英文的,才能进行排序"zhang san" ,中文式的不能比较"张三"
*这是定义的一个普通类型
*比较学生名字的类实现Comparator接口,重写compare方法
*/
class StudentCompareName implements Comparator{
@Overridepublic intcompare(Student o1, Student o2) {returno1.getName().compareTo(o2.getName());
}
}
第二种方式:定义一个实现了Comparator接口的匿名对象,不用再创建一个新类:
/*** 用一个静态方法来实现名字的比较,产生匿名对象类,不用再创建新的类*/Arrays.sort(stuArr,NAME_COPM);
System.out.println(Arrays.toString(stuArr));//打印数组
/*** new Comparator() 不是new实例化接口,而是实现了一个实现了Comparator接口的匿名对象而已*/
public static Comparator NAME_COPM=new Comparator() {
@Overridepublic intcompare(Student o1, Student o2) {returno1.getName().compareTo(o2.getName());
}
};
第三种方式:直接将匿名对象放在sort方法里(最简单)
Arrays.sort(stuArr, new Comparator() {
@Overridepublic intcompare(Student o1, Student o2) {returno1.getName().compareTo(o2.getName());
}
});
System.out.println(Arrays.toString(stuArr));//打印数组
输出一个泛型的顺序栈:实现出栈、入栈、判断栈空栈满
/**
*输出一个泛型的顺序栈*实现构造函数 入栈出栈栈空栈满 获取栈顶元素peek* @param
*/
public class GenericStack{privateT[] stack;private inttop;publicGenericStack() {this(4);
}/*** 带参数的构造函数
*@paramsize*/
public GenericStack(intsize) {this.stack = (T[])newObject[size];this.top=0;
}/*** 获取栈顶元素
*@return
*/
publicT peek(){if(empty()){return null;
}return this.stack[this.top-1];
}/**ctrl+Q 键生成说明
* 入栈操作
*@paramdata*/
public voidpush(T data){if(full()){this.stack=Arrays.copyOf(this.stack,this.stack.length*2);
}this.stack[this.top++]=data;
}public intsize(){return this.top;
}/*** 出栈并返回出栈元素
*@return
*/
publicT pop(){if(empty()){return null;
}
T t= this.stack[this.top-1];--this.top;
returnt;
}/*** 判断栈满
*@return
*/
private boolean full() { return this.top==this.stack.length; }/*** 判断栈空
*@return
*/
private boolean empty() { return this.top==0; }public static void main(String[] args)throwsInterruptedException {
GenericStack 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 classStudent{privateInteger id;privateString name;privateInteger age;publicStudent(Integer id,String name,Integer age) {this.id =id;this.name=name;this.age=age;
}publicInteger getId() {returnid;
}public voidsetId(Integer id) {this.id =id;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicInteger getAge() {returnage;
}public voidsetAge(Integer age) {this.age =age;
}
@OverridepublicString toString() {return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class GenericStack {
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 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 intsize(){return this.top;
}
}public static voidmain(String[] args){
Student[] st=newStudent[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.普通泛型方法
在一个数组中寻找最大值
publicT 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];
}
}returnmax;
}
2.静态的泛型方法
/*** static 方法能不能使用泛型类定义的泛型类型参数呢?? 不能。静态方法的调用不需要依赖对象
* java的泛型可以定义三种东西:1.泛型类public class A { }
* 2.泛型方法: 在函数的返回值前面增加类型列表
* 3.泛型接口 Comparable Comparator
*@paramarr
*@param 可改变的方法的类型,要实现compareTo方法,该类需要继Comparable接口
* 第二个E相当于其他方法里的void ,即返回一个E类型的参数
*@return
*/
public static >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];
}
}returnmax1;
}
总程序:
/*** 要求T类型向上擦除到Comparable才能实现compareTo方法
* 此处,给泛型类型定义类泛型的上界(不定义上界,就擦除到Object类了)
*现在擦除到接口类型
*@param
* 2019/10/22*/
class Compare>{publicT 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];
}
}returnmax;
}/*** static 方法能不能使用泛型类定义的泛型类型参数呢?? 不能。静态方法的调用不需要依赖对象
* java的泛型可以定义三种东西:1.泛型类public class A { }
* 2.泛型方法: 在函数的返回值前面增加类型列表
* 3.泛型接口 Comparable Comparator
*@paramarr
*@param 可改变的方法的类型,要实现compareTo方法,该类需要继Comparable接口
* 第二个E相当于其他方法里的void ,即返回一个E类型的参数
*@return
*/
public static >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];
}
}returnmax1;
}
}public class泛型的上界 {public static voidmain(String[] args) {
Integer[] arr=new Integer[]{12,5,78,9,45};
Compare comp=newCompare();
System.out.println(comp.findMaxValue(arr));
System.out.println(Compare.findMax(arr));
}
}
♥总结:
泛型可以用来定义什么?
1.泛型类 public class A{ }
2.泛型方法 (1).普通方法:public T fly(T t) { } (若要实现比较应用compareTo方法,该类需要继承Comparable接口,需要给泛型定义上界)
(2).静态方法:public E fly(E e)(若要实现比较应用compareTo方法,该类需要继承Comparable接口 )
3.泛型接口 class StudentCompareName implements Comparator ,比较的是Student里面的属性,所以
Comparble(重写compareTo方法) Comparator(重写compare方法,两个参数)