• java的反射机制


    反射:将类的各个组成部分封装为其他对象,这就是反射机制

    好处:1.在程序运行中,这些对象

    2.可以解耦,提高程序的可扩展性

    在Java开发特别是数据库开发中,经常会用到Class.forName( )这个方法。通过查询Java Documentation我们会发现使用Class.forName( )静态方法的目的是为了动态加载类。在加载完成后,一般还要调用Class下的newInstance( )静态方法来实例化对象以便操作。因此,单单使用Class.forName( )是动态加载类是没有用的,其最终目的是为了实例化对象。

    这里有必要提一下就是Class下的newInstance()和new有什么区别?,首先,newInstance( )是一个方法,而new是一个关键字,其次,Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用new关键字生成对象没有这个限制。

    newInstance方法:通过反射生成Student对象,

    Class c=Class.forName("com.Student");             

    Student s1=(Student)c.newInstance();//调用默认的无参构造函数

    //想调用三个参数的构造

    Constructor con=c.getConstructor(String.class,int.class,String.class);  //用Consructor接收,参数指定类型的Class对象
    con.newInstance("lauraa",20,"women");
    (与这样的操作相同: Student s=new Student("lauraa",20,"women");

    通过反射生成对象
    重写toString方法后,可打印 Student{name='lauraa', age=20, sex='women'}
    Class c2=Class.forName("com.load.Student");//包路径

    Student s1=(Student)c2.newInstance();

    Constructor con=c2.getConstructor(String.class,int.class,String.class); //想调用三个参数的构造,参数为类型.class
    Student stu=(Student)con.newInstance("lauraa",20,"women"); //后面的newInstance是Object类型,所以强转为Student类型;传入参数的值用stu接收
    System.out.println(stu);

    当一个类的构造函数被私有化,不能new对象访问该类。可以通过反射访问

     通过反射访问构造函数:

    1.获取Class对象(Class类的c变量里面包含该类的全部信息)

    2.c.getDeclaredConstructor()调用该类所有的构造函数用Constructor类的c4接收(

    getMethod():获取自身能用所有的公共方法       

    getDeclaredMethod():获取类自身声明的所有方法。)

    3.设置c4是可访问的

    4.再调用newInstance()函数传入构造函数的参数,可以填null或不填

            //Test t=new Test();//私有构造函数,不能new
            Class c=Test.class;
            //c.newInstance(); //运行异常
    Class com.load.ClassLoadTest can not access a member of class com.load.Test with modifiers "private"
    Constructor c4=c.getDeclaredConstructor();//打印构造函数
    //若想调用带参数的构造函数,在getDeclaredConstructor()里面加入int.class,在下面的newInstance方法里面传值
    c4.setAccessible(true); c4.newInstance();
           class Test{
    private Test(){
    System.out.println("测试类的私有构造");
    }
    private Test(int a){
    int b=a;
    System.out.println("b:"+b);
    }
    }

     访问带参数的构造和默认的构造实例:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    class Student {
        private String name;
        private int age;
        private String sex;
    
        private Student(String name, int age, String sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
            System.out.println("66");
        }
        private Student(){
            System.out.println("Student()");
        }
    
        public static void main(String[] args)throws Exception {
    
        }
    }
    class A{
        public static void main(String[] args)throws Exception {
            Class c1=null;
            c1=Student.class;//获取类的Class对象
            Constructor con=c1.getDeclaredConstructor();//得到该类的默认构造函数,若传入参数,访问带参数的构造函数
            con.setAccessible(true);
            con.newInstance();//可以访问别的类默认的私有构造函数(构造函数私有化的时候不能new)
    
            Constructor con1=c1.getDeclaredConstructor(String.class,int.class,String.class);//得到该类的默认构造函数
    若传入参数,访问带参数的构造函数
    con1.setAccessible(true); con1.newInstance("aa",20,"male");//在上面得到参数类型时,newInstance应该传入对应类型的参数 } }

    另一个例子:2020/1/20完善

     获取带参数和不带参数的私有方法、获取和设置私有成员变量的值:

     注:测试通过反射获取私有的方法和设置获得私有变量时,必须要获取该对象;所以只能将获取类中的构造函数设置成私有的,才能通过new得到该对象,才能调用invoke函数和seet函数来得到该方法与设置该值。

    补充:getName方法,获取类或者方法或者成员变量的名字:

    如c.getName( )  :包名.类名(Class c=Class.forName("TestCase01");)            con.getName( ):构造函数的名字    m.getName( ):方法的名字  f.getName( ):成员变量的名字

     完整测试类:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 2020/2/20改进
     */
    public class TestCase01 {
        private int ma = 10;
        private TestCase01() {
            System.out.println("构造函数");
        }
        private TestCase01(int m) {
            this.ma=m;
            System.out.println(ma);
        }
    }
    
     class TestCase02 {
        private int mam = 10;
        public TestCase02() {
            System.out.println("构造函数2");
        }
        private void show() {
            System.out.println("mam:" + mam);
        }
         private void show(String b) {
             System.out.println("mam:" + mam+" b:"+b);
         }
    }
    
    class Test {
        public static void main(String[] args) throws Exception {
                Class c=Class.forName("TestCase01");
                //1.调用无参的构造函数
                Constructor con=c.getDeclaredConstructor();
                con.setAccessible(true);
                con.newInstance();//创建对象10
                //2.调用有参的构造函数
                Constructor con2=c.getDeclaredConstructor(int.class);
                con2.setAccessible(true);
                con2.newInstance(40);//创建对象
    
                //当构造函数不private时,new对象反射私有成员变量和私有方法
                TestCase02 t2=new TestCase02();
                Class c2=t2.getClass();
                /**
                 * 1.先获取私有方法
                 * getDeclaredMethod("show", null); 方法名"show",参数列表show
                 * invoke(t2, null);对象t2,参数方法
                 */
                Method m = c2.getDeclaredMethod("show", null);
                m.setAccessible(true);
                m.invoke(t2, null);
                //获取带参数的私有方法
                Method m2 = c2.getDeclaredMethod("show", String.class);
                m2.setAccessible(true);
                m2.invoke(t2, "6");//mam:10 b:6
                /**
                 * 2.设置私有成员变量的值
                 * f1.set(t2, 20);//设置t2对象私有成员变量的值为20
                 */
                Field f1 = c2.getDeclaredField("mam");
                f1.setAccessible(true);
                System.out.println("打印当前属性的值:"+f1.get(t2));
                f1.set(t2, 20);
                //3.再次打印该方法,得到获取后的值
                Method m3 = c2.getDeclaredMethod("show", null);
                m3.setAccessible(true);
                m3.invoke(t2, null);
            }
        }

     反射的不足:但是,通过反射任意访问对象的私有成员并改变成员变量的值,类得不到保护

    public class ClassLoadTest {
    public static void main(String[] args) throws Exception
    //访问私有方法
     Test t1=new Test();
    Method m=c.getDeclaredMethod("show",null);
    m.setAccessible(true);
    m.invoke(t1,null);
    //访问私有成员变量并改变值
    Field f=c.getDeclaredField("ma");//参数为String类型,所以""双引号变量
    f.setAccessible(true);
    f.set(t1,8); //将属性值设为8
    m.invoke(t1,null); //再次调用show方法打印私有属性ma的值
    }
    }
    class Test{
    private int ma = 10;
    public Test(){
    System.out.println("测试类的私有构造");
    }
    private void show(){
    System.out.println("ma:" + ma);
    }
    }

     •案例需求:2020/1/20

    写一个框架,在不改变框架类代码的前提下,可以创建任意类的对象,并且可以执行其中的方法

    实现该需求:1.配置文件  2.反射机制

    步骤:1.将需要创建的对象的类名与方法定义在配置文件中

    2.在程序中加载并读取配置文件

    3.使用反射技术加载类文件进入内存

    4.创建对象

    5.执行方法

    创建配置文件的方法:src下-New -File

    具体代码:

    package test;
    
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    public class ReflectTest {
        public static void main(String[] args) throws Exception {
            //提供一个模板(写一个框架类),可以创建任意类的对象,但是不能改变该类的对象的代码,但是不能改变框架的代码
            //1.加载配置文件  1)创建pro对象  2).加载配置文件,转换为一个集合(  (1)获取class目录下的配置文件  (2)将配置文件用load方法加载进去)
            Properties pro=new Properties();
            ClassLoader classLoader=ReflectTest.class.getClassLoader();//将ReflectTest类加载进内存
            InputStream is= classLoader.getResourceAsStream("pro.properties");//获取资源对应的字节流InputStream类型,将配置文件传进去
            pro.load(is);//需要抛异常
    
            //2.获取配置文件中定义的数据
            String className=pro.getProperty("className");
            String methodName=pro.getProperty("methodName");
    
            //3.加载该类进内存
            Class cls=Class.forName(className);
            //4.创建对象
            Object obj= cls.newInstance();
            //5.获取方法对象
            Method method=cls.getMethod(methodName);
            //6.执行方法
            method.invoke(obj);
        }
    }

    配置文件:  className=包名.类名

     出现的错误及处理:

    错误原因:InstantiationEcxeption 实例化异常              原因:创建的类没有无参构造

     

     在Person类添加无参构造之后:

    也可以同时加载两个类或者两个方法(方法要求public),只需要在配置文件添加methodName2=person(方法名) 即可:

     注:访问私有的方法但是设置权限之后,会出现NoSuchMethodsException异常,不明所以??

    改配置文件的好处:改代码工程庞大,需要重新测试等;该配置文件使程序扩展性更强,更简单(使用到了反射机制)!

     总结:反射的步骤

             * 通过Java的反射访问类的成员或者是构造对象(访问构造函数),步骤是:
             * 1. 先获取类的Class对象
             * 2. 构造函数类Constructor,方法类Method, 属性类Field
                    通过Class的getDeclaredConstructor,getDeclaredMethod,  getDeclaredField三个方法,分别获取三个对象
             * 3. 如果方法是private的,需要调用反射对象的setAccessible设置访问权限
             * 4. 访问相应的成员
             *    newInstance - 构造对象
             *    invoke - 成员方法调用
             *    set - 成员变量设置  get-获取成员变量的值

    解释java的反射:       

             *  Java之所以支持反射,是因为java的每一个类,都有一个Class对象,
             *  通过Class对象可以获取这个类的构造方法,成员方法,成员变量,通过 反射的方式进行调用或者修改,而不是通常的通过对象调用相应的成员进行访问,反               射提供了setAccessible方法,可以通过返回访问对象任意的成员
             *  反射涉及的类,都在java.lang.reflect包下面,分别是import java.lang.reflect.Field;    import java.lang.reflect.Method;

  • 相关阅读:
    如何搜索IP的地理位置
    Windows Sockets错误标识及对应解释
    【转】GDB中应该知道的几个调试方法
    手动安装OpenCV下的IPP加速库
    Ubuntu16.04调整屏幕分辨率至1920*1080
    win10家庭版删除文件提示没有权限最简单的方式
    Centos7 HyperLedger Fabric 1.4 生产环境部署
    解析key值不确定的json数据
    (转)Centos下,Docker部署Yapi接口管理平台
    (转)手工释放linux内存——/proc/sys/vm/drop_cache
  • 原文地址:https://www.cnblogs.com/laurarararararara/p/11780112.html
Copyright © 2020-2023  润新知