反射机制
什么是反射
宏观上理解一下:我们对数据操作的时候,都要先知道这个数据是什么类型,是八种基本的数据类型还是引用类型,然后根据数据的类型创建变量或者对象进行操作,编译器就在编译阶段对代码进行检查的;但是对于反射,就不是在编译的时候获取数据的类型,而是在运行时获取不同数据的类型信息,然后根据这些信息创建对象,调用方法等。
Class类
所有的类都是Class
类的实例——每一个运行时加载到内存中的类都是Class
类的一个对象——存放着不同类的信息
这里的类和平时说的数据类型中的类不太一样,这个类涵盖了数据类型中的普通类,数组,接口,枚举,注解,基本数据类型,
void
关键字
这个Class
类还有一个特别之处:虽然是类,但是没有public
构造器,所有的该类对象都是由JVM
装载类的时候创建的,并且同一个类只在JVM
中有一个Class
对象实例(对应的是.class
文件)
Class
对象举例
-
普通类对应的
Class
的对象//获取了ArrayList类对应的Class对象 Class<ArrayList> arrayListClass = ArrayList.class;
-
定义的数组对应的
Class
对象int[] array = new int[10]; Class<? extends int[]> aClass = array.getClass();
-
接口对应的
Class
对象Class<List> listClass = List.class;
-
基本数据类型和关键字
void
对应的Class
对象Class<Integer> integerClass = int.class; Class<Short> shortClass = short.class; Class<Boolean> booleanClass = boolean.class; Class<Character> characterClass = char.class; Class<Void> voidClass = void.class;
获取Class
对象的几种方法
-
通过类获取:调用类的属性
class
获取,上面代码中Stirng
类对应的Class
对象就是这样 -
有了某个类的对象,可以由这个对象的
.getClass()
方法获取,比如上面个数组的例子 -
如果知道全类名(包名.类名),并且在这个类的类路径下,可以用
Class
的静态方法public static Class<?> forName(String className)
获取 -
通过类加载器
ClassLoader cl = this.getClass().getClassLoader(); Class clazz = cl.loadClass(“类的全类名”);
Class
对象的使用
通过不同类对应的Class
对象,可以得到关于类型的很多信息,获取的方式大同小异,下面一起列举一下:
1.获得关于类的结构和信息
-
获取类名,包名
public String getName()//类在Java内部真正的名字 public String getSimpleName()//平时声明类时使用的类名,不带包名 public String getCanonicalName()//上面那个的基础上带包名 public Package getPackage()//包信息
-
返回类中字段(静态变量和实例变量)信息——返回
Field
对象(它也对应好多方法,可以获取字段的例如名称,访问权限,字段值)//返回所有的public字段,包括其父类的,如果没有字段,返回空数组 public Field[] getFields() //返回本类声明的所有字段,包括非public的,但不包括父类的 public Field[] getDeclaredFields() //返回本类或父类中指定名称的public字段,找不到抛出异常NoSuchFieldException public Field getField(String name) //返回本类中声明的指定名称的字段,找不到抛出异常NoSuchFieldException public Field getDeclaredField(String name)
-
获取方法信息——返回
Method
对象//返回所有的public方法,包括其父类的,如果没有方法,返回空数组 public Method[] getMethods() //返回本类声明的所有方法,包括非public的,但不包括父类的 public Method[] getDeclaredMethods() //返回本类或父类中指定名称和参数类型的public方法, //找不到抛出异常NoSuchMethodException public Method getMethod(String name, Class<?>... parameterTypes) //返回本类中声明的指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
除了字段和方法之外,根据反射
Class
的对象还可以获得运行时类的构造器(Constructor),父类(Superclass),接口(Interface),注解(Annotation)。类对应的修饰符等。其中Method
和Field
Constructor
都是java.lang.reflect
包下面的类,他们还有对应的方法获取字段,方法,构造器自己本身的属性
java.lang.reflect
结构
-
创建运行时类的对象
public T newInstance() throws InstantiationException, IllegalAccessException
- 调用这个方法的要求
- 这个运行时类必须有无参构造器
- 构造器的权限要够
- 如果没有无参构造器,可以通过上面说的,获取运行时类的
Constructor
结构,然后调用Constructor
的方法创建对象
- 调用这个方法的要求
2.获取例如内部类,基本数据类型,数组等的信息
-
给定一个
Class
对象,判断是什么类型的public native boolean isArray() //是否是数组 public native boolean isPrimitive() //是否是基本类型 public native boolean isInterface() //是否是接口 public boolean isEnum() //是否是枚举 public boolean isAnnotation() //是否是注解 public boolean isAnonymousClass() //是否是匿名内部类 public boolean isMemberClass() //是否是成员类,成员类定义在方法外,不是匿名类 public boolean isLocalClass() //是否是本地类,本地类定义在方法内,不是匿名类
3.综合举例
//定义的相关结构
class Person implements Comparable{
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
if(o instanceof Person) {
Person p = (Person) o;
return (this.age == p.age) ? 0 : (this.age > p.age ? 1 : -1);
}
throw new RuntimeException("类型出错");
}
public void show(){
System.out.println("我叫" + name);
}
}
enum StudentList{
STUDENT1("学生1",22),
STUDENT2("学生2",23),
STUDENT3("学生3",24),
STUDENT4("学生4",25);
private final String name;
private final int id;
private StudentList(String name,int id){
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
}
//测试及结果
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Person p1 = new Person();//无参构造器实例化的普通类
Person p2 = new Person("人",12);//带参构造器实例化的普通类
Person[] array = new Person[]{p1,p2};//数组
int length = array.length;//基本数据类型
/**
* Class类对象的获取
*/
//普通类获取对应Class类对象的方式
Class<Person> personClass = Person.class;
System.out.println(personClass);//控制台输出:class leetcode.Person
Class<? extends Person> aClass = p1.getClass();
System.out.println(aClass);//控制台输出:class leetcode.Person
Class<?> aClass2 = Class.forName("leetcode.Person");
System.out.println(aClass2);//控制台输出:class leetcode.Person
ClassLoader classLoader = Person.class.getClassLoader();
Class<?> aClass3 = classLoader.loadClass("leetcode.Person");
System.out.println(aClass3);//控制台输出:class leetcode.Person
//数组对应的Class类对象
Class<? extends Person[]> aClass1 = array.getClass();
System.out.println(aClass1);//控制输出:class [Lleetcode.Person;
//基本数据类型对应的Class类对象
Class<Integer> integerClass = int.class;
System.out.println(integerClass);//控制台输出:int
//枚举类
Class<StudentList> studentListClass = StudentList.class;
System.out.println(studentListClass);//class leetcode.StudentList
/**
* 获取不同数据类型的Class类对象之后可以惊醒的操作
*/
//普通类
Field[] fields = personClass.getDeclaredFields();//获取类的包括非public的字段:静态变量和实例变量
for(Field f : fields)
System.out.println(f);
//上面控制台的输出
//java.lang.String leetcode.Person.name
//int leetcode.Person.age
Method[] declaredMethods = personClass.getDeclaredMethods();//获取所有的方法
for(Method m : declaredMethods)
System.out.println(m);
//上面控制台的输出
//public java.lang.String leetcode.Person.toString()
//public int leetcode.Person.compareTo(java.lang.Object)
//public void leetcode.Person.show()
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();//获取所所有声明的构造器
for(Constructor c : declaredConstructors)
System.out.println(c);
//public leetcode.Person()
//public leetcode.Person(java.lang.String,int)
Class<?>[] interfaces = personClass.getInterfaces();
for(Class c : interfaces)
System.out.println(c);
//interface java.lang.Comparable
//数组获取存储对象的类型
Class<?> componentType = aClass1.getComponentType();
System.out.println(componentType);//class leetcode.Person
//枚举类
Field[] declaredFields = studentListClass.getDeclaredFields();
for(Field f : declaredFields)
System.out.println(f);
//public static final leetcode.StudentList leetcode.StudentList.STUDENT1
//public static final leetcode.StudentList leetcode.StudentList.STUDENT2
//public static final leetcode.StudentList leetcode.StudentList.STUDENT3
//public static final leetcode.StudentList leetcode.StudentList.STUDENT4
//private final java.lang.String leetcode.StudentList.name
//private final int leetcode.StudentList.id
//private static final leetcode.StudentList[] leetcode.StudentList.$VALUES
/**
* Class对象创建对应类的对象:调用方法或者先获取构造器然后构造
*/
//直接调用方法的
Person person = personClass.newInstance();
System.out.println(person);//调用的是空参构造器,输出:Person{name='null', age=0}
//调用构造器的
Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class, int.class);//注意顺序不能反
Person cPerson = declaredConstructor.newInstance("构造器创建的", 12);
System.out.println(cPerson);//调用的是有参构造器,输出为:Person{name='构造器创建的', age=12}
}
}
总结
Class
类是Reflection的根源,针对任何想动态加载、运行的类都要获取到相应数据类型对应的Class
对象- 获取了某个数据类型对应的
Class
类对象之后,就获取了关于这个数据类型的所有相关信息 - 通过
Class
对象可以创建对应类的对象,获取它的字段,方法,构造器等等 - 达到的效果就是系统是在运行时而不是在编译时确定类型的信息,实现动态的效果