java反射总结

反射

为什么使用反射

类的执行过程

  • 编译——>加载——>连接(验证,准备,解析)——>初始化

类的加载步骤

  • JVM将.class文件读入内存方法区
  • JVM为.class创建唯一的Class对象
package shangpin;
//商品工厂,根据厂家需求,生产不同商品
public class Factory {
    //生产商品:使用父类作为商品返回值类型
    //str代表用户想获取的相拼类型Foods  TVs-累的完整限定名(包括包名)
    public Goods getGoods(String className){
        //获取类型对应的Class对象实例
        //在通过Class的newInstance()生成响应类的实例
        Goods goods =null;
        try {
            //反射应用
            goods = (Goods) Class.forName(className).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return goods;
    }
}

package shangpin;
//测试商品生产,输出商品价格功能
public class Test {
    public static void main(String[] args) {
        Factory factory=new Factory();
        Goods goods=factory.getGoods("shangpin.Foods");
        goods.printPrince();

        goods=factory.getGoods("shangpin.TVs");
        goods.printPrince();
    }
}

package shangpin;

public class TVs extends Goods{
    public void printPrince(){
        System.out.println("打印输出电视价格");
    }
}
package shangpin;

public abstract class Goods {
    //打印输出商品价格
    public abstract void printPrince();
}
package shangpin;

public class Foods extends Goods{
    public void printPrince(){
        System.out.println("打印输出食品价格");
    }
}

什么是反射

反射:Reflection

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

Person p=new Student()

person是编译时对象

student是运行时对象

反射让我们在运行时看清一个类的运行情况并使用

反射让java被视为动态或准动态语言

反射允许程序在运行时加载,探查,使用一个在编译期可能未知的类

反射API

  • Class类:反射的核心类,可以获取类的属性,方法等信息,生成类的实例位于java.lang包
  • Field类:表示类的成员变量,可用来获取和设置类的属性值位于java.lang.reflect包
  • Method类:表示类的方法,可用来获取类中的方法信息或执行方法
  • Constructor类:表示类的构造方法

Class

通过Class类的对象可以

  • 获取类的属性,方法
  • 生成类的实例
  • 调用实例的方法,属性

反射通过操作Class类的对象

  • 实现对类的操作
  • 实现对对象的操作

使用反射的步骤

  • 获取想要操作的类的Class对象

    • 通过Class类的静态方法:

      Class class=Class.forName(className)

      Goods goods=(Goods)Class.forName(className).newInstance();

    • 调用对象实例的getClass()方法

    • 调用某个类的class属性

  • 调用Class类中的方法

    • 获取类的属性
      • getFields()——所有可访问的公共字段
      • getDeclaredFields()——所有字段
      • getField(String name)返回一个特点的公共字段
    • 获取类的方法
      • getMethods()—所有公共方法,包括从超类和超接口继承的声明
      • getDeclaredMethods()—所有方法,包括公共,保护,默认访问,私有方法,但不包括继承的方法
      • getMethods(String name,Class[] parameterTypes)–返回一个方法对象
  • 使用反射API来操作这些信息

package zhujie;
//获取person类对应的Class对象
public class Person {
    public static void main(String[] args) {
        //方式1.通过class。forname
        try {
            Class c=Class.forName("zhujie.Person");
            System.out.println(c);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //2.通过对象实例的getclass方法
        Person p=new Person();
        Class c2=p.getClass();
        System.out.println(c2);

        //3.通过类的class属性
        Class c3=Person.class;
        System.out.println(c3);
    }
}

方式一更常用,对于编译期并不知道对象类型,在运行期对未知类型进行操作时更常用

基于反射获取类信息

需求:给予反射,通过调用Class的相应方法,实现Person类信息的获取(属性,方法)

package zhujie;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestRef {
    public static void main(String[] args) throws Exception{
        //获取class对象
        Class c = Class.forName("zhujie.Person1");
        //调用class方法
        //获取类的相关属性
        Field[] fields1 = c.getFields();//范文所有公共字段
        Field[] fields2=c.getDeclaredFields();//所有字段
        Field address=c.getField("address");//返回一个特定的公共field字段
        System.out.println("获取公共的特定字段"+address);

        //获取相关方法
        Method[] methods1 = c.getMethods();//所有公共方法,包括继承的
        Method[] methods2 = c.getDeclaredMethods();//包括公共,保护,默认访问,私有方法,但不包括继承的方法


        //调用相应反射API完成需求
        System.out.println("所有可访问的公共字段");
        for(Field f:fields1){
            System.out.println(f.getName());
        }
        System.out.println("所有可访问的字段");
        for(Field f:fields2){
            System.out.println(f.getName());
        }
        System.out.println("所有公共方法");
        for(Method m:methods1){
            System.out.println(m.getName());
        }
        System.out.println("包括公共,保护,默认访问,私有方法,但不包括继承的方法");
        for(Method m:methods2){
            System.out.println(m.getName());

        }
    }
}

基于反射生成类的实例

获取构造方法

  • getConstructor(Class[] parameterTypes)—返回一个构造方法对象

括号里面不带参数得到无参构造,带参数得到有参构造

通过Constructor-newInstance(Object[] initargs)可以生成类的实例

通过Method-invoke(Object obj,Object[] args)可在具有指定参数的方法对象上调用此方法对象表示的基础方法

需求:基于反射,通过调用Class的相应方法,通过构造方法实现Person类对象的创建

package zhujie;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestRef {
    public static void main(String[] args) throws Exception{
        //获取class对象
        Class c = Class.forName("zhujie.Person1");
        //调用class方法
        //获取类的相关属性
        Field[] fields1 = c.getFields();//范文所有公共字段
        Field[] fields2=c.getDeclaredFields();//所有字段
        Field address=c.getField("address");//返回一个特定的公共field字段
        System.out.println("获取公共的特定字段"+address);

        //获取相关方法
        Method[] methods1 = c.getMethods();//所有公共方法,包括继承的
        Method[] methods2 = c.getDeclaredMethods();//包括公共,保护,默认访问,私有方法,但不包括继承的方法


        //调用相应反射API完成需求
        System.out.println("所有可访问的公共字段");
        for(Field f:fields1){
            System.out.println(f.getName());
        }
        System.out.println("所有可访问的字段");
        for(Field f:fields2){
            System.out.println(f.getName());
        }
        System.out.println("所有公共方法");
        for(Method m:methods1){
            System.out.println(m.getName());
        }
        System.out.println("包括公共,保护,默认访问,私有方法,但不包括继承的方法");
        for(Method m:methods2){
            System.out.println(m.getName());

        }
        System.out.println("利用对象调用方法");
        //获取构造方法对象
        Constructor constructor = c.getConstructor();//获取无餐构造方法对象
        //根据构造方法对象完成对象实例化
        Object obj = constructor.newInstance();
        //通过实例,调用相应方法
        //1.获取相应的方法对象method对象,第一个参数方法名,第二个参数,方法中的参数类型(无参写null)
        //如果两个参数new Class[]{int.class,String.class}
        Method method=c.getMethod("showInfo",null);
        //2.调用相应方法
        method.invoke(obj,null);

        System.out.println("利用对象调用方法");
        //获取构造方法对象
        Constructor constructor2 = c.getConstructor(new Class[]{int.class,String.class,String.class});//获取无餐构造方法对象
        Object obj2=constructor2.newInstance(new Object[]{18,"张三","北京超燕"});
        Method method2=c.getMethod("showInfo",null);
        method2.invoke(obj2,null);

    }
}

反射实现解耦

需求:

​ 现有一电商系统,需要按促销时间点, 采用不同促销策略,, 其中包括儿童节促销策略、国庆促销策略、双11促销策略等
​ 原实现思路为:根据节日选择不同的促销策略(创建相应对象,调用方法)现要求:使用反射根据节日名称字符串创建节日对象,调用相应促销方法

普通思路

package dianshang;
//儿童节
public class ChildrenDayPS {
    public ChildrenDayPS() {
    }
    public double promoteSales(String productId){
        System.out.println("编号"+productId+"的商品打六折");
        return 0.6;
    }
}

package dianshang;

public class NationalDayPS {
    public NationalDayPS() {
    }
    public double promoteSales(String productID){
        System.out.println("编号"+productID+"的商品打七折");
        return 0.7;
    }
}


package dianshang;

public class ProductSale {
    public void productSale(String productId) {
        //其他操作
        //......
        //不同节日,调用不同促销
        ChildrenDayPS childrenDayPS = new ChildrenDayPS();
        double discount = childrenDayPS.promoteSales(productId);
        System.out.println("节日折扣为:"+discount);
        /*NationalDayPS nationalDayPS=new NationalDayPS();
        double discount=nationalDayPS.promotateSales(productId);
        System.out.println("节日折扣为:"+discount);*/
        //其他操作
        //。。。。。。
    }

    public static void main(String[] args) {
        ProductSale ps=new ProductSale();
        ps.productSale("p001");
    }
}

反射思路修改

实现步骤

  • 获取促销类的构造方法
  • 通过促销类的构造方法,生成促销类的实例
  • 调用该实例的促销方法

用到的反射的类

  • java.lang.Class
  • java.lang.reflect.Constructor
  • java.lang.reflect.Method
package dianshang;
//儿童节
public class ChildrenDayPS {
    public ChildrenDayPS() {
    }
    public double promoteSales(String productId){
        System.out.println("编号"+productId+"的商品打六折");
        return 0.6;
    }
}

package dianshang;

public class NationalDayPS {
    public NationalDayPS() {
    }
    public double promoteSales(String productID){
        System.out.println("编号"+productID+"的商品打七折");
        return 0.7;
    }
}

package dianshang;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ProductSale {
    //className:不同商品的锉削方案
    public void productSaleByRef(String productId,String className) {
        try {
            Class c=Class.forName(className);
            //根据clss获取构造方法对象
            Constructor constructor=c.getConstructor();
            //根据构造方法去完成对象实例化
            Object obj = constructor.newInstance();
            Method method=c.getMethod("promoteSales",String.class);
            double discount = (double) method.invoke(obj, productId);
            System.out.println("节日折扣为:"+discount);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        ProductSale productSale=new ProductSale();
        productSale.productSaleByRef("p001","dianshang.ChildrenDayPS");
        productSale.productSaleByRef("p001","dianshang.NationalDayPS");

    }
}

反射总结

优点

  • 运行期类型的判断,动态加载类
  • 提高了程序的灵活性,扩展性,降低耦合性
  • 提高自适应能力,无序提前硬编码目标类

缺点:

  • 性能问题
  • 安全限制
  • 内部暴露

反射机制是目前众多java框架实现的基础

  • JDBC
  • Hibernate
  • Spring AOP,IoC
  • 分布式微服

实现项目安装插件功能

基于反射,架构师搭建项目框架,自行封装框架,使得项目更加组件化和通用化

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值