Java基础:反射机制的简单总结

什么是反射机制?

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

动态获取类中信息,就是java反射 。
可以理解为对类的解剖。

看了上面的文字可能对反射机制还是不会特别清楚,那么我们就直接来看看反射机制的实现的代码。

再讲之前呢?我们需要做一件小小的事,不然后面无法演示:
创建一个 cn.itcast.bean 的包,然后创建一个Person的类:

package cn.itcast.bean;

public class Person {

	private int age;
	private String name;
	
	public Person(String name,int age) {
		super();
		this.age = age;
		this.name = name;
		
		System.out.println("Person param run...有参数构造函数     "+this.name+":"+this.age);
	
	}
	public Person() {
		super();		
		System.out.println("person run  无参数构造函数");		
		
	}
	
	public void show(){
		System.out.println("...show run...");
	}
	
	private void privateMethod(){
		System.out.println(" method run ");
	}
	
	public void paramMethod(String str,int num){
		System.out.println("paramMethod run....."+str+":"+num);
		
	}
	
	public static void staticMethod(){
		System.out.println(" static method run......");
	}
}

我们先来看看这样一个问题:如何获取字节码文件对象呢?

这里有三种方法,我们依次来看看。

方法一:

import cn.itcast.bean.Person;
public class RefllectDemo {
	public static void main(String[] args) {
		getClassObject();
	}
	/*
	 * 获取字节码对象的方式:
	 * 1,Object类中的getClass()方法的。
	 * 想要用这种方式,必须要明确具体的类,并创建对象。
	 * 麻烦 .
	 * 
	 */
	public static void getClassObject() {	
		Person p = new Person();	
		System.out.println(p.getClass());		
	}
}

方法二:

import cn.itcast.bean.Person;
public class RefllectDemo {
	public static void main(String[] args) {
		getClassObject();
	}

	/*
	 * 方式二:
	 * 2,任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
	 * 相对简单,但是还是要明确用到类中的静态成员。
	 * 还是不够扩展。 
	 * 
	 * 此方法不好的原因是,使用这种方法获取,需要导入包,才可以。
	 * 
	 */
	public static void getClassObject() {
		System.out.println(Person.class);		
	}
}

方法三:

public class RefllectDemo {
	public static void main(String[] args) throws ClassNotFoundException {
		getClassObject();
	}
	/*
	 * 方式三:
	 * 只要通过给定的类的 字符串名称就可以获取该类,更为扩展。
	 * 可是用Class类中的方法完成。
	 * 该方法就是forName.
	 * 这种方式只要有名称即可,更为方便,扩展性更强。 
	 */
	public static void getClassObject() throws ClassNotFoundException {	
		String name = "cn.itcast.bean.Person";
		Class clazz = Class.forName(name);
		
		System.out.println(clazz);			
	}
}

这三种方法的运行结果中都包含:class cn.itcast.bean.Person

我们发现,只有第三种方法我们只需要知道类的完整名字就可以获取这个类了,这种方法的扩展性更强,
为什么?
我简单的说明一下,如果我们把我们需要的类的完整的名字放在一个文件里面,然后在我们的程序中这个文件与IO流相连,然后我们在利用反射机制,调用这些类,执行其他程序,那么以后呢?当我们想要执行其他类的时候,这时反射机制的用处就来了,我们不需要修改代码,只需要将需要执行的类的完整名字放在文件中就可以了。

可能看到这里还有疑惑,没事,一会在举一个代码的例子,现在我们来看看反射机制中我们需要了解的东西。

获取构造函数

获取无参构造函数:

public class RefllectDemo2 {
	public static void main(String[] args) throws Exception {
		createNewObject();
	}

	/*
	 * 获取无参构造函数
	 */
	public static void createNewObject() throws Exception {		
		//早期:new时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存,
//		并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象.
//		cn.itcast.bean.Person p = new cn.itcast.bean.Person();
		
		//现在:
		String name = "cn.itcast.bean.Person";
		//找寻该名称类文件,并加载进内存,并产生Class对象。
		Class clazz = Class.forName(name);
		//如何产生该类的对象呢?
		Object obj  = clazz.newInstance();
		System.out.println(obj);
	}
}

运行结果:

person run  无参数构造函数
cn.itcast.bean.Person@15db9742

获取有参构造函数:

import java.lang.reflect.Constructor;

public class RefllectDemo2 {
	public static void main(String[] args) throws Exception {
		createNewObject();
	}
	/*
	 * 获取有参构造函数
	 */
	public static void createNewObject() throws Exception {		
		String name = "cn.itcast.bean.Person";
		Class clazz = Class.forName(name);

		//获取有参构造函数
		Constructor constructor = clazz.getConstructor(String.class,int.class);				
		//public Constructor<T> getConstructor(Class<?>... parameterTypes)
		
		Object obj = constructor.newInstance("cxf",20);	
	}
}

运行结果:

Person param run...有参数构造函数     cxf:20

获取字段

import java.lang.reflect.Field;
public class ReflectDemo3 {
	public static void main(String[] args) throws Exception {
		/*
		 * 获取字节码文件中的字段。
		 */
		
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		//Field field = clazz.getField("age");无法使用,因为age是private所以无法返回所以必须使用getDeclaredField()方法。
		Field field = clazz.getDeclaredField("age");
		
		//对私有字段的访问取消权限检查。暴力访问。
		field.setAccessible(true);
		//如果没有这局代码,那么会抛java.lang.IllegalAccessException
		
		/*
		 * 我们获取一个字段的基本思路是:先创建一个对象,然后在调用,所以在反射机制中也是这样
		*	Person p = new Person()
		*	p.getAge()
		*/
				
		Object obj = clazz.newInstance();
		
		//设置obj对象的age的值
		field.set(obj, 89);
		
		//获取obj对象的age的值
		Object o = field.get(obj);
		
		System.out.println(o);

	}
}

输出结果:

person run  无参数构造函数
89

获取一般方法

	/*
	 * 获取所有公有方法,包括父类的方法
	 */	
	public static void getMethod() throws Exception {	
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		Method[] methods = clazz.getMethods();  
		//获取所有公有方法,包括父类的方法
		
		for(Method method : methods)
			System.out.println(method);
	}

输出结果:

public void cn.itcast.bean.Person.show()
public void cn.itcast.bean.Person.paramMethod(java.lang.String,int)
public static void cn.itcast.bean.Person.staticMethod()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
	/*
	 * 获本类中的所有方法
	 */	
	private static void getMethod_1() throws Exception {
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		Method[] methods = clazz.getDeclaredMethods(); 
		//获本类中的所有方法
		
		for(Method method : methods)
			System.out.println(method);		
	}

输出结果:

public void cn.itcast.bean.Person.show()
public void cn.itcast.bean.Person.paramMethod(java.lang.String,int)
private void cn.itcast.bean.Person.privateMethod()
public static void cn.itcast.bean.Person.staticMethod()

获取无参一般函数:

import java.lang.reflect.Method;
public class ReflectDemo4 {
	public static void main(String[] args) throws Exception {
		getMethod();
	}
	//获取无参函数
	public static void getMethod() throws Exception {	
		Class clazz = Class.forName("cn.itcast.bean.Person");
		
		//无参所以是null
		Method method = clazz.getMethod("show", null);
		
		/*
		 * 我们获取一个字段的基本思路是:先创建一个对象,然后在调用,所以在反射机制中也是这样
		*	Person p = new Person()
		*	p.getAge()
		*/
		//如果方法时私有的话可以使用
		//method.setAccessible(true);
		
		Object obj = clazz.newInstance();
		
		//运行此方法
		method.invoke(obj, null);		
	}
}

输出结果:

person run  无参数构造函数
...show run...

获取有参一般函数:

import java.lang.reflect.Method;
public class ReflectDemo4 {
	public static void main(String[] args) throws Exception {
		getMethod();
	}
	//获取无参函数
	public static void getMethod() throws Exception {	
		Class clazz = Class.forName("cn.itcast.bean.Person");

		Method method = clazz.getMethod("paramMethod", String.class,int.class);		
		/*
		 * 我们获取一个字段的基本思路是:先创建一个对象,然后在调用,所以在反射机制中也是这样
		*	Person p = new Person()
		*	p.getAge()
		*/
		//如果方法时私有的话可以使用
		//method.setAccessible(true);
		
		Object obj = clazz.newInstance();
		
		//运行此方法
		method.invoke(obj, "cxf",20);		
	}
}

输出结果:

person run  无参数构造函数
paramMethod run.....cxf:20

汇总

获取构造方法

无参构造方法:

Class clazz = Class.forName(“cn.itcast.bean.Person”);
Object obj = clazz.newInstance();

有参构造方法:
Constructor constructor = clazz.getConstructor(Class<?>… parameterTypes)
获取全部公有字段,包括父类

Constructor constructor = clazz.getDeclaredConstructor(Class<?>… parameterTypes)
获取本类的所有字段,包括私有,但不包括父类。

Object obj = constructor.newInstance(Object … initargs);

获取字段用:Field
可用方法:
getField( ):获取全部公有字段,包括父类
getDeclaredField( ):获取本类的所有字段,包括私有,但不包括父类。

获取一般函数
字段用Method

Class clazz = Class.forName(“cn.itcast.bean.Person”);

Method methods[] = clazz.getMethods();
获取共有方法,包括父类中的

Method methods[] = clazz.getDeclaredMethods();
获取本类的所有字段,包括私有,但不包括父类。

如果想要使用私有的东西的话需要:
setAccessible(true)

反射机制样例

创建一个包 cn.itcast.reflect.test
然后

创建 MainBoard 类:

package cn.itcast.reflect.test;

public class MainBoard {
	public static void run() {
		System.out.println("main board run..........");
	}
	
	public static void usePCI(PCI p) { // PCI p = new SoundBoard()
		p.open();
		p.close();
	}
}

创建 PCI 接口:

package cn.itcast.reflect.test;
public interface  PCI {
	
	public void open() ;
	public void close();
}

创建 SoundBoard 类:

package cn.itcast.reflect.test;

public class SoundBoard implements PCI {

	@Override
	public void open() {
		System.out.println("sound open........");
	}

	@Override
	public void close() {
		System.out.println("sound close........");
	}
}

创建 NetBoard 类:

package cn.itcast.reflect.test;

public class NetBoard implements PCI {

	@Override
	public void open() {
		System.out.println("net open.........");
	}

	@Override
	public void close() {
		// TODO Auto-generated method stub
		System.out.println("net close.........");
	}
}

创建 ReflectTest 类:

package cn.itcast.reflect.test;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
public class ReflectTest {

	public static void main(String[] args) throws Exception {
		
		MainBoard mainboard = new MainBoard();
		
		mainboard.run();		
		//每次添加一个设备都需要修改代码传递一个新创建的对象
//		mainboard.usePCI(new SoundCard());
		//能不能不修改代码就可以完成这个动作。 
//		不用new来完成,而是只获取其class文件。在内部实现创建对象的动作。 
		
		File file = new File("className.properties");		
		if(!file.exists())
			file.createNewFile();
	
		FileInputStream fis = new FileInputStream(file);
	
		Properties prop = new Properties();
	
		prop.load(fis);
	
		for(int x = 1;x<=prop.size();x++) {
		
			String className = prop.getProperty("pci"+x);
		
			Class clazz = Class.forName(className);
		
			PCI p = (PCI)clazz.newInstance();
		
			mainboard.usePCI(p);			
		}		
		fis.close();		
	}
}

创建一个 className.properties 的配置文件

pci1=cn.itcast.reflect.test.SoundBoard
pci2=cn.itcast.reflect.test.NetBoard

运行结果:

main board run..........
sound open........
sound close........
net open.........
net close.........

在这里呢?我只想说,这个例子进一步说明了反射机制的高效,我们不需要修改程序,我们只需要改动配置文件就可以让程序产生不易样的结果,使代码变得更加的高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值