Java反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

要想了解一个类,必须先要获取到该类的字节码文件对象。在Java中,每一个字节码文件,被加载到内存后,都存在一个对应的Class类型的对象。

得到Class的三种方式:

1. 如果在编写代码时, 已知类的名称, 且类已经存在, 可以通过

包名.类名.class 得到一个类的 类对象

2. 如果拥有类的对象, 可以通过

Class 对象.getClass() 得到一个类的 类对象

3. 如果在编写代码时, 知道类的名称 , 可以通过

Class.forName("包名+类名"): 得到一个类的 类对象

注意:上述三种获取类的方式,如果该类已经被加载到内存中了则不会重复加载(双亲委派机制),如果没有则会加载到内存中。(一个class文件在内存中只会存在一个类对象)

public static void main(String[] args) throws ClassNotFoundException {
    //通过包名.类名.class 加载类
    Class c1 = reflect.Person.class;
    System.out.println(c1);

    //通过类的对象获取类的信息
    Person p = new Person();
    Class c2 = p.getClass();
    System.out.println(c2);

    //通过包名.类名的字符串获取类
    Class c3  = Class.forName("reflect.Person");
    System.out.println(c3);
    System.out.println(c1 == c2 && c1 == c3);

}

 

获取构造方法Constructor并创建对象

1.通过class对象 获取一个类的构造方法

为方便演示,先创建一个带有不同属性的Person类

public class Person {
    private String name;
    private int age;
    public String number;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Person() {
    }

    private Person(String name) {
        this.name = name;
        this.age = 1;
    }

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

获取类对象的方式一共分为两种:获取指定的单个构造方法以及获取构造方法数组

由于不同的构造方法的权限不同(可能为私有构造方法),因此上面两类中分别又包含获取正常权限的构造方法和获取所有权限的构造方法。

我们首先来看获取指定的单个构造方法:

  • 通过指定的参数类型, 获取指定的单个构造方法:getConstructor(参数类型的class对象数组)
    Class<Person> p = (Class<Person>) Class.forName("reflect.Person");
    
    //获取单个构造方法
    Constructor<Person> c = p.getConstructor(String.class,int.class);
    //Constructor<Person> c = p.getConstructor(new Class[]{String.class,int.class});
  • 获取所有权限的单个构造方法 :getDeclaredConstructor(参数类型的class对象数组)
    Class<Person> p = (Class<Person>) Class.forName("reflect.Person");
    Constructor<Person> c2 = p.getDeclaredConstructor(String.class);
    c2.setAccessible(true);//如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)
  • 获取构造方法数组:getConstructors();

通过getConstructors可以返回类的所有构造方法,返回的是一个数组因为构造方法可能不止一个;

通过getModifiers可以得到构造方法的类型;

getParameterTypes可以得到构造方法的所有参数,返回的是一个Class数组;

所以我们如果想获取所有构造方法以及每个构造方法的参数类型,可以有如下代码:

//获取所有构造方法
Constructor[] constructors = p.getConstructors();
for(int i = 0; i < constructors.length; i++){
    System.out.println(Modifier.toString(constructors[i].getModifiers())+"参数:");
    Class[] parametertypes = constructors[i].getParameterTypes();
    for(int j = 0; j < parametertypes.length; j++){
        System.out.println(parametertypes[j].getName());
    }
}
  • 获取所有权限的构造方法数组:getDeclaredConstructors();(和上文一致 就不再赘述)
2.根据获取到的Constructor 创建对象
//参数: 是一个Object类型可变参数, 传递的参数顺序必须匹配构造方法中形式参数列表的顺序!
newInstance(Object... para);

案例示意:

Class<Person> p = (Class<Person>) Class.forName("reflect.Person");

//获取单个构造方法
Constructor<Person> c1 = p.getConstructor(String.class,int.class);
//Constructor<Person> c1 = p.getConstructor(new Class[]{String.class,int.class});
//创建对象
Person person = c1.newInstance("张三",12);
System.out.println(person);

//获取单个所有权限的构造方法
Constructor<Person> c2 = p.getDeclaredConstructor(String.class);
c2.setAccessible(true);
Person person2 = c2.newInstance("李四");
System.out.println(person2);

//获取所有构造方法
Constructor[] constructors = p.getConstructors();
for(int i = 0; i < constructors.length; i++){
    System.out.println(Modifier.toString(constructors[i].getModifiers())+"参数:");
    Class[] parametertypes = constructors[i].getParameterTypes();
    for(int j = 0; j < parametertypes.length; j++){
        System.out.println(parametertypes[j].getName());
    }
}

反射获取类的Method

1.通过class对象 获取一个类的方法

  • getMethod(String methodName , class.. class) 根据参数列表的类型和方法名, 得到一个方法(public修饰的)

  • getMethods(); 得到一个类的所有方法 (public修饰的)

  • getDeclaredMethod(String methodName , class.. class) 根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)

  • getDeclaredMethods(); 得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)

2.Method 执行方法

//参数1. 要调用方法的对象
//参数2. 要传递的参数列表
invoke(Object o,Object... para);
//获取方法的方法名称
getName();

案例:

Class c = Class.forName("reflect.Person");//获取类对象
Constructor constructor = c.getConstructor();//获取构造方法
Object o = constructor.newInstance();//创建类的对象
//获取类的方法
Method setName = c.getMethod("setName", String.class);
Method setAge = c.getDeclaredMethod("setAge", int.class);
setAge.setAccessible(true);
//利用invoke执行方法
//参数1:执行方法的类的对象
//参数2:调用方法时传的参数
setName.invoke(o,"张三");
System.out.println(o);
setAge.invoke(o,20);
System.out.println(o);

反射获取类的属性

1. 通过 class 对象 获取一个类的属性
  • getDeclaredField(String filedName) 根据属性的名称, 获取一个属性对象 (所有属性)
  • getDeclaredFields() 获取所有属性
  • getField(String filedName) 根据属性的名称, 获取一个属性对象 (public属性)
  • getFields() 获取所有属性 (public)

2.属性的对象类型

常用方法:

//参数: 要获取属性的对象
//获取指定对象的此属性值
get(Object o );

//参数1. 要设置属性值的 对象
//参数2. 要设置的值
//设置指定对象的属性的值
set(Object o , Object value);

//获取属性的名称
getName()

//如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的属性)
setAccessible(boolean flag)
案例:
Class c = Class.forName("reflect.Person");//获取类对象
Constructor constructor = c.getConstructor();//获取构造方法
Object o = constructor.newInstance();//创建类的对象
//设置public权限的属性
Field number = c.getField("number");
number.set(o,"1001");
System.out.println(o);
//设置private权限的属性
Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(o,"王五");
System.out.println(o);

反射获取注解信息

1.获取类/属性/方法的全部注解对象

Annotation[] annotations01 = Class/Field/Method.getAnnotations();
for (Annotation annotation : annotations01) {
    System.out.println(annotation);
}

2.根据类型获取类/属性/方法的注解对象

注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class);

案例(用于对应类和数据库内容):

先创建两个自定义注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TableAnnotation {
    //用于标注类对应的表格名称
    String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ColumnAnnotation {
    String columnName();//描述列名
    String type();//描述类型
    String length();//描述数据长度
}

创建一个类并对类和属性加上注解:

@TableAnnotation("test_book")
public class Book {
    @ColumnAnnotation(columnName = "id",type = "int",length = "11")
    private int id;
    @ColumnAnnotation(columnName = "name",type = "varchar",length = "50")
    private String name;
    @ColumnAnnotation(columnName = "info",type = "varchar",length = "1000")
    private String info;

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

    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;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    public Book(int id, String name, String info) {
        this.id = id;
        this.name = name;
        this.info = info;
    }

    public Book() {
    }
}

获取注解信息:

Class c = Class.forName("annotation.Book");
//        Annotation[] as = c.getAnnotations();
//        for (Annotation a:as) {
//            System.out.println(a);
//        }
TableAnnotation ta = (TableAnnotation) c.getAnnotation(TableAnnotation.class);
String value = ta.value();
System.out.println("表名"+value);

Field[] fs = c.getDeclaredFields();
for(Field f:fs){
    ColumnAnnotation ca = f.getAnnotation(ColumnAnnotation.class);
    System.out.println(f.getName()+"对应数据库中的字段:"+ca.columnName()+",数据类型:"+ca.type()+",数据长度:"+ca.length());
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值