• Java基础学习总结--反射


    一、什么是反射?

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

    二、能利用反射做什么?

    ​ 我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其constructor(构造方法),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!

    三、前期了解知识

    (一)类的加载

    ​ 想知道如何使用反射,就先要了解类的加载。

    ​ 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来对这个类进行初始化。

    1. 加载
      就是指将class文件读入内存,并为之创建一个Class对象。
      任何类被使用时系统都会建立一个Class对象。
    2. 连接
      验证 是否有正确的内部结构,并和其他类协调一致
      准备 负责为类的静态成员分配内存,并设置默认初始化值
      解析 将类的二进制数据中的符号引用替换为直接引用
    3. 初始化 基本的的初始化步骤

    (二)类加载器

    1. 类加载器:负责将.class文件加载到内在中,并为之生成对应的Class对象。
      虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。

    2. 类加载器的组成
      Bootstrap ClassLoader 根类加载器
      Extension ClassLoader 扩展类加载器
      Sysetm ClassLoader 系统类加载器

    3. 类加载器的作用
      (1) Bootstrap ClassLoader 根类加载器,也被称为引导类加载器。负责Java核心类的加载
      比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

      (2)Extension ClassLoader 扩展类加载器。负责JRE的扩展目录中jar包的加载。
      在JDK中JRE的lib目录下ext目录
      (3)Sysetm ClassLoader 系统类加载器。负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所 指定的jar包和类路径

    四、如何使用反射?

    现有一个Person类

    public class Person {
    	
    	private String name;
    	int age;
    	public String address;
    
    	public Person() {
    	}
    
    	private Person(String name) {
    		this.name = name;
    	}
    
    	Person(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    
    	public Person(String name, int age, String address) {
    		this.name = name;
    		this.age = age;
    		this.address = address;
    	}
    
    	public void show() {
    		System.out.println("show");
    	}
    
    	public void method(String s) {
    		System.out.println("method " + s);
    	}
    
    	public String getString(String s, int i) {
    		return s + "-->" + i;
    	}
    
    	private void function() {
    		System.out.println("function");
    	}
    
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + ", address=" + address
    				+ "]";
    	}
    }
    
    

    那么对于这个类,如何使用反射呢?

    (一)获取该类的字节码文件在内存中对应的Class对象

    /**
     *有三种方式可以获得Class对象
     */
    //1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
    //  类型的对象,而我不知道你具体是什么类,用这种方法
      Person p1 = new Person();
      Class c1 = p1.getClass();
            
    //2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
    //  这说明任何一个类都有一个隐含的静态成员变量 class
      Class c2 = Person.class;
            
    //3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
    //   但可能抛出 ClassNotFoundException 异常
      Class c3 = Class.forName("com.ys.reflex.Person");
    

    注意:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 == 比较,结果都是true

    (二)通过Class类提供的方法获取构造方法

    1. 获取构造方法
    /**
     *反射之获取无参构造方法,并使用
     */
    public class TestConstructor01 {
    	public static void main(String[] args) throws Exception {
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
            //获取无参构造函数
    		Constructor con = c.getDeclaredConstructor();	
    	
    		//创建实例
    		Object obj = con.newInstance();
    	}
    }
    
    /**
    * 如果只需要利用无参构造来创建实例,那么也可以不用获取构造器,直接使用newInstance方法
    */
    public class TestConstructor01 {
    	public static void main(String[] args) throws Exception {
    		Class c = Class.forName("com.zuobiao.testReflection.Person");
    		Person p = (Person)c.newInstance();
    	}
    }
    
    /**
     *反射之获取带参构造方法,并使用
     */
    public class TestConstructor02 {
    	public static void main(String[] args) throws Exception {
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
    		//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
    		//获取构造方法
    		Constructor con = c.getDeclaredConstructor(String.class, int.class, String.class);
    		
    		//创建实例
    		Object obj = con.newInstance("张三", 27, "北京");
    	}
    }
    
    /**
     *反射之获取private修饰的构造方法,并使用
     */
    public class TestConstructor03 {
    	public static void main(String[] args) throws Exception{
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
    		//获取构造方法
    		Constructor con = c.getDeclaredConstructor(String.class);
    		
    		//设置访问权限
    		con.setAccessible(true);	//值为true则指示反射的对象在使用时应该取消Java语言访问检查。
    		
    		//创建实例
    		Object obj = con.newInstance("林青霞");
    	}
    }
    

    (三)通过Class类提供的方法操作成员变量

    1. 给成员变量赋值
    public class TestField01 {
    	public static void main(String[] args) throws Exception{
    		test();
    	}
    
    	public static void test() throws Exception {
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
    		//获取Constructor
    		Constructor con = c.getDeclaredConstructor();
    		
    		//创建实例
    		Object obj = con.newInstance();
    		System.out.println(obj);
    		
    		//获取Field
    		Field addressField = c.getDeclaredField("address");
    		//赋值Field
    		addressField.setAccessible(true);
    		addressField.set(obj, "北京");
    		System.out.println(obj);
    		
    		Field nameField = c.getDeclaredField("name");
    		nameField.setAccessible(true);  	
    		nameField.set(obj, "张三");
    		System.out.println(obj);
    		
    		Field ageField = c.getDeclaredField("age");
    		ageField.setAccessible(true);
    		ageField.set(obj, 27);
    		System.out.println(obj);
    	}
    }
    
    

    测试结果如下:

    1. 获取成员变量
    public class TestField02 {
    	public static void main(String[] args) throws Exception{
    		test();
    	}
    
    	public static void test() throws Exception {
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
    		//获取Constructor
    		Constructor con = c.getDeclaredConstructor();
    		
    		//创建实例
    		Object obj = con.newInstance();
    		System.out.println(obj);
            
    		//获取Field
    		Field addressField = c.getDeclaredField("address");
    		//赋值Field
    		addressField.setAccessible(true);
    		addressField.set(obj, "北京");
    		System.out.println(obj);
    		System.out.println(addressField.get(obj));
    	}
    }	
    
    

    测试结果如下:

    (四) 通过Class类提供的方法调用成员方法

    /**
     * 反射之获取无参无返回值成员方法,并调用
     */
    public class TestMethod01 {
    	public static void main(String[] args) throws Exception {
    		test0();
    	}
    	
    	//获取单个方法对象
        /**
        * 1、public 方法 getMethod(String name, Class<?>... parameterTypes):获取所有方法,包括父类的
        * 参数 :	name:方法的名称  parameterTypes:该方法的参数列表 
        * 2、public 方法 getDeclaredMethod(String name, Class<?>... parameterTypes):获取所有子类定义的方法
        * 参数 :	name:方法的名称  parameterTypes:该方法的参数列表 
        */	
    	public static void test() throws Exception {
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
    		//获取构造方法
    		Constructor con = c.getDeclaredConstructor();
    		
    		//创建实例
    		Object obj = con.newInstance();
    		
    		//获取需要的方法对象
    	
    		Method m = c.getDeclaredMethod("show");
    		
    		//调用方法
    		/**
    		 * public Object invoke(Object obj, Object... args)
    		 * obj - 被调用的对象  args - 用于方法调用的参数
    		 */
    		m.invoke(obj);
    	}
    }
    
    

    测试结果如下:

    /**
     * 反射之获取有参有返回值成员方法,并调用
     */
    public class TestMethod02 {
    	public static void main(String[] args) throws Exception {
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
    		//获取构造方法
    		Constructor con = c.getDeclaredConstructor();
    		
    		//创建实例
    		Object obj = con.newInstance();
    		
    		//获取需要的方法对象
    		//public void method(String s)
    		Method m = c.getDeclaredMethod("method", String.class);
    		m.invoke(obj, "java");
    		
    		//public String getString(String s, int i) 
    		Method m1 = c.getDeclaredMethod("getString", String.class, int.class);
    		Object rst = m1.invoke(obj, "hello", 200);
    		System.out.println(rst);
    	}
    }
    

    测试结果如下:

    /**
     * 反射之获取privste成员方法,并调用
     */
    public class TestMethod02 {
    	public static void main(String[] args) throws Exception {
    		//获取Class对象
    		Class c = Class.forName("study01.Person");
    		
    		//获取构造方法
    		Constructor con = c.getDeclaredConstructor();
    		
    		//创建实例
    		Object obj = con.newInstance();
    		
    		//获取需要的方法对象
    		//private void function()
    		Method m2 = c.getDeclaredMethod("function");
    		m2.setAccessible(true);
    		m2.invoke(obj);
    	}
    }
    

    测试结果如下:

    五、补充

    当使用反射调用静态变量静态方法时,传递的对象出只需要传递null即可。

    六、总结

    灵活使用反射能让我们代码更加灵活,但是凡事都有两面性,反射也会消耗系统的性能,增加复杂性等,合理使用才是真!

    Java新手,若有错误,欢迎指正!

  • 相关阅读:
    FineUIPro v5.1.0 发布了!
    FineUI十周年纪念版即将发布(基于像素的响应式布局,独此一家)!
    FineUIPro/Mvc/Core/JS v4.2.0 发布了(老牌ASP.NET控件库,WebForms,ASP.NET MVC,Core,JavaScript)!
    选择IT公司的雇主提问
    项目管理趋势
    突发事件下的项目管理
    12-Factor与微服务
    CQRS(Command and Query Responsibility Segregation)与EventSources实例
    阅读理解力的四个层次
    2018年Code Review状态报告
  • 原文地址:https://www.cnblogs.com/Java-biao/p/12589636.html
Copyright © 2020-2023  润新知