你必须了解的Java:反射

你必须了解的Java:反射

首先,反射是框架设计的灵魂 ,这句话的分量,读者自行体会。

1、几个概念:

  • 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码;所谓框架,就是能够适应所有的情况,情况的不同,只需在配置文件中进行修改,而不是在原代码(这个指的框架)中修改;
  • 反射:将类的各个组成部分封装为其他对象,这就是反射机制。
  • 好处:
      1. 可以在程序运行过程中,操作这些对象。idea 就是在程序的运行过程中,运用了反射的机制。 比如:定义了一个字符串,结果字符串的方法就已经可以选取调用了。·因为在这个过程中,idea是一边写,一边编译,将字符串编译为字节码文件,并且已经将字节码文件通过ClassLoader加载进了内存中;
    •         2. **可以解耦,提高程序的可扩展性。**
      

2、java在计算机中经历的三个阶段:

java代码 ------>javac进行编译------>字节码文件.class------>classLoader类加载器------>在堆中创建对象 new someoneClass
source源代码阶段 class类对象阶段 RUNTIME运行时阶段

3、反射中的对象:

​ 1、成员变量 Field[] field

​ 2、构造方法 Constructor[]

​ 3、成员方法 Method[] method

4、获取Class对象的方式

  1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
    多用于配置文件,将类名定义在配置文件中,配置文件中写的都是全类名。读取文件,加载类;

  2. 通过类名属性class获得:( 多用于参数的传递)

//  ClassName.class  不执行静态块和不执行动态块儿
Class<?> class = ClassName.class;
  1. 对象.getClass() 封装在object中:getClass()方法在Object类中定义着。
    多用于对象的获取该类的 字节码文件 的方式

注意: 前两个是直接根据类名获取class文件 区别是全类名 或者类名(与绝对路径和相对路径相似)第三个是需要先创建类的对象,再有对象获取类的字节码文件。

结论: 三种获取的方式都是同一个字节码文件(*.class)。在一次程序运行过程中,一个类只会被加载一次,所以不论通过哪一种方式获取的Class对象都是同一个。

静态代码块与动态代码块在 获取class文件中的应用:

首先定义一个测试类:

 public class ClassTest {
     static{
         // 静态代码块
         System.out.println("静态代码块儿...");
     }
     {
         // 动态代码块
         System.out.println("动态构造块儿...");
     }
     public ClassTest(){
         // 类中的构造方法
         System.out.println("构造方法...");   
     }
 }
使用第1种方法:
public class MainTest {
     public static void main(String[] args) {
         try {
             Class<?> calss = Class.forName("com.souche.lease.admin.mytest.reflect.ClassTest");
         } catch (ClassNotFoundException e) {
             e.printStackTrace();
         }
     }
}
/* 打印结果是:
 * 静态代码块儿...
 * 说明Class.forName("类名全路径")执行静态块但是不执行动态块儿(需要异常处理)
 */
使用第2种方法:
public class MainTest {
    public static void main(String[] args) {
        Class<?> calss = ClassTest.class;
    }
}
/**
 * 打印结果是:
 * 什么都没打印
 * 说明ClassName.class不执行静态块和不执行动态块儿
 */
使用第3种方法:
public class MainTest {
     public static void main(String[] args) {
         Class<?> calss = new ClassTest().getClass();
     }
 }
 
 /**
  * 打印结果是:
  * 静态代码块儿...
    动态构造块儿...
    构造方法...
  * 说明对象.getClass()执行静态块也执行动态块儿
  */
关于获取class文件方法不同的总结:

第1种方法:类字面常量使得创建Class对象的引用时不会自动地初始化该对象,而是按照之前提到的加载,链接,初始化三个步骤,这三个步骤是个懒加载的过程,不使用的时候就不加载。

第2种方法:Class类自带的方法。

第3种方法:是所有的对象都能够使用的方法,因为getClass()方法是Object类的方法,所有的类都继承了Object,因此所有类的对象也都具有getClass()方法。

建议:使用类名.class,这样做即简单又安全,因为在编译时就会受到检查,因此不需要置于try语句块中,并且它根除了对forName()方法的调用,所以也更高效。

补充注意:静态块仅在类加载时执行一次,若类已加载便不再重复执行;而动态构造块在每次new对象时均会执行。

补充:生成对象四种方式

请参见blog: https://blog.csdn.net/qq_39817135/article/details/101313225

5、Class对象功能( 就是获取class文件之后,拿它来做什么。)

1. 获取 成员变量们 field

获取了成员变量之后,就可以将获取的成员变量进行设置、获取(也就是get和set)原类中成员变量的值。从而让其达到不创建对象就可以操作类中成员变量Field,构造方法Constructor,成员方法Method三个大的类对象;

    Field[] getFields()// 获取所有 public 修饰的成员变量  
			
	Field getField(String name)      // 获取的指定的成员变量  获取指定名称的 public修饰的成员变量
			
	Field[] getDeclaredFields()      // 获取所有的成员变量,不考虑修饰符
			
	Field getDeclaredField(String name)   //获取的指定的成员变量
			
	Field[] getDeclaredFields();	//获取所有的成员变量,不考虑修饰符

2. 举例说明 成员变量类对象 的获取,并设置、获取 成员变量类对象中的值
   // e.g. 成员变量类对象 的获取,并设置、获取 成员变量类对象中的值
    Field[] declaredFields = personClass.getDeclaredFields(); 
    
	// 获取了所有的成员变量,并且将其放在了数组中;并可以将其遍历:
    for (Field declaredField : declaredFields) {
        System.out.println(declaredField);
    }
    
	// 获取指定的成员变量,如果成员变量是私有的,那么需要将获取权限  即暴力反射获得权限
    Field getDeclaredField(String name)
    Field d = personClass.getDeclaredField("d");

	// 成员变量类对象d 就可以进行d.get();d.set();
    // 忽略访问权限修饰符的安全检查
    d.setAccessible(true);     //暴力反射
    
	d.set(oo, "wangwu")
    Object value2 = d.get(p);
    System.out.println(value2);
3、获取构造方法们 :之后就创建对象
Constructor<?>[]  getConstructors()  	   //获取的是空参构造

Constructor<T>  getConstructor(<?>... parameterTypes) 
    
 //  获取的是有参构造
	Constructor<T> getDeclaredConstructor(<?>... parameterTypes)  
	Constructor<?>[] getDeclaredConstructors()  
		
//Constructor<T> getConstructor(类<?>... parameterTypes)
 Constructor constructor = personClass.getConstructor(String.class, int.class);
 //这里返回得到的是  构造器		
 //这里传递的参数 
 //String.class,int.class是原构造方法中参数类型的class对象
 System.out.println(constructor);
    /**创建对象****重要:constructor.newInstance("张三", 23)    
	  **当然也可以利用空参构造,创建对象:但是提供了比较简单的方法:
	  **直接跳过获取构造器,直接使用类名就可以利用空参构造的创建对象:
	  **	Class对象的newInstance方法
	  **	personClass.newInstance()*/
    Object person = constructor.newInstance("张三", 23);
    System.out.println(person);
4、 获取成员方法们:之后就执行方法
Method[] getMethods()  
Method getMethod(String name, 类<?>... parameterTypes)  
Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, 类<?>... parameterTypes)  
    	//获取指定名称的方法 其实是总的方法类对象
	    Method eat_method = personClass.getMethod("eat"); 
		// 这里是无参方法,可以不用传递参数类型的对象
	     Person p = new Person();

 //获取到成员方法之后,需要执行方法,关键词:.invoke(p)  
 //获取的法类对象来执行 需要传递的是一个对象,以及该方法执行所需的参数类对象
   eat_method.invoke(p);	  // 这里是无参方法,可以不用传递参数类型的对象
 	// 有参方法举例并说明:
       Method eat_method = personClass.getMethod("eat",String.class,int.class,...); 
       eat_method.invoke(p,"abcdef",6);
			
	 //获取所有public修饰的方法
    Method[] methods = personClass.getMethods();
    for (Method method : methods) {
        System.out.println(method);
        String name = method.getName();
		//获取所有的方法名,
		//这里获取的方法名不仅仅是方法中的public修饰的方法名,
		//还有其继承Object中的方法
        System.out.println(name);
        //method.setAccessible(true);
    }
   }		
		/* Field:成员变量
	获取了成员变量之后,就可以用将获取的成员变量进行设置、获取原类中成员变量的值。
	这样就可以 不通过构造方法,对目标类(最开始的类)进行获取或者设置成员变量的值。*/
	// 操作:
		//1. 设置值
			* void set(Object obj, Object value)  
		//2. 获取值
			* get(Object obj) 
		//3. 忽略访问权限修饰符的安全检查
			* setAccessible(true):   暴力反射(私有的成员变量都可以访问)
			
// Constructor:构造方法
	// 创建对象:
		 T newInstance(Object... initargs)  
		// 如果使用空参数构造方法创建对象,操作可以简化:
			Class对象的newInstance方法

// Method:方法对象
	// 执行方法:
		 Object invoke(Object obj, Object... args)  
	// 获取方法名称:
		 String getName:获取方法名

面试题:

需求

写一个"框架",不能改变该类的任何代码的前提下,
可以帮我们创建任意类的对象,并且执行其中任意方法。

回答思路

实现
  1. 配置文件 properties

  2. 反射(如果配置文件中 出现了全类名,绝大部分情况就是运用了反射)

    步骤

    1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
    2. 在程序中加载读取配置文件
    3. 使用反射技术来加载类文件进内存
    4. 创建对象
    5. 执行方法
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值