Java反射学习积累

Java反射学习积累

什么是反射

  • 反射概述

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

怎么使用反射

  • 配置文件的应用

对于这样一个类

interface Fruit{
	public void squeeze();
}
class Apple implements Fruit{
	public void squeeze() {
		System.out.println("榨出苹果汁");
	}
}
class Orange implements Fruit{
	public void squeeze() {
		System.out.println("榨出橘子汁");
	}
}

...

class Juicer{
	public void run(Fruit f) {
		f.squeeze();
}

可以看到,为了“喝到不同的果汁”,我们就需要使用多态完成。即run()方法的参数Fruit f = new Apple();

public static void main(String[] args){
	//没有反射,只有多态
	Juicer j = new Juicer();
	j.run(new Apple());
	j.run(new Orange());
	}

但是这样的代码不易维护,一旦需求不同,就需要在源码上修改。

  • 反射怎么做

public static void main(String[] args){
	BufferedReader br = new BufferedReader(new 
		FileReader("27.config.properties"));//关联配置文件
	Class clazz = Class.forName(br.readLine());//读取到配置信息(类名)
	Fruit f = (Fruit) clazz.newInstance();//获得实例
	Juicer j = new Juicer();
	j.run(f);
	}

配置文件内容

com.heima.day27.reflect.Apple

这样当我们需要修改时,只需要修改配置文件内容就可以,而不需要改动源码。

  • 使用反射创建对象

我们刚才已经使用了newInstance()方法来创建对象,但这个方法是使用该类的无参构造器创建对象,如果一个类没有无参构造,就不能这样创建。
解决方法:使用Class类的getConstructor

public class Demo3 {
	public int age;
	private String name;
	public Demo3(int age) {
		super();
		this.age = age;
		System.out.println(age);
	}
	public void eat() {
		System.out.println("今天我好饿");
	}
	@Override
	public String toString() {
		return "Demo3 [age=" + age + ", name=" + name + "]";
	}
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		Class clazz = Class.forName("com.heima.day27.reflect.Demo3");
		
		/* Demo3 d = (Demo3) clazz.newInstance(); *///有参构造无法创建对象
		
		Constructor c = clazz.getConstructor(int.class);//获取有参构造
		Demo3 d = (Demo3) c.newInstance(5);//通过有参构造创建对象
	}
}

  • 设置成员变量

  • Class.getField(String)方法可以获取类中的指定字段(可见的)
  • 如果是私有属性可以用getDeclaedField(“name”)方法获取
  • 通过set(obj, “李四”)方法可以设置指定对象上该字段的值
  • 私有属性还需要使用setAccessi(true)设置访问权限
public class Demo4 {
	public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException, SecurityException, NoSuchFieldException {
		Class clazz = Class.forName("com.heima.day27.reflect.Demo3");
		Constructor c = clazz.getConstructor(int.class);//获取有参构造
		Demo3 d = (Demo3) c.newInstance(5);//通过有参构造创建对象
		
		Field f1 = clazz.getField("age");
		/* Field f2 = clazz.getField("name"); *///私有属性无法找到
		Field f2 = clazz.getDeclaredField("name");//暴力反射获取字段
		f2.setAccessible(true);//去除私有权限
		f1.set(d, 12);
		f2.set(d, "张三");
		System.out.println(d);
	}
}
  • 获取和使用方法

与获取属性相似,我们可以使用类似的方法获取并使用某一个类的方法

  • Class.getMethod(String, Class…)和Class.getDeclaredMethod(String, Class…)获得方法
  • invok(Object, Object…)可以调用该方法
  • 使用setAccessi(true)设置访问权限
public class Demo5 {
	public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("com.heima.day27.reflect.Demo3");
		Constructor c = clazz.getConstructor(int.class);//获取有参构造
		Demo3 d = (Demo3) c.newInstance(5);//通过有参构造创建对象
		
		Method m = clazz.getMethod("eat");
		m.invoke(d);
	}
}

反射应用实例

  • 使用反射越过泛型检查

  • 原理:泛型只在编译期有效,在运行期会被擦除掉

例题:向ArrayList添加字符串数据

public class Test1 {
	public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<>();
		list.add(111);
		list.add(222);
		/*list.add("abc");*///由于泛型的存在,直接添加会出异常
		
		Class clazz = Class.forName("java.util.ArrayList");// 获取字节码对象
		Method m = clazz.getMethod("add", Object.class);//获取add方法
		m.invoke(list, "abc");
		
		System.out.println(list);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值