目录
什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
在了解反射机制之前我们首先了解一下Java类加载的过程
加载
加载是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
验证
这是虚拟机安全的重要保障,JVM需要核检字节信息是符合Java虚拟机规范的,否则就被认为是VerifyError。这样就防止了恶意信息或者不合规的信息危害JVM的运行。
准备
准备阶段主要是为类变量(注意,不是实例变量)分配内存,并且赋予初值。 特别需要注意初值,不是代码中具体写的初始化的值,而是Java虚拟机根据不同变量类型的默认初始值。
解析
将常量池内的符号引用替换为直接引用的过程。
初始化
主要是对类变量初始化,是执行类构造器的过程。
反射机制的相关类
通过反射可访问的主要描述信息
组成部分 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
包路径 | getPackage() | Package对象 | 获得该类的存放路径 |
类名称 | getName() | String对象 | 获得该类的名称 |
继承类 | getSuperclass() | Class对象 | 获得该类继承的类 |
实现接口 | getInterfaces() | Class型数组 | 获得该类实现的所有接口 |
构造方法 | getConstructors() | Constructor型数组 | 获得所有权限为public的构造方法 |
getConstructor(Class<?>...parameterTypes) | Constructor对象 | 获得权限为public的指定构造方法 | |
getDealaredConstructors() | Constructor型数组 | 获得所有构造方法,按声明顺序返回 | |
getDealaredConstructor(Class<?>...parameterTypes) | Constructor对象 | 获得指定的构造方法 | |
方法 | getMethods() | Method型数组 | 获取所有权限为public的方法 |
getMethod(String name,Class<?>...parametersTypes) | Method对象 | 获得权限为public的指定方法 | |
getDeclareMethods() | Method型数组 | 获得所有方法,按声明顺序返回 | |
getDeclareMethod(String name,Class<?>...parameterTypes) | Method对象 | 获得指定方法 | |
成员变量 | getFields() | Field型数组 | 获得所有权限为public的成员变量 |
getField(Sgtring name) | Field对象 | 获得权限为public的指定成员变量 | |
getDeclaredFields() | Field型数组 | 获得所有成员变量,按声明顺序返回 | |
getDeclaredField(String name) | Field对象 | 获得指定的成员变量 | |
内部类 | getClasses() | Class型数组 | 获得所有权限为public的内部类 |
getDeclaredClasses() | Class型数组 | 获得所有内部类 | |
内部类的声明类 | getDeclaringClass() | Class对象 | 如果该类为内部类,则返回它的成员类,否则返回null |
说明: 通过getFields()和getMethods()方法依次获得权限为public的成员变量和方法时,将包含从超类中继承到的成员变量和方法;而通过方法getDeclaredFields()和getDeclaredMethods()只能获得在本类中定义的所有成员变量和方
实例演练
// 根据包名获取Class的对象
Class<Student> clazz = (Class<Student>) Class.forName("com.zking.reflect.Student");
// 2. 类.class
Class clazz02 = Student.class;
// 3. 对象.getClass()
Student stu = new Student();
Class clazz03 = stu.getClass();
实体类
package com.zking.reflect;
public class Student {
private String sid;
private String sname;
public Integer age;
static{
System.out.println("加载进jvm中!");
}
public Student() {
super();
System.out.println("调用无参构造方法创建了一个学生对象");
}
public Student(String sid) {
super();
this.sid = sid;
System.out.println("调用带一个参数的构造方法创建了一个学生对象");
}
public Student(String sid, String sname) {
super();
this.sid = sid;
this.sname = sname;
System.out.println("调用带二个参数的构造方法创建了一个学生对象");
}
@SuppressWarnings("unused")
private Student(Integer age) {
System.out.println("调用Student类私有的构造方法创建一个学生对象");
this.age = age;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public void hello() {
System.out.println("你好!我是" + this.sname);
}
public void hello(String name) {
System.out.println(name + "你好!我是" + this.sname);
}
@SuppressWarnings("unused")
private Integer add(Integer a, Integer b) {
return new Integer(a.intValue() + b.intValue());
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
}
}
测试代码
package com.zking.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
@SuppressWarnings({ "unchecked", "unused" })
public static void main(String[] args) throws Exception {
// 根据包名获取Class的对象
Class<Student> clazz = (Class<Student>) Class.forName("com.zking.reflect.Student");
// 2. 类.class
Class clazz02 = Student.class;
// 3. 对象.getClass()
Student stu = new Student();
Class clazz03 = stu.getClass();
//获得包名目录
Package package1 = clazz03.getPackage();
System.out.println(package1);
System.out.println(clazz);
System.out.println(clazz02);
System.out.println(clazz03);
//通过反射实例化对象如果有参数就在后面括号填入参数类型,下面是无参构造
Constructor<Student> c1 = clazz.getConstructor();
Student stu01 = c1.newInstance();
stu01.setSname("ls");//调用属性方法赋值
System.out.println(stu01);
//通过反射获取私无参有构造
Constructor<Student> c2 = clazz.getDeclaredConstructor();
c2.setAccessible(true);//获取权限操作符
Student stu02 = c2.newInstance();
System.out.println(stu02);
//通过反射获取私有有参构造
Constructor<Student> c3 = clazz.getDeclaredConstructor(String.class);//填入参数类型
c3.setAccessible(true);//获取权限操作符
Student stu03 = c3.newInstance("11");//实例填入参数
System.out.println(stu03);
//获取方法
Method method = clazz.getMethod("hello");//根据方法名获取方法对象
stu03.setSname("hh");
method.invoke(stu03);//调用了stu03的hello方法
//获取私有方法
Method method2 = clazz.getDeclaredMethod("add",Integer.class,Integer.class);//根据对象名获得方法,根据方法传入对应的参数
method2.setAccessible(true);
int c = (int)method2.invoke(stu03, 11,11);//根据对象调用对应的方法,传入参数
System.out.println(c);//打印返回结果
//反射读写的属性
Field f = clazz.getField("age");//根据对象属性名获得属性
f.set(stu03, 99);//传入对象和要设置的值
System.out.println(stu03);
System.out.println(f.get(stu03));
//反射读写的私有属性
Field f01 = clazz.getDeclaredField("sname");//根据对象属性名获得属性
f01.setAccessible(true);
f01.set(stu03, "xxxx");//传入对象和要设置的值
System.out.println(stu03);
}
}
结果:
加载进jvm中!
调用无参构造方法创建了一个学生对象
package com.zking.reflect
class com.zking.reflect.Student
class com.zking.reflect.Student
class com.zking.reflect.Student
调用无参构造方法创建了一个学生对象
Student [sid=null, sname=ls, age=null]
调用无参构造方法创建了一个学生对象
Student [sid=null, sname=null, age=null]
调用带一个参数的构造方法创建了一个学生对象
Student [sid=11, sname=null, age=null]
你好!我是hh
22
Student [sid=11, sname=hh, age=99]
99
Student [sid=11, sname=xxxx, age=99]