反射,也可以称为反向获取,在一个程序的运行时,我们可以获取任意一个类的所有方法和属性,同样也可以调用任意一个对象的所有方法和属性。反射应用在一些通用性较高的代码,后面的框架大多数就是用反射来实现的。
一、反射的原理
- 首先需要把Java文件保存到本地磁盘
- 编译Java文件,生成.class文件
- 使用JVM运行,把class文件通过类加载到内存中,再执行
- class文件在内存中使用Class对象存储
当使用反射的时候,首先需要获取到Class对象,得到这个对象之后,就可以得到class文件里面的所有内容,包含属性,构造方法,普通方法
- 属性通过一个类Filed
- 构造方法通过一个类Constructor
-
普通方法通过一个类Method
二、获取Class对象
上面我们说,使用反射的时候,首先要获取类的对象,这是反射的前提。获取类的对象有三种方法:
1、通过对象的getClass()方法获取,必须要有对象
2、通过类名.class创建类的对象
3、调用Class类中的forName()方法,获取类的对象,这种方法最常用
- 学生类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(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;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
- 测试类
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//方法一:创建类的对象,调用getClass()方法
Student s = new Student();
Class clazz1 = s.getClass();
//输出类的对象,会得到类所在位置的全路径
System.out.println(clazz1);
//方法二:通过类名.class创建类的对象
Class clazz2 = Student.class;
System.out.println(clazz2);
//方法三:调用Class类中的forName()方法获取类的对象,传入的参数必须是类的全路径,需要抛出异常
Class clazz3 = Class.forName("com.itheima.review01.Student");
System.out.println(clazz3);
}
}
三、获取类中的构造方法并使用
我们获取到类的对象以后,就可以获取类中的所有内容了。首先我们来获取构造方法,有了构造方法后,我们就可以创建对象,然后就可以调用属性和方法。
1、获取构造方法
获取构造方法的方式有两种:
Constructor<?>[] getConstructors():获取所有public修饰的构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes):获取带参的构造方法,如果没有参数,则获取的是无参的构造方法
2、使用构造方法
T newInstance(Object... initargs): 供Constructor创建对象使用,括号内填写构造方法的参数。
3、案例
import java.lang.reflect.Constructor;
public class ReflectDemo2 {
public static void main(String[] args) throws ReflectiveOperationException {
Class clazz = Class.forName("com.itheima.review01.Student");
//获取构造方法
//方法一
Constructor[] cs = clazz.getConstructors();
for (Constructor constructor : cs) {
//输出构造方法
System.out.println(constructor);
}
//方法二
//获取无参构造:
Constructor c1 = clazz.getConstructor();
System.out.println(c1);
//获取有参构造,括号内是参数的类的对象
Constructor c2 = clazz.getConstructor(String.class,int.class);
System.out.println(c2);
//使用构造方法
Object obj1 = c1.newInstance();
System.out.println(obj1);//Student类中重写了toString()方法,Student [name=null, age=0]
Object obj2 = c2.newInstance("张三",20);
System.out.println(obj2);//Student [name=张三, age=20]
}
}
四、获取属性并使用
1、获取属性
获取属性的方法有四种:
Field[] getFields():拿到类中所有的public修饰的字段对象
Field getField(String name):根据字段名称获取指定的public修饰的字段对象
Field[] getDeclaredFields():拿到类中所有的字段对象(私有也能拿到)
Field getDeclaredField(String name):根据字段名称获取指定的字段对象(私有也能拿到)
2、获取和修改属性
Object get(Object obj):获取指定对象中字段的值(需要传一个对象)
void set(Object obj, Object value):修改指定对象中的值(需要传一个对象)
3、案例
import java.lang.reflect.Field;
public class ReflectDemo3 {
public static void main(String[] args) throws ReflectiveOperationException {
// 创建学生类的字节码对象
Class clazz = Class.forName("com.itheima.review01.Student");
// 创建学生类的对象
Object obj = clazz.newInstance();
// 获取学生类的属性
Field f1 = clazz.getDeclaredField("name");
Field f2 = clazz.getDeclaredField("age");
// 设置反射时取消Java的访问检查(去除私有权限)
f1.setAccessible(true);
f2.setAccessible(true);
//设置属性
f1.set(obj, "张三");
f2.set(obj, 20);
//获取属性
Object name = f1.get(obj);
Object age = f2.get(obj);
System.out.println(name);
System.out.println(age);
}
}
五、获取普通方法并使用
1、获取普通方法
Method getMethod(String name, Class<?>... parameterTypes):拿到类里面的成员方法
2、使用普通方法
Object invoke(Object obj, Object... args)
3、案例
public class ReflectDemo4 {
public static void main(String[] args) throws ReflectiveOperationException {
Class clazz = Class.forName("com.itheima.review01.Student");
Object obj = clazz.newInstance();
// 获取普通方法
// 获取无参无返回值的方法并调用
Method m1 = clazz.getMethod("method");
m1.invoke(obj);
// 获取有参无返回值的方法并调用
Method m3 = clazz.getMethod("setName", String.class);
m3.invoke(obj, "张三");
// 获取无参有返回值的方法并调用
Method m2 = clazz.getMethod("getName");
Object name = m2.invoke(obj);
System.out.println(name);
}
}
六、补充
创建一个ArrayList<Integer>的一个对象,如何添加一个字符串类型的数据?
首先来简单介绍一下泛型:
Java泛型中的标记符含义:
E - Element(在集合中使用,因为集合中存放的是元素)
T - Type(Java类)
K - Key(键)
V - Value(值)
N - Number(数值类型)泛型的三种
[1]ArrayList<T> al = new ArraList<T>();
指定集合元素只能是T类型
[2]ArrayList<?> al = new ArraList<?>();
指定集合元素可以使任何类型,这个声明没有任何意义
[3]ArrayList<? extends E> al = new ArraList<? extends E>();
? extends E:接收E类型或者E的子类型
? super E:接收E类型或者E的父类型