一、什么是反射?
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
Class
类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)。
Class
没有公共构造方法。
Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的
defineClass
方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
二、反射的使用(这里使用Person类做演示)
1.获取Class对象的三种方式:
(1)Object ——> getClass();
(2)何数据类型(包括基本数据类型)都有一个“静态”的class属性
(3)通过Class类的静态方法:forName(String className)(常用)
public class Person {
//成员变量
public String name;
public int age;
private String address;
//构造方法
//无参构造
public Person() {
System.out.println("空参数构造方法");
}
//有参构造
public Person(String name) {
this.name = name;
System.out.println("带有String的构造方法");
}
//私有的构造方法
private Person(String name, int age){
this.name = name;
this.age = age;
System.out.println("带有String,int的构造方法");
}
//多个参数的构造方法
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
System.out.println("带有String, int, String的构造方法");
}
//成员方法
//没有返回值没有参数的方法
public void method1(){
System.out.println("没有返回值没有参数的方法");
}
//没有返回值,有参数的方法
public void method2(String name){
System.out.println("没有返回值,有参数的方法 name= "+ name);
}
//有返回值,没有参数
public int method3(){
System.out.println("有返回值,没有参数的方法");
return 123;
}
//有返回值,有参数的方法
public String method4(String name){
System.out.println("有返回值,有参数的方法");
return "哈哈" + name;
}
//私有方法
private void method5(){
System.out.println("私有方法");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
}
}
2.通过反射获取构造方法、成员变量、成员方法并使用
Person类:
package com.pp;
public class Person {
//成员变量
public String name;
public int age;
private String address;
//构造方法
//无参构造
public Person() {
System.out.println("空参数构造方法");
}
//有参构造
public Person(String name) {
this.name = name;
System.out.println("带有String的构造方法");
}
//私有的构造方法
private Person(String name, int age){
this.name = name;
this.age = age;
System.out.println("带有String,int的构造方法");
}
//多个参数的构造方法
public Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
System.out.println("带有String, int, String的构造方法");
}
//成员方法
//没有返回值没有参数的方法
public void method1(){
System.out.println("没有返回值没有参数的方法");
}
//没有返回值,有参数的方法
public void method2(String name){
System.out.println("没有返回值,有参数的方法 name= "+ name);
}
//有返回值,没有参数
public int method3(){
System.out.println("有返回值,没有参数的方法");
return 123;
}
//有返回值,有参数的方法
public String method4(String name){
System.out.println("有返回值,有参数的方法");
return "哈哈" + name;
}
//私有方法
private void method5(){
System.out.println("私有方法");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
}
}
测试类:
(1)获取构造方法:
public Constructor[] getConstructors():所有”公有的”构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的”公有的”构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取”某个构造方法”可以是私有的,或受保护、默认、公有;
调用构造方法:
newInstance(Object… initargs)
使用此
Constructor
对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
public class Main {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.pp.Person");
//1.拿所有的构造方法
Constructor[] cons = c.getDeclaredConstructors();
for(Constructor con:cons){
System.out.println(con);
}
//2.拿到单个的构造方法
Constructor constructor = c.getDeclaredConstructor(String.class,int.class,String.class);
System.out.println(constructor);
//通过构造方法创建对象
Object instance = constructor.newInstance("张三", 12, "陕西");
System.out.println(instance);
//针对私有方法,通过暴力反射来获取
Constructor con = c.getDeclaredConstructor(String.class,int.class);
con.setAccessible(true);//取消java语言检查
Object instance= con.newInstance("李四", 12);
System.out.println(instance);
}
}
(2)获取成员变量:
1.批量的 :
1).Field[] getFields():获取所有的"公有字段" *
2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
2.获取单个的: *
1).public Field getField(String fieldName):获取某个"公有的"字段;
2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
设置字段的值: *
Field --> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的字段所在的对象;
2.value:要为字段设置的值;
public class Main {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.pp.Person");
//通过反射拿成员变量
//1.拿到所有的字节码
Field[] fields = c.getFields();
for(Field field:fields){
System.out.println(field);
}
//2.获取单个字节码
Field age = c.getField("age");
System.out.println(age);
//通过构造方法创建对象之后,拿到其对应的成员变量字节码
Constructor constructor = c.getDeclaredConstructor(String.class);
Object instance = constructor.newInstance("王五");
//公有的字节码
Field age = c.getField("age");
//私有的字节码,通过暴力反射
Field address = c.getDeclaredField("address");
address.setAccessible(true);
//通过方法成员变量获取值初始值
age.get(instance);
address.get(instance);
//赋值
age.set(instance,13);
address.set(instance,"北京");
System.out.println(age.get(instance));
System.out.println(address.get(instance));
}
}
(3)获取成员方法:
1.批量的: *
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
2.获取单个的: public Method getMethod(String name,Class<?>... parameterTypes):
参数:
name : 方法名;
Class ... : 形参的Class类型对象
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
调用方法: Method --> public Object invoke(Object obj,Object... args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;
public class Main {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.pp.Person");
//通过反射拿成员方法
//拿公有的成员方法
Constructor constructor = c.getDeclaredConstructor(String.class);
Object instance = constructor.newInstance("娃哈哈");
Method method = c.getMethod("method4", String.class);
Object invoke = method.invoke(instance, "任小米");
System.out.println(invoke);
//拿私有的成员方法
Constructor constructor = c.getDeclaredConstructor(String.class);
Object instance = constructor.newInstance("娃哈哈");
//暴力反射
Method method5 = c.getDeclaredMethod("method5");
method5.setAccessible(true);
Object invoke = method5.invoke(instance, null);
System.out.println(invoke);
}
}
反射小结
1. Class: 是一个类; 一个描述类的类.
封装了描述方法的 Method,
描述字段的 Filed,
描述构造器的 Constructor 等属性.
2. 如何得到 Class 对象:
2.1 Person.class
2.2 person.getClass()
2.3 Class.forName("com.atguigu.javase.Person")
3. 关于 Method:
3.1 如何获取 Method:
1). getDeclaredMethods: 得到 Method 的数组.
2). getDeclaredMethod(String methondName, Class ... parameterTypes)
3.2 如何调用 Method
1). 如果方法时 private 修饰的, 需要先调用 Method 的 setAccessible(true), 使其变为可访问
2). method.invoke(obj, Object ... args);
4. 关于 Field:
4.1 如何获取 Field: getField(String fieldName)
4.2 如何获取 Field 的值:
1). setAccessible(true)
2). field.get(Object obj)
4.3 如何设置 Field 的值:
field.set(Obejct obj, Object val)
5. 了解 Constructor 和 Annotation
6. 反射和泛型.
6.1 getGenericSuperClass: 获取带泛型参数的父类, 返回值为: BaseDao<Employee, String>
6.2 Type 的子接口: ParameterizedType
6.3 可以调用 ParameterizedType 的 Type[] getActualTypeArguments() 获取泛型参数的数组.