深入理解java反射机制

1、反射的根本 -- Class类


1.1 Class类的概念理解

在我们的生活中,很多很多的人,我们可以用一个java类Person来表示,那么我们的java程序有很多很多的类,又该怎样表示呢?就是Class!!

java程序中的各个java类属于同一类事物,描述这一类事物的java类就是Class。


1.2 Class类的内容


我们知道,在Person这个类中,每一个实例对象就是一个具体的人,如张三李四等,那Class的实例对象又代表什么呢?它代表的就是java类的二进制字节码,一个java类的所有内容,如类名、成员变量、成员方法、构造函数、继承的抽象类或实现的接口等等,这些信息都存储在二进制字节码中,而Class对象就是表示着这些东西。


1.3 三种方法获取Class对象(字节码)


(1)类名.class(),如System.class();

(2)对象.getClass(),如Person.getClass();

(3)Class.forName("类名"),如Class.forName("java.util.Date")。


1.4 Class.forName()的作用


获取类的字节码,而获取类的字节码有两种情况,一是类已经被java虚拟机缓存,这时是直接从虚拟机缓存中取;二是类还没有被虚拟机缓存,就通过类加载器,把类的字节码从磁盘加载到java虚拟机内存,然后获取,同时虚拟机将该字节码缓存,以便以后获取。


2、认识反射


2.1 反射的概念


             反射主要是指程序可以访问、检测和修改它本身的状态或行为的一种能力。

             换一种更容易理解的说法,反射就是把java类中的各种成分(如类的成员变量、方法、构造方法等)映射成相应的java类 

        (Method、Constructor等)。

             映射成相应的java类该怎么理解呢?汽车是一个类,而汽车的发动机、刹车装置等也都是一个个类,那么对于java类也可以这

         样理解,在jdk中,java类的每一种成分都有相应的类来表示,这些类就是Method、Constructor等。

           

2.2 反射与Class类的关系


             上文说到,java类的所有信息都存储于二进制字节码中,而Class的对象就是指这些二进制字节码,当我们需要用反射获取一个

         java类的一些信息时,我们就得通过字节码来获取。所以我们才说Class是反射的根本,是反射的基石。

             

2.3 反射的一些基本操作实例


              (以下代码的输出结果笔者已写在System.out.println()语句旁边,以便读者阅读),以下程序用到了一个属性配置文件,笔者

          想运行可以自己建立


             

public class ReflectTest {

	public static void main(String[] args) throws Exception{
		Person p2 = new Person();
		Person p3 = new Person();
		//从输出结果可知,同属一个类的所有成员,对象,获取出来的字节码是一样的,也就是说,类的字节码只有一份
		System.out.println(p2.getClass() == p3.getClass());//true
		
		
		
		//一、反射与构造方法
		//1.普通方式创建String对象
		String str1 = new String(new StringBuffer("abc"));
		System.out.println("str1:" + str1);//str1:abc
		//反射的方式创建:思路是参考正常的方式,创建对象需要到相应的构造方法,那么可以通过反射来得到相应的构造方法,再创建对象
		Constructor construct = String.class.getConstructor(StringBuffer.class);
		String str2 = (String) construct.newInstance(new StringBuffer("abc"));
		System.out.println("str2:" + str2);//str1:abc
		
		//二、反射与成员变量
		//对Person类进行测试,以下会告诉你,反射是会破坏封装的
		Person p1 = new Person("xian", 22);
		
		//1、访问public的成员的变量
		Field fieldName = p1.getClass().getField("name");
		//注意,此时fieldName的值并不是创建对象时赋的"xian",而是Person这个类的字节码所对应的成员变量名,见下行输出
		System.out.println(fieldName);//public java.lang.String com.xiaoxian.app.Person.name
		//要获取它的值,可以用下行方法,针对public的成员
		String name = (String)fieldName.get(p1);
		System.out.println(name);//xian
		//2、访问private成员
		//下行会报错,是因为getField方法不能获取到private的成员变量
		//Field fieldAge = p1.getClass().getField("age");
		//用此方法,获取已声明的的成员变量
		Field fieldAge = p1.getClass().getDeclaredField("age");
		System.out.println(fieldAge);//private java.lang.Integer com.xiaoxian.app.Person.age
		//对于private的成员变量,也不能用get方法来获取值,因为没有权限
		//int age = (int) fieldAge.get(p1);
		//用暴力反射来获取,先将访问权限设为可访问,见下行,从此可以看出,反射可以访问private的成员,破坏了封装性
		//设置访问权限为可访问
		fieldAge.setAccessible(true);
		int age = (int) fieldAge.get(p1);
		System.out.println(age);//22
		
		//应用:将Person类的成员变量类型为String的含有b的全部换成a
		Field fields[] = Person.class.getFields();
		for (Field field : fields) {
			//这里用 == 进行比较,因为字节码一个类只有一个
			if (field.getType() == String.class) {
				String oldStr = (String) field.get(p1);
				String newStr = oldStr.replace('b', 'a');
				field.set(p1, newStr);
				
			}
			
		}
		
		System.out.println(p1);//Person [name=xian, age=22, str1=aallaase, str2=aasketaall, str3=xiaoxian]
		
		//三、反射与成员方法
		//如果m1方法是private的,访问形式与访问private成员变量一样
		//Method m1 = p1.getClass().getMethod("printTest", null);
		Method m1 = p1.getClass().getDeclaredMethod("printTest", null);
		m1.setAccessible(true);
		//正常情况下,方法调用通常是对象名.方法名(参数列表),而反射恰恰是反着过来的
		//取得方法m1,通过invoke()方法执行,invoke()的参数即为调用该方法的对象和相应的实参
		m1.invoke(p1, null);//输出:i am printTest()!
		Method m2 = p1.getClass().getMethod("printTest", int.class);
		//传入的第一个参数值为null,表示该方法是static的,不需要用实例对象来调用
		m2.invoke(null, 18);//输出:you give me a number :18
		
		//四、反射与数组
		int[] a1 = new int[]{1, 2, 3};
		
		if (a1.getClass().isArray()) {
			System.out.println(Array.get(a1, 1));//2
			System.out.println(Array.getLength(a1));//3
		}
		//java.lang.Reflect.Array提供了动态创建和访问 Java 数组的方法。 
		String[] ss = (String[]) Array.newInstance(String.class, 3);
		System.out.println(ss.getClass().isArray());//true
		
		//利用反射读取配置文件,模拟框架思想动态调用java类
		FileInputStream in = new FileInputStream(new File("conf.properties"));
		Properties ps = new Properties();
		ps.load(in);
		in.close();
		String className = ps.getProperty("className");
		Constructor con = Class.forName(className).getConstructor(className.getClass());
		String a = (String)con.newInstance("aaaa");
		System.out.println(a);//aaaa
		
		//应用:ArrayList与HashSet的hashcode比较分析,并根据此举例java的内存泄漏
	}
}

class Person{
	public String name;
	private Integer age;
	
	public String str1 = "ballbase";
	public String str2 = "basketball";
	public String str3 = "xiaoxian";
	
	public char cc = 'a';
	
	public Person() {}
	
	public Person(String name, Integer age) {
		super();
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", str1=" + str1
				+ ", str2=" + str2 + ", str3=" + str3 + "]";
	}
	
	private void printTest(){
		System.out.println("i am printTest()!");
	}
	
	public static void printTest(int para){
		System.out.println("you give me a number :" + para);
	}
}

2.4 反射与框架技术

             做过开发的读者应该或多或少都接触过框架,如spring、Struts2等,其实框架技术的基石也是反射,我们平时开发基本都是通

         过配置文件的方式来为java类的各种成员设置属性值,我们最常见的也是最容易看到的(意思是指不用看源代码就能知道)就

         是在配置文件中指定某个类的全路径名,那个就是利用了反射机制来动态获取那个类的信息(字节码),当获取到这个类的信

         息之后,框架技术就可以在后台动态的操作这些类了,上文的实例代码中也有一个小例子模拟框架用反射来加载类,可见,

         架技术是与反射离不开,当然,要真正领会到一个框架技术的奥妙所在,还得仔细的去研读源代码才行哦!


2.5 反射的强大用处

             尽管反射会破坏了封装性,但是它的强大用处是毋庸置疑的:

        (1)在运行时判断任意一个对象所属的类;

        (2)在运行时构造任意一个类的对象;

        (3)在运行时判断任意一个类所具有的成员变量和方法;

        (4)在运行时调用任意一个对象的方法;

        (5)动态的创建一个类,这个类的继承关系可以动态指定(动态代理设计模式)

        (6)用于自定义注解,(所以说自定义注解要会写反射)

        (7)……(反射的用处还有很多,就需要笔者自己慢慢摸索了)


3.总结

              总之记住一句话,反射就是把java类中的各种成分(如类的成员变量、方法、构造方法等)映射成相应的java类(Method、

         Constructor等)。而类的全部信息都存储在.class的二进制字节码文件中,想要获取到类的有关信息,就要先取得它的字节

         码。并且,反射的思维基本上跟我们平时写程序的思维是逆过来的,只要理解了反射的本质,就能很灵活的应用反射!!!


希望对笔者有帮助!转载请注明出处!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yeqiu1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值