一、引入
回顾之前java程序如何使用类
1、分析确定类名,属性名,方法....创建类
public class Car{
private String name;
private String color;
....
public void run(){}
}
2、创建类的对象
Car bm = new Car();
Car bc = new Car();
3、使用
bm.run();
bc.run();
一切都是已知的
在程序开发中,在哪里需要使用哪个类的对象,就在哪创建这个类的对象,去使用即可。这种写法对于业务开发是没有问题的 。
但是在一些组件、框架开发中,他们本身时不知道要处理那些类的。
例如jackson组件,我们给他什么类,他就要处理转换什么类
new ObjectMapper().writeValueAsString(result)
在web.xml中配置了那些servlet类,tomcat就要创建哪些类的对象
<servlet-class>com.wbc.dorm.web.LoginServlet</servlet-class>
<servlet-class>com.wbc.dorm.web.TestServlet</servlet-class>
在myBatis中,给了什么类型,myBatis就可以将结果封装映射到给定的类的对象中
<select id="login1" parameterType="Admin" resultType="Admin">
<update id="updateTeacher" parameterType="Teacher" >
框架只需要写一套程序,就可以处理我们给他的类。框架是如何做到写一套程序,就可以处理任意类的呢?
以前已知类名的使用方式可以看作是正向使用类,框架需要对任意类处理时,只是知道类的信息,通过类的名字,动态采取获得类中的信息。
我们把这种对类的使用方式,称为反向使用
二、java的反射机制
1、反射机制的定义
在运行状态中,在仅知道一个类名时,就可以动态获得类中信息,创建对象,调用成员的机制,称为java的反射机制
2、反射机制的实现
1、获得class类对象 java.lang.Class
Class类的对象,表示当前正在运行中的类和接口
我们通过Class类的对象来获取类的信息,主要有三种方法
package com.wbc.javaReflect;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//以前创建类是已知类名,new创建对象 调用对象成员
/* User user = new User();
user.login();*/
//使用反射机制时,我们只知道类的名称(包含包名+类名)
String className = "com.wbc.javaReflect.User";
//如何活得类的信息呢---可以通过Class类来获得类中信息
//如何获得类的Class对象?
//方式1
Class clazz1=Class.forName(className);
System.out.println(clazz1);
//方式2
Class clazz2=User.class;
System.out.println(clazz1==clazz2);//true 一个类被加载进内存只能生成一个Class对象
//方式3
User user=new User();
Class clazz3=user.getClass();
}
}
需要注意的是,一个类在内存中只会为该类创建一个class对象,通过这个对象就可以调用类的成员
2、通过class对象获得类的对象
package com.wbc.javaReflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
/*
Java中创建对象的方式
1、new对象
2、序列化--反序列化重新生成对象
3、反射机制(反射机制有2种 一种是调用class类的对象创建对象;一种是调用.getConstructor()构造方法,通过构造方法创建对象)
4、对象克隆
*/
String className = "com.wbc.javaReflect.User" ;
//1、通过类名,获得到类的Class对象
Class aClass = Class.forName(className);
//通过类的Class对象,创建对象
Object object = aClass.newInstance();
System.out.println(object);
//获取user对象的属性
Field[] fields = aClass.getDeclaredFields();
//2、通过类中的构造方法,通过构造方法API中的方法创建对象
Constructor constructor1 = aClass.getConstructor();//获取公共的无参构造方法
Object object1 = constructor1.newInstance();
Constructor constructor2=aClass.getConstructor(String.class,String.class);//获取公共的有两个String参数的构造方法
Object object2 = constructor2.newInstance("张三","111");
System.out.println(object1);
System.out.println(object2);
}
}
3、获得对象的构造方法
//获取公共的无参构造方法 Constructor constructor1 = aClass.getConstructor(); //获取公共的有参数的构造方法 Constructor constructor2=aClass.getConstructor(String.class,String.class); //获得所有公共的构造方法 Constructor[] constructors = aClass.getConstructors(); //获取类中任意的构造方法(包括私有的) Constructor constructor = aClass.getDeclaredConstructor(); //获取类中所有的构造方法(包括私有的) Constructor[] constructors2 = aClass.getDeclaredConstructors();
4、获取对象的属性
//拿到指定名称公共的属性(成员变量) Field field = aClass.getField("account"); //拿到指定名称的属性(成员变量)包括属性 Field accountField = aClass.getDeclaredField("account") //允许访问私有属性 accountField.setAccessible(true);
例:简单模拟myBatis从数据库查找后的自动封装的原理(破解权限)
Uesr类
package com.wbc.javaReflect;
public class User {
private String account;
private String password;
public User(String account, String password) {
this.account = account;
this.password = password;
System.out.println("User的有参构造");
}
public User() {
System.out.println("User无参构造");
}
public void login(){
System.out.println("用户登录");
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"account='" + account + '\'' +
", password='" + password + '\'' +
'}';
}
}
模拟赋值
package com.wbc.javaReflect;
import java.lang.reflect.Field;
import java.util.HashMap;
public class Test3 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String className = "com.wbc.javaReflect.User" ;
//1、通过类名,获得到类的Class对象
Class aClass = Class.forName(className);
//通过类的Class对象,创建对象
Object object = aClass.newInstance();
//获得类中的成员变量
/*Field field = aClass.getField("account");//拿到指定名称公共的属性(成员变量)
Field accountField = aClass.getDeclaredField("account");//拿到指定名称的属性(成员变量)包括属性
accountField.setAccessible(true);//允许访问
accountField.set(object,"admin");//赋值
*/
//模拟myBatis自动封装到对象
//模拟从数据库中拿到的数据
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put("account","admin");
hashMap.put("password","123456");
Field[] fields = aClass.getDeclaredFields();
//封装
for (Field field : fields) {
field.setAccessible(true);//允许访问私有属性
field.set(object,hashMap.get(field.getName()));
}
System.out.println(object);
}
}
5、获得对象方法
Method method1 =aClass.getMethod("login");//无参的方法 Method method2 = aClass.getMethod("login", String.class, String.class);//有参的方法 Method method = aClass.getDeclaredMethod("方法名", "类型.class");//私有方法 Method[] methods = aClass.getMethods();//方法集合 Method[] methods1 = aClass.getDeclaredMethods();//私有方法集合 //调用方法对象的invoke方法调用方法 method1.invoke(object); method2.invoke(object,"admin","111");
例1:简单模拟myBatis从数据库查找后的自动封装的原理(通过set方法)
package com.wbc.javaReflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
public class Test5 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
String className = "com.wbc.javaReflect.User" ;
//1、通过类名,获得到类的Class对象
Class aClass = Class.forName(className);
//通过类的Class对象,创建对象
Object object = aClass.newInstance();
//通过属性的get和set方法,对类中的私有属性进行赋值,取值操作
//模拟的数据
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put("account","admin");
hashMap.put("password","123456");
//先拿到类中所有的私有属性
Field[] fields = aClass.getDeclaredFields();
//循环拿到其set方法名字
for (Field field : fields) {
field.setAccessible(true);
//根据属性名生成set方法名称 需要注意的是,属性名和方法名称需要规范命名才可以动态生成
String setMethodName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
//通过Class对象获得相对应的set方法对象
Method setMethodObj = aClass.getMethod(setMethodName,field.getType());
//调用set方法
setMethodObj.invoke(object, hashMap.get(field.getName()));
}
//测试是否封装成功
System.out.println(object);
}
}
封装成功
例2:简单模拟生成json环节
package com.wbc.javaReflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class JsonUtil {
public static String objectToJson(Object obj) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class clazz = obj.getClass();
String json="{";
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//生成属性get方法名字
field.setAccessible(true);
String methodsName = "get"+field.getName().substring(0, 1).toUpperCase()+field.getName().substring(1);
//获得方法对象
Method method = clazz.getMethod(methodsName);
Object value=method.invoke(obj);
//把属性名字和值拼接成键值
json+=field.getName()+":"+value+",";
}
//去掉最后一位多余的逗号
json=json.substring(0,json.length()-1)+"}";
return json;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
User user = new User();
user.setAccount("admin");
user.setPassword("123456");
Car car = new Car();
car.setName("宝马");
car.setColor("红的");
System.out.println(objectToJson(user));
System.out.println(objectToJson(car));
}
}
完成
三、总结
反射机制的优缺点
优点:
● 1.增加程序的灵活性,可以在运行的过程中动态对类进行修改和操作
● 2.提高代码的复用率,比如动态代理
● 3.可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态 调用
缺点:
● 1.反射会涉及到动态类型的解析,导致性能要比非反射调用更低
● 2.使用反射技术通常要在一个没有安全限制的程序运行.使用时需要一些限制
● 3.反射可以绕过一些限制访问的属性或者方法,可能会导致破坏代码本身的封装性