黑马程序员——反射——Class、Constructor、Field、Method及简单框架原理

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
反射的基石:Class

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

Class常用方法:

isPrimitive()//是否基本类型字节码文件

isArray()//是否数组类型的Class字节码文件

//类的基本组成:Field、Method、Contructor、package

Method getMethod();

 

总之,只要要源程序中出现的类型,都有各自的Class实例对象,比如int[],void

9大预定义的Class对象:八个基本数据类型+void

 

注:包装类.TYPE ;得到的是包装类所包装的基本数据类型的class文件

         数组也是一种类型,使用isArray();来判断


Java反射机制:

         反射就是把Java类中的各种成分映射成相应的java类。

         Java运行状态,对任意的类(class文件),都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个属性和方法;这种动态的获取信息和调用对象的方法的功能称为java的反射机制。

Ps

         框架就是对外提供一些接口(功能扩张的标准),由实现类按照这个接口的标准去实现,框架内部如果需要操作这些实现类的对象完成某些操作,那么就需要把这些实现类的全名(包名+类名)写在某个配置文件中,框架代码只需要读取这个配置文件,就可以获取这个实现类的字节码文件,然后利用反射技术创建这个实现类的对象并调用相应的方法完成一些操作。

         用于描述字节码文件的类就是Class类,创建对象,可以提出字节码文件中的内容,如字段/构造函数/一般函数。该类就可以获取字节码文件中的所有内容,那么反射就是依靠该类完成的。想要对一个类文件进行解刨,只要获取该类的字节码文件对象就可以了。

获取字节码文件对象的3种方式(注:3种方式所得到的字节码都是同一个字节码对象):

1.      类名.class

2.      对象.getClass();

3.      Class.forName(“类名”);

测试类,用于后续的被反射的类:

package com.leaf.bean;

public class Person {
	private int age;
	private String name;

	public Person() {
	}

	public Person(String name, int age) {
		this.age = age;
		this.name = name;
		System.out.println(“paramConstructor run”);
	}

	public void personMethod() {
		System.out.print("personMethod run");
	}

	public void show() {
		System.out.println("name:" + name + "….age:" + age);
	}

	private void privateMethod() {
		System.out.println("privateMethod run");
	}

	public static void staticMethod() {
		System.out.println("staticMethod run");
	}
}

示例:获取Class对象

package com.leaf.test;

import com.leaf.bean.*;


//下面开始反射的演示:
public class ReflectDemo{
	public static void main(String[] args)throws ClassNotFoundException{
		getClassFile_1();
		getClassFile_2();
		getClassFile_3();
	}
	/*
		方式1:Object类中的getClass()方法。
	用这种方式:优点,所有的类都实现了这个办法
				缺点:必须要明确具体的类,并创建对象。
	*/
	public static void getClassFile_1(){
		Person p = new Person("lisi",15);
		Class clazz = p.getClass();

		Person p1 = new Person("lisi",15);
		Class clazz1 = p1.getClass();

		System.out.println(clazz == clazz1);//result:true
		System.out.println("...................");
		}
	/*
		方式2:任何数据类型都具备一个静态属性.class,用来获取其对应的Class对象
		相对于第一种办法来说简单一些,但是还是要明确用到类中的静态成员。
		还是不够扩展。
	*/
	public static void getClassFile_2(){
		Class clazz = Person.class;
		Class clazz1 = Person.class;

		System.out.println(clazz == clazz1);//result:true
		System.out.println("...................");
	}
	/*	方法3:
		通过给定的类的字符串名称获取
		扩展性最好
		可以用Class类中的方法完成
		该方法就是forName();
		只要知道类名就可以了,最方便。
	*/
	public static void getClassFile_3()throws ClassNotFoundException{
		//把类的字符串名称写到配置文件中去,然后读出来
		String className = "com.leaf.bean.Person";//"包名+类名"
		Class clazz = Class.forName(className);

		System.out.println(clazz);//result:class com.leaf.bean.person
	}
}

result:

根据结果分析:三种方法得到的是同一份字节码文件。

示例:

获取Class中的构造函数

package com.leaf.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo1 {
	public static void main(String[] args) {
		try {
			createNewObject_1();
			createNewObject_2();
		} catch (ClassNotFoundException | InstantiationException
				| IllegalAccessException | ClassCastException
				| NoSuchMethodException | InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void createNewObject_1() throws ClassNotFoundException,
			InstantiationException, IllegalAccessException {
		// 早期:new的时候,先根据被new的类的名称寻找到该类的字节码文件,并加载进内存。
		// 并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象。
		// Person p = new Person();

		// 现在:
		String name = "com.leaf.bean.Person";
		// 找寻该文件类文件,并加在进内存,并产生Class对象。
		Class clazz = Class.forName(name);
		// 如何产生该类的对象??
		Object obj = clazz.newInstance();// 调用Person的空参构造函数

	}

	public static void createNewObject_2() throws ClassCastException,
			InstantiationException, NoSuchMethodException,
			IllegalAccessException, InvocationTargetException,
			ClassNotFoundException {
		// Person p = new Person("xiaoqiang",39);
		/*
		 * 当获取指定名称对应类中的所体现的对象时,而该对象初始化不使用空参数构造函数该怎么办呢?既然是通过指定的构造函数进行对象的初始化,
		 * 所有应该先获取到该构造函数,通过字节码文件对象即可完成。方法:getConstructor(paramterTypes);
		 */
		String name = "com.leaf.bean.Person";
		// 找寻该名称类文件,并加载进内存,并产生Class对象;
		Class clazz = Class.forName(name);
		// 获取到了指定的构造函数对象
		Constructor constructor = clazz.getConstructor(String.class,int.class);
		// 通过该构造函数对象的newInstance方法进行对象的初始化
		Object obj = constructor.newInstance("xiaoming",38 );
	}
}
result:



示例:

获取字节码文件中的字段

package com.leaf.test;

import java.lang.reflect.Field;

public class ReflectDemo2 {
	public static void main(String[] args) throws Exception {
		getFieldDemo();

	}

	public static void getFieldDemo() throws Exception {
		Class clazz = Class.forName("com.leaf.bean.Person");

		// gerField只能获取所有可访问公共字段,private获取不到
		Field fieldAge = clazz.getDeclaredField("age");
		// 对私有字段的访问取消权限检查,暴力访问
		fieldAge.setAccessible(true);
		Object obj = clazz.newInstance();
		// 为对象的属性值赋值
		fieldAge.set(obj, 89);
		// 获取某对象的某属性值
		Object o = fieldAge.get(obj);
		System.out.println(fieldAge);
	}
}

result:


示例:

获取Class文件中的类的方法:

package com.leaf.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectDemo3 {

	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("com.leaf.bean.Person");
		getMethod_1(clz);
		getMethod_2(clz);
		getMethod_3(clz);
		
	}
	/*
	 * 获取指定Class文件中的所有的公共函数和所有函数的方法
	 */
	public static void getMethod_1(Class clz)throws Exception{
		//获取所有的公共方法
		Method[] methods = clz.getMethods(); 
		//获取所有的方法。包括私有的
		methods = clz.getDeclaredMethods();
		
		for(Method method :methods){
			
			System.out.println(method);
		}
		
	}
	/*
	 * 获取class文件中的指定的无参方法
	 */
	public static void getMethod_2(Class clz)throws Exception{
		Method method = clz.getMethod("personMethod",null);
		Object obj = clz.newInstance();
		Constructor con = clz.getConstructor(String.class,int.class);
		obj = con.newInstance("小明",25);
		method.invoke(obj, null);
	}
	/*
	 * 获取class文件中指定的有参的方法
	 */
	public static void getMethod_3(Class clz)throws Exception{
		Method method = clz.getMethod("paramMethod", String.class);
		Constructor con = clz.getConstructor(String.class,int.class);
		Object obj = con.newInstance("小明",25);
		method.invoke(obj, "我是参数");
		
	}
	/*
	 * 注:私有的获取方法和私有字段的获取方法相同。
	 */
}

result:

黑马程序员——反射——Class、Constructor、Field、Method及简单框架原理_第1张图片

反射的应用:框架

    框架与框架要解决的核心问题:

                我做房子卖给用户,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中,框架和工具类有区别,工具类被用户 的类调用,而框架则是调用用户提供的类。

    那什么是要解决的核心问题??

              我再写框架时,你这个用户可能还在上小学,还不会写程序,我写的框架程序怎样能调用到你以后写的类呢?因为在写程序时,无法知道要被调用的类名,所以在程序 中,无法直接new某个类的实例对象了,而是要通过反射的方式来实现。

      综合案例:

             框架程序怎样能调用到你以后写的类(门窗)呢? 因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来.

示例:

简单框架:通过反射来调用配置文件中的类,并调用其中的方法!

配置文件:config.properties

className=java.util.ArrayList

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;

public class ReflectTest2 {
	public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
		//使用流把配置文件加载进内存
		/*
		 * 配置文件的存放目录的选择:
		 * 1.使用绝对路径,在配置文件中写上文件要存放的位置或使用api得到项目存放在硬盘上的绝对路径,在拼凑出配置文件的目录。
		 * 2.类加载器。但是只能只读,不能修改。配置文件都是放在ClassPath目录下。(在Eclipse中直接放在源程序文件夹下,会自动加载进ClassPath)
		 * 	ReflectTest2.class.getClassLoader().getResoourceAsStream("com/leaf/ReflectDemo/config.properties");
		 * 	ReflectTest2.class.getResoourceAsStream("config.properties");相对路径加载
		 * 	ReflectTest2.class.getResoourceAsStream("/com/leaf/ReflectDemo/config.properties");绝对路径加载
		 
		 * */
		InputStream is = new FileInputStream("config.properties");
		
		//使用Properties来解读配置文件
		Properties pro = new Properties();
		pro.load(is);
		is.close();
		//读取的类名可以通过反射来调用,这样就可以在不知道类名的情况下,先设计出运行的框架。
		String className = pro.getProperty("className");
		Collection col = (Collection)Class.forName(className).newInstance();
				
//		Collection col = new HashSet();
		ReflectPoint rp1 = new ReflectPoint(3, 3);
		ReflectPoint rp2 = new ReflectPoint(5, 5);
		ReflectPoint rp3 = new ReflectPoint(3, 3);
		ReflectPoint rp4 = new ReflectPoint(4, 3);
		
		col.add(rp1);
		col.add(rp2);
		col.add(rp3);
		col.add(rp4);
		col.add(rp1);
//		rp4.x = 7;
//		col.remove(rp4);
		System.out.println(col.size());
		System.out.println(col);
		
	}
	
}
用来进行测试的类
package com.leaf.ReflectDemo;

public class ReflectPoint {
	int x;
	int y;
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}
	@Override
	public int hashCode() {
		return x*39+y+41;
	}
	@Override
	public boolean equals(Object obj) {
		if(this == obj){
			return true;
		}
		if(!(obj instanceof ReflectPoint))
			try {
				throw new Exception("类型异常");
			} catch (Exception e) {
				e.printStackTrace();
			}
		ReflectPoint rp = (ReflectPoint)obj;
		int temp = rp.x - this.x;
		return temp==0?(this.y - rp.y)==0:temp==0;
	}
	@Override
	public String toString() {
		return "RP:"+x+":"+y;
	}
}
result:


反射生效,我们能修改配置文件中的类,来调用其他集合中的类为我们使用,反射是不是很神奇?



你可能感兴趣的:(java基础)