Java基本三:反射


                       

              反射是什么呢?反射是在运行状态中,动态获取信息以及动态调用对象方法。这么抽象的的说法,真的很让人难理解。这么说吧,反射在现实生活中,就像一个神奇的识别器和控制器。给一个物体,就能知道它是什么类,并且知道它的所有属性,还有能随意控制让这个物体去执行它所拥有的方法。比如给一个东西,通过反射,我们认识了这个东西是一只羊,并且知道这个羊的身高,这个羊多大了等等信息,我们还能控制这只羊主动吃草,主动跑路等;另外给我们一个物种,我们就能知道这个物种的所有信息,和行为。并且可以根据这个物种生成新的个体。让它去做某些事情。我理解为识别和控制机器。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、配置文件可以有好多种解析的方式,看自己怎么理解了。

         配合注解

          由于还没有说到注解,所以,把这块放到注解那去说。这也是注解应用的重点。







            

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值