一、泛型
1、什么是泛型?
List<T> list = new ArrayList<>();
这里我们之前在使用集合时,就已经用过泛型,其中的 “<T>” 就是泛型。
所谓的泛型就是在类定义时,不为类中属性和方法指定数据类型,而是在类对象创建时为其指定相应的数据类型。
2、为什么使用泛型?
我们举一个例子来说。
现在要求定义一个Point类,该类中要有属性:坐标x,坐标y。
要求1: x和y的值可以都是整数类型
要求2: x和y的值可以都是小数类型。
要求3:x和y的值可以都是字符串类型。
在不考虑泛型这种方法,我们之前想要实现上面的要求,类该如何定义?
我们可以将属性x和属性y的类型设置为Object,这样就可以实现上面的要求。
Point类代码:
package test.demo01;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:00
**/
public class Point {
private Object x;
private Object y;
//无参构造
public Point() {
}
//有参构造
public Point(Object x, Object y) {
this.x = x;
this.y = y;
}
//自定义一个show方法,方便演示
public void show(){
System.out.println("x坐标:"+x+";y坐标:"+y);
}
//get set方法
public Object getX() {
return x;
}
public void setX(Object x) {
this.x = x;
}
public Object getY() {
return y;
}
public void setY(Object y) {
this.y = y;
}
}
要求实现测试代码:
package test.demo01;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:02
**/
public class TestPoint {
public static void main(String[] args) {
Point p1=new Point(10,20);//坐标为整数,实现要求1
p1.show();
Point p2=new Point(10.5,20.5);//坐标为小数,实现要求2
p2.show();
Point p3=new Point("E105°","N100°");//坐标为字符串,实现要求3
p3.show();
}
}
控制台输出:
x坐标:10;y坐标:20
x坐标:10.5;y坐标:20.5
x坐标:E105°;y坐标:N100°
但是我们思考一下,这种情况虽然满足了我们提出的三个要求,但是这种方式会出现要求外的情况,比如x属性我传一个字符串,y属性我传一个Double,这显然违背了我们的预期。这种情况在java中可以作为一种数据类型安全问题。如果想解决这种问题,就需要用到我们的泛型。
使用泛型后的Point类:
package test.demo01;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:00
**/
public class Point<T> {
private T x;
private T y;
public Point() {
}
public Point(T x, T y) {
this.x = x;
this.y = y;
}
//自定义一个show方法,方便演示
public void show(){
System.out.println("x坐标:"+x+";y坐标:"+y);
}
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
将Point类改写成这样后,就避免了出现数据类型安全问题。这时我们规定好泛型后,再想一个传字符串类型数据,一个传Double类型数据就不可能实现了。
其实用泛型,如果想的话,也可以实现一个传字符串,一个传Doule这种形式,这时需要我们在定义类时,规定两个泛型<T,E>。具体的定义泛型我们在后面详细讲解。
注意:上面的泛型类型必须都是引用类型。不能是基本类型。
4、泛型的定义
泛型可以定义在类上(泛型类),接口上(泛型接口),方法上(泛型方法)。
泛型可以解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识,表示类中某个属性的数据类型或者是某个方法的返回值及参数类型。这样在类声明或者实例化时只要指定好需要的类型即可。
注意:
- 标识可以任意起名,在创建对象时,必须为每个泛型指定数据类型。
- 在类声明或实例化时,如果没有指定泛型的类型,那么默认类型为Object.
- 如果不声明类型,想使用真正的类型接收,则必须进行强转。
格式:
public class 类名<泛型标志,泛型标志....>{
//类成员
}
package test.demo01;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:02
**/
public class TestPoint {
public static void main(String[] args) {
Point<Integer> p1=new Point<>(10,20);
p1.show();
Point<Double> p2=new Point<>(10.5,20.5);
p2.show();
Point<String> p3=new Point<>("E105°","N100°");
p3.show();
//不规定泛型,默认为Object
Point point = new Point(10,20);
//想要类型为Integer,则必须要强转
Integer x = (Integer) point.getX();
}
}
5、通配符
在开发中对象的引用传递是最常见的,例如在ssm中controller层需要调用service层。但是如果在泛型类的操作中,在进行引用传递时泛型类型必须匹配才可以传递,否则是无法传递的。如果想传递,可以定义泛型为 ? 通配符。
例子:我们在测试类中定义一个方法---doshow(Point<?> point),让该方法的参数为Point
package test.demo01;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:02
**/
public class TestPoint {
public static void main(String[] args) {
Point<Integer> point = new Point<>(10,20);
doshow(point);
}
public static void doshow(Point<?> point){
point.show();
}
}
6、受限泛型
在引用传递中,在泛型操作中也可以设置一个泛型对象的范围上限和范围下限。范围上限使用extends关键字声明,表示参数化的类型可能是所指定的类型或者是此类型的子类。范围下限使用super进行声明,表示参数化的类型可能是所指定的类型或者此类型的父类型。
语法:
[设置上限]
声明对象: 类名称<? extends 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 extends 类>{ }[设置下限]
声明对象: 类名称<? super 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 super 类>{ }
例子:设置fun方法引用point对象中的show方法,且泛型上限为Number;设置look方法引用point对象 的show方法,且泛型下限为Number;
7、泛型接口
上面那些例子都是使用泛型类。而在jdk1.5以后,泛型也可以定义在接口上了,定义接口的泛型和定义泛型类语法相似。
语法:
public interface 接口名<泛型标志,泛型标志....>{
//静态常量
//抽象方法。
}
类实现泛型接口
USB泛型接口:
package test.demo02;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:47
**/
public interface USB<T> {
//常量
String NAME="";
//抽象方法
T show();
}
子类实现接口时,确定泛型类型
package test.demo02;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:49
**/
public class Mouse implements USB<String>{
@Override
public String show() {
return "show mouse";
}
}
子类实现接口,子类也实现和父类相同的泛型
package test.demo02;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:52
**/
public class KeyBoard<T> implements USB<T> {
@Override
public T show() {
return null;
}
}
测试两个子类:
package test.demo02;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:54
**/
public class Test02 {
public static void main(String[] args) {
//子类在实现接口时,确定泛型类型
Mouse mouse = new Mouse();
mouse.show();
//子类也实现泛型和父类名相同的泛型
KeyBoard<Integer> keyBoard = new KeyBoard<>();
keyBoard.show();
}
}
8、泛型方法
前面学习的所有泛型操作都是将整个类进行泛型化,但同样也可以在类中定义泛型化的方法。泛型方法的定义与其所在的类是否是泛型类是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。
泛型方法的定义:
[访问权限] ==<泛型标识>== 泛型标识 方法名称(泛型标识 参数名称)
package test.demo03;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:57
**/
public class Point {
public <T> T fun(T t){
return t;
}
}
测试类代码:
package test.demo03;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 20:59
**/
public class Test03 {
public static void main(String[] args) {
Point point = new Point();
String str = point.fun("fanxingfangfa");
System.out.println(str);
Integer fun = point.fun(11);
System.out.println(fun);
Double fun1 = point.fun(22.5);
System.out.println(fun1);
}
}
二、注解
注解就是给程序看的东西,当程序看到这个注解时,就会解析这个注解。
注解本身没有任何意义,它只有被解析了,才会赋予真正的意义。
例子:@Override 它被JVM解析,从而使其具有相应的意义。
@Controller @RequestMapping 它被Spring框架解析,所以具有相应的意义
1、预定义注解
预定义注解就是JDK自带的一些注解,该注解被JVM而解析。
常用的预定义注解:
1. @Override: 重写得注解。符合重写得规则。
2. @Deprecated: 表示已过时。
3. @SuppressWarnings: 表示压制警告。
4. @FunctionInterface: 表示函数式接口。表示该接口中有且仅有一个抽象方法。
2、自定义注解(初级篇)
语法:
public @interface 注解名{
//注解属性
}
使用自定义注解:
类 方法 属性 加@注解名
例子:注意,该例子中的自定义注解没有任何意义,因为他没有被解析,只有解析过后才会被赋予真正的意义。解析方法需要用到我们的反射。这个后面我们再讲
package demo03;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 19:32
**/
public class Test03 {
public static void main(String[] args) {
Fun fun = new Fun();
fun.setA(15);
fun.dofun();
}
}
//自定义注解
@interface lrs{
//注解属性
}
//使用注解
@lrs
class Fun{
@lrs
private Integer a;
@lrs
public void dofun(){
System.out.println(a);
}
public Integer getA() {
return a;
}
public void setA(Integer a) {
this.a = a;
}
}
3、元注解
元注解就是定义在注解上的注解称为元注解。 我们只需要知道下面这四个。
@Target(value=可以取下面这些内容): 作用限制注解使用得位置。
ElementType.TYPE :表示注解可以作用在类,接口,枚举上
ElementType.FIELD :表示注解可以作用在属性上
ElementType.METHOD:表示注解可以作用在普通方法上
ElementType.PARAMETER:表示注解可以作用在方法参数上
ElementType.CONSTRUCTOR:表示注解可以作用在构造方法上
ElementType.LOCAL_VARIABLE:表示注解可以作用在局部变量上
@Retention: 注解什么时候生效。默认时源码 java经历了那些阶段。
源码阶段-->字节码阶段--->运行阶段
RetentionPolicy.SOURCE:源码时生效
RetentionPolicy.CLASS:字节码时生效
RetentionPolicy.RUNTIME:运行时生效
范围最大的是RUNTIME,他会在源码阶段,字节码阶段,运行阶段都有该注解
@Documented 当生产API文档时该注解还存在。
@Inherited 是否运行被子类继承。
4.自定义注解(高级篇)
语法:
@interface 注解名{
数据类型 属性名() default 默认值;
}
注意:这里的数据类型必须为:基本类型,字符串类型,枚举类型【常量】,注解类型,数组类型【必须是上面这些类型的数组】
例子:
package question4;
/**
* @作者:刘壬杉
* @创建时间 2022/7/13 19:40
**/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//只能在类上使用
@Target(value= ElementType.TYPE)
//在运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationByCustom {
String[] name () default "dimooo时光漫游系列";
String[] value();
}