JAVA学习笔记(十二)(注解与反射)

本文详细介绍了Java中的注解(Annotation),包括其作用、格式、内置注解、元注解以及自定义注解的创建。同时,深入探讨了反射机制,包括反射的优点和缺点,以及获取Class对象、方法对象、属性对象的方式,并展示了如何通过反射执行方法和修改属性。此外,还讲解了如何获取泛型类型信息。
摘要由CSDN通过智能技术生成

注解(Annotation)

注解是一种面向对象的注释

Annotation是从JDK5.0开始引入的新技术.

1.Annotation作用:
  • 不是程序本身,可以对程序作出解释(这一点和注释(comment)没什么区别)
  • 可以被其他程序(比如:编译器等)读取.
2.Annotation格式:
  • 注解是以"@注释名"在代码中存在的,还可以添加一些参数值, 例
    如:@SuppressWarnings(value="unchecked").
3.Annotation在哪里使用?
  • 可以附加在package , class , method , field等上面,相当于给他们添加了额外的辅助信息,

我们可以通过反射机制编程实现对这些元数据的访问

1.内置注解

  • @Override :定义在java.lang.Override中, 此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明.
  • @Deprecated :定义在java.lang.Deprecated中, 此注释可以用于修辞方法,属性,类,表示不鼓励程序员使用这样的元素, 通常是因为它很危险或者存在更好的选择.
  • @SuppressWarnings : 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息.
    • 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是E经定义好了的,我们选择性的使用就好了.
      @SuppressWarnings("all") @SuppressWarnings("unchecked") @SuppressWarnings(value={"unchecked" "deprecation"})等等.

@SuppressWarnings()`:镇压警告 使程序不再有警告提示
必须传递镇压的参数

2.元注解

作用

负责注解其他注解, Java定义了4个标准的meta- annotation类型,他们被用来提供对其他annotation类型作说明.
这些类型和它们所支持的类在java.lang.annotation包中可以找到.( @ Target , @Retention,@Documented , @Inherited )

  • @Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方)。需要传递参数
  • @Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期
    • (SOURCE < CL ASS < RUNTIME)

Reteniton定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy枚举中

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}
  • SOURCE
    被-编译器忽略
  • CLASS
    注解将会被保留在Class文件中,但在运行时并不会被VM保留。这是默认行为,所有没有用Retention注解的注解,都会采用这种策略。
  • RUNTIME
    保留至运行时。所以我们可以通过反射去获取注解信息。
  • @Documented:描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

  • @Inherited:@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

  • @Document:说明该注解将被包含在javadoc中

  • @Inherited: 说明子类可以继承父类中的该注解

3.自定义注解

使用@interface自定义注解且自动继承java.lang.annotation.Annotation接口

使用default来设置默认值

如果注解内只有一个值,建议类型名设置为value。因为此处有一个不成文的规范。当类型名为value时,使用注解的时候可以不写value=直接写传递的参数。
但要是参数名不为value,那么使用注解时value=不可省略

分析:

  • @ interface用来声明一个注解,格式: public @ interface注解名{定义内容}
  • 其中的每一个方法实际上是声明了一个配置参数.
  • 方法的名称就是参数的名称.
  • 返回值类型就是参数的类型(返回值只能是基本类型,Class,String , enum ).
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员, 一般参数名为value
  • 注解元素必须要有值, 我们定义注解元素时,经常使用空字符串,0作为默认值.

反射(Reflection)

一.概述

Java中可以通过包名+类名直接可以定位到具体的某个类,而且是唯一的一个类,当我们知道了这个类,我们也就知道了这个类的一切(万物皆对象)。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象) ,这个对象就包含了完整的类的结构信息。
我们可以通过这个对象看到类的结构。这个对象就像一 面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

在这里插入图片描述

  • class类可以说是管理反射的类

二.反射机制

Reflection 是 Java 程序开发语言的特征之一,它允许运行状态中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息
                                                    – 百度百科

反射是以后学习框架设计的灵魂,可以用来获取任意类的名称、成员变量和方法等对象信息,并且还能改变对象信息,也可以调用该对象的方法。

重要:

  • 一个类只有一个class对象, JRE为每一个类都保留一个不变的Class类型的对象,唯一
  • Class本身也是一个类
  • Class对象只能由系统创建,我们只可以去得到
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源, 针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
反射优点
  • 可以实现动态创建对象和编译(在程序运行过程中,操作对象)
  • 可以降低代码的耦合度,提供程序扩展性
反射缺点
  • method.setAccessible(true);破坏分装性
  • 对性能有影响
反射主要API
  • java.lang.Class :代表- 个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field :代表类的成员变量
  • java.lang.reflect.Constructor :代表类的构造器

三.获取Class对象

1.获取class对象的三种方式

  1. 类名.getClass()
  2. 类名.class

以上两种方式,其类必须存在

  1. Class.forName(“类路径”)
import com.pangsir.model.Teacher;

public class A_获取Class对象的方式 {
	public static void main(String[] args) throws ClassNotFoundException {
		Teacher t1 = new Teacher();
		Class c1 = t1.getClass();
		System.out.println("方式1:"+c1);//调用重写后的toString方法

		Class c2 = Teacher.class;
		System.out.println("方式2:"+c2);

		//上述的两种方式,我们使用的class是必须存在

		Class c3 =  Class.forName("com.pangsir.model.Teacher");
		System.out.println("方式2:"+c3);

	}
}

2.获取类的信息

package com.Demo03;

import java.lang.reflect.Modifier;

public class A_获取class对象的方式 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz =  Class.forName("yue.model.Teacher");
        String className = clazz.getName();
        System.out.println("类名的全路径 = " + className);
        String classSimpleName = clazz.getSimpleName();
        System.out.println("类名 = " + classSimpleName);
        int classType = clazz.getModifiers();
        System.out.println("类的修饰符 = " + classType);
        System.out.println("类的修饰符 = " + Modifier.toString(classType));
        String packageName = clazz.getPackage().toString();
        System.out.println("包名="+packageName);
        System.out.println("========打印类的信息==============");
        System.out.println(packageName+";");
        System.out.println(Modifier.toString(classType) +" class "+classSimpleName +"{");
        System.out.println("}");
    }
}


结果:
在这里插入图片描述

3.创建对象的方式

  1. new关键字
    Teacher t1 = new Teacher();
  2. newInstance()方法
    Class c1 = Teacher.class;
    Teacher t2 = (Teacher)c1.newInstance();

以上两种方式类必须存在

  1. Class.forName()方法
    Class clazz = Class.forName("com.pangsir.model.Teacher");
    Teacher t3 = (Teacher)clazz.newInstance();
  2. 获取构造方法的对象Constructor(该类只能是公共类:public修饰
Class clazz = Class.forName("com.pangsir.model.Student");
		Constructor<Student> c1 = clazz.getConstructor();//修饰符public的构造方法
		Student t4 = c1.newInstance();
  1. 当类以“private”修饰,如何获取信息
  • 无参
Class clazz = Class.forName("com.pangsir.model.Student");
		Constructor<Student> c1 = clazz.getDeclaredConstructor();
		c1.setAccessible(true);//破坏了封装性,允许访问
		Student s1 = c1.newInstance();
  • 有参
		Constructor<Student> c2 = clazz.getDeclaredConstructor(String.class);
		c2.setAccessible(true);//破坏了封装性,允许访问
		Student s2 = c2.newInstance("天河");

四.获取方法对象

  • 访问修饰符只能是public
package com.pangsir.demo03;

import com.pangsir.model.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class D_获取成员方法对象 {
	public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("com.pangsir.model.Student");
		
		//一.只能获取 public 修饰符
		Method[] methodArray = clazz.getMethods();
		//遍历所有
		for (Method method : methodArray) {
			System.out.println(method);
		}
		//通过方法名,方法传递参数类型来获取一个 方法对象
		Method method01 = clazz.getMethod("m1",int.class);
		System.out.println("method01 = " + method01);
		
	}
}

clazz.getDeclaredMethod方法

  • 不限访问修饰符,破坏封装性
  • 返回一个数组 方法对象反射的类或接口的所有的声明方法
  • 不包括继承的方法
	
		Method[] methodArray = clazz.getDeclaredMethods();
		for (Method method : methodArray) {
			System.out.println("method = " + method);
		}

		Method method01 = clazz.getDeclaredMethod("m2",Integer.class);
		System.out.println("method01 = " + method01);

结果:
在这里插入图片描述

五.方法如何执行(方法对象.invoke(执行对象,参数))

package com.pangsir.demo03;

import com.pangsir.model.Student;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class E_方法的执行 {
	public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("com.pangsir.model.Student");
		Constructor<Student> studentConstructor = clazz.getDeclaredConstructor();
		studentConstructor.setAccessible(true);
		Student student = studentConstructor.newInstance();

		Method method = clazz.getDeclaredMethod("m4",int[].class);
		method.setAccessible(true);//破坏分装性

		int[] arr = {10,20,30};
		int result = (int)method.invoke(student,arr);//方法在student对象下执行
		//等价于student.m4(arr),但是破坏了封装性;
		System.out.println(result);
	}
}

六.获取属性对象

1.获取成员变量

jdk文档查找方法:

  • Field : getField(String name) :返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
  • Field[] : getFields() :返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。
  • Field : getDeclaredField(String name) :返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。
  • Field[] : getDeclaredFields() :返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。

测试类:

package yue.model;

public class Cat<type> {
    private String name;
    int age;
    protected String type;
    public Integer weight;
}

获取成员变量代码:

package com.Demo03;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class C_获取成员变量的对象 {
    public static void main(String[] args) throws Exception{
        System.out.println("获取所有变量==============================");
        System.out.println();
        Class clazz =  Class.forName("yue.model.Cat");
        Field[] fields  = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("field = " + field);
        }
        System.out.println();
        System.out.println("获取具体某一个变量==============================");
        System.out.println();
       /* //1.获取的的成员变量名称不存在
        Field f1 = clazz.getDeclaredField("dsf");
        System.out.println("f1 = " + f1);//结果:Exception in thread "main" java.lang.NoSuchFieldException: dsf
*/
        //2.成员变量存在
        Field f2 = clazz.getDeclaredField("name");
        System.out.println("变量修饰符(int类型)"+f2.getModifiers());
        System.out.println("变量修饰符(string类型)"+ Modifier.toString(f2.getModifiers()));
        Class typeClass = f2.getType();//返回的是一个class
        System.out.println("变量类型(单独)"+typeClass.getSimpleName());
        System.out.println("变量类型(全部)"+typeClass.getName());
        System.out.println("变量名字"+f2.getName());
    }
}

结果:
在这里插入图片描述

2.成员变量的修改和访问

package com.yue.demo03;

import com.yue.model.Dog;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class G_获取成员变量的值和赋值 {
	public static void main(String[] args) throws Exception {
	//反射之前的写法:
		/*Dog d1 = new Dog();
		System.out.println("dogName = "+d1.getDogName());
		d1.setDogName("家犬");
		System.out.println("dogName = "+d1.getDogName());*/
	//反射写法
		Class clazz = Dog.class;//获取Class对象
		Dog dog = (Dog)clazz.newInstance();//实例化该对象
		Field dogNameField = clazz.getDeclaredField("dogName");//获取成员变量对象
		dogNameField.setAccessible(true);//允许访问,破坏封装性
		System.out.println(dogNameField.get(dog));//获取值
		dogNameField.set(dog,"小家犬");//赋值
		System.out.println(dogNameField.get(dog));//获取值
	}
}

七.获取泛型类型

package yue.model;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Dog extends Animal<Dog,Teacher>{
    public static void main(String[] args) {
        Object dog = new Dog();
        System.out.println("返回此Object类运行时真正的类");
        Class clazz = dog.getClass();
        System.out.println(clazz);
        System.out.println();
        Class parentClass = clazz.getSuperclass();
        System.out.println("获取该类父类的类型对象:"+parentClass);
        Type type = clazz.getGenericSuperclass();
        System.out.println("获取该类父类的类型:"+type);
        //输出:yue.model.Animal<yue.model.Dog> =>父类的方法中带有了子类的参数
        System.out.println();
        ParameterizedType type1 = (ParameterizedType) type;//将type强转为ParameterizedType类型
        Class fxClass01 = (Class) type1.getActualTypeArguments()[0];
        System.out.println("获取第一个泛型类型:" + fxClass01);
        Class fxClass02 = (Class) type1.getActualTypeArguments()[1];
        System.out.println("获取第二个泛型类型:" + fxClass02);


    }
}
class Animal<T,U> {
}

结果:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月色夜雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值