由于Java反射技术运用的方面很广泛,借此机会做一个总结,也为自己忘记了可以随时翻阅便可快速回忆起来!
首先我们来说说什么是类的加载?
类的加载:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化 三步来实现对这个类进行初始化。
加载:
*就是指将class文件读入内存,并为之创建一个Class对象。
*任何类被使用时系统都会建立一个Class对象。
连接:
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化:类栈开辟空间、堆开辟空间、默认初始化、显示初始化、构造初始化……
类初始化时机:
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
类加载器的作用:负责将.class文件加载到内在中,并为之生成对应的Class对象。
类加载器的组成:
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器
**什么是反射?**
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射获取class文件的三种方式:
A:Object类的getClass()方法 (对象.getClass())
B:数据类型的静态属性class、(类.class)
C:Class类中的静态方法(class.forName(“类的全路径名称”),开发中推荐使用这种,因为可以将名称配置在配置文件中,比较灵活)
通过反射获取无参构造方法并使用:
准备一个Person类对象:
package com.yida.reflect;
public class Person {
private String name;
private String id;
public Person() {
super();
// TODO Auto-generated constructor stub
}
private Person(String name) {
super();
this.name = name;
}
public Person(String name, String id) {
super();
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public void sleep(){
System.out.println("人要睡觉");
}
public void eat(String name){
System.out.println("人吃"+name);
}
@Override
public String toString() {
return "Person [name=" + name + ", id=" + id + "]";
}
}
接下来就是关于反射的具体应用,注释已经写在代码里了,就不做过多解释了!
package com.yida.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
public class Test01 {
@Test
public void test001() throws Exception{
//获取字节码文件对象
Class clazz = Class.forName("com.yida.reflect.Person");
//获取构造方法(只能获取该对象的所有公共构造方法,就是被public修饰符修饰的)
Constructor[] constructors = clazz.getConstructors();
//遍历构造方法并将方法名输出
for (Constructor constructor : constructors) {
//使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
//Object newInstance = constructor.newInstance();
//Person p1 = (Person) newInstance;
//p1.sleep();
//p1.eat("肉");
System.out.println(constructor);//只能获取公有的构造 方法
/*输出结果:
* public com.yida.reflect.Person(java.lang.String,java.lang.String)
public com.yida.reflect.Person()*/
}
//获取 所有构造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println(constructor);
/*运行结果:
public com.yida.reflect.Person(java.lang.String,java.lang.String)
private com.yida..j.Person(java.lang.String)
public com.yida.reflect.Person()*/
}
}
@Test
public void test002() throws Exception{
//获取字节码文件对象
Class clazz = Class.forName("com.yida.reflect.Person");
//获取单个构造方法 public Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor con = clazz.getConstructor();//返回的是构造方法对象
Object newInstance = con.newInstance();
System.out.println(newInstance);//输出结果:Person [name=null, id=null]
}
@Test
public void test003() throws Exception{
//通过反射获取带参构造方法并使用
//获取字节码文件对象
Class clazz = Class.forName("com.yida.reflect.Person");
Constructor constructor = clazz.getConstructor(String.class,String.class);
Object instance = constructor.newInstance("zhoujie","66666");
System.out.println(instance);//运行结果:Person [name=zhoujie, id=66666]
}
/**
* 通过反射获取私有方法并使用
* @throws Exception
*
*/
@Test
public void test004() throws Exception{
//获取字节码文件对象
Class clazz = Class.forName("com.yida.reflect.Person");
//获取私有构造方法对象
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//值为true,则指示反射的对象在使用时应取消Java语言的访问检查
constructor.setAccessible(true);
//用私有构造方法创建 对象
Object obj = constructor.newInstance("zhoujie");
System.out.println(obj);//运行结果:Person [name=zhoujie, id=null]
}
/**
* 通过反射获取成员变量并使用
* @throws Exception
*/
@Test
public void test005() throws Exception{
//获取字节码文件对象
Class clazz = Class.forName("com.yida.reflect.Person");
//只能获取用public修饰的成员变量
//Field[] fields = clazz.getFields();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
/*运行结果:
private java.lang.String com.yida.reflect.Person.name
private java.lang.String com.yida.reflect.Person.id*/
}
@Test
public void test006() throws Exception{
//获取字节码文件对象
Class clazz = Class.forName("com.yida.reflect.Person");
//Method[] methods = clazz.getMethods();获取自己的包括父亲的公共方法
//Method[] declaredMethods = clazz.getDeclaredMethods();获取自己的所有方法
Constructor con = clazz.getConstructor();
Object obj = con.newInstance();
//获取单个方法 第一个参数表示方法名,第二个参数表示的是方法参数的class类型
Method method = clazz.getMethod("eat",String.class);
method.invoke(obj, "肉");//运行结果:人吃肉
}
}
这里只是简单的介绍反射的的基本用法,最后在分享一个反射的运用—-一键封装
原始需求:用户提交的表单数据我们需要进行封装到数据库中,那么这么一个操作如果我们运用request.getParameter(“name”)……这么 一条一条的进行取值存值的话未免工作量太大,特别是数据一多的情况下根本不好使,那么我们这里就可以运用反射的原理对数据进行抽取然后“一键封装 ”插到数据库,是不是很神奇呢?具体我们先来看看一键封装的原理及具体用法吧!
比如数据库中有一个用户表user,首先我们准备一个User对象用来封装数据User.java如下:
package com.example.domain;
public class User {
private Integer id;
private String name;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", password=" + password + "]";
}
}
具体原理 如下:BeanPushUtils.java
package com.example.utils;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.example.domain.User;
public class BeanPushUtlis {
//把数据自动封装到bean对象,user
public static void push(Object obj) throws Exception{
//1.获取user的描述类
BeanInfo info = Introspector.getBeanInfo(obj.getClass());
//2.获取user里面的属性和方法: setXXX
PropertyDescriptor[] ps = info.getPropertyDescriptors();
//3.遍历数组
for (PropertyDescriptor por : ps) {
//4.获取user里面的属性名称
String attrName = por.getName();
//5.获取属性里面的读方法,写方法
Method readMethod = por.getReadMethod();
Method writeMethod = por.getWriteMethod();
System.out.println(attrName+" "+ readMethod+" "+ writeMethod);
}
}
//把数据自动封装到bean对象, Map<String,Object> map = request.getParameterMap();
// map的key: name属性的值,和 javaBean的属性名称一致
//map的value: 用户输入的值
public static void pop(Object obj,Map<String,Object> map ) throws Exception{
//1.获取user的描述类
BeanInfo info = Introspector.getBeanInfo(obj.getClass());
//2.获取user里面的属性和方法: setXXX
PropertyDescriptor[] ps = info.getPropertyDescriptors();
//3.遍历map,获取map的key ,和 属性描述器的属性名称进行匹配
Set<String> keys = map.keySet();
for (String key : keys) {//key就是name属性的值 , 和PropertyDescriptor的属性匹配
for (PropertyDescriptor por: ps) {//里面的属性 其实就是javaBean的属性名称
String userAttr = por.getName();
if(key.equals(userAttr)){//表单提交的name属性值 和 javaBean的属性名称一致
//获取写方法
Method setMethod = por.getWriteMethod();
setMethod.invoke(obj,map.get(key));// new User().setId("001");
}
}
}
}
public static void main(String[] args) throws Exception {
User user = new User();
//Map map = request.getParameterMap();
Map<String,Object> map = new LinkedHashMap<String,Object>();
// map.put("id", 100);
// map.put("name", "张三");
// map.put("passwordss", "123456");
BeanPushUtlis.pop(user,map);
System.out.println(user);
}
}
实际上Apache已经为我们封装好了BeanUtils工具类,我们要使用的话直接引入jar包就可以了,我这里使用的是commons-beanutils-1.8.3.jar
具体使用很简单,本来是想截图的,但是这个博客管理器老是上传不了图片(上传的时候显示不了)也由于时间关系,我也不做详细的步骤了,因为这个使用真的很简单!
比如用户提交的商品信息数据,最后我们可能需要插入到数据库中,那么提交到servlet时怎么处理呢?见如下代码:
//在servlet里封装用户信息
Manager manager = new Manager();
Map<String, String[]> map = request.getParameterMap();
try {
BeanUtils.populate(manager, map);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//交由service层处理....
//ManagerService ms = new ManagerServiceImpl();
ManagerService ms = BeanFactory.getBean("managerServiceImpl");
Boolean login = ms.login(manager);
//....代码省略
//最后在Dao层进行查询或者插入操作
@Override
public Manager login(Manager manager) {
// 用户登录
QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
String sql = "select * from manager where userName = ? and password = ?";
try {
Manager query = qr.query(sql, new BeanHandler<Manager>(Manager.class),manager.getUserName(),manager.getPassword());
return query;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
怎么样,是不是很神奇呢!(今天就到这里了,如有错误欢迎指正批评!纯手打加所有代码注释都是自己打上去的,喜欢的点个赞哦~)