Java进阶--注解与反射

Java进阶--注解与反射

  • 注解与反射
    • 注解
    • 反射
      • 反射的功能及优缺点
      • 通过反射获取Class类的方法
        • 通过反射获取类的信息
        • 通过反射创建类的对象
        • 通过反射操作泛型
        • 通过反射获取注解
    • 其他相关文章

注解与反射

注解

注解是JDK5.0引入的新技术。注解不是程序本身,但可以对程序做出解释。且可以被其他程序(如:编译器)读取。
注解的格式是 “@注释名”。还可以添加一些数值,如抑制警告,@SuppressWarnings(value=“unchecked”) 。
注解可分为:内置注解,元注解,自定义注解。

  • 内置注解
    • @Override :重写方法
    • @Deprecated :不推荐程序员使用,但是可以使用,或者存在更好的方法
    • @SuppressWarnings(value=“”) :抑制警告
public class testAnnotation extends Object {
    //重写方法
    @Override
    public String toString(){
        return super.toString();
    }
    //不推荐程序员使用,但是可以使用,或者存在更好的方法
    @Deprecated
    public static void test(){
        System.out.println("Deprecated");
    }

    public void test1(){

    }
}
  • 元注解(meta-annotation):负责注解其他注解
    • @Target :用于描述注解的使用范围(被描述的注解可以用在什么地方)
    • @Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期。SOURCE《CLASS《RUNTIME
    • @Documented :说明该注解将被包含在javadoc中
    • @Inherited :说明子类可以继承父类中的该注解
@MyMetaAnnotation
public class testMetaAnnotation {
    public void test(){

    }
}

//定义一个注解
@interface MyAnnotation{

}

//定义一个元注解
//Target用于描述注解的使用范围(被描述的注解可以用在什么地方)
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Retention表示我们的注解在什么地方还有效
@Retention(value = RetentionPolicy.RUNTIME)
//Documented表示是否将我们的注解生成在JAVAdoc中
@Documented
//Inherited说明子类可以继承父类中的该注解
@Inherited
@interface MyMetaAnnotation{

}
  • 自定义注解:使用@interface 注解名,自定义一个注解,自动继承java.lang.annotation.Annotation接口
    Java进阶--注解与反射_第1张图片

    这里的RetentionPolicy value();方法,实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值的类型就是参数的类型(返回值只能是基本类型,Class,String,enum)。可以通过default来声明参数的默认值,如果只有一个参数成员,一般参数名为value,注解元素必须有值,定义注解元素时,经常用(空字符串,0)作为默认值。

public class tesDefineAnnotation {
    //注解可以显示赋值,如果没有默认值必须赋值
    @MyAnnotation2(name = "yang", age = 20, id = 1234, schools = "家里蹲")
    public void test(){}

    @MyAnnotation3("yang")
    public void test1(){}
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //注解的参数:参数类型+参数名();
    //default "" 默认为空
    String name() default "";
    int age() default 0;
    int id() default -1;//如果默认值为-1,代表不存在

    String[] schools() default {"清华"};
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    //只有一个参数,且为默认名称value时,添加注解时可以省略value=,其他名称不行
    String value();
}

反射

动态语言:在运行时可以改变其结构的语言,如Object-C,C#,JavaScript,PHP,Python等。静态语言:运行时结构不可变的语言,如Java,C,C++。Java作为静态语言,因为反射机制拥有了动态性。

反射机制允许程序在执行期间借助于Reflection API获取任何类的内部信息。
Java进阶--注解与反射_第2张图片

简单的反射案例

public class testReflection {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的Class对象
        Class c=Class.forName("com.yang.learning.AnnotationAndReflection.User");
        System.out.println(c);

        Class c1=Class.forName("com.yang.learning.AnnotationAndReflection.User");
        Class c2=Class.forName("com.yang.learning.AnnotationAndReflection.User");
        //一个类在内存中只有一个Class对象
        //一个类被加载后,类的整个结构都会被封装在Class对象中
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
    }

}
//实体类pojo,entity
class User{
    private String name;
    private int id;
    private int age;

    public User(){}

    public User(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    private void test(){}

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

反射的功能及优缺点

  • 在运行时
    • 判断任意一个对象所属的类
    • 构造任意一个类的对象
    • 判断任意一个类所具有的成员变量和方法
    • 获取泛型信息
    • 调用任意一个对象的成员变量和方法
    • 处理注解
    • 生成动态代理

优点:可以实现动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响

通过反射获取Class类的方法

  • 已知具体的类,通过class属性获取。该方法最为安全可靠,程序性能最高。
    • Class c3=Student.class;
  • 已知某个类的实例,调用该实例的getClass()方法获取
    • Class c1=person.getClass();
  • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
    • Class c2=Class.forName(“com.yang.learning.AnnotationAndReflection.Student”);
  • 内置基本数据类型可以直接用类名.Type获取
    • Class c4=Integer.TYPE;

具体代码如下:

public class testClassMethod {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person=new Student();
        System.out.println("这个人是:"+person.name);

        //方式一:通过对象获得
        Class c1=person.getClass();
        System.out.println(c1.hashCode());

        //方式二:forname获得
        Class c2=Class.forName("com.yang.learning.AnnotationAndReflection.Student");
        System.out.println(c2.hashCode());

        //方式三:通过类名.class获得
        Class c3=Student.class;
        System.out.println(c3.hashCode());

        //方式四:基本内置类型的包装类都有一个Type属性
        Class c4=Integer.TYPE;
        System.out.println(c4);

        //获得父类类型
        Class c5=c1.getSuperclass();
        System.out.println(c5);
    }
}

class Person{
    public String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    public Student() {
        this.name="学生";
    }
}

class Teacher extends Person{
    public Teacher() {
        this.name="老师";
    }
}

通过反射获取Class对象后,我们能做什么?

通过反射获取类的信息
public class testGetClassInformation {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1=Class.forName("com.yang.learning.AnnotationAndReflection.User");

        //获得类的名字
        System.out.println(c1.getName());           //获得包名+类名
        System.out.println(c1.getSimpleName());     //获得类名

        //获得类的属性
        System.out.println("=================================");
        Field[] fields = c1.getFields();    //只能找到public属性
//        for(Field field:fields){
//            System.out.println(field);
//        }

        fields = c1.getDeclaredFields();    //可以找到全部属性
        for(Field field:fields){
            System.out.println(field);
        }

        //获得指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        System.out.println("=================================");
        //获得类的方法
        Method[] methods = c1.getMethods();     //获得本类及其父类的全部public方法
        for (Method method:methods){
            System.out.println("正常的:"+method);
        }

        methods=c1.getDeclaredMethods();    //获得本类的全部方法
        for (Method method:methods){
            System.out.println("getDeclaredMethods:"+method);
        }

        //获得指定方法
        Method getName=c1.getMethod("getName",null);
        Method setName=c1.getMethod("setName",String.class);

        System.out.println(getName);
        System.out.println(setName);

        System.out.println("=================================");
        //获得指定的构造器
        Constructor[] constructors = c1.getConstructors();
        for(Constructor constructor:constructors){
            System.out.println(constructor);
        }

        constructors=c1.getDeclaredConstructors();
        for(Constructor constructor:constructors){
            System.out.println("##"+constructor);
        }

        //获得指定构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println(declaredConstructor);
    }
}
通过反射创建类的对象
  • 调用Class对象的newInstance()方法创建类的对象
    • 类必须有一个无参构造器
      • 如果没有无参构造器需要先获取有参构造器,再将参数传入,进行实例化
    • 类的构造器的访问权限需要足够
  • 调用Class类的方法
    • 通过反射,利用Method类的getMethod方法获取类的方法
    • 然后,使用Object类的invoke方法进行调用,并传参
      • Object对应的是原方法的返回值,如果没有返回值,返回null
      • 若原方法是静态方法,形参Object可为null
      • 若原方法形参为空,则Object[] args为null
      • 若原方法为私有方法,则需要再调用invoke前使用setAccessible(true),关闭安全检测
        • setAccessible:作用是启动和禁用安全检测;提高反射的效率,使原本无法访问的私有成员变得可以访问
**public class testReflectionCreateObject {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得class对象
        Class c1=Class.forName("com.yang.learning.AnnotationAndReflection.User");

        //构造一个对象
//        User user=(User) c1.newInstance();      //本质上是调用类的无参构造
//        System.out.println(user);

        //通过构造器创建对象
//        Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
//        User user2 = (User) constructor.newInstance("yang", 123, 18);
//        System.out.println(user2.toString());

        //通过反射调用普通方法
        User user3 =(User) c1.newInstance();
        //通过反射操作方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke激活
        //(对象,”方法的值“)
        setName.invoke(user3,"yang");
        System.out.println(user3.getName());

        //通过反射操作属性
        System.out.println("========================");
        User user4 =(User) c1.newInstance();
        Field name= c1.getDeclaredField("name");
        //不能直接操作私有属性,需要关闭程序的安全检测,属性或者方法的setAccessible(true)
        name.setAccessible(true);
        name.set(user4,"yang1");
        System.out.println(user4.getName());

    }
}**
通过反射操作泛型

Java使用的是泛型擦除机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。

Java新增了ParameterizedType,GenericArrayType,TypeVariable,WildcardType几种类型来代表不能被归一到Class类中的类型,但是有何原始类型齐名的类型

  • ParameterizedType:表示一种参数化类型,如,Collection
  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable:是各种类型变量的公共父接口
  • WildcardType:代表一种通配符类型表达式
public class testGenerics {
    public void test1(Map<String ,User> map, List<User> list){
        System.out.println("test1");
    }

    public Map<String ,User> test2(){
        System.out.println("test2");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = testGenerics.class.getMethod("test1", Map.class, List.class);
        Type[] genericParameterTypes = method.getGenericParameterTypes();   //获取泛型的参数类型
        for(Type genericParameterType:genericParameterTypes){
            System.out.println("#"+genericParameterType);
            if (genericParameterType instanceof ParameterizedType){     //获得的参数类型是否等于结构化的参数类型
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for(Type actualTypeArgument:actualTypeArguments){
                    System.out.println(actualTypeArgument);
                }
            }
        }

        method = testGenerics.class.getMethod("test2", null);
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType){     //获得的参数类型是否等于结构化的参数类型
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for(Type actualTypeArgument:actualTypeArguments){
                System.out.println(actualTypeArgument);
            }
        }
    }
}
通过反射获取注解
public class testReflectionOAnnotation {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.yang.learning.AnnotationAndReflection.Student2");

        //通过反射获得类的注解
        Annotation[] annotations = c1.getAnnotations();
        for(Annotation annotation:annotations){
            System.out.println(annotation);
        }

        //获得类的注解的value的值
        Tableyang Tyang = (Tableyang) c1.getAnnotation(Tableyang.class);
        String value=Tyang.value();
        System.out.println(value);

        //获得类指定的注解
        Field f = c1.getDeclaredField("name");
        Fieldyang annotation = f.getAnnotation(Fieldyang.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@Tableyang("db_student")
class Student2{
    @Fieldyang(columnName = "db_age",type = "int",length = 10)
    private int age;
    @Fieldyang(columnName = "db_id",type = "int",length = 10)
    private int id;
    @Fieldyang(columnName = "db_name",type = "String",length = 3)
    private String name;

    public Student2() {
    }

    public Student2(int age, int id, String name) {
        this.age = age;
        this.id = id;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Student2{" +
                "age=" + age +
                ", id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tableyang{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldyang{
    String columnName();
    String type();
    int length();
}

其他相关文章

  • Java知识梳理:Java知识梳理
  • Java知识梳理–内部类:Java知识梳理–内部类
  • Java常用类–Object类:Java常用类–Object类
  • Java常用类–包装类:Java常用类–包装类
  • Java常用类–String类:java常用类–String类
  • Java常用类–时间相关类:Java常用类–时间相关类
  • Java常用类–BigDecimal类和System类:Java常用类–BigDecimal类和System类
  • Java集合–Collection集合:Java集合–Collection集合
  • Java集合–泛型集合:Java集合–泛型集合
  • Java集合–Map集合:Java集合–Map集合
  • Java进阶–多线程:Java进阶–多线程
  • Java进阶–I/O流:Java进阶–I/O流
  • Java进阶–网络编程:Java进阶–网络编程
  • Java进阶–GUI图形用户界面编程(计算器,贪吃蛇)Java进阶–GUI图形用户界面编程(计算器,贪吃蛇)

你可能感兴趣的:(Java知识梳理,java,java,jvm,spring)