JAVA的反射类讲解

举报
上山小胖子 发表于 2020/05/28 15:29:08 2020/05/28
【摘要】 大家都知道,反射的功能是非常强大的,但是只有在做框架的时候才会用到,在平常编程过程中一般是用不到的,不过了解还是很有必要的。下面我主要从四个方面的示例代码开始讲解反射类的实现。 首先给出要反射的类Person,从下面的代码可以看到,主要有构造方法,成员方法,字段,main方法,下面一一各个部分做出反射;另外,由于测试用例比较多,所以方便起见,我是采用junit进行测试的。pac...

    大家都知道,反射的功能是非常强大的,但是只有在做框架的时候才会用到,在平常编程过程中一般是用不到的,不过了解还是很有必要的。

下面我主要从四个方面的示例代码开始讲解反射类的实现。

    首先给出要反射的类Person,从下面的代码可以看到,主要有构造方法,成员方法,字段,main方法,下面一一各个部分做出反射;另外,由于测试用例比较多,所以方便起见,我是采用junit进行测试的。

package com.wang.reflect;
 
import java.io.InputStream;
import java.util.List;
 
public class Person {
	//字段
	public String name = "aaaa";
	private int password = 123;
	private static int age = 25;
 
	//构造方法
	public Person() {
		System.out.println("person");
	}
	public Person(String name) {
		System.out.println(name);
	}
	public Person(String name, int age){
		System.out.println(name+":"+age);
	}
	private Person(List list){
		System.out.println(list);
	}
	
	//成员方法
	public void aa(){
		System.out.println("aa");
	}
	public void aa(String name, int age){
		System.out.println(name+":"+age);
	}
	public Class[] aa(String name, int[] password){
		return new Class[]{String.class};
	}
	private void aa(InputStream is){
		System.out.println(is);
	}
	private static void aa(int num){
		System.out.println(num);
	}
	
	//main方法
	public static void main(String[] args){
		System.out.println("main");
	}
}
  1. 首先是构造方法,由于Person中提供了四个构造方法,对应有无参,多参,公有,私有;不过主要步骤可以总结为一下三步:

    • 通过Class.forName()加载类

    • 通过加载的clazz类生成构造方法的构造器

    • 通过构造器得到Person的实例

    下面主要讲解关于私有类的反射,大家都知道私有的东西正常情况下是不能被外界访问的,但是再反射中我们是可以做到这一点的,这里针对下面的test4(),首先需要通过getDeclareConstructor()得到私有方法的构造器,但这还不够,下面我们需要使用setAccessible(true)将其设置为外界可访问,这样就可以实现私有方法被外界可访问。

    • test1和test5是:

          person

          aaaa


    • test2 是:

          wangwu

          aaaa


    • test3是:

          wagnwu:25

          aaaa


    • test4是:

      []  //这里表示list为空

          aaaa

    import java.lang.reflect.Constructor;
    import java.util.ArrayList;
    import java.util.List;
     
    import org.junit.Test;
     
    //反射(解剖)类的构造方法,获取类的实例
    public class Demo1 {
     
    	@Test   //反射构造函数:public Person()
    	public void test1() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");   //加载类
    		Constructor c = clazz.getConstructor(null);
    		Person person = (Person) c.newInstance(null); //通过构造函数获取类的实例
    		
    		System.out.println(person.name);
    	}
    	
    	@Test   //反射构造函数:public Person(String name) 
    	public void test2() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Constructor c = clazz.getConstructor(String.class);
    		Person person = (Person) c.newInstance("wangwu");
    		
    		System.out.println(person.name);
    	}
    	
    	@Test   //反射构造函数:public Person(String name, int age) 
    	public void test3() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Constructor c = clazz.getConstructor(String.class,int.class);
    		Person person = (Person) c.newInstance("wangwu",25);
    		
    		System.out.println(person.name);
    	}
    	
    	@Test   //反射构造函数:private Person(List list)
    	public void test4() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Constructor c = clazz.getDeclaredConstructor(List.class);  //得到private的构造方法
    		c.setAccessible(true);  //正常情况下我们是不能访问私有的构造方法的,所以这里使用暴力反射
    		Person person = (Person) c.newInstance(new ArrayList(){});
    		
    		System.out.println(person.name);
    	}
    	
    	@Test
    	public void test5() throws Exception{  //该代码实际上同test1,直接通过clazz得到Person无参实例
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		
    		System.out.println(person.name);
    	}
     
    }
  2. 下面介绍反射类的方法,同样的Person中给出了多种方法,对应有无参,多参,公有,私有,还有静态的,同样其实现步骤总结成如下几步:

    • 通过Class.forName()加载类

    • 得到Person类的实例对象

    • 通过加载的clazz类生成对应方法的对象

    • 通过得到的对象调用方法

    可以看出反射方法和反射构造方法实际上很类似,这里主要说一下大的区别,就是在使用生成的方法对象进行方法的调用的时,还需要指定一个对象就是Person,这里实际上可以这样理解,可以把调用方法看成一个执行动作,那么Person就是该动作所依附的对象;不过还需要注意的就是对于static的方法,由于他是在类加载的时候就被加载,所以当调用静态方法时,是可以不指定对象的设置成null,当然也可以设置为Person。

    • test1结果为:

          person

          aa


    • test2结果为:

          person

          wangwu:25


    • test3结果为:

          person

          [Ljava.lang.Class;@1ad6884  //这里不同的机器可能或有所差异


    • test4结果为:

          person

          java.io.FileInputStream@1ad6884


    • test5结果为:

          person

          25

    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    
    import org.junit.Test;
     
    //反射类的方法
    public class Demo2 {
     
    	@Test  //反射类的方法:public void aa()
    	public void test1() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		Method method = clazz.getMethod("aa", null);
    		method.invoke(person, null);  //调用指定对象的方法
    	}
    	
    	@Test  //反射类的方法:public void aa(String name, int age)
    	public void test2() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		Method method = clazz.getMethod("aa", String.class, int.class);
    		method.invoke(person, "wangwu", 25);
    	}
    	
    	@Test  //反射类的方法:public Class[] aa(String name, int[] password)
    	public void test3() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		Method method = clazz.getMethod("aa", String.class, int[].class);
    		Class[] cs = (Class[]) method.invoke(person, "wangwu", new int[]{1,2,3});
    		System.out.println(cs);
    	}
    	
    	@Test  //反射类的方法:private void aa(InputStream is)
    	public void test4() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		Method method = clazz.getDeclaredMethod("aa", InputStream.class);
    		method.setAccessible(true);
    		method.invoke(person, new FileInputStream("1.txt"));
    		
    	}
    	
    	@Test  //反射类的方法:private static void aa(int num)
    	public void test5() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		Method method = clazz.getDeclaredMethod("aa", int.class);
    		method.setAccessible(true);
    		method.invoke(person, 25); //static的方法实际上可以不要对象,及person可以写成null
    		
    	}
    	
    }
  3. 下面再来单独说一下main方法,由于jvm在处理main参数传递时会有所区别,所以这里单独拿出来,当然反射的步骤还是同上的,我们知道main方法的参数是String[] args数组

    • jdk1.4: Method.invoke(Object obj, Object[] obj) 

      使用的是一个对象数组,所以在真正执行时虚拟机会把该对象数组拆开来对应各个参数来实现多参数

    • jdk1.5: Method.invoke(Object obj, Object...arg0) 使用的是可变参数

      所以jdk1.5为了实现向下兼容,也是采用的上面的方法,故在执行时String[]{"aaa","bbb"}, 会被拆成String aaa,String bbb传进去,而这样参数的main方法是不存在的,所以会报参数错误。解决方法有两种,一种是让它拆完之后还是一个String[],即上面的invoke里的参数可以写成new Object[]{new String[]{"aaa","bbb"}};另一种解决方法是欺骗jvm让它认为不是一个数组,即可写成(Object)new String[]{"aaa","bbb"}

    • test结果为:

      main

    import java.lang.reflect.Method;
    import org.junit.Test;
     
    //反射类的main方法
    public class Demo3 {
     
    	@Test
    	public void test() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Method method = clazz.getMethod("main", String[].class);
    		//method.invoke(null, new String[]{"aaa","bbb"}); //这里会被当成main(String aaa,String bbb)
    		method.invoke(null, (Object)new String[]{"aaa","bbb"});
    		
    		
    		//这里需要注意
    		//jdk1.5 Method.invoke(Object obj, Object...arg0) 使用的是可变参数
    		//jdk1.4 Method.invoke(Object obj, Object[] obj)  
    		//使用的是一个对象数组,所以在真正执行时虚拟机会把该对象数组拆开来对应各个参数来实现多参数
    		//所以jdk1.5为了实现向下兼容,也是采用的上面的方法,故在执行时String[]{"aaa","bbb"},
    		//会被拆成String aaa,String bbb传进去,而这样参数的main方法是不存在的,所以会报参数错误
    		//解决方法有两种,一种是让它拆完之后还是一个String[],即上面的invoke里的参数可以写成
    		//				new Object[]{new String[]{"aaa","bbb"}}
    		//          另一种解决方法是欺骗jvm让它认为不是一个数组,即可写成
    		//				(Object)new String[]{"aaa","bbb"}
    	}
    }
  4. 最后我们来看一下反射字段,在Person中给了三种字段,公有,私有,静态,同样反射字段可归纳成以下步骤:


    • 使用Class.forName()加载类

    • 生成Person类实例

    • 生成字段实例对象

    • 通过字段对象调用get(obj)和set(obj,value )分别实现获取值和设置值


    这部分和前面也很类似,就不细说

    • test1结果为:

      person
      aaaa
      bbbb


    • test2结果为:

      person

      123


    • test3结果为:

      25

    import java.lang.reflect.Field;
    import org.junit.Test;
    
    //反射字段
    public class Demo4 {
     
    	@Test  //反射字段:public String name = "aaaa"
    	public void test1() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		Field field = clazz.getField("name");  //得到字段对象
    		//获取字段的值
    		String value = (String) field.get(person); //通过对象 Person得到字段具体的值
    		System.out.println(value);
    		
    		//设置字段的值
    		field.set(person, "bbbb");
    		System.out.println(field.get(person));
    		
    		//另外我们不知道字段的类型,实际上可以先通过getType()方法获取字段的类型在进行操作
    	}
    	
    	@Test  //反射字段:private int password = 123
    	public void test2() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		Person person = (Person) clazz.newInstance();
    		Field field = clazz.getDeclaredField("password");
    		field.setAccessible(true);
    		System.out.println(field.get(person));
    	
    	}
    	
    	@Test  //反射字段:private int password = 123
    	public void test3() throws Exception{
    		Class clazz = Class.forName("com.wang.reflect.Person");
    		//Person person = (Person) clazz.newInstance(); 由于是static,所以不需要指定字段所属的对象
    		Field field = clazz.getDeclaredField("age");
    		field.setAccessible(true);
    		System.out.println(field.get(null));
    	
    	}
    }
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。