黑马程序员—高新技术:Reflect反射

本文深入讲解Java反射机制的基础概念及应用场景,包括通过不同方式获取Class对象、利用反射调用构造方法、成员变量与成员方法等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------/p>

一 反射的基石,class类。

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

例如我们将人定义为person类,将动物定义为Anime类,而众多诸如此类该如何通过Java描述呢,我们称其为class类。

这个特性自Java1.2开始出现,非常强大。将来我们所用的很多框架,都要用到反射技术。

一般我们在定义一个类的时候,class这个单词是小写的。而当我们对类进行描述的时候,类类型Class要采取大写。

class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。

Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class。

        一个Java类有它的各种属性和方法,有它的父类等等。而通过Class,我们可以得到它方方面面的信息。这个类似逆推的过程就叫做反射。

Class类中的常用方法:

getName()        获取类的名字。

getPakage()        得到自己所属的包。

getMethod()        得到自己所有的方法。

getInterface()      得到自己实现的多个接口。

等等这些方法,我们可以通过它们得到所需的信息。

反射是什么?

反射是个动词,就是通过字节码文件对象,来使用构造方法,成员变量,成员方法。这个过程叫做反射。

反射的一切都是建立在Class字节码文件之上,首先要考虑的是该如何得到一份字节码文件。

获取字节码文件对象有三种方式:

A:使用Object类的getClass()方法

B:使用数据类型.class静态属性

C:使用Class类的forName()静态方法

推荐使用:第三种,因为第三种可以结合配置文件使用。

/*
 * 
 * 你想使用这些内容,那么首先要做的事情,就是你能够获取到字节码文件对象。
 * 如何获取Class的对象呢?
 * A:使用Object类的getClass()方法。
 * B:使用数据类型.class这个静态的成员变量。
 * C:使用的是Class类的forName()静态方法。
 * 		public static Class<?> forName(String className)
 */
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException {
		// 方式1
		Person p = new Person();
		Person p2 = new Person();

		Class c = p.getClass();
		Class c2 = p2.getClass();

		System.out.println(p == p2);// false
		System.out.println(c == c2);// true

		// 方式2
		Class c3 = Person.class;
		System.out.println(c == c3);

		// 方式3
		// public static Class<?> forName(String className)
		Class c4 = Class.forName("cn.itcast_01.Person");
		// cn.itcast_01.Person
		// cn.itcast_01.Person
		System.out.println(c == c4);
	}
}
class Person {
	private String name;
	public int age;

	public Person() {
	}

	Person(String name) {
		this.name = name;
	}

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public void show() {
		System.out.println("show");
	}

	public void method(String name) {
		System.out.println("method " + name);
	}

	public String function() {
		return "hello";
	}

	public String sum(int a, String b) {
		return String.valueOf(a).concat(b);
	}

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

	public String toString() {
		return "name:" + name + ",age:" + age;
	}
}

以上的代码实现了获取Class字节码文件的三种方式。反射纠结能做哪些比较具体的应用呢?

简单说有三种:

A:通过反射获取构造方法并使用

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

/*
 * 通过反射获取带参构造器并创建对象。
 */
public class ReflectDemo2 {
	public static void main(String[] args) throws ClassNotFoundException,
			NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException,
			InvocationTargetException {
		// 获取字节码文件对象
		Class c = Class.forName("Person");

		// 获取带参构造器对象
		Constructor con = c.getConstructor(String.class, int.class);
		// Person(String name,int age);

		// 创建对象
		// public T newInstance(Object... initargs)
		Object obj = con.newInstance("奶茶", 22);
		System.out.println(obj);
	}
}

B:通过反射获取成员变量并使用

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

/*
 * 通过反射获取成员变量对象并使用。
 * 
 * 需求:
 * Person p = new Person();
 * p.name = "奶茶";
 * p.age = 22;
 */
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException,
			NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException,
			InvocationTargetException, NoSuchFieldException {
		// 获取字节码文件对象
		Class c = Class.forName("Person");

		// 创建一个对象
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();

		// 获取单个成员变量对象
		Field field = c.getField("age");
		
		//给obj对象的指定的字段赋值为指定的值
		field.set(obj, 20);//给obj对象的field字段赋值为20
		System.out.println(obj);
		
	}
}

C:通过反射获取成员方法并使用

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

/*
 * 通过反射获取成员方法对象并使用。
 */
public class ReflectDemo {
	public static void main(String[] args) throws Exception {
		// 获取字节码文件对象
		Class c = Class.forName("Person");

		// 通过无参构造创建对象
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();

		// 单个方法的获取

		// 无参,无返回值;
		Method m = c.getMethod("show");
		m.invoke(obj);
		System.out.println("*********");

		// 带参,无返回值
		Method m2 = c.getMethod("method", String.class);
		m2.invoke(obj, "hello");
		System.out.println("*********");

		// 无参,有返回值
		Method m3 = c.getMethod("function");
		Object m3Obj = m3.invoke(obj);
		System.out.println(m3Obj);
		System.out.println("*********");

		// 带多个参数,有返回值
		Method m4 = c.getMethod("sum", int.class, String.class);
		Object m4Obj = m4.invoke(obj, 100, "abc");
		System.out.println(m4Obj);
		System.out.println("*********");
		
		//私有方法的使用
		Method m5 = c.getDeclaredMethod("print");
		m5.setAccessible(true);
		m5.invoke(obj);
	}
}
案例:改变任意对象中所有STRING类型成员变量所对应的字符串内容;
import java.lang.reflect.Field;

//题目:将任意一个对象中的所有string类型的成员变量所对应的字符串内容的“b”改成“a”.
/*
* 思路要对对象进行扫描,得到它所有的成员变量,也就是字段。再做修改
* 	步骤1,写一个方法,传入对象。
* 	2,通过对象,可以反射得到他的所有字段,并 传入数组
* 	3,迭代每一个字段,然后再得出每个字段的类型。(这个时候类型并不明确),如何确认类型呢?则通过比较完成。
* 然后对获得的每一个字段进行修改。注意,在判断迭代条件的时候,由于同属一个字节码,==号的语义比equals来的准确。
* */
public class ReflectTest {
	/**
	 * @param args
	 */
	public static void main(String[] args)throws Exception {
		//先得有个类的实例对象
		ReflectExample re =new ReflectExample();
		changeStringValue(re);
		System.out.println(re);
	}

	private static void changeStringValue(Object obj) throws Exception{//方法
		//获得所有字段
		Field[] fields =obj.getClass().getFields();
		//这里要注意,获得指定字段的方法是getField(指定名称); 获得所有字段的方法是getFields();
		for (Field field : fields) {
			//getType()能获得字段的类型。
			if(field.getType()==(String.class)){
				//这里先要获得具体的值再作修改;返回值是String类型。经过判断,知道这里得出的也是String类型。
				String oldValue=(String)field.get(obj);
				//得到具体的字段值之后呢,可以对值进行改变。
				String newValue=oldValue.replace("b", "a");
				//这里输入换完了值,但是对象本身的值没有被改变。目前只是将值取出而已。只get还没有set.下面的方法是对对象的值进行设置。	 
				field.set(obj, newValue);
				//为了得到我们的结果,还要重写toString方法。
			}
		}
	}

}
class ReflectExample {
	public String str1 ="ball";
	public String str2 ="baskeball";
	public String str3 ="itcast";
	@Override
	public String toString() {
		return str1+"***"+str2+"***"+str3+"***";
	}
			
}

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------详细请查看: http://edu.youkuaiyun.com




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值