反射
一、什么是反射?
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为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() 获取泛型参数的数组.