Java高级--泛型和注解

1.泛型

1.1什么是泛型

泛型是 Java SE5 出现的新特性,泛型的本质是类型参数化或参数化类型,在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。通俗的将就是在类定义时,不为类中属性和方法指定数据类型,而是在类对象创建时为其指定相应的数据类型。

1.2为什么使用泛型

例子: 要求定义一个Point点类,该类中属性有x坐标和y坐标。

要求:
       x和y的值可以都是整数类型。

​       x和y的值可以都是小数类型。

​       x和y的值可以都是字符串类型。

如何定义该类呢? 如何确定属性的类型。----Object类型

public class Point {
    //x坐标
    private Object x;
    //y坐标
    private Object y;

    //输出坐标的值
    public void show(){
        System.out.println("x坐标:"+x+";y坐标:"+y);
    }

    public Point() {
    }

    public Point(Object x, Object y) {
        this.x = x;
        this.y = y;
    }

    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;
    }
}
public class Test {
    public static void main(String[] args) {
        Point p1=new Point(10,20);//坐标为整数int--自动装箱->Integer--->Object(向上转型)
        p1.show();

        Point p2=new Point(25.5,36.6);//坐标为小数
        p2.show();

        Point p3=new Point("东经180度","北纬25度");//坐标为字符串
        p3.show();
    }
}

如若使用数据类型不一致的x和y 如

Point p4=new Point(25,"北纬50度");
p4.show();

尽管这种方式不会报错,但是违背了设计的要求,即数据类型安全问题,如何解决该类问题,使用泛型即可解决。

public class Point<T> {
    //x坐标
    private T x;
    //y坐标
    private T y;

    public void show(){
        System.out.println("x坐标:"+x+";y坐标:"+y);
    }

    public Point() {
    }

    public Point(T x, T y) {
        this.x = x;
        this.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;
    }
}
public class Test {
    public static void main(String[] args) {
        Point<Integer> p1=new Point<>(10,20);
        p1.show();

        Point<Double> p2=new Point<>(25.5,36.6);//坐标为小数
        p2.show();

        Point<String> p3=new Point("东经180度","北纬25度");//坐标为字符串
        p3.show();

    }
}

注意: 上面的泛型类型必须都是引用类型,不能是基本类型。
使用泛型就保证了数据类型安全问题。

1.3泛型的定义

1.泛型可以定义在类上,接口上,方法上。 
		泛型类,泛型接口以及泛型方法。
2.泛型可以解决数据类型的安全性问题,
其主要原理是在类声明时通过一个标识表示类中某个属性的数据类型或者是某个方法的返回值及参数类型。这样在类声明或者实例化时只要指定好需要的类型即可。

格式
public class 类名<泛型标志,泛型标志....>{
 //类成员
}
public class Point<T,E> {
    private T x;
    private E y;

    public Point() {
    }

    public Point(T x, E y) {
        this.x = x;
        this.y = y;
    }

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    public E getY() {
        return y;
    }

    public void setY(E y) {
        this.y = y;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }
}

public class test {
    public static void main(String[] args) {
        Point<String,Integer> p1 = new Point<String,Integer>("age",18);
        System.out.println(p1);

        Point p2 = new Point("age",18); //如果没有指定类型,默认为object
        System.out.println(p2);

        String p2X = (String)p2.getX(); //如果想要得到真正的类型 需要强转
        System.out.println(p2X);
    }
}

1.4 通配符

在开发中对象的引用传递是最常见的,但是如果在泛型类的操作中,在进行引用传递时泛型类型必须匹配才可以传递,否则是无法传递的。如果想传递,可以定义泛型为?通配符。

class Point<T> {
    //x坐标
    private T x;

    public Point() {
    }

    public Point(T x) {
        this.x = x;
    }

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                '}';
    }
}

public class Test {
    public static void main(String[] args) {

        Point<String> p1 = new Point<String>("北纬30度");
        info(p1);

        Point<Integer> p2 = new Point<Integer>(20);
        info(p2);


    }

    /*用通配符?可以想用什么类型的引用类型就用什么样的引用类型*/
    public static void info(Point<?> point){
        System.out.println(point.getX());
    }
}

1.5受限泛型

在引用传递中,在泛型操作中也可以设置一个泛型对象的范围上限范围下限。范围上限使用extends关键字声明,表示参数化的类型可能是所指定的类型或者是此类型的子类,而范围下限使用super进行声明,表示参数化的类型可能是所指定的类型或者此类型的父类型。

语法:

[设置上限]
声明对象: 类名称<? extends 类> 对象名称;
定义类:  [访问权限] 类名称<泛型标识 extends 类>{}

[设置下限]
声明对象: 类名称<? super 类> 对象名称;
定义类:  [访问权限] 类名称<泛型标识 super 类>{}
public class Point<T> {
    private T x;

    public Point() {
    }

    public Point(T x) {
        this.x = x;
    }

    public T getX() {
        return x;
    }

    public void setX(T x) {
        this.x = x;
    }


    @Override
    public String toString() {
        return "x=" + x ;
    }
}
public class test {
    public static void main(String[] args) {

        //Integer 是Number的子类
        Point<Integer> p1 = new Point<>(20);
        fun(p1);

        //用本身
        Point<Number> p2 = new Point<>(30.6);
        fun1(p2);

        //Object是Number的父类
        Point<Object> p3 = new Point<>("yksb");
        fun1(p3);


        /*反例:这里使用Number的子类会报错*/
        /*Point<Double> p4 = new Point<>(30.1);
        fun1(p4);*/
    }


    // 设置下限 使用super关键字 参数类型要么是本身要么是其父类 所以这里要么是Number 要么是Number的父类
    public static void fun1(Point<? super Number> point){
        System.out.println(point);
    }


    // 设置上限 使用extends关键字 参数类型要么是本身要么是其子类 所以这里要么是Number 要么是Number的子类
    public static void fun(Point<? extends Number> point){
        System.out.println(point);
    }
}

1.6 泛型接口

jdk1.5以后,泛型也可以定义在接口上了,定义接口的泛型和定义泛型类语法相似。

语法:
public interface 接口名<泛型标志,泛型标志....>{
//静态常量
//抽象方法。
}
//泛型抽象类
 interface USB<T> {
    public static final String NAME = "";//常量的命名必须全部大写
    T fun(T t);//定义抽象方法
}
/*
第一种定义泛型接口类型:
子类实现父类的时候可以与父类有相同的泛型 
即可以先不确定泛型类型最后实现的时候用
*/
public class Mouse <T> implements USB<T>{
    @Override
    public T fun(T t) {
        return t;
    }
}
/*
第二种定义泛型接口类型:
子类实现父类的时候,可以先确定泛型类型
*/
public class Upan implements USB<String>{
    @Override
    public String fun(String t) {
        return "I am Upan";
    }
}
public class Test {
    public static void main(String[] args) {
        Upan u = new Upan();
        System.out.println(u.fun("123"));

        Mouse<Integer> mouse = new Mouse<>();
        Integer fun = mouse.fun(123);
        System.out.println(fun);
    }
}

1.7泛型方法

泛型方法的定义与其所在的类是否是泛型类是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。

【泛型方法的简单定义】

[访问权限] <泛型标识> 泛型标识  方法名称(泛型标识 参数名称)
public class Demo {
    public static void main(String[] args) {
        String name = Student.fun("zs");
        Integer age = Student.fun(1);
        Double height = Student.fun(180.0);
    }
}

class Student{
    public static <T> T fun(T t){
        return t;
    }
}

2.Java高级–注解

注释: java不会编译注释的内容,注释给程序员看的。

注解: 它是程序看,当程序看到这个注解时,就应该解析它。

分类:
	1. 预定义注解
	2. 自定义注解
	3. 元注解

2.1 预定义注解

预定义注解就是JDK自带的一些注解,该注解被JVM而解析。

1. @Override: 重写时注解。
		特点:该方法必须与父类中的方法名相同,参数相同,返回类型相同,
		修饰符不能小于父类方法的修饰符,抛出的异常不能大于父类的
2. @Deprecated: 表示已过时。
		但是还可以用,点进入该方法可以提示有取代该方法的最新方法
3. @SuppressWarnings: 表示压制警告。
	@SuppressWarnings(value = "all") //抑制警告 一般不用。
4. @FunctionInterface: 表示函数式接口。
		表示该接口中有且仅有一个抽象方法,如果写入多个抽象方法会报错

2.2 自定义注解。—初级

语法:
	public @interface 注解名{
	  //注解属性
	}
使用自定义注解范围:
	在类,方法 ,属性上加@注解名
@interface MyAnnotation {
    //注解属性
}
@MyAnnotation
public class Info {

    @MyAnnotation
    public String name;

    @MyAnnotation
    public void show(){
        System.out.println("show================="+name);
    }
}
public class Test {
    public static void main(String[] args) {
        Info info = new Info();
        info.name = "aa";
        info.show();
    }
}

上面使用注解和没有使用注解效果一样
但是使用注意: 使用注解和不使用注解区别是:

注解本身没有任何意义,它只有被解析了,才会赋予真正的意义。

我们后会使用反射来对象注解进行解析。

像:@Override 它被JVM解析,从而使其具有相应的意义。

​ @Controller @RequestMapping 它被Spring框架解析,所以具有相应的意义。

2.3 元注解

元注解的作用就是负责注解其他注解,元注解有以下6种

@Retention:指定其所修饰的注解的保留策略
@Document:该注解是一个标记注解,用于指示一个注解将被文档化
@Target:用来限制注解的使用范围
@Inherited:该注解使父类的注解能被其子类继承
@Repeatable:该注解是Java8新增的注解,用于开发重复注解
类型注解(Type Annotation):该注解是Java8新增的注解,可以用在任何用到类型的地方
  1. @Target(value=“”): 作用限制注解使用的位置。

       /** 表示可以作用在类,接口,枚举 */
     	TYPE,
    
     	/** 属性 */
     	FIELD,
    
     	/** 普通方法上 */
     	METHOD,
    
     	 /** 方法参数 */
     	PARAMETER,
    
         /** 构造方法上 */
     	CONSTRUCTOR,
    
     	 /** 局部变量 */
     	LOCAL_VARIABLE
    
//value值是一个数组,所以使用两个以上的值就要用数组的定义方法 该值表示可以作用在类 接口 枚举 以及 普通方法上
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@interface MyAnnotation {
	//属性
}
  1. @Retention: 注解什么时候生效(默认时源码)

       java经历了那些阶段===> 源码阶段-->字节码阶段--->运行阶段
     
     	/**
     	* 源码时生效
     	*/
     	SOURCE,
    
     	/**
     	* 字节码时生效
     	*/
     	CLASS,
    
     	/**
     	* 运行时生效。
     	* 在JVM内存中还有该注解。
     	 都会被设置为运行时有效
     	 */
     	RUNTIME
    
//表示注解什么时候生效--source--class[默认字节码有效]---runtime[反射可验证]
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation {

}

  1. @Documented 当生产API文档时使用该注解

     //生成api文档时使用该注解
     @Documented
     @interface MyAnnotation {
    
     }
    
  2. @Inherited 是否运行被子类继承。

@Inherited
@interface MyAnnotation {
    String value(); //如果设置default默认值,那么在使用该注解时可以不为该属性赋值。
    int age() default 15;
}
//如果只有一个属性 value可以省略不写 但是该属性名必须为value
@MyAnnotation("zs")
class Base {
}
//@Inherited注解指定注解具有继承性,如果某个注解使用@Inherited进行修饰,则该类使用该注解时,其子类将自动被修饰
public class InheritDemo extends Base {
    public static void main(String[] args) {
        MyAnnotation annotation = InheritDemo.class.getAnnotation(MyAnnotation.class);
        //输出MyAnnotation中获取的注解成员信息
        System.out.println(annotation.value()+"======"+annotation.age());

        //打印InheritDemo类中是否具有@MyAnnotation修饰
        System.out.println(InheritDemo.class.isAnnotationPresent(MyAnnotation.class));
    }
}

多个属性的情况下的使用:

//@Inherited注解指定注解具有继承性,如果某个注解使用@Inherited进行修饰,则该类使用该注解时,其子类将自动被修饰
@Inherited
@interface MyAnnotation {
    String value(); 
    int age() default 15;//设置default默认值,那么在使用该注解时可以不为该属性赋值。
    String[] hobby();
}
//如果只有一个属性 value可以省略不写 但是该属性名必须为value 如果存在多个属性必须要写value="" hobby=""
@MyAnnotation(value = "zs",hobby = {"游泳,唱歌"})
class Base {
}
//@Inherited注解指定注解具有继承性,如果某个注解使用@Inherited进行修饰,则该类使用该注解时,其子类将自动被修饰
public class InheritDemo extends Base {
    public static void main(String[] args) {
        MyAnnotation annotation = InheritDemo.class.getAnnotation(MyAnnotation.class);
        //输出MyAnnotation中获取的注解成员信息
        System.out.println(annotation.value()+"======"+annotation.age()+"====="+ Arrays.asList(annotation.hobby()));

        //打印InheritDemo类中是否具有@MyAnnotation修饰
        System.out.println(InheritDemo.class.isAnnotationPresent(MyAnnotation.class));
    }
}

2.4 自定义注解—高级

@interface 注解名{
     数据类型 属性名() default 默认值;
}

数据类型: 基本类型,字符串类型,枚举类型【常量】,注解类型,数组类型
【自定义注解的数据类型必须是上面这些类型】

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值