浅析Java反射

一、Java反射机制

运行状态中,对于任意一个类都能知道它的属性和方法,对于任意一个对象都能调用它的任意属性和方法,这种获取的信息以及动态调用对象方法的功能称为java语言的反射机制。

RTTI:运行时类型信息,假定我们在编译时已经知道了所有的类型信息,类型信息是指一个类里的属性、方法等信息。比如正常创建对象的过程。
反射机制:允许我们在运行时发现和使用类的信息。

二、创建一个对象

1、正常new一个对象的过程

①JVM把.java文件编译成.class文件;
类的加载:类加载器首先会去方法区判断这个类的Class对象是否已经被加载过,如果没有被加载,类加载器会加载.class文件并把Class对象存到方法区中;
类实例化:引用类型,栈指向堆。

2、利用反射动态创建对象

第一步:获取class对象

  public static void main(String[] args) throws Exception{
       Animal dog=new Animal("狗","男",2);
       Animal cat = new Animal("猫","女",3);
       //方法一:利用getClass()方法
       Class c1= dog.getClass();
       Class c2 = cat.getClass();
        System.out.println(c1==c2);
        //方法二:利用class属性
        Class c3 = Animal.class;
        System.out.println(c1==c3);
        //方法三:利用Class.forName("类的全限定名")方法
        Class c4=Class.forName("com.etime01.Animal");
        System.out.println(c3==c4);

Animal类的class对象是唯一的

第二步:动态创建对象

        //方法一:newInstance()创建对象
        Object obj1 = c3.newInstance();
        System.out.println(obj1.toString());

        //方法二:构造方法创建对象
        Constructor constructor = c3.getConstructor(String.class,String.class,int.class);
        Object obj2 = constructor.newInstance("猪","男",12);
        System.out.println(obj2.toString());

通过newInstance()方法创建对象要保证Class对象对应类(Animal)有无参构造函数

三、利用反射调用方法

public static void main(String[] args) throws Exception {

     //获取class对象
    Class c1= Class.forName("com.etime01.Animal");
    //创建对象实例
    Animal obj1 = (Animal)c1.newInstance();
    obj1.setName("人");
    //通过反射获取method对象
    Method method = c1.getMethod("getName");
    //通过method对象的invoke方法调用方法
    Object invoke = method.invoke(obj1);
    System.out.println(invoke);

四、动态操作属性(get、set方法)

    Class c2 = Class.forName("com.etime01.Animal");
    Animal  obj2 =(Animal)c2.newInstance();
    //获取属性
    Field[] fields = c2.getFields();
    for(Field f :fields){
        System.out.println("公有属性"+f.getName());
    }
    Field[] fields1 = c2.getDeclaredFields();
    for(Field f :fields1){
        System.out.println("私有属性"+f.getName());
        //设置私有属性的访问权限
        f.setAccessible(true);
        //根据属性类型给属性赋值
        if(f.getType().equals(String.class)){
           f.set(obj2,"123");
        }else if(f.getType().equals(int.class)){
            f.set(obj2,123);
        }
    }
    System.out.println(obj2.toString());
   //调用get、set方法
    PropertyDescriptor p =new PropertyDescriptor("name",c2);
    //获取写方法
    Method method1 = p.getWriteMethod();
    method1.invoke(obj2,"小狗");
    //获取读方法
    Method readMethod = p.getReadMethod();
    System.out.println(readMethod.invoke(obj2));
    //或者拼接一个get或则set方法的名称
    String fname="name";
    String mname="get"+fname.substring(0,1).toUpperCase()+fname.substring(1);
    Method method2 = c2.getMethod(mname);
    System.out.println(method2.invoke(obj2));

五、反射的简单应用

以简单工厂模式计算器为例

1、简单工厂模式

简单工厂模式一般通过一个接口和多个实现类来完成计算器的功能,首先定义接口的抽象方法是操作,多个实现类分别是加、减、乘、除。
这儿为什么不在一个接口直接定义加减乘除四个抽象方法呢?
原因是如果有增加新的计算操作,那这个接口的改动也非常大,所以把接口的抽象方法直接定义为操作,那么包含了所有的计算操作,以后再想增加某些操作时,直接创建一个实现类即可,拓展性比较强。

接口:

package com.etime04;

public interface Operation {
    double operation(double a, double b);
    
}

4个实现类:

package com.etime04;
public class AddImpl implements Operation {
    @Override
    public double operation(double a, double b) {
        return a+b;
    }
}

package com.etime04;

public class SubImpl implements Operation {

    @Override
    public double operation(double a, double b) {
        return a-b;
    }
}

package com.etime04;

public class MulImpl implements Operation {

    @Override
    public double operation(double a, double b) {
        return a*b;
    }
}

package com.etime04;

public class DivImpl implements Operation {

    @Override
    public double operation(double a, double b) {
        return a/b;
    }
}

2、配置xml文件

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <oper>
        <name>+</name>
        <value>com.etime04.AddImpl</value>
    </oper>
    <oper>
        <name>-</name>
        <value>com.etime04.SubImpl</value>
    </oper>
    <oper>
        <name>*</name>
        <value>com.etime04.MulImpl</value>
    </oper>
    <oper>
        <name>/</name>
        <value>com.etime04.DivImpl</value>
    </oper>
</root>

3、解析xml配置文件

**
     * 该方法用于解析xml中操作的实现类的全限定名
     */
    private static String getClazz(String oper) throws ParserConfigurationException, IOException, SAXException {
        File f =new File("src/com/etime04/config.xml");
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(f);
        NodeList n1 = doc.getElementsByTagName("oper");
        String clazz = "";
        for(int i=0;i<n1.getLength();i++){
            //获取<name>的值
            String s = doc.getElementsByTagName("name").item(i).getFirstChild().getNodeValue();
            System.out.println(s);
            //判断输入的操作与获取的xml的<name>的值是否相等
            if(s.equals(oper)){
                //获取实现类的全限定名
                clazz=doc.getElementsByTagName("value").item(i).getFirstChild().getNodeValue();
                break;
            }
        }
        System.out.println("clazz:"+clazz);
        return clazz;

    }

4、测试

java
public class Test {
    private static Operation operation;
    public static void main(String[] args){
        Double result=cal(5,4,"+");
        System.out.println("result:"+result);
    }

    public static double cal(double a,double b,String oper){
        try {
            //通过getClazz方法获取到操作对应的实现类的全限定名
            String clazz = getClazz(oper);
           //创建一个class对象
            Class<Operation> c = (Class<Operation>) Class.forName(clazz);
            //通过class对象创建一个实现类的实例对象
            operation = c.newInstance();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //通过对象.方法名 获得最终的计算值
        return operation.operation(a,b);
    }
 }   

此文章为学习记录文章,参考了别的文章,如有不正之处请指教。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值