反射机制是在运行状态中,对于任意一个类,都能够获取该类的所有属性和方法
反射机制指的是程序在运行时能够获取自身的信息(类的属性方法构造器)
1.反射的特点
-
优点
- 体现java的灵活性,体现了多态的应用
- 降低类之间的耦合性
-
缺点: 代码效率低 (可以忽略不计)
2.Class
对Class文件的一种描述
- 反射:通过Class获取字节码文件对象,使用字节码文件对象来获取类的一些描述信息
- 成员变量 Field
- 成员方法 Method
- 构造方法 Constructor
2.1类加载
Java文件 - 编译 -> 字节码文件 - 运行 -> JVM执行Class文件(类加载)
当我们执行java.exe命令,这个字节码文件会被加载到内存当中,同时还会为这个字节码文件创建一个Class的实例
3.获取字节码文件对象的三种方式
- Object的getClass方法
Student s = new Student();
Class<?> c = s.getClass();
- 通过数据类型的静态属性 class属性
- 如果形参需要我们传入Class类型,那么我们可以考虑使用方式二
- public void show(Class c){}
- show(int.class)
Class<?> c = Student.class;
- Class类中一个静态方法
- static Class<?> forName(String className)
- 方式三的好处: 因为传入的是一个字符串,而这个字符串可以通过配置文件配置
- 满足了设计原则: 开闭原则 对扩展开放,对修改关闭
- 配置文件: xml文件, properties文件
//创建properties对象
Properties pro = new Properties();
//加载配置文件
pro.load(new FileReader("prop.txt"));
//读取配置文件中的值
String value = pro.getProperty("ClassName");
System.out.println(value);
//通过静态方法获取class对象
Class<?> c4 = Class.forName(value);
4.获取构造器
通过 Class 类可以获取构造器
- 常用方法
说明 | |
---|---|
Constructor<?>[] getConstructors() | 获取公有修饰的构造方法对象数组 |
Constructor<?>[] getDeclaredConstructors() | 获取所有的构造方法对象数组 |
Constructor getConstructor(Class<?>… parameterTypes) | 获取公有修饰的构造方法对象 |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 获取所有的构造方法对象 |
- Contructor 类
说明 | |
---|---|
getName() | 构造器的名字 |
getParameterCount() | 构造器的参数个数 |
getModifiers() | 修饰符 |
newInstance(…) | 创建对象的方法 |
- 获取所有构造器,并获取构造器信息
public class Demo {
public static void main(String[] args) throws Exception {
// 获取 class对象
Class clazz1 = Class.forName("demo.User");
// getConstructors()拿到所有的Constructor
Constructor[] cons = clazz1.getConstructors();
System.out.println("拿到所有的构造器 .....");
/**
* Constructor 封装了构造器的描述
* 封装了属性,构造器的名字,构造器的参数个数
*/
for (Constructor c : cons) {
// 构造器的名称
System.out.println("构造器的名字: " + c.getName());
System.out.println("构造器的参数个数: " +c.getParameterCount());
// 1 是 public
// 2 是 private
// 3 是 protected
System.out.println("修饰符: "+ c.getModifiers());
}
}
- 获取构造器并创建对象
public class Demo {
public static void main(String[] args) throws Exception {
// 获取Class对象
Class clazz1 = Class.forName("demo.User");
// class给指定的泛型
// 获取指定的构造器 创建的对象 返回的直接是 user
Class<User> clazz2 = (Class<User>) Class.forName("demo.User");
// 获取指定的构造器
Constructor con1 = clazz1.getConstructor(null); // 拿到空构造器
// newInstance 调用空构造器 创建一个对象
// 返回的obj 可以给一个指定对象 进行强转
// 获取
Class 直接给泛型 可以拿到指定的对象
User u = (User) con1.newInstance(null);
System.out.println(u);
Constructor con2 = clazz1.getConstructor(String.class, int.class);
// 获取指定的构造器
// 调用构造器方法给指定的参数
// 构造器给的参数要和获取的构造器参数一致 (String,int)
User u2 = (User) con2.newInstance("aa", 22);
System.out.println(u2);
//class方法:newInstance:直接调用创建一个对象
// class方法创建的是空对象,内部调用con1.newInstance(null);
User u3 = (User) clazz1.newInstance();
System.out.println(u3);
}
}
5.获取属性
通过反射获取成员变量对象 Field
- 获取属性方法
说明 | |
---|---|
Field[] getFields() | 获取公有修饰的成员变量对象数组 |
Field[] getDeclaredFields() | 获取所有成员变量对象数组 |
Field getField(String name) | 获取指定的公共成员变量对象 |
Field getDeclaredField(String name) | 获取指定任意成员变量对象 |
Object get(Object obj) | 返回指定对象上此 Field 表示的字段的值 |
- 获取字段信息方法
说明 | |
---|---|
getModifiers() | 属性修饰符 |
etName() | 属性名称 |
getType() | 属性数据类型 |
set(obj,value) | obj:是属性的具体对象 value:属性的值 |
setAccessible(true); | 跳过属性的安全检查 |
- 获取属性,并获取属性信息
public class Demo {
public static void main(String[] args) throws Exception {
// 拿到 Class 对象
Class<User> clazz1 = (Class<User>) Class.forName("demo.User");
// 拿到所有的属性
Field[] fs = clazz1.getDeclaredFields();
// 遍历
for (Field f : fs) {
System.out.println("属性修饰符:" + f.getModifiers());
System.out.println("属性名称::" + f.getName());
System.out.println("属性数据类型:" + f.getType());
}
}
- 对属性进行赋值
public class Demo {
public static void main(String[] args)throws Exception {
// 拿到 Class 对象
Class<User> clazz1 = (Class<User>) Class.forName("demo.User");
// 通过class 创建一个对象
User u = clazz1.newInstance();
// 拿到指定的属性
Field name = clazz1.getDeclaredField("name");
// with modifiers "private" 赋值会报错
// 可以设置属性的安全检查跳过,解决private属性修饰符检查
name.setAccessible(true);
/**
* obj 这个属性属于那大具体的对象
* value 属性的值
*/
name.set(u, "Tom");
System.out.println(u);
}
}
6.获取方法
- 获取方法的方法
说明 | |
---|---|
c.getMethods() | 获取所有公有方法,包括父类继承过来的成员 |
c.getDeclaredMethods() | 获取本类中所有方法,包括私有 |
Method getMethod(String name, Class<?>… parameterTypes) | 第一个参数是方法名称, 第二个参数是参数类型列表 |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 获取指定方法,包括私有 |
Object invoke(Object obj, Object… args) | 执行方法 |
- Method类
说明 | |
---|---|
getName() | 方法的名称 |
getModifiers() | 方法的修饰符 |
getReturnType() | 方法返回值类型 |
getParameterCount() | 方法参数个数 |
- 获取方法,及方法相关信息
public class Demo {
public static void main(String[] args)throws Exception {
//获取clss对象
Class<User> clazz1 = (Class<User>) Class.forName("demo.User");
//获取所有的方法
Method[] ms = clazz1.getDeclaredMethods();
//遍历
for (Method m : ms) {
System.out.println("方法的名称: " + m.getName());
System.out.println("方法的参数个数: " + m.getParameterCount());
System.out.println("方法的修饰符: " + m.getModifiers());
System.out.println("方法的返回值数据类型 : " + m.getReturnType());
}
}
- 通过反射获取成员方法对象并且调用
public class Demo {
public static void main(String[] args) throws Exception {
//获取clss对象
Class<User> clazz1 = (Class<User>) Class.forName("demo.User");
User u = clazz1.newInstance();
// 获取具体的方法
Method m = clazz1.getDeclaredMethod("setAge", int.class);
/**
* invoke 执行获取的方法
* obj 方法的具体对象
* value 方法执行的参数
* u.setAge(34); 通过反射调用方法
*/
m.invoke(u, 34);
System.out.println(u);
}
}
7.Demo
7.1map —> class对象
bean对象:类中只有属性 set/get 、构造器、toString()
(比较简单的对象都是javaBean)
model entry pojo vo 都是bean对象
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
*
* 反射案例:通过反射把map数据复制到Bean中
*
*
*/
public class Demo {
public static void main(String[] args) {
// 初始化map
Map<String, String> map = new HashMap<String, String>();
map.put("name", "马云");
map.put("age", "53");
// map转成bean,操作bean获取bean的Class对象
Class<User> clazz = (Class<User>) new User().getClass();
User u = null;
try {
u = mapToBean(map, clazz);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(u);
}
/**
* map 转成 bean(User)对象
*
* @param map
* @param clazz
* @return 思路: 1.通过clazz获取所有的属性
2.获取属性名字,通过属性名字获取map中相对应的value
3.通过反射把value赋值给属性
4.方法返回对象
* @throws Exception
*/
private static User mapToBean(Map<String, String> map, Class<User> clazz) throws Exception {
// 创建一个bean
User u;
u = clazz.newInstance();
// 获取属性
Field[] fs = clazz.getDeclaredFields();
for (Field f : fs) {
// 获取属性名字,通过属性名字获取map中相对应的value
String key = f.getName();
// 通过属性的名称获取map中的值
String value = map.get(key);
// 通过反射可以把value赋值到class具体对象中(u对象)
// 设置跳过属性安全检查
f.setAccessible(true);
// 反射设置值的时候需要判断数据类型
if (f.getType() == int.class) {
// 如果value是int类型
// map中value是string,需要转成int类型
int v = Integer.parseInt(value);
f.set(u, v);
}
if (f.getType() == String.class) { // 字符类型
f.set(u, value);
}
if (f.getType() == Date.class) { // Date类型
}
}
return u;
}
}
package cn.szsxt.demo01;
/**
*
* bean对象:类中只有属性 set/get 、构造器、toString()
* (比较简单的对象都是javaBean)
* model entry pojo vo 都是bean对象
*/
public class User {
private String name;
private int 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;
}
public User() {
// TODO Auto-generated constructor stub
}
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
7.2通过配置文件使用反射
- 测试类
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
/**
*
* 读取配置文件,通过反射创建对象调用指定的方法
*/
public class Demo09 {
public static void main(String[] args) throws Exception {
// 读取配置文件
// 1 创建properties()对象
Properties pro = new Properties();
// 2 加载配置文件
try {
pro.load(new FileInputStream(new File("src/web.properties")));
} catch (IOException e) {
e.printStackTrace();
}
// 3 读取配置文件中的值
// 通过k - v 方式取值
String class_path = (String) pro.get("servlet-class");
String class_method = (String) pro.get("class-method");
System.out.println(class_path);
System.out.println(class_method);
// 反射创建对象
Class<MyServlet> clazz= (Class<MyServlet>) Class.forName(class_path);
// 创建对象
MyServlet myServlet = clazz.newInstance();
// 获取指定方法
Method m = clazz.getDeclaredMethod(class_method, null);
// 通过反射调用指定的方法
m.invoke(myServlet, null);
}
}
- 方法类MyServlet
public class MyServlet {
/**
* service方法 提供服务
*/
public void service() {
System.out.println("你过来了 我给提供服务器 ......");
}
}
- properties配置文件
servlet-class=cn.szsxt.demo01.MyServlet
class-method=service