反射是什么呢?反射是在运行状态中,动态获取信息以及动态调用对象方法。这么抽象的的说法,真的很让人难理解。这么说吧,反射在现实生活中,就像一个神奇的识别器和控制器。给一个物体,就能知道它是什么类,并且知道它的所有属性,还有能随意控制让这个物体去执行它所拥有的方法。比如给一个东西,通过反射,我们认识了这个东西是一只羊,并且知道这个羊的身高,这个羊多大了等等信息,我们还能控制这只羊主动吃草,主动跑路等;另外给我们一个物种,我们就能知道这个物种的所有信息,和行为。并且可以根据这个物种生成新的个体。让它去做某些事情。我理解为识别和控制机器。Java中,只要给定类名或者对象,就能获取这个类的所有信息,和方法。并且知道这个对象所有信息和调用这个对象的方法。
反射一直都是动态中的。那么就是在程序执行的时候发生的。通常的类,都是写好了new 具体对象,编译之后。反射是在运行的时候,给一个类的名字,就可以获取类信息,执行类的构造方法,生成对象。
Class<?> forName = Class.forName("SeaSoldier");
SeaSoldier就是所知道的类名字,这个名字必须是全的,包名+类名。比如 com.test.Soldier 。我这里默认不写包名。
有了这个forName就可以获取好多信息了;
类中获取属性:
Field[] fields = forName.getDeclaredFields();
类中获取构造方法:
Constructor<?>[] declaredConstructors = forName.getDeclaredConstructors();
类中获取方法:
Method[] declaredMethods = forName.getDeclaredMethods();
执行new 一个对象:
Object newInstance = forName.newInstance();
遍历属性,获取属性名字,属性类型:
for(int i=0;i<fields.length;i++){
Field field = fields[i];
String name = field.getName();
Class<?> type = field.getType();
System.out.println(name+","+type.getName()+",");
}
其中属性的设置需要执行field.setAccessible(true);需要判断属性的type,根据属性,设置相应的属性类型值。
遍历方法,获取方法名字和方法执行;
Method[] declaredMethods = forName.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method method = declaredMethods[i];
String name = method.getName();
System.out.println(name);
method.invoke(newInstance);
}
方法的执行invoke,第一个参数是目标类,Object,其余的参数列表就是方法的参数列表,从第一个到最后一个。没有参数就不增加。
其中对于属性和方法都有一个getAnnotation的方法,这个是注解。
还有好多没有提到,看看Java的api吧。
上例子:
下面声明一个枪类,包括名字,子弹数,出厂地址;
package com.test;
public class Gun {
private String name;
private int bullet;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getBullet() {
return bullet;
}
public void setBullet(int bullet) {
this.bullet = bullet;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
测试代码(不重要部分省略):
try {
Class<?> forName = Class.forName("com.test.Gun");
Gun gun = (Gun) forName.newInstance();
Field nameField = forName.getDeclaredField("name");
Field bulletField = forName.getDeclaredField("bullet");
Field addressField = forName.getDeclaredField("address");
nameField.setAccessible(true);
nameField.set(gun, "加特林");
bulletField.setAccessible(true);
bulletField.setInt(gun, 60);
addressField.setAccessible(true);
addressField.set(gun, "德国");
System.out.println(gun.getName()+","+gun.getBullet()+","+gun.getAddress());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
输出:
加特林,60,德国
这是事先知道这个类,并且生成一个对象,设置私有属性的值并不是通过这个类的方法设置的,不是不是很厉害。
还可以这样通过反射执行对象的方法:
Object object1 = forName.newInstance();
Method method = forName.getMethod("setName", String.class);
method.invoke(object1, "手枪");
Method method2 = forName.getMethod("getName");
Object invoke = method2.invoke(object1);
System.out.println(invoke);
是否想到了什么?没错,spring 就是通过这种方式注入对象的。
来看一下这个反射到底用在哪,
1、ORM,对象关系映射。一般指的是从数据库表关系映射到Java pojo类。比如hibernate
2、配置文件。例如spring的beans容器,
3、配合注解使用。注解把参数信息注入到注解中,处理类反射获取注解信息,进行对注解的类,方法,属性进行处理。spring 中也有。
4、脑补。
ORM:
仍然以Gun 类为例,重写Gun的toString方法
@Override
public String toString() {
// TODO Auto-generated method stub
return "{"+name+","+bullet+","+address+"}";
}
内容比较简单,看懂就好。没有其它的框架复杂。
1、创建数据库,和表格:
数据库名为test;
2、创建mysql的管理类。
如下:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import com.mysql.jdbc.Connection;
public class MySqlManager {
private String url="jdbc:mysql://localhost/test";
private String user="root";
private String pass="";
private Connection connection;
public MySqlManager(){
inite();
}
private void inite(){
try {
Class.forName("com.mysql.jdbc.Driver");
connection=(Connection) DriverManager.getConnection(url, user, pass);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Statement getStatement(){
try {
return connection.createStatement();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public void close(){
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
以获取Statement类和关闭方法。
3、转换类:
从数据库表的数据映射到对象的具体实现类。
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class Convert {
private MySqlManager manager;
private Statement statement;
public Convert(){
manager=new MySqlManager();
statement=manager.getStatement();
}
public<T> List<T> getAllData(String className){
Class class1=null;
List<T> data=new ArrayList<>();
try {
class1 = Class.forName(className);
String sql="select * from "+getTableName(class1);
ResultSet executeQuery = statement.executeQuery(sql);
Field[] declaredFields = class1.getDeclaredFields();
while (executeQuery.next()) {
T data1=(T) class1.newInstance();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
Object object = executeQuery.getObject(field.getName());
field.setAccessible(true);
field.set(data1, object);
}
data.add(data1);
}
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return data;
}
public<T> T getData(String className,String id){
Class class1=null;
T data=null;
try {
class1 = Class.forName(className);
String sql="select * from "+getTableName(class1)+" where id="+id;
ResultSet executeQuery = statement.executeQuery(sql);
Field[] declaredFields = class1.getDeclaredFields();
while (executeQuery.next()) {
data=(T) class1.newInstance();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
String name = field.getName();
Object object = executeQuery.getObject(field.getName());
field.setAccessible(true);
field.set(data, object);
}
}
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return data;
}
public<T> boolean addData(T data){
boolean is=false;
Class<? extends Object> class1 = data.getClass();
String type="insert into "+getTableName(class1)+"(";
String value="values (";
Field[] declaredFields = class1.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
Field field = declaredFields[i];
try {
field.setAccessible(true);
Object object = field.get(data);
if (i==declaredFields.length-1) {
type+=field.getName()+")";
value+="'"+object+"')";
}else {
type+=field.getName()+",";
value+="'"+object+"',";
}
} catch (IllegalArgumentException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
System.out.println(type+" "+value);
is=statement.execute(type+value);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return is;
}
private String getTableName(Class class1){
String name = class1.getName();
return name.substring(name.lastIndexOf(".")+1,name.length());
}
}
上面的转换类中有添加数据,根据id获取数据,和获取所有数据。
可以从代码中看出,这些操作并没有说明哪个具体的类生成的具体对象。而是通过传入的类名参数经过反射机制,创建对象,设置数据,和获取数据。
4、测试:
public class test {
public static void main(String[] args) {
Convert convert=new Convert();
Gun gun1=new Gun();
gun1.setName("沙鹰");gun1.setBullet(30);gun1.setAddress("德国");
Gun gun2=new Gun();
gun2.setName("猎枪");gun2.setBullet(10);gun2.setAddress("法国");
convert.addData(gun1);
convert.addData(gun2);
Gun gun=convert.getData("com.test.Gun","1");
System.out.println(gun);
List<Gun> guns=convert.getAllData("com.test.Gun");
System.out.println(guns);
}
}
输出:
{沙鹰,30,德国}
[{沙鹰,30,德国}, {猎枪,10,法国}]
如果换一个类,比如子弹类。那么只要创建一个子弹类的表格,然后可以直接在测试类中用Convert类中的方法去增加子弹数据和获取子弹数据。不需要更改Convert类和数据库管理类。是不是特别方便?其它的修改数据,删除数据,我就不说了,自己增加吧。
看到这里也就明白了吧,数据和客户端的调用是可以随意变化,中间的框架却不用变。此刻,你想到了什么?
里面用到了一个之前没有说到的泛型机制。之后还会说的。敬请期待。
配置文件:
这里引用一下之前的文章:http://blog.csdn.net/xiaoyunchengzhu/article/details/51240255
github地址:https://github.com/xiaoyunchengzhu/XmlConvertObject
虽然里面是以安卓的环境写的,但是里面的主要点已经不是安卓的技术了,而是配置文件解析向对象的转化。
主要是这几点
1、XML文件的方式,读取xml文件,我这里用的DOM4J的方式去解析的,一个元素解读为一个类。这个类可以是基本类型、可以是集合、可以是复杂类等。类的属性由自己去定义,如何规定XML元素中的属性和类的属性对应等;如果类的属性是另一个类,而不是基本类型的时候,怎么在XML中表示。等。
2、在XML中的类,表达的是对象之间的关系或者说对象的属性值,对象之间的关系,对象的属性值,都是已经在xml文件中规定好了。然后在程序运行时被加载、解析,调用等。实际上就是控制反转的思想。其实和spring相似,当然,没有spring复杂,健全。
3、配置文件可以有好多种解析的方式,看自己怎么理解了。
配合注解
由于还没有说到注解,所以,把这块放到注解那去说。这也是注解应用的重点。