反射机制_java反射机制面试 - CSDN
  • 1.理解JAVA反射机制的原理,作用;2.掌握反射机制的应用;3.什么是单例模式,单例模式的作用及应用场合;4.单例模式类库的开发,与线程安全等注意问题的讲解;
  • JAVA中的反射机制

    2014-01-08 22:35:03
    反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!  一,先看一下反射的概念:   主要是指程序可以访问,检测和...

               反射,当时经常听他们说,自己也看过一些资料,也可能在设计模式中使用过,但是感觉对它没有一个较深入的了解,这次重新学习了一下,感觉还行吧!


               一,先看一下反射的概念:

                  主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

                 反射是java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!

                 看概念很晕的,继续往下看。

     

          二,反射机制的作用:

                  1,反编译:.class-->.java

                  2,通过反射机制访问java对象的属性,方法,构造方法等;

                 这样好像更容易理解一些,下边我们具体看怎么实现这些功能。


          三,在这里先看一下sun为我们提供了那些反射机制中的类:

    java.lang.Class;                

    java.lang.reflect.Constructor; java.lang.reflect.Field;        

    java.lang.reflect.Method;

    java.lang.reflect.Modifier;


                很多反射中的方法,属性等操作我们可以从这四个类中查询。还是哪句话要学着不断的查询API,那才是我们最好的老师。


             四,具体功能实现:

                    1,反射机制获取类有三种方法,我们来获取Employee类型

    //第一种方式:
    Classc1 = Class.forName("Employee");
    //第二种方式:
    //java中每个类型都有class 属性.
    Classc2 = Employee.class;
     
    //第三种方式:
    //java语言中任何一个java对象都有getClass 方法
    Employeee = new Employee();
    Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)

     

        2,创建对象:获取类以后我们来创建它的对象,利用newInstance

                Class c =Class.forName("Employee");
     
                //创建此Class 对象所表示的类的一个新实例
                Objecto = c.newInstance(); //调用了Employee的无参数构造方法.


        3,获取属性:分为所有的属性和指定的属性:

          a,先看获取所有的属性的写法:

    //获取整个类
    			Class c = Class.forName("java.lang.Integer");
    		      //获取所有的属性?
    			Field[] fs = c.getDeclaredFields();
    	 
    	               //定义可变长的字符串,用来存储属性
    			StringBuffer sb = new StringBuffer();
    	        //通过追加的方法,将每个属性拼接到此字符串中
    			//最外边的public定义
    			sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");
    	        //里边的每一个属性
    			for(Field field:fs){
    				sb.append("\t");//空格
    				sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等
    				sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字
    				sb.append(field.getName()+";\n");//属性的名字+回车
    			}
    	
    			sb.append("}");
    	
    			System.out.println(sb);
    

           b,获取特定的属性,对比着传统的方法来学习:


    public static void main(String[] args) throws Exception{
    			
    	//以前的方式:
    	/*
    	User u = new User();
    	u.age = 12; //set
    	System.out.println(u.age); //get
    	*/
    			
    	//获取类
    	Class c = Class.forName("User");
    	//获取id属性
    	Field idF = c.getDeclaredField("id");
    	//实例化这个类赋给o
    	Object o = c.newInstance();
    	//打破封装
    	idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。
    	//给o对象的id属性赋值"110"
    	idF.set(o, "110"); //set
    	//get
    	System.out.println(idF.get(o));
    }
    

     4,获取方法,和构造方法,不再详细描述,只来看一下关键字:

    方法关键字

    含义

    getDeclaredMethods()

    获取所有的方法

    getReturnType()

    获得方法的放回类型

    getParameterTypes()

    获得方法的传入参数类型

    getDeclaredMethod("方法名",参数类型.class,……)

    获得特定的方法

     

     

    构造方法关键字

    含义

    getDeclaredConstructors()

    获取所有的构造方法

    getDeclaredConstructor(参数类型.class,……)

    获取特定的构造方法

     

     

    父类和父接口

    含义

    getSuperclass()

    获取某类的父类

    getInterfaces()

    获取某类实现的接口

      

             这样我们就可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。

     

      五,反射加配置文件,使我们的程序更加灵活:

                 在设计模式学习当中,学习抽象工厂的时候就用到了反射来更加方便的读取数据库链接字符串等,当时不是太理解,就照着抄了。看一下.NET中的反射+配置文件的使用:

                 当时用的配置文件是app.config文件,内容是XML格式的,里边填写链接数据库的内容:

              <configuration>
    			<appSettings>
    				<add     key=""  value=""/>
    			</appSettings>
                </configuration>
    

     反射的写法:   

      assembly.load("当前程序集的名称").CreateInstance("当前命名空间名称".要实例化的类名);

     

              这样的好处是很容易的方便我们变换数据库,例如我们将系统的数据库从SQL Server升级到Oracle,那么我们写两份D层,在配置文件的内容改一下,或者加条件选择一下即可,带来了很大的方便。

                

             当然了,JAVA中其实也是一样,只不过这里的配置文件为.properties,称作属性文件。通过反射读取里边的内容。这样代码是固定的,但是配置文件的内容我们可以改,这样使我们的代码灵活了很多!


        综上为,JAVA反射的再次学习,灵活的运用它,能够使我们的代码更加灵活,但是它也有它的缺点,就是运用它会使我们的软件的性能降低,复杂度增加,所以还要我们慎重的使用它。

    展开全文
  • 理解JAVA反射机制的原理,作用;掌握反射机制的应用;单例模式程序的编写步骤与注意问题
  • 看了好多关于Java反射机制的文章,大多都太过官方,消化起来比较稍显费劲,本篇,我会依据自己的理解去阐述什么是Java的反射机制,反射用在什么地方,以及怎么来使用? 开篇前,我们还是要了解一下,什么是Java的...

    看了好多关于Java反射机制的文章,大多都太过官方,消化起来比较稍显费劲,本篇,我会依据自己的理解去阐述什么是Java的反射机制,反射用在什么地方,以及怎么来使用?

     

    开篇前,我们还是要了解一下,什么是Java的反射机制:

     

     

    “程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl、Python看过我写的Python3学习系列的博文,不止一次突出Python动态语言的特点)、Ruby是动态语言,C++、Java、C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制——Reflection(反射),用在Java身上指的是可以于运行时加载探知使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体(newInstance)或对其fields设值,或唤起(invoke)其methods方法。

    注意 方法的声明和定义不是一回事,
    声明:public string Method(string parm1,int param2,...)
    定义:public string Method(string parm1,int param2,...)
              {
                    // do something
              }


    反射用在什么地方?


    由于,我们还不清楚反射究竟是什么玩意,怎么用,是不是我们平时写代码的时候会用得上? 这些,都不知道的话,我们也没法定论,这个Java反射机制,用在什么地方比较和合适(注意,一项技术的诞生,一定是为了方便另一项技术的使用,否则会失去本身存在的意义!)


    因此,我们先来说一下,反射怎么用?

     



    一、反射的应用



           我们可能听过,Java编写的程序,一次编译,到处运行。这也是Java程序为什么是无关平台的所在,原因在于,java的源代码会被编译成.class文件字节码,只要装有Java虚拟机JVM的地方(Java提供了各种不同平台上的虚拟机制,第一步由Java IDE进行源代码编译,得到相应类的字节码.class文件,第二步,Java字节码由JVM执行解释给目标计算机,第三步,目标计算机将结果呈现给我们计算机用户;因此,Java并不是编译机制,而是解释机制),.class文件畅通无阻。

           Java的反射机制,操作的就是这个.class文件,首先加载相应类的字节码(运行eclipse的时候,.class文件的字节码会加载到内存中),随后解剖(反射 reflect)出字节码中的构造函数、方法以及变量(字段),或者说是取出,我们先来定义一个类Animal,里面定义一些构造函数,方法,以及变量:




    Animal.java:

     

    package com.appleyk.reflect;
    
    public class Animal {
    
    	public String name ="Dog";
    	private int   age  =30 ;
    	
    	//默认无参构造函数
    	public Animal(){
    		System.out.println("Animal");
    	}
    	
    	//带参数的构造函数 
    	public Animal(String name , int age){
    		System.out.println(name+","+age);
    	}
    	
    	//公开 方法  返回类型和参数均有
    	public String sayName(String name){
    		return "Hello,"+name;
    	}
    	
    }
    

     


     



    我们再定义一个测试类:

    ReflectTest.java

     

    package com.appleyk.test;
    
    public class ReflectTest {
     
    	public static void main(String args[]) throws Exception{
    		
    		//do something 
    	}
      
    }
    




     




    我们运行一下我们的项目,会发现如下:



     


    对应内存中就是:




     


     

    我们借助javap命令查看一下,这个Animal.class里面的内容是什么:

     

    F:\Java\ReflectClass\bin\com\appleyk\reflect>javap -c Animal.class
    Compiled from "Animal.java"
    public class com.appleyk.reflect.Animal {
      public java.lang.String name;
    
      public com.appleyk.reflect.Animal();
        Code:
           0: aload_0
           1: invokespecial #12                 // Method java/lang/Object."<init>":
    ()V
           4: aload_0
           5: ldc           #14                 // String Dog
           7: putfield      #16                 // Field name:Ljava/lang/String;
          10: aload_0
          11: bipush        30
          13: putfield      #18                 // Field age:I
          16: getstatic     #20                 // Field java/lang/System.out:Ljava/
    io/PrintStream;
          19: ldc           #26                 // String Animal
          21: invokevirtual #28                 // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
          24: return
    
      public com.appleyk.reflect.Animal(java.lang.String, int);
        Code:
           0: aload_0
           1: invokespecial #12                 // Method java/lang/Object."<init>":
    ()V
           4: aload_0
           5: ldc           #14                 // String Dog
           7: putfield      #16                 // Field name:Ljava/lang/String;
          10: aload_0
          11: bipush        30
          13: putfield      #18                 // Field age:I
          16: getstatic     #20                 // Field java/lang/System.out:Ljava/
    io/PrintStream;
          19: new           #39                 // class java/lang/StringBuilder
          22: dup
          23: aload_1
          24: invokestatic  #41                 // Method java/lang/String.valueOf:(
    Ljava/lang/Object;)Ljava/lang/String;
          27: invokespecial #47                 // Method java/lang/StringBuilder."<
    init>":(Ljava/lang/String;)V
          30: ldc           #49                 // String ,
          32: invokevirtual #51                 // Method java/lang/StringBuilder.ap
    pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          35: iload_2
          36: invokevirtual #55                 // Method java/lang/StringBuilder.ap
    pend:(I)Ljava/lang/StringBuilder;
          39: invokevirtual #58                 // Method java/lang/StringBuilder.to
    String:()Ljava/lang/String;
          42: invokevirtual #28                 // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
          45: return
    
      public java.lang.String sayName(java.lang.String);
        Code:
           0: new           #39                 // class java/lang/StringBuilder
           3: dup
           4: ldc           #64                 // String Hello,
           6: invokespecial #47                 // Method java/lang/StringBuilder."<
    init>":(Ljava/lang/String;)V
           9: aload_1
          10: invokevirtual #51                 // Method java/lang/StringBuilder.ap
    pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          13: invokevirtual #58                 // Method java/lang/StringBuilder.to
    String:()Ljava/lang/String;
          16: areturn
    }

     




    我们发现,字节码里面包含了类Animal的构造函数、变量以及方法,但注意,全都是public类型的,我们的定义的类的私有变量 private int   age  =30 哪去了?当然,既然是类的私有部分,肯定不会暴露在外面的,但是不阻碍我们通过反射获得字节码中的私有成员(本篇只举例说明私有变量(字段field),其他私有类成员同理)。

    我们的类Animal在Anima.java中定义,但在Animal.class文件中,我们的Animal类阐述如下:



     




    下面,我们来写一段demo,来演示一下,如何使用反射机制,将.class文件中的类加载出来,并解剖出字节码中对应类的相关内容(构造函数、属性、方法):

    看代码前,我们学两个小技巧:



    (1)获得类的完全限定名:




     

    copy以后,直接paste

     




    (2)自动生成返回值对象

     




     

     

    ReflectTest.java:

     

    package com.appleyk.test;
    
    import java.lang.reflect.Constructor;
    
    import com.appleyk.reflect.Animal;
    
    public class ReflectTest {
     
    	public static void main(String args[]) throws Exception{
    		
    		//do something 
    		//1、加载类 ,指定类的完全限定名:包名+类名
    		 Class c1 = Class.forName("com.appleyk.reflect.Animal");
    		 System.out.println(c1);//打印c1,发现值和字节码中的类的名称一样
    		
    		 //2、解刨(反射)类c1的公开构造函数,且参数为null 
    		 Constructor ctor1= c1.getConstructor();
    		 
    		//3、构造函数的用途,就是创建类的对象(实例)的
    		//除了私有构造函数外(单列模式,禁止通过构造函数创建类的实例,保证一个类只有一个实例)
    		//ctor1.newInstance()默认生成一个Object对象,我们需要转化成我们要的Animal类对象
    		// Object a1 = ctor1.newInstance();
    		 Animal a1 = (Animal)ctor1.newInstance(); 
    		
    		//4、证明一下a1确实是Animal的实例,我们通过访问类中的变量来证明
    		 System.out.println(a1.name);
    	}
      
    }
    

     





    我们看下,上述demo 的执行结果:



     

     


     

     

    我们接着走,获得类中的变量(字段)和方法,两种方式,一个是getXXX,一个是getDeclaredXXX,二者是有区别的,下面demo注释的很详细,并且,我们使用反射出的字段和方法,去获取相应实例的字段值和唤起方法(相当于执行某实例的方法),我们看下完整版demo:

     

    加强版的 ReflectTest.java

     

    package com.appleyk.test;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    import com.appleyk.reflect.Animal;
    
    public class ReflectTest {
    
    	public static void main(String args[]) throws Exception {
    
    		// do something
    
    		System.out.println("A(无参构造函数)--加载类、反射类的构造函数、利用构造函数new一个Animal实例instance--");
    
    		// 1、加载类 ,指定类的完全限定名:包名+类名
    		Class c1 = Class.forName("com.appleyk.reflect.Animal");
    		System.out.println(c1);// 打印c1,发现值和字节码中的类的名称一样
    
    		// 2.a、解刨(反射)类c1的公开构造函数,且参数为null
    		Constructor ctor1 = c1.getConstructor();
    
    		// 3、构造函数的用途,就是创建类的对象(实例)的
    		// 除了私有构造函数外(单列模式,禁止通过构造函数创建类的实例,保证一个类只有一个实例)
    		// ctor1.newInstance()默认生成一个Object对象,我们需要转化成我们要的Animal类对象
    		// Object a1 = ctor1.newInstance();
    		Animal a1 = (Animal) ctor1.newInstance();
    
    		// 4、证明一下a1确实是Animal的实例,我们通过访问类中的变量来证明
    		System.out.println(a1.name);
    
    		System.out.println("A(有参构造函数)--加载类、反射类的构造函数、利用构造函数new一个Animal实例instance--");
    		// 2.b、 解刨(反射)类c1的公开构造函数,参数为string和int
    		Constructor ctor2 = c1.getConstructor(String.class, int.class);
    		Animal a2 = (Animal) ctor2.newInstance("Cat", 20);
    
    		System.out.println("B--获得本类中的所有的字段----------------------------");
    
    		// 5、获得类中的所有的字段 包括public、private和protected,不包括父类中申明的字段
    		Field[] fields = c1.getDeclaredFields();
    		for (Field field : fields) {
    			System.out.println(field);
    
    		}
    
    		System.out.println("C--获得本类中的所有公有的字段,并获得指定对象的字段值-----");
    
    		// 6、获得类中的所有的公有字段
    		fields = c1.getFields();
    		for (Field field : fields) {
    			System.out.println(field + ", 字段值 = " + field.get(a1));
    			// 注意:私有变量值,无法通过field.get(a1)进行获取值
    			// 通过反射类中的字段name,修改name的值(注意,原值在类中name="Dog")
    			// 如果,字段名称等于"name",且字段类型为String,我们就修改字段的值,也就是类中变量name的值
    			if (field.getName() == "name" && field.getType().equals(String.class)) {
    				String name_new = (String) field.get(a1);// 记得转换一下类型
    				name_new = "哈士奇";// 重新给name赋值
    				field.set(a1, name_new);// 设置当前实例a1的name值,使修改后的值生效
    			}
    		}
    
    		System.out.println("利用反射出的字段,修改字段值,修改后的name = " + a1.name);
    		System.out.println("D--获取本类中的所有的方法--------------------");
    
    		// 7、获取本类中所有的方法 包括public、private和protected,不包括父类中申明的方法
    		Method[] methods = c1.getDeclaredMethods();
    		for (Method m : methods) {
    			System.out.println(m);// 我们在类Animal中只定义了一个public方法,sayName
    		}
    
    		System.out.println("E--获取本类中的所有的公有方法,包括父类中和实现接口中的所有public方法-----------");
    
    		// 8、获取类中所有公有方法,包括父类中的和实现接口中的所有public 方法
    		methods = c1.getMethods();
    		for (Method m : methods) {
    			System.out.println(m);// 我们在类Animal中只定义了一个public方法,sayName
    		}
    
    		System.out.println("F--根据方法名称和参数类型获取指定方法,并唤起方法:指定所属对象a1,并给对应参数赋值-----------");
    
    		// 9、唤起Method方法(执行) getMethod:第一个参数是方法名,后面跟方法参数的类
    		Method sayName = c1.getMethod("sayName", String.class);
    		System.out.println(sayName.invoke(a1, "Tom"));
    
    	}
    
    }
    

     


    我们看下对应的执行结果:

     

     

     


     

    如果,你对上述执行的结果,一次性接收不了的话,建议将上述测试demo自己亲自敲一遍,先别急着一次性敲完,一点点来,按照序号来,你会发现,反射的机制,无非就是先加载对应字节码中的类,然后,根据加载类的信息,一点点的去解剖其中的内容,不管你是public的还是private的,亦或是本类的还是来自原继承关系或者实现接口中的方法,我们java的反射技术 reflect,均可以将其从字节码中拉回到现实,不仅可以得到字段的名字,我们还可以获得字段的值和修改字段的值,不仅可以得到方法的申明我们还可以拿到方法的定义和唤起方法(执行方法),当然,你会有一个这样的疑惑

     

    为什么new一个对象那么简单,非要用反射技术中的newInstance?

    为什么,我可以直接对象a1. 变量访问变量,却非要用反射那么费劲的获得name字段呢?

    为什么,我几行代码就能搞定的事情,非要用反射呢?

     

     

     

     


     

     

    回到最开始我们讲的地方:

     

     


     

    ok,解密答案之前,我们先来思考一个问题?

     

    假设我们定义了很多类,有Animal、Person、Car..... ,如果我想要一个Animal实例,那我就new Animal(),如果另一个人想要一个Person实例,那么他需要new Person(),当然,另一个说,我只要一个Car实例,于是它要new Car()......这样一来就导致,每个用户new的对象需求不相同,因此他们只能修改源代码,并重新编译才能生效。这种将new的对象写死在代码里的方法非常不灵活,因此,为了避免这种情况的方法,Java提供了反射机制,典型的应用如下:

     

     

     


     

    我们知道Spring的IOC吧,即“控制反转”(通过第三方配置文件实现对 对象的控制)。简单说是将我们设计好的对象交给容器控制,而不是直接交给程序内部进行对象的控制。

     

    比如,在Spring中,我们经常看到:

     

     


     

    针对上述的配置,我们Spring是怎么帮助我们实例化对象,并放到容器中去了呢? 没错,就是通过反射!!!!

     

    我们看下,下面的伪代码实现过程:

     

     

    //解析<bean .../>元素的id属性得到该字符串值为"sqlSessionFactory" 
    	    String idStr = "sqlSessionFactory";  
    	    //解析<bean .../>元素的class属性得到该字符串值为"org.mybatis.spring.SqlSessionFactoryBean"  
    	    String classStr = "org.mybatis.spring.SqlSessionFactoryBean";  
    	    //利用反射知识,通过classStr获取Class类对象  
    	    Class cls = Class.forName(classStr);  
    	    //实例化对象  
    	    Object obj = cls.newInstance();  
    	    //container表示Spring容器  
    	    container.put(idStr, obj);  
    		
    	    //当一个类里面需要用另一类的对象时,我们继续下面的操作
    	    
    	    //解析<property .../>元素的name属性得到该字符串值为“dataSource”  
    	    String nameStr = "dataSource";  
    	    //解析<property .../>元素的ref属性得到该字符串值为“dataSource”  
    	    String refStr = "dataSource";  
    	    //生成将要调用setter方法名  
    	    String setterName = "set" + nameStr.substring(0, 1).toUpperCase()  
    	            + nameStr.substring(1);  
    	    //获取spring容器中名为refStr的Bean,该Bean将会作为传入参数  
    	    Object paramBean = container.get(refStr);  
    	    //获取setter方法的Method类,此处的cls是刚才反射代码得到的Class对象  
    	    Method setter = cls.getMethod(setterName, paramBean.getClass());  
    	    //调用invoke()方法,此处的obj是刚才反射代码得到的Object对象  
    	    setter.invoke(obj, paramBean);  
    		

     


     

     

    是不是很熟悉,虽然是伪代码,但是和我们本篇讲的反射机制的使用是相同的,现在知道我们的反射机制用在哪了吧,没错就是我们经常提到的Java web框架中,里面就用到了反射机制,只要在代码或配置文件中看到类的完全限定名(包名+类名),其底层原理基本上使用的就是Java的反射机制

     

     

    因此,如果你不做框架的话,基本上是用不到反射机制的,我们大多时候是使用框架的一方,而反射机制都已经在底层实现过了,因此,我们不必担心,我们会写那么复杂的代码。但是,我们必须要理解这种机制的存在!

     

     

     

     

     

     

     

     

    展开全文
  • 这是『Java学习指南系列』的第18篇教程 ,是Java开发的高级课程,介绍反射机制、注解和框架设计的一般性原理。 二、主要内容  本篇包含以下内容: * 使用反射机制,读取Class中的...
  • 思考:在讲反射之前,先思考一个问题,java中如何创建一个对象,有哪几种方式? Java中创建对象大概有这几种方式: 1、使用new关键字:这是我们最常见的也是最简单的创建对象的方式 2、使用Clone的方法:无论何时...

    视频功能审核通过了,可以看视频啦!记得点关注啊

    用最通俗易懂的话来说一说Java中的反射机制

    思考:在讲反射之前,先思考一个问题,java中如何创建一个对象,有哪几种方式?

    Java中创建对象大概有这几种方式:

    1、使用new关键字:这是我们最常见的也是最简单的创建对象的方式

    2、使用Clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去

    3、使用反序列化:当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象

    上边是Java中常见的创建对象的三种方式,其实除了上边的三种还有另外一种方式,就是接下来我们要讨论的 “反射”

    1、反射概述

    1.1什么是反射

    反射就是把Java类中的各个部分,映射成一个个的Java对象,拿到这些对象后可以做一些事情。

    既然说反射是反射Java类中的各个组成部分,所以说咱们得知道一个类中有哪儿些部分?

    例如,一个类有:成员变量,方法,构造方法,等信息,利用反射技术咱们可以把这些组成部分映射成一个个对象。

    1.2、反射能干什么

    说完反射的概念后,咱们说一下反射能干什么?

    一般来说反射是用来做框架的,或者说可以做一些抽象度比较高的底层代码,反射在日常的开发中用到的不多,但是咱们还必须搞懂它,因为搞懂了反射以后,可以帮助咱们理解框架的一些原理。所以说有一句很经典的话:反射是框架设计的灵魂。现在说完这个可能还不太能理解,不急,等下说完一个快速入门的例子后,应该会稍微有点感觉

    1.3、怎么得到想反射的类

    刚才已经说过,反射是对一个类进行解剖,想解剖一个东西,前提是首先你得拿到这个东西,那么怎么得到咱们想解剖的类呢?

    首先大家要明白一点,咱们写的代码是存储在后缀名是 .java的文件里的,但是它会被编译,最终真正去执行的是编译后的 .class文件。Java是面向对象的语言,一切皆对象,所以java认为 这些编译后的 class文件,这种事物也是一种对象,它也给抽象成了一种类,这个类就是Class,大家可以去AIP里看一下这个类

    所以拿到这个类后,就相当于拿到了咱们想解剖的类,那怎么拿到这个类?

    看API文档后,有一个方法forName(String className); 而且是一个静态的方法,这样咱们就可以得到想反射的类了

    到这里,看Class clazz = Class.forName("com.cj.test.Person");这个应该有点感觉了吧

    Class.forName("com.cj.test.Person");因为这个方法里接收的是个字符串,字符串的话,我们就可以写在配置文件里,然后利用反射生成我们需要的对象,这才是我们想要的。很多框架里都有类似的配置

    2、解剖类

    我们知道一个类里一般有构造函数、方法、成员变量(字段/属性)这三部分组成

    翻阅API文档,可以看到

    Class对象提供了如下常用方法:

    public Constructor getConstructor(Class<?>…parameterTypes)

    public Method getMethod(String name,Class<?>… parameterTypes)

    public Field getField(String name)

    public Constructor getDeclaredConstructor(Class<?>…parameterTypes)

    public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

    public Field getDeclaredField(String name)

    这些方法分别用于帮咱们从类中解剖出构造函数、方法和成员变量(属性)。

    然后把解剖出来的部分,分别用Constructor、Method、Field对象表示。

    2.1反射构造方法

    2.1.1反射无参的构造函数

    可以看到 默认的无参构造方法执行了

    从上边的例子看出,要想反射,首先第一步就是得到类的字节码

    所以简单说一下得到类的字节码的几种方式

    (1)、Class.forName("com.cj.test.Person"); 这就是上边我们用的方式

    (2)、对象.getClass();

    (3)、类名.class;

    2.1.2反射“一个参数”的构造函数

    2.1.3反射“多个参数”的构造函数

    2.1.4反射“私有”的构造函数

    注意:在反射私有的构造函数时,用普通的clazz.getConstructor()会报错,因为它是私有的,所以提供了专门反射私有构造函数的方法 clazz.getDeclaredConstructor(int.class);//读取私有的构造函数,用这个方法读取完还需要设置一下暴力反射才可以

    c.setAccessible(true);//暴力反射

    2.1.5反射得到类中所有的构造函数

     

    2.2反射类中的方法

    package com.cj.test;
    
    import java.util.Date;
    
    public class Person {
    	
    	public Person(){
    		System.out.println("默认的无参构造方法执行了");
    	}
    
    	public Person(String name){
    		System.out.println("姓名:"+name);
    	}
    	
    	public Person(String name,int age){
    		System.out.println(name+"="+age);
    	}
    	
    	private Person(int age){
    		System.out.println("年龄:"+age);
    	}
    	
    	public void m1() {
    		System.out.println("m1");
    	}
    	
    	public void m2(String name) {
    		System.out.println(name);
    	}
    	
    	public String m3(String name,int age) {
    		System.out.println(name+":"+age);
    		return "aaa";
    	}
    	
    	private void m4(Date d) {
    		System.out.println(d);
    	}
    	
    	public static void m5() {
    		System.out.println("m5");
    	}
    	
    	public static void m6(String[] strs) {
    		System.out.println(strs.length);
    	}
    
            public static void main(String[] args) {
    		System.out.println("main");
    	}
    
    }
    
    
    
    
    
    package com.cj.test;
    
    import java.lang.reflect.Method;
    import java.util.Date;
    import org.junit.Test;
    
    public class Demo2 {
    
    	@Test//public void m1()
    	public void test1() throws Exception{
    		Class clazz = Class.forName("com.cj.test.Person");
    		Person p = (Person)clazz.newInstance();
    		Method m = clazz.getMethod("m1", null);
    		m.invoke(p, null);
    	}
    	@Test//public void m2(String name)
    	public void test2() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person) clazz.newInstance();
    		Method m = clazz.getMethod("m2", String.class);
    		m.invoke(p, "张三");
    	}
    	@Test//public String m3(String name,int age)
    	public void test3() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person) clazz.newInstance();
    		Method m = clazz.getMethod("m3", String.class,int.class);
    		String returnValue = (String)m.invoke(p, "张三",23);
    		System.out.println(returnValue);
    	}
    	@Test//private void m4(Date d)
    	public void test4() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person) clazz.newInstance();
    		Method m = clazz.getDeclaredMethod("m4", Date.class);
    		m.setAccessible(true);
    		m.invoke(p,new Date());
    	}
    	@Test//public static void m5()
    	public void test5() throws Exception{
    		Class clazz = Person.class;
    		Method m = clazz.getMethod("m5", null);
    		m.invoke(null,null);
    	}
    	@Test//private static void m6(String[] strs)
    	public void test6() throws Exception{
    		Class clazz = Person.class;
    		Method m = clazz.getDeclaredMethod("m6",String[].class);
    		m.setAccessible(true);
    		m.invoke(null,(Object)new String[]{"a","b"});
    	}
    	@Test
    	public void test7() throws Exception{
    		Class clazz = Person.class;
    		Method m = clazz.getMethod("main",String[].class);
    		m.invoke(null,new Object[]{new String[]{"a","b"}});
    	}
    }
    

    *****注意:看下上边代码里test6和test7的invoke方法里传的参数和其他的有点不一样

    这是因为 jdk1.4和jdk1.5处理invoke方法有区别

    1.5:public Object invoke(Object obj,Object…args)

    1.4:public Object invoke(Object obj,Object[] args)

    由于JDK1.4和1.5对invoke方法的处理有区别, 所以在反射类似于main(String[] args) 这种参数是数组的方法时需要特殊处理

    启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数个数不对的问题。

    上述问题的解决方法:

    (1)mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

    这种方式,由于你传的是一个数组的参数,所以为了向下兼容1.4的语法,javac遇到数组会给你拆开成多个参数,但是由于咱们这个Object[ ] 数组里只有一个元素值,所以就算它拆也没关系

    (2)mainMethod.invoke(null,(Object)new String[]{"xxx"});

    这种方式相当于你传的参数是一个对象,而不是数组,所以就算是按照1.4的语法它也不会拆,所以问题搞定

    编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了

    对上边的描述进行一下总结:在反射方法时,如果方法的参数是一个数组,考虑到向下兼容问题,会按照JDK1.4的语法来对待(JVM会把传递的数组参数拆开,拆开就会报参数的个数不匹配的错误)
    解决办法:防止JVM拆开你的数组
        方式一:把数组看做是一个Object对象
        方式二:重新构建一个Object数组,那个参数数组作为唯一的元素存在。

    2.3反射类中的属性字段

    package com.cj.test;
    
    import java.util.Date;
    
    public class Person {
    	
    	public String name="李四";
    	private int age = 18;
    	public static Date time;
    	
    	public int getAge() {
    		return age;
    	}
    	
    	public Person(){
    		System.out.println("默认的无参构造方法执行了");
    	}
    
    	public Person(String name){
    		System.out.println("姓名:"+name);
    	}
    	
    	public Person(String name,int age){
    		System.out.println(name+"="+age);
    	}
    	
    	private Person(int age){
    		System.out.println("年龄:"+age);
    	}
    	
    	public void m1() {
    		System.out.println("m1");
    	}
    	
    	public void m2(String name) {
    		System.out.println(name);
    	}
    	
    	public String m3(String name,int age) {
    		System.out.println(name+":"+age);
    		return "aaa";
    	}
    	
    	private void m4(Date d) {
    		System.out.println(d);
    	}
    	
    	public static void m5() {
    		System.out.println("m5");
    	}
    	
    	public static void m6(String[] strs) {
    		System.out.println(strs.length);
    	}
    	
    	public static void main(String[] args) {
    		System.out.println("main");
    	}
    	
    }
    
    
    
    
    
    package com.cj.test;
    
    import java.lang.reflect.Field;
    import java.util.Date;
    import org.junit.Test;
    
    public class Demo3 {
    	//public String name="李四";
    	@Test
    	public void test1() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person)clazz.newInstance();
    		Field f = clazz.getField("name");
    		String s = (String)f.get(p);
    		System.out.println(s);
    		
    		//更改name的值
    		f.set(p, "王六");
    		System.out.println(p.name);
    	}
    	@Test//private int age = 18;
    	public void test2() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person)clazz.newInstance();
    		Field f = clazz.getDeclaredField("age");
    		f.setAccessible(true);
    		int age = (Integer)f.get(p);
    		System.out.println(age);
    		
    		f.set(p, 28);
    		age = (Integer)f.get(p);
    		System.out.println(age);
    	}
    	@Test//public static Date time;
    	public void test3() throws Exception{
    		Class clazz = Person.class;
    		Field f = clazz.getField("time");
    		f.set(null, new Date());
    		System.out.println(Person.time);
    	}
    }
    

    以上就是自己对Java中反射的一些学习总结,欢迎大家留言一起学习、讨论

    看完上边有关反射的东西, 对常用框架里的配置文件是不是有点思路了

    上边是Spring配置文件里的常见的bean配置,这看起来是不是可以用反射很轻易的就可以实现:解析xml然后把xml里的内容作为参数,利用反射创建对象。

    除了这个,常用的框架里还有很多地方都用到了反射,反射是框架的灵魂,具备反射知识和思想,是看懂框架的基础

    平常用到的框架,除了配置文件的形式,现在很多都使用了注解的形式,其实注解也和反射息息相关,使用反射也能轻而易举的拿到类、字段、方法上的注解,然后编写注解解析器对这些注解进行解析,做一些相关的处理,所以说不管是配置文件还是注解的形式,它们都和反射有关

    感兴趣的话可以看下这篇,这篇里边包含了XML解析、反射的东西,模拟了一个Struts2的核心代码

    利用Java反射模拟一个Struts2框架 Struts2主要核心设计 手动实现Struts2核心代码

    跟反射相关的还有内省、自定义注解,最近也抽时间大概整理了一下

    Java反射——内省(Introspector)以及BeanUtils内省框架

    Java中的注解以及自定义注解

    感兴趣的小可爱可以了解一下~

    觉得对你有帮助的话,可以点个关注,点个赞,3Q~

     

    展开全文
  • 反射机制学习之路

    2018-05-09 23:47:56
    这里我首先粘贴一下百度百科上的解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的...


    一.什么是反射机制?

    这里我首先粘贴一下百度百科上的解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

    在百度百科后面还说了Class对象与反射之间的关系:

    众所周知Java有个Object 类,是所有Java 类的继承根源,其内声明了数个应该在所有Java 类中被改写的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class 对象。
    Class 类十分特殊。它和一般类一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。如果您想借由“修改Java标准库源码”来观察Class 对象的实际生成时机(例如在Class的constructor内添加一个println()),这样是行不通的!因为Class并没有public constructor。
    Class是Reflection故事起源。针对任何您想探勘的类,唯有先为它产生一个Class 对象,接下来才能经由后者唤起为数十多个的Reflection APIs。这些APIs将在稍后的探险活动中一一亮相。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public final class Class<T> implements Serializable, 
            java.lang.reflect.GenericDeclaration,
            java.lang.reflect.Type,
            java.lang.reflect.AnnotatedElement {
         private Class() {}
         public String toString() {
             return ( isInterface() ? "interface " : (isPrimitive() ? "" "class "))
                          + getName();
    }
    上述为Class class片段。注意它的private Class() {},意指不允许任何人经由编程方式产生Class object。是的,其object 只能由JVM 产生

    由于class 对象是由final来修饰的,也就是说class对象是不能被继承且只能由jvm生成唯一一个。

    在这里就开始了解到其实反射机制是通过jvm加载一个类生成的class文件,解析出这个对象保存了什么信息,方法名,参数等等的信息。


    class文件的头4个字节称为魔数,它的唯一作用就是确定这个文件时候是一个能被虚拟机接受的class文件。所以你看到了蛋糕宝贝(cafebabe)证明他能被虚拟机接受。很多图片格式都用一个魔数来标识文件类型,比如png和jpg等。在java的class文件中,这个数是0xcafebabe。

    接下来就是class文件的版本号,第5、6个字节是次版本号,第7、8个字节是主版本号。在这里,次版本号是0,主版本号是52,(十六进制是34)。Java的版本号是从45开始的,JDK1.1之后的每一个JDK大版本发布主版本号向上加1,高版本的JDK能向下兼容低版本的JDK。

    在这里我借鉴了其他博主弄的一张图,在这张图里面很好的解释了类与class,jvm在加载过程的关系。



    1.当我们运行new一个对象时,jvm就会到我们本地磁盘中寻找对象的class文件并加载到jvm内存中。

    2.在jvm内存中,.class文件读入内存后,生成一个对象空间,同时在jvm生成这个对象有且唯一的class对象-(Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader;翻译:class没有公共构造方法,但class对象会在加载时被jvm用类加载器用defineClass 方法自动构造)

    二反射的使用

        反射的获取类

    public static void main(String[] args){
    		//反射的三种方式
    		//1.通过名字获取class
    		try {
    			Class class1= Class.forName("Person");
    		} catch (ClassNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		//2.object下的getCLASS方法
    		Person p= new Person();
    		Class class2= p.getClass();
    		
    		//3.类下.class属性
    		Class class3 = Person.class;
    		
    	}




    展开全文
  • 反射机制及反射原理

    2018-04-25 19:32:36
    反射机制:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的...

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

    java反射机制有三个动态特性:

    (1)运行时生成对象实例。

    (2)运行时调用方法.

    (3)运行时更改属性。

    1.反射机制获取类有三种方法

     

    2.反射机制创建类对象


    3.访问类的方法及属性

    总结:使用反射虽然会很大程度上提高代码灵活性,但不能滥用反射,因为 通过反射创建对象时性能稍微低一些。实际上,只有当程序需要动态创建某个类的对象时才会考虑使用反射。通常在开发通用性比较广的框架,基础平台时可能大量使用反射。因为在很多java框架中都需要根据配置信息创建Java对象,从配置文件读取的只是某个类的字符串类名,程序员需要根据字符串来创建对应的实例,就必须使用java.

    
    展开全文
  • 什么是反射机制

    2018-03-01 04:47:55
    Java反射学习步骤:1、 什么是反射机制?2、 获取字节码文件对象(class)3、 (Consrtuctor)用字节码对象(class) new一个对象4、 获取和设置字段(Field)对象的值5、获取指定class的函数(Method)并对其调用6、...
  • 反射是Java的特征之一,是一种间接操作目标对象的机制,核心是JVM在运行的时候才动态加载类,并且对于任意一个类,都能够知道这个类的所有属性和方法,调用方法/访问属性,不需要提前在编译期知道运行的对象是谁,...
  • Java 反射机制

    2018-08-15 16:43:11
    Java 反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为Java 的反射...
  • 由于反射机制能够实现在运行时对类进行装载,因此能够增加程序的灵活性,但是不恰当地使用反射机制,也会严重影响系统的性能。 具体而言,反射机制的主要功能主要有:①得到一个对象所属的类,②获取一个类的所有...
  • java的反射机制浅谈

    2010-11-11 01:21:00
    一、java的反射机制浅谈最近研究java研究得很给力,主要以看博文为学习方式。以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出。受到各位指教之处,如若让小生好好感动,说不定会请各位...
  •  Java中反射机制使用的还是比较广泛的,系统的灵活性、可扩展性大都都是通过反射等方式来加载外部插件,使得系统与插件解耦的同时,增加了功能。但是很多人都只是会用,却是不知道它的实现机制,今天就由我来带大家...
  • C++反射机制的实现

    2016-07-06 15:02:21
    Java中有天然的反射机制,因为Java本身就是半编译语言,很多东西可以在运行时来做,但是c++就不同了。要建立c++的反射机制,就需要登记每个类名与对象的对应关系。 1.前言 在实际的项目中,听到师兄说C++中用到了...
  • 反射机制介绍 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java...
  • Java 反射机制详解

    2019-08-09 21:53:12
    为什么要写这一系列的博客呢?因为在 Android 开发的过程中, 泛型,反射,注解这些知识进场会用到,几乎所有的框架至少都会...java Type 详解java 反射机制详解注解使用入门(一)反射机制什么是反射机制简单来说,
  • 反射----1 反射机制 反射的应用场景 Tomcat服务器1. Java的反射机制(1).反射和类的关系在程序运行状态中,对任意一个类(指的是.class文件),都能够知道这个类的所有的属性和方法。(2).反射和类对象的关系 反射...
  • Java反射机制与应用

    2015-11-20 15:24:28
    JAVA反射机制  JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的...
  • Java 反射机制是 Java 语言的一个重要特性,它在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 java 反射机制...
1 2 3 4 5 ... 20
收藏数 186,989
精华内容 74,795
关键字:

反射机制