反射、泛型和注解

一、反射的概述

1.1 概念

  • 不同于通过类new对象,反射是先得到对象,再由对象获取到类及该类的对象,调用该类中的成员属性和成员方法。让代码动态化的基础,有了反射可以做到需要对象的时候才创建对象。

    1.2 过程

  • 先通过反射获取字节码对象(Class类的对象),通过字节码对象里面的方法获取成员属性、成员方法和构造方法。

    二、反射操作

  • 通过反射操作某个类中的属性和方法,要先获取该类的字节码对象。

    2.1 获取字节码对象

  • 方式1:通过Object类中的getClass()

    1. //获取Person类字节码对象,用Object类中定义的getClass();不推荐
    2. Person p1 = new Person();
    3. Class<?> clazz = p1.getClass();
  • 方式2:通过类名调用静态属性class获取

    1. //获取Person类字节码对象,不推荐
    2. Class<?> clazz = Person.class;
  • 方式3:通过Class类中定义的静态方法forName()

    1. //获取Person类字节码对象,推荐方式
    2. Class<?> clazz = Class.forName("com.woniuxy.demo.Person");

    2.2 方法解析

  • getConstructor(Class<?>…parameterType):获取公有的构造方法,参数为可变参数

  • getDeclaredConstructor(Class<?>…parameterType):获取所有的构造方法,一般只有在获取私有构造方法的时候才使用。
  • geteMethod(String name,Class<?>…parameterType):获取公有的方法,参数为方法名和可变参数
  • getDeclaredMethod(String name,Class<?>…parameterType):获取所有的方法,一般只有在获取私有方法的时候才使用。
  • getField(String name):获取公有的属性,参数为属性名称。
  • getDeclaredField(String name):获取所有的属性,一般只是在获取私有属性的时候使用。

    2.3 获取构造方法(目的是创建对象)

  • 获取无参的公有方法 ```java Class<?> clazz = Class.forName(className);

//调用getConstructor()方法获取构造方法的对象 //有可变参数:parameterType:表示的是形式参数的数据类型的字节码文件,如果是无参,传递null Constructor<?> c = clazz.getConstructor(null);

//调用newInstance方法获取Person类的对象 //参数:initargs:实际参数,数据类型要和上面的一致 Person person = (Person)c.newInstance(null); System.out.println(person);

  1. - 获取私有的构造方法
  2. ```java
  3. //获取字节码文件
  4. Class<?> clazz = Class.forName(className);
  5. //获取构造方法对象,包装类和基本数据类型不一样。
  6. Constructor<?> c = clazz.getDeclaredConstructor(int.class,String.class);
  7. //暴力破解私有
  8. c.setAccessible(true);
  9. //调用newInstance方法创建对象
  10. Person person = (Person)c.newInstance(19,"张三");
  11. System.out.println(person);

2.3 用反射操作方法

  • 公有的方法 ```java //获取字节码对象 Class<?> clazz = Class.forName(className);

/*

  • 调用getMethod(),获取方法的对象
  • String name:方法的名称
  • Class<?>…parameterType:方法中参数数据类型的字节码对象 */ Method m = clazz.getMethod(“eat”, null);

/*调用invoke()方法执行eat方法

  • obj:用哪个对象调用该方法
  • args:方法的参数,没有写null */ Object obj = m.invoke(clazz.getConstructor(null).newInstance(null), null); System.out.println(obj); ```
  • 私有的成员方法

    1. //获取字节码对象
    2. Class<?> clazz = Class.forName(className);
    3. Method m = clazz.getDeclaredMethod("sleep", null);
    4. m.setAccessible(true);
    5. m.invoke(clazz.getConstructor(null).newInstance(null), null);

    2.4 反射操作字段

  • 公有的属性 ```java //获取 字节码对象 Class<?> clazz = Class.forName(className);

//通过字节码对象获取属性的对象 //参数:String name,属性名称 Field field = clazz.getField(“age”);

//通过调用get方法,获取该字段的值 //参数:obj:表是哪个对象调用该字段 Object obj = field.get(clazz.getConstructor(null).newInstance(null)); System.out.println(obj);

  1. - 私有的属性
  2. ```java
  3. //获取字节码对象
  4. Class<?> clazz = Class.forName(className);
  5. //获取属性对象
  6. Field field = clazz.getDeclaredField("name");
  7. //破解私有
  8. field.setAccessible(true);
  9. //调用get方法获取
  10. Object obj = field.get(clazz.getConstructor(null).newInstance(null));
  11. System.out.println(obj);

三、泛型

3.1 泛型的概念

  • 概念:在集合中使用过,作用是用来约束规范集合中存入元素的数据类型,往广泛了定义,用来在不知道数据类型的时候,用一个泛型的字母来进行代替,当传入真实的数据类型时候,将其替换。
  • 泛型存在:自定义泛型方法和自定义泛型类。
  • 举例:

    1. public class Demo1 {
    2. public static void main(String[] args) {
    3. Demo1 d1 = new Demo1();
    4. //定义数组
    5. Integer[] num = {1,2,3,4,5,6};
    6. String[] str = {"a","b","c","d","e"};
    7. //调用交换的方法
    8. d1.change(num,1,3);
    9. //遍历数组
    10. d1.print(num);
    11. System.out.println();
    12. //字符串交换
    13. d1.change(str, 1, 3);
    14. //输出字符串数组
    15. d1.print(str);
    16. }
    17. //定义一个方法,实现数组中任意两个元素的交换
    18. public void change(Integer[] num,int start,int end) {
    19. int temp = num[start];
    20. num[start] = num[end];
    21. num[end] = temp;
    22. }
    23. public void change(String[] str,int start,int end) {
    24. String temp = str[start];
    25. str[start] = str[end];
    26. str[end] = temp;
    27. }
    28. //遍历数组
    29. public void print(Integer[] num) {
    30. for(Integer i:num) {
    31. System.out.print(i+",");
    32. }
    33. }
    34. public void print(String[] str) {
    35. for(String i:str) {
    36. System.out.print(i+",");
    37. }
    38. }
    39. }
  • 总结问题:同样是交换数组中的任意两个元素,因为数据类型的不一样,多写了两个相同功能的方法,有了泛型的概念,可以只用一个方法来实现任意数组中任意两个元素的交换。

    3.2 自定义泛型方法

  • 语法 ```java public 返回值数据类型 方法名(T t){ //其中T代表数据类型,t表示对象或者变量

}

  1. - 将上面的方法进行改造
  2. ```java
  3. public class Demo1 {
  4. public static void main(String[] args) {
  5. Demo1 d1 = new Demo1();
  6. //定义数组
  7. Integer[] num = {1,2,3,4,5,6};
  8. String[] str = {"a","b","c","d","e"};
  9. //交换整形
  10. d1.change(num, 1, 3);
  11. d1.print(num);
  12. System.out.println();
  13. //交换字符串
  14. d1.change(str, 1, 3);
  15. d1.print(str);
  16. }
  17. //定义 一个泛型方法,可以适用于所有数据类型的数组任意两个元素的交换
  18. public <T> void change(T[] t,int start,int end) {
  19. //T:表示数据类型,t表示数组名
  20. T temp = t[start];
  21. t[start] = t[end];
  22. t[end] = temp;
  23. }
  24. public <T> void print(T[] t) {
  25. for(T i : t) {
  26. System.out.print(i+",");
  27. }
  28. }
  29. }

3.3 自定义泛型类

  • 语法: ```java //如果一个类中存在多个泛型方法,那么可以将方法上面的泛型声明,提高到类层面,在类上面进行泛型声明,该类中的方法都 能够使用该泛型 public class 类名 {

}

  1. - 注意:只有普通成员方法才能使用类上面的泛型,静态方法只能在静态方法上面定义泛型。
  2. <a name="xhOkM"></a>
  3. ## 3.4 泛型的通配符及边界
  4. - ? :表示在泛型中的通配符,还不知道是什么数据,则使用?来进行填充。
  5. - 上边界:<? extends Object>,如果用了上边界,那么Object作为上面的边界,该泛型中能传入Object本类及其子类。
  6. - 下边界:<? super Object>,如果用了下边界,那么Object作为下边界,该泛型中只能存入Object的本类及其父类或者接口。
  7. <a name="8rdxn"></a>
  8. # 四、注解
  9. 概念:以@来头的一些关键字,称为注解。
  10. <a name="xfQ3k"></a>
  11. ## 4.1 定义注解
  12. - 关键字:@interface
  13. - 定义:实际本身如果注解的情况下是没有意义的,必须配合上元注解
  14. ```java
  15. public @interface 注解名称{
  16. 注解中的属性;可以给属性赋值
  17. }
  • 以前见过的注解:

@override:重写注解
@Test:junit单元测试注解

4.2 元注解

元注解表示在jdk中已经定义好的注解,专门用来给注解使用的注解。

  • 1、Retention:用来规定该注解作用的时机,一般使用RUNTIME表示在运行时起作用,CLASS字节码文件、RESOURCE源码。
  • 2、Target:用来规定该注解使用的位置,位置包括:类(接口)、包、方法、属性、局部变量、方法中的形式参数。

对应:
在类或者接口上使用:TYPE
在包上使用:PACKAGE
在方法上使用:METHOD
在属性上使用:FIELD
在形式参数使用:PARAMETER
在局部变量使用:LOCAL_VARIABLE

  • 3、Document:表示在生成javadoc文档的时候使用。
  • 4、Inherited:表示注解的继承关系。
  • 定义一个有效注解:
    1. @Retention(RetentionPolicy.RUNTIME)//设置该注解的作用位置是在运行时起作用
    2. @Target(ElementType.PARAMETER)//说明该注解只能在类或者接口上使用
    3. public @interface 注解名称{
    4. 注解中的属性;可以给属性赋值
    5. }