反射是框架设计的灵魂
(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))
1. 反射的概念
Java的反射(reflection)机制是指在程序运行的状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2. Class对象
要想了解一个类,必须先要获取到该类的字节码文件对象. 在Java中,每一个字节码文件,被加载到内存后,都存在一个对应的Class类型的对象。
3. 得到Class的几种方式
1. 如果在编写代码时, 知道类的名称, 且类已经存在, 可以通过 包名.类名.class 得到一个类的 类对象
2. 如果拥有类的对象, 可以通过 Class 对象.getClass() 得到一个类的 类对象
3. 如果在编写代码时, 知道类的名称 , 可以通过 Class.forName("包名+类名"),类不存在的时候也可以使用(代码不会报红,只身运行时报错,更适用与动态编程),找不到指定的类会报ClassNotFoundException (重点使用)
得到一个类的 类对象 上述的三种方式, 在调用时, 如果类在内存中不存在, 则会加载到内存 ! 如果类已经在内存中存在, 不 会重复加载, 而是重复利用 !
4. Constructor 创建对象
常用方法: newInstance(Object... para)
调用这个构造方法, 把对应的对象创建出来 参数: 是一个Object类型可变参数, 传递的参数顺序 必须匹配构造方法中形式参数列表的顺 序!
setAccessible(boolean flag)
如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的方法)
5. 通过class对象 获取一个类的构造方法
1. 通过指定的参数类型, 获取指定的单个构造方法 getConstructor(参数类型的class对象数组)
例如: 构造方法如下: Person(String name,int age)
得到这个构造方法的代码如下:
Constructor c = p.getClass().getConstructor(String.class,int.class);
2. 获取构造方法数组 getConstructors();
3. 获取所有权限的单个构造方法 getDeclaredConstructor(参数类型的class对象数组)
4. 获取所有权限的构造方法数组 getDeclaredConstructors();
package bean;
public class Person {
private int age;
private String name;
public String phoneNumber;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", phoneNumber='" + phoneNumber + '\'' +
'}';
}
//私有的构造方法
private Person(String name) {
this.age = 18;
this.name = name;
}
public Person() {
}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package test;
import bean.Person;
import java.lang.reflect.Constructor;
public class Test {
public static void main(String[] args) throws Exception {
Class<Person> aClass = (Class<Person>) Class.forName("bean.Person");
//找到无参构造方法
Constructor<Person> c = aClass.getConstructor();
//使用无参构造方法,创建对象
Person p = c.newInstance();
System.out.println(p);
//找到包含String name 和 int age 的构造方法
Constructor<Person> c1 = aClass.getConstructor(int.class,String.class);
//使用全参数的构造方法,创建对象
Person p1 = c1.newInstance( 18,"李伟");
System.out.println(p1);
//获得所有权限的构造方法
Constructor<Person> d = aClass.getDeclaredConstructor(String.class);
//忽略权限检查
d.setAccessible(true);
Person p2 = d.newInstance("刘洋");
System.out.println(p2);
System.out.println("getDeclaredConstructors方法能获得所有权限的构造函数");
Constructor<?>[] gdc = aClass.getDeclaredConstructors();
for (Constructor<?> gd : gdc) {
System.out.println(gd);
}
System.out.println("getConstructors方法只能得到public方法修饰的构造函数");
Constructor<?>[] gdc1 = aClass.getConstructors();
for (Constructor<?> gd : gdc1) {
System.out.println(gd);
}
}
}
输出结果:
6. 获取Method
通过class对象 获取一个类的方法
1. getMethod(String methodName , class.. clss)
根据参数列表的类型和方法名, 得到一个方法(public修饰的)
2. getMethods();
得到一个类的所有方法 (public修饰的)
3. getDeclaredMethod(String methodName , class.. clss)
根据参数列表的类型和方法名, 得到一个方法(除继承以外所有的:包含私有, 共有, 保护, 默认)
4. getDeclaredMethods();
得到一个类的所有方法 (除继承以外所有的:包含私有, 共有, 保护, 默认)
Method 执行方法
invoke(Object o,Object... para) :
调用方法 ,
参数1. 要调用方法的对象 参数
2. 要传递的参数列表
示例:
使用上面的person类
//加载类
Class<Person> aClass = (Class<Person>) Class.forName("bean.Person");
//获取类中的构造方法
Constructor<Person> c = aClass.getConstructor();
//创建对象
Person person = c.newInstance();
//获取类的方法
Method setName = aClass.getMethod("setName", String.class);
//获取所有权限的方法
Method setAge = aClass.getDeclaredMethod("setAge", int.class);
//忽略访问权限检查
setAge.setAccessible(true);
//参数1. 哪个对象要执行setName方法
//参数2. 调用方法时传递的产生 0-n
setName.invoke(person, "张三");
setAge.invoke(person, 18);
System.out.println(person);
7. 获取Field 通过class对象 获取一个类的属性
1. getDeclaredField(String filedName) 根据属性的名称, 获取一个属性对象 (所有属性)
2. getDeclaredFields() 获取所有属性
3. getField(String filedName) 根据属性的名称, 获取一个属性对象 (public属性)
4. getFields() 获取所有属性 (public)
Field 属性的对象类型
常用方法:
1. get(Object o ) 参数: 要获取属性的对象 获取指定对象的此属性值
2. set(Object o , Object value)
参数1. 要设置属性值的 对象
参数2. 要设置的值 设置指定对象的属性的值
3. getName() 获取属性的名称
4. setAccessible(boolean flag)
如果flag为true 则表示忽略访问权限检查 !(可以访问任何权限的属性)
//加载类
Class c = Class.forName("bean.Person");
Constructor ct = c.getConstructor();
Object o = ct.newInstance();
Field phoneNumber = c.getField("phoneNumber");
phoneNumber.set(o, "13843838438");
Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(o, "张三");
System.out.println(o);
7. 利用反射获取注解信息(ORM框架的实现原理)
获取类/属性/方法的全部注解对象
Annotation[] as = Class/Field/Method.getAnnotations();
for (Annotation a : as) {
System.out.println(a);
}
根据类型获取类/属性/方法的注解对象
注解类型 对象名 = (注解类型) c.getAnnotation(注解类型.class);
示例:
包结构
package demo;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.TYPE})
@Documented
public @interface ColumnAnnotation {
/**
* 描述列名
* @return
*/
String columnName();
/**
* 描述类型
* @return
*/
String type();
/**
* 描述数据的长度
* @return
*/
String length();
}
package demo;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface TableAnnotation {
/**
* 用于描述表格名称
* @return
*/
String value();
}
package demo;
import java.util.Objects;
//@ColumnAnnotation(columnName = "aa",type = "aa",length = "aa")
@TableAnnotation("test_book")
public class Book {
@ColumnAnnotation(columnName = "id",type = "int",length = "11")
private int id;
@ColumnAnnotation(columnName = "name",type = "varchar",length = "20")
private String name;
@ColumnAnnotation(columnName = "info",type = "varchar",length = "500")
private String info;
public Book() {
}
public Book(int id, String name, String info) {
this.id = id;
this.name = name;
this.info = info;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return id == book.id &&
Objects.equals(name, book.name) &&
Objects.equals(info, book.info);
}
@Override
public int hashCode() {
return Objects.hash(id, name, 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;
}
}
package demo;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class Demo {
public static void main(String[] args) throws Exception {
Class c = Class.forName("demo.Book");
//获取book类上面的所有注解 获取一组注解
Annotation[] as = c.getAnnotations();
for (Annotation a : as) {
System.out.println(a);
}
//通过注解名称称获取值
TableAnnotation annotation = (TableAnnotation) c.getAnnotation(TableAnnotation.class);
//注解的名称
System.out.println(annotation);
//获取到注解的value值
String value = annotation.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());
}
}
}
运行结果: