注解和泛型

内容

  • 掌握注解的本质
  • 注解的分类
  • 自定义注解
  • 注解的属性设置
  • 元注解
  • 注解解析
  • 泛型的作用
  • 自定义泛型
  • 泛型的使用
  • 泛型的继承关系以及上下限

一,注解

1.1 注解的概念

**为什么要学习注解?**注解可以帮助大家做很多事情,简化大量的操作。一般情况下,如果想封装框架,自己封装一些东西,都离不开注解。

定义:注解(annotation),一种代码级别的说明。是JDK1.5以后版本引入的一个特性,与类,接口,是在同一个层次。可以声明在包,类,字段,方法,局部变量,方法参数等的前面,用来对这些元素进行说明,注释。

作用分类:

  1. 编写文档:通过代码里标识的注解生成文档
  2. 代码分析:通过代码里标识的注解对代码进行解析
  3. 编译检查:通过对代码里标识的注解让编译器能够实现基本的编译检查 @override

1.2 注解的分类

1.2.1 JDK 内置注解
@Override					检测方法是否是重写父类的方法
@Deprecated					声明该方法是已经过时的方法
@SuppressWarnings("all")	干掉警告
    all代表忽略所有  deprecation:忽略不推荐使用 unchecked:不检查,忽略泛型引起的问题

在这里插入图片描述

1.2.2 自定义注解

格式:

[元注解]
public @interface 注解名称 {}

**本质:**注解本质上就是一个接口,该接口默认继承Annotation接口

public interface 注解名称 extends java.lang.annotation.Annotation{}

**属性:**指接口中的抽象方法

​ 1. 特点:属性的返回值类型有以下取值

​ 基本数据类型,String类型,枚举类型,注解类型,以上类型的数组。

	2. 定义过属性后,在使用时需要给属性赋值。
     - 如果定义属性时,使用default关键字可以给属性默认初始值,那么在使用这个注解时,这个属性可以不进行赋值。
     - 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略。
     - 数组赋值时,值使用{}包裹。如果数组的值只有一个,则{}可以省略。

在这里插入图片描述

1.2.3 元注解

定义:所谓元注解就是标记其他注解的注解

@Target: 用来约束注解可以使用的地方 (方法,类,字段等)
@Retention:用来约束注解的生命周期
@Documented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承

@Target 用来约束注解可以使用的地方 (方法,类,字段等)

在这里插入图片描述

@Retention用来约束注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),其含有如下:

SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义Retention值时,默认值是CLASS,如Java内置注解,@Override@Deprecated@SuppressWarnning等

RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息),如SpringMvc中的@Controller@Autowired@RequestMapping等。

@Documented:描述注解是否被抽取到api文档中

1.2.4 解析注解

定义:解析注解一般情况下就是通过反射获取类 属性或者方法等上边的注解,然后自己定义你的注解要完成什么。

定义注解类:

@Target(ElementType.TYPE)//定义注解可以使用的位置
@Retention(RetentionPolicy.RUNTIME)//定义注解可以保留的位置
public @interface MyAnno {
    String className();
    String methodName();
}

定义一个需要被调用到的类

public class People {

    public void test(){
        System.out.println("执行了People类中的test方法");
    }
}

定义测试类:

@MyAnno(className = "com.bai.test1.People",methodName = "test")
public class PeopleTest {
    public static void main(String[] args) throws Exception {
        //1.解析注解
        //1.1获取该类的字节码对象
        Class<PeopleTest> pc = PeopleTest.class;
        //2.获取上边的注解对象
        MyAnno anno = pc.getAnnotation(MyAnno.class);
        //3.调用注解对象中的抽象方法
        String className = anno.className();//得到上边注解中的类名
        String methodName = anno.methodName();//得到上边注解中的方法名
        System.out.println(className+"==="+methodName);
        //4.根据读取到的className  加载该类进入内存
        Class<?> name = Class.forName(className);
        //5.创建对象
        Object o = name.newInstance();
        //6.根据读取到的方法的名字获取方法对象
        Method method = name.getMethod(methodName);
        //7.执行方法
        method.invoke(o);
    }
}

二,泛型

2.1 泛型概述

​ java 泛型,是java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为【泛型类】、【泛型接口】、【泛型方法】。

​ 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。

​ 所谓泛型:就是指在类创建时不指定类中属性的具体类型,而由外部在声明及实例化对象时指定类型。

2.2 为什么要用泛型

1,类型安全。

​ 泛型的主要目标是提高 Java 程序的类型安全。编译时的强类型检查;通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

2,消除强制类型转换。

​ 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

3,潜在的性能收益。

​ 泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。

	Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。

2.3 泛型的使用(自定义泛型)

2.3.1 泛型类

泛型类:创建对象的时候指明具体的数据类型。

定义格式:

修饰符 class 类名<代表泛型的变量> {  }
<>中的内容是泛型的占位符。和方法参数变量名类似,只不过参数变量名接收的值,而占位符:数据类型
/**
 * 继承中:
 * @param <A>
 * @param <B>
 *
 *     如果子类继承的父类中没有写泛型,可以的
 *     如果子类继承的父类中写了泛型
 *           要么:在子类的泛型中添加上父类中的泛型
 *                  public class Student<A,B,T> extends People<T> { }
 *           要么:在父类的泛型中不写 占位符了,直接指定 确定的数据类型
 *                  public class Student<A,B> extends People<String> { }
 */
public class Student<A,B> extends People<String> {}
public class GenericDemo2<A,B,C>{

    private A name;
    private B age;
    private C sex;

    public GenericDemo2(A name, B age, C sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public A getName() {
        return name;
    }

    public void setName(A name) {
        this.name = name;
    }

    public B getAge() {
        return age;
    }

    public void setAge(B age) {
        this.age = age;
    }

    public C getSex() {
        return sex;
    }

    public void setSex(C sex) {
        this.sex = sex;
    }

    public GenericDemo2() {
    }

    public static void main(String[] args) {
        //GenericDemo2<Object, Object, Object> genericDemo2 = new GenericDemo2<>();
        //创建对象时,传入什么类型,类中的A,B,C占位符就表示什么类型。
        GenericDemo2<String, Integer, String> genericDemo2 = new GenericDemo2<>();
        genericDemo2.setName("123");
        genericDemo2.setAge(12);
        genericDemo2.setSex("sss");
    }
2.3.2 泛型方法

泛型方法:是指调用的时候才去具体的指明是什么数据类型。

格式:

修饰符 <代表泛型的变量> 返回值类型 方法名(参数){  }
public class Teacher {

    public void test(){

    }

    //泛型方法
    public <T> void test1(T t){
        System.out.println(t);
    }
    //静态方法
    public static <E> void test2(E e){
        System.out.println(e);
    }
    //泛型方法 当方法被调用时,确定泛型的数据类型
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        teacher.test1(new ArrayList<>());

        Teacher.test2("asdfads");
    }
}
2.3.3 泛型接口

定义格式:

修饰符 interface 接口名<代表泛型的变量> {  }
public interface GenericInterface<E> {
    void aaa(E e);
}

1、定义类时确定泛型的类型

public class GenericInterfaceImpl implements GenericInterface<String>{
    //1.定义类的时候,直接确定泛型接口占位符的具体数据类型
    @Override
    public void aaa(String s) {

    }

    *//*@Override
    public void aaa(Object o) {

    }*//*
}

2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型

public class GenericInterfaceImpl<E> implements GenericInterface<E>{
    //2.始终不确定类型,当真正的创建实现类的对象时,才确定泛型的数据类型
    @Override
    public void aaa(E e) {
        System.out.println(e);
    } 

确定泛型:

public static void main(String[] args) {
        //如果不传参数,默认还是Object
        GenericInterfaceImpl<Object> objectGenericInterface = new GenericInterfaceImpl<>();
        //传什么类型 泛型就是什么类型
        GenericInterfaceImpl<String> stringGenericInterface = new GenericInterfaceImpl<>();
    }
}
2.3.4 泛型的继承关系以及上下限通配符

泛型通配符是 “?” 任何的参数类型都可以赋值给它,

public static void main(String[] args) {
        ArrayList<String> strings = new ArrayList<>();
        strings.add("123");
        String s = strings.get(0);

        ArrayList<Integer> integers = new ArrayList<>();
        ArrayList<Object> objects = new ArrayList<>();


        //泛型通配符是 “?” 任何的参数类型都可以赋值给它,
        /**
         * 当把确定泛型的变量赋值给不确定类型的变量时,只能取,不能add
         */
        List<?> list1 = strings;
        Object o = list1.get(0);
        System.out.println(o);
        //list1.add("");


        List<?> list2 = integers;
        List<?> list3 = objects;
    }

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

例如:有一个people类,具有子类 Student,Teacher ,具有父类Object

class People{}
class Teacher extends People{}
class Student extends Teacher{}
public class Generic02 {

    public static void main(String[] args) {
        //需求:定义两个方法
        ArrayList<People> people = new ArrayList<>();
        ArrayList<Teacher> teachers = new ArrayList<>();
        ArrayList<Student> students = new ArrayList<>();
        ArrayList<Object> objects = new ArrayList<>();

        Generic02 generic02 = new Generic02();
        /**
         * 测试上限
         *      参数传递时,可以传递people和people的子类类型
         */
        generic02.extendTest(teachers);
        generic02.extendTest(students);
        generic02.extendTest(people);
        //generic02.extendTest(objects);

        /**
         * 测试下限
         *      参数传递时,可以传递people和people的父类类型
         */
        generic02.superTest(objects);
        generic02.superTest(people);
        //generic02.superTest(teachers);
    }

    // ? 只能是People的子类  people就是?的上限
    public void extendTest(List<? extends People> list){}
    // ? 只能是People的父类  people就是?的下限
    public void superTest(List<? super People> list){}
}
2.3.5 泛型擦除

**概念:**将有泛型信息的对象赋值给没有泛型信息的对象,会丢失泛型信息。称之为擦除。JDK1.5之后才出现 让你的泛型只保留在编译期,运行期间是没有 虚拟机而言所有的泛型占位符都是一个Object。如果将一个具备泛型的对象赋值给不具备泛型的对象,那么泛型丢失。

ArrayList<People> peoples = new ArrayList<>();
List p = peoples;
Object o = p.get(0);

将具有泛型的Peoples赋值给没有具体泛型的的p,此时我们从p中取值,发现本来peoples具有的泛型,现在么得了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值