Java教程

Java基础知识-泛型、反射、注解

本文主要是介绍Java基础知识-泛型、反射、注解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、泛型

泛型是 Java 中另一个使用非常广泛的特性,泛型中的「泛」指的是参数类型不固定, 也就是说什么数据类型都可以,它还有另一个名字,就是「参数化类型」——也就是说不仅 数据本身是参数,数据类型也可以被指定为参数——类、接口或者方法可以被不同类型的参数所重用。你只需告诉编译器要使用什么类型,剩下的细节交给它来处理。

1、泛型类

通过泛型可以完成对一组类的操作对外开放相同的接口

//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{ 
    //key这个成员变量的类型为T,T的类型由外部指定  
    private T key;

    public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
        this.key = key;
    }

    public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
        return key;
    }
}
//可以传入类型不同的参数
Generic generic = new Generic("1");
Generic generic1 = new Generic(1);
Generic generic2 = new Generic(1.11);
Generic generic3 = new Generic(false);

2、泛型接口

未传入泛型实参时,在声明类的时候,需将泛型的声明也一起加到类中。

public interface Generator<T> {
    public T next();
}

class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}

传入实参时:

public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

3、泛型方法

//可变参数
public <T> void printMsg( T... args){
    for(T t : args){
        Log.d("泛型测试","t is " + t);
    }
}
printMsg("111",222,"aaaa","2323.4",55.55);

4、通配符

  • ?:表示通配符,它无法声明变量,但在泛型中却非常重要,它表示可以持有任何类型, 例如:List<?> list,表示 List 集合中可以存放任何数据类型(虽然它本来就可以这么做)。
  • <? extends T>是上界通配符,表示只允许 T 及 T 的子类调用,所以如果传入的类型不 是 T 或 T 的子类,编译会报错。
  • <? super T>是下界通配符,表示只允许 T 及 T 的父类调用,所以如果传入的类型不是 T 或 T 的父类,编译会报错。

练习1: 上界通配符有一个特性:只能获取,不能添加,请通过搜索引擎了解这句话的含义并 用代码实现它

答:上界只取不放是指当操作一个泛型参数为上界通配符<? extends T>的引用时,只能操作其返回泛型参数相关的方法(返回值为T的方法)而无法操作设置泛型参数相关(修改T)的方法。

练习2: 下界通配符有一个特性:只能添加,不能获取,请通过搜索引擎了解这句话的含义并 用代码实现它。

答:而下界只存不取则是指当操作一个泛型参数为下届通配符<? super T>的引用时,只能操作设置泛型参数相关(修改T)而无法操作返回泛型参数相关的方法(返回值为T的方法)。

package com.company;

public class Task08_01 {
    public static void main(String[] args){
        /*
         * 上界<? extends T>:不能往里存,只能往外取
         * <? extends Fruit>会使盘子里放东西的set()方法失效,但是get()方法还有效
         * */
        Plate<? extends Fruit> p1 = new Plate<Apple>(new Apple());
        /*
         * p.set(new Fruit()); 报错
         * p.set(new Fruit());报错
         */

        //读取出来的东西只能存放在Fruit或者它的基类里
        Fruit newFruit1 = p1.get();
        Object newFruit2 = p1.get();
        //Apple newFruit3 = p.get();报错

        /*
        * 下界<? super T>:不影响往里存,但是往外取的只能放在Object对象里
        * <? super Fruit>:get()方法获得的对象只能放在Object对象里,set()方法正常
        * */
        Plate<? super Fruit> p2 = new Plate<Fruit>(new Fruit());
        //存入元素正常
        p2.set(new Fruit());
        p2.set(new Apple());
        //取出来的东西只能放在Object对象中
        Object newFruit4 = p2.get();
    }
    static class Fruit{

    }

    static class Apple extends Fruit{

    }

    static class Plate<T>{
        private T item;
        public Plate(T t){
            item  = t;
        }
        public void set(T t){
            item = t;
        }
        public T get(){
            return item;
        }
    }


}

总结:

  • 频繁往外读取内容的,适合用上界Extends。
  • 经常往里插入的,适合用下界Super。

二、反射

Java 有一种特性,能够在程序运行时获取某个类对象的所有的属性、方法、包信息和 注解等等,并且还能改变对象的属性值、调用对象方法、验证注解信息,这种动态获取信息、 动态改变属性值和动态调用方法的能力,就是反射(Reflection)。反。反射是将类中各个成分映射成各个对象。

1、获取Class对象

  • 通过类名获取:类名.class
  • 通过对象获取:对象名.getClass();
  • 通过全类名获取:Class.forName(类的全名),这里的类的全名指的是包括包名在内的 对象名称,例如:net.csdn.java.User,就是 User 类的全名

2、获取构造方法

  • public Constructor[] getConstructors():获取所有公有的构造方法
  • public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
  • public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法
  • public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有

3、获取成员变量

  • Field[] getFields():获取所有的"公有字段"
  • Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有
  • public Field getField(String fieldName):获取某个"公有的"字段
  • public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
  • Field --> public void set(Object obj,Object value):设置字段的值

**练习:**使用反射解析某个你创建的类,把类中所有的属性、方法、接口都提取出来。要求: 通过三种不同的方式获取 Class 对象; 修改解析出来的类的属性值,然后再给类添加新的属性; 调用类方法(包括私有方法、静态方法和构造方法),执行后打印结果; 解析出该类的父类对象,并同样修改其父类的属性值并调用父类的方法。

package com.company;

import java.awt.datatransfer.FlavorEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Task09_反射 {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //第一种方式获得Class对象
        Student student1 = new Student();
        Class stuClass = student1.getClass();

        //第二种方式获得Class对象
        Class stuClass2 = Student.class;

        //第三种方式获得Class对象
        try{
            Class stuClass3 = Class.forName("com.company.Student");
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }

        //获取Student类的所有属性
        System.out.println("获取所有属性:");
        Field[] fieldArray = stuClass.getDeclaredFields();
        for(Field f : fieldArray)
            System.out.println(f);

        //为属性设置值
        System.out.println("为属性设置值:");
        Field f = stuClass.getField("name");
        Object obj = stuClass.getConstructor().newInstance();//产生Student对象
        f.set(obj,"FXW");
        Student stu = (Student)obj;
        System.out.println("验证名字"+stu.name);

        //调用构造方法
        System.out.println("调用构造方法");
        Constructor con = stuClass.getConstructor(null);
        System.out.println(con);
        Object obj2 = con.newInstance();

        //调用私有方法
        System.out.println("调用私有方法");
        Method m = stuClass.getDeclaredMethod("show",int.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj,20);
        System.out.println(result);
    }
}

class Student{
    public String name;
    protected int age;
    char sex;
    private String number;

    public Student(){
        System.out.println("共有无参构造方法");
    }

    public Student(String name,int age){
        System.out.println("共有有参构造方法");
    }

    private String show(int age){
        System.out.println("私有方法");
        return "a";
    }
}

在这里插入图片描述

三、注解

1、代码注解:作用在代码上

  • @Override:检查该方法是否是重写方法,如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
  • @Deprecated:标记过时方法,如果使用该方法,会报编译警告
  • @SuppressWarnings:指示编译器去忽略注解中声明的警告

2、元注解:作用在其他注解上

  • @Retention:标识注解怎么保存,是只在代码中,还是编入 class 文件中,或者是在 运行时可以通过反射访问
  • @Documented:标记注解是否包含在用户文档中;
  • @Target:标记注解应该是哪种 Java 成员
  • @Inherited:标记注解是继承于哪个注解类(默认注解并没有继承于任何子类)
  • @SafeVarargs:Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数 调用产生的警告
  • @FunctionalInterface:Java 8 开始支持,标识一个函数式接口
  • @Repeatable:Java 8 开始支持,标识注解可以在同一个声明上使用多次

练习1: 自定义一个 ElementType 类别为 METHOD,RetentionPolicy 类别为 RUNTIME 的 注解,任务要求:创建一个类,类的方法使用自定义的注解; 通过反射解析类注解,判断该方法是否有注解,如果有则打印出注解的内容。

注:

  1. RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
  2. RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
  3. RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
package com.company;

import java.lang.annotation.*;
import java.lang.reflect.Method;

public class Task10_01 {
    public static void main(String[] args) throws NoSuchMethodException {
        //获取类
        Class stuClass = Student.class;

        //获取方法
        Method method = stuClass.getMethod("show");

        //获取注解对象
        M m = method.getAnnotation(M.class);

        System.out.println(m.value());
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.METHOD})
    public @interface M {
        String value() default "这是默认注释内容";
    }

    class Student{
        @M
        public void show(){
            System.out.println("a");
        }
    }
}

在这里插入图片描述

练习2: 自定义一个 ElementType 类别为 FIELD,RetentionPolicy 类别为 CLASS 的注解, 任务要求: 创建一个类,给类的某个字段使用自定义的注解; 通过反射解析类注解,判断该方法是否有注解,如果有则打印出注解的内容。

package com.company;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;

public class Task10_02 {
    public static void main(String[] args) throws NoSuchFieldException {
        Class stuClass = Student.class;
        Field f = stuClass.getDeclaredField("name");
        M m = f.getAnnotation(M.class);
        System.out.println(m.value());
    }

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface M {
        String value() default "这是默认注释内容";
    }

    public class Student{
        @M
        public String name;
    }
}

在这里插入图片描述
总结:
@Target(ElementType.TYPE) //接口、类、枚举
@Target(ElementType.FIELD) //字段、枚举的常量

练习3: 模仿上面的嵌套注解,除了可以字段类型的注解外,再加上字段长度和是否可以为空 的注解,并加入到@TableColumn 注解中去

package com.company;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class Task10_03 {
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ColumnLength{
        int value() default 0;
    }

    public @interface ColumnEmptyOrNot{
        @Target(ElementType.FIELD)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface ColumnLength{
            boolean value() default true;
        }
    }

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TableColumn{
        ColumnLength columnlength() default  @ColumnLength;
        ColumnEmptyOrNot columnEmptyOrNot() default @ColumnEmptyOrNot;
    }
}

练习4: 自定义嵌套注解,用来描述用户类中的「岗位」字段。
第一层注解定义岗位所在的部门
第二层注解定义岗位名称和描述。

 //岗位名称
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PostName {
        String postName() default "";
    }

    //岗位描述
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PostDescription {
        String postDescription() default "";
    }

    //岗位所在部门
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Department {
        String departmentName() default "";
        PostName postName() default @PostName;
        PostDescription postDescription() default @PostDescription;
    }

    public class Position {
        @Department(postName = @PostName , postDescription = @PostDescription)
        private String postName;
        private String postDescription;
    }
这篇关于Java基础知识-泛型、反射、注解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!