Java提供了反射技术,使我们可以通过类的全名创建对象,利用反射机制,我们可以在运行时再确定对象类型,从而对不同对象进行分别处理,多少无意,看代码
Class对象的获取
通常jvm在程序运行时会为每个类自动加载一个Class对象,通常我们创建对象和对对象里的函数进行使用或操作时都是JVM通过这个Class对象来完成我们对一个类或对象的相应操作,而一般有三种方式获取该Class对象
//Emp为一个员工数据表封装类
public class CreateClassObject {
public static void main(String[] args) throws Exception {
//1、通过class.forname()传入完整类名来获取Class对象
// Class clazz = Class.forName("com.mypackage.entity.Emp");
//2、通过类名.class来获取
// Class<Emp> clazz = Emp.class;
//3、通过对象的getClass()来获取
Class clazz = new Emp().getClass();
System.out.println(clazz.getPackage());//获取所在的包
System.out.println(clazz.getName());//获取完整类名
System.out.println(clazz.getSimpleName());//获取简单类名
System.out.println(clazz.getCanonicalName());//获取符合java规范的类名
//4、如果是一个基本数据类型,那么可以通过Type的方式来获取Class对象
Class type = Integer[].TYPE;
//getName和getCanonicalName
System.out.println(type.getName());
System.out.println(type.getCanonicalName());
}
}
Class类相应API
首先创建两个简单的类以便于查看Class的API相应效果
Person类
package com.mypackage.reflect;
//Person类
public class Person {
public String name;
public int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String show(){
return "name:"+name+", age :"+age;
}
}
Student类 继承了Person,和实现了一堆getter和setter
package com.mypackage.reflect;
public class Student extends Person{
public String className;
private String address;
public Student(){
super();
}
private Student(String name,int age,String className){
super(name,age);
this.className= className;
}
public Student(String name,int age,String className,String address){
super(name,age);
this.className = className;
this.address = address;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
private void add(int a,int b){
System.out.println(a+b);
}
@Override
public String toString() {
return "Student{" +
"className='" + className + '\'' +
", address='" + address + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
Class类API
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassAPI {
public static void main(String[] args) throws Exception{
Class<?> clazz = Class.forName("com.mypackage.reflect.Student");
//获取成员变量,包括子类及父类,同时只包含public修饰的成员变量
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
System.out.println(field.getName());//获取字段名
System.out.println(field.getType());//获取字段类型
System.out.println(field.getModifiers());//获取访问修饰符类型,1为public
System.out.println("-----");
}
System.out.println("=======================");
//此方法返回的是当前类的所有属性,不仅仅局限于公共访问修饰符,所有的访问修饰符都可以拿到
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
}
System.out.println("========================");
//反射在一定程度上破坏了封装性,需要合理使用
Field address = clazz.getDeclaredField("address");
//私有属性需设置该属性是否能被访问,true表示能被访问,破坏了封装性
address.setAccessible(true);
System.out.println(address.getName());
//通过Class对象clazz的newInstance()方法创建对象
Object o = clazz.newInstance();
//更改对象o的成员变量address的值
address.set(o,"北京市");
System.out.println(((Student)o).getAddress());
System.out.println("===============================");
//获取该对象的普通方法,包含的方法范围是当前对象及父类对象的所有公共方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println("===============================");
//获取当前类中所有的方法,无论什么访问修饰符
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
System.out.println("===============================");
//通过传入函数名及参数类型的class获取对应的method
Method add = clazz.getDeclaredMethod("add", int.class, int.class);
add.setAccessible(true);
Object o1 = clazz.newInstance();
//通过invoke调用add方法本身,但第一个参数需要传入newInstance()创建的对象
add.invoke(o1,123,123);
System.out.println("===============================");
//获取对象的所有构造方法,只能获取公有的改造方法
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName());
}
System.out.println("===============================");
//获取所有的构造方法,无论是私有还是公有
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
//如何调用私有的构造方法呢?
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(String.class, int.class, String.class);
declaredConstructor.setAccessible(true);
Student o2 = (Student)declaredConstructor.newInstance("lisi", 23, "java");
System.out.println(o2);
}
}
反射的一个应用实例
反射有上面作用呢?
在java JDBC使用简易教程中如果要对一个对象进行持久化,往数据库插入一条数据时(一般会把每条数据封装成一个对象,不同的数据表封装成不同对象),不同对象还需要另外实现各自的DAO(Data Access Object),即需要为每个封装的数据对象分别写不同的数据持久化类,实现对数据的增删改查操作,而利用反射技术,我们可以简化DAO的编写,实现一个类能为所有表的数据进行增删改查操作
package com.mypackage.reflect;
import com.mypackage.entity.Dept;
import com.mypackage.entity.Emp;
import com.mypackage.util.DBUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* 要查询N张表的数据,但是不想写N多的方法,能否写一个方法完成所有表的查询工作
*
* */
public class BaseDaoImpl {
/**
* 统一的查询表的方法
* @param sql 不同的sql语句
* @param params sql语句的参数
* @param clazz sql语句查询返回的对象
* @return
*/
public List getRows(String sql,Object[] params,Class clazz){
List list = new ArrayList();
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
try {
//建立连接,DBUtil封装了创建连接的操作
connection = DBUtil.getConnection();
//创建pstmt对象
pstmt = connection.prepareStatement(sql);
//给sql语句填充参数
if(params!=null){
for(int i = 0;i<params.length;i++){
pstmt.setObject(i+1,params[i]);
}
}
//开始执行查询操作,resultset中有返回的结果,需要讲返回的结果放置到不同的对象中
resultSet = pstmt.executeQuery();
//获取结果集合的元数据对象
ResultSetMetaData metaData = resultSet.getMetaData();
//判断查询到的每一行记录中包含多少个列
int columnCount = metaData.getColumnCount();
//循环遍历resultset
while(resultSet.next()){
//通过clazz反射创建放置具体结果属性的对象
Object obj = clazz.newInstance();
for(int i= 0;i<columnCount;i++){
//从结果集合中获取单一列的值,从1开始
Object objValue = resultSet.getObject(i+1);
//获取列的名称,注意数据封装类的成员变量名需要与数据库表中的列名一致
//以便于用字符串获取Method对象进而调用相应属性的set方法
String columnName = metaData.getColumnName(i+1).toLowerCase();
//获取类中的属性
Field declaredField = clazz.getDeclaredField(columnName);
//获取类中属性对应的set方法
Method method = clazz.getMethod(getSetName(columnName),declaredField.getType());
if(objValue instanceof Number){//java.sql.Number类型需要对数字类型分别处理
Number number = (Number) objValue;
//获取成员变量 的类型 的类名
String fname = declaredField.getType().getName();
//根据成员变量 的类型 的类名 区分是哪种数据类型,以获取相应数值类型的数据并传入
if("int".equals(fname)||"java.lang.Integer".equals(fname)){
method.invoke(obj,number.intValue());
}else if("byte".equals(fname)||"java.lang.Byte".equals(fname)){
method.invoke(obj,number.byteValue());
}else if("short".equals(fname)||"java.lang.Short".equals(fname)){
method.invoke(obj,number.shortValue());
}else if("long".equals(fname)||"java.lang.Long".equals(fname)){
method.invoke(obj,number.longValue());
}else if("float".equals(fname)||"java.lang.Float".equals(fname)){
method.invoke(obj,number.floatValue());
}else if("double".equals(fname)||"java.lang.Double".equals(fname)){
method.invoke(obj,number.doubleValue());
}
}else{//其他类型直接传入对象本身调用set方法即可
method.invoke(obj,objValue);
}
}
list.add(obj);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DBUtil.closeConnection(connection,pstmt,resultSet);
}
return list;
}
public String getSetName(String name){//获取字段名相应的setter函数名
return "set"+name.substring(0,1).toUpperCase()+name.substring(1);
}
public static void main(String[] args) {//测试用main函数
BaseDaoImpl baseDao = new BaseDaoImpl();
//注意传入的是Emp数据表封装类的class
List rows1 = baseDao.getRows("select empno,ename,sal,deptno from emp where deptno =?",
new Object[]{10}, Emp.class);
for(Iterator it = rows1.iterator();it.hasNext();){
Emp emp = (Emp) it.next();
System.out.println(emp);
}
//传入的是Dept数据表封装类的class
List rows2 = baseDao.getRows("select deptno,dname,loc from dept",
new Object[]{}, Dept.class);
for(Iterator it = rows2.iterator();it.hasNext();){
Dept dept = (Dept) it.next();
System.out.println(dept);
}
}
}
上面的main函数测试了一个类对不同数据表的查询操作,可以看到用反射简化了DAO的编写工作,不再需要为不同的数据表封装不同的DAO了,很多高级框架这中都使用的了反射技术,但根据其他资料了解到,反射技术影响了程序的性能,因此引入了运行时注解,但此篇幅中不再展开