s反射学习

本文介绍了Java反射机制的原理与应用,包括通过反射动态创建对象、调用方法和访问属性。通过示例展示了如何利用反射实现接口的解耦,避免在代码中硬编码类名,从而提高代码的灵活性。同时,讲解了从配置文件读取数据的方法,进一步增强了程序的可扩展性。

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

反射

在运行时,将类的各个组成部分封装成其他对象,利用了java的动态编译
好处:解耦

生成对象的三个阶段
1 字节码/源码阶段  -- 根据.java编译生成了.class
2 类阶段 -- 把所有的属性、方法、构造器分别封装到Fields[]、Methods[]、Constructors[]中
3 对象阶段 -- 已经new出来了

在这里插入图片描述

编译
1 静态编译:在编译时确定类型
2 动态编译:运行时才确定类型 --- 体现了java的灵活性
反射的三种方式
1 Class.forName("类的完整限定名(包名+类名)")     返回Class反射对象 --字节码阶段
​		-- 搭配配置文件   *.properties : driver=com.mysql.jdbc.Driver
2 类.class   返回Class反射对象  --类阶段
​		用于参数的传递
3 对象.getClass()    返回Class反射对象  --对象阶段
​		当已经生成对象实例之后
用例子来证明解耦的好处

业务:有一个接口,定义吃各种水果的方法,以后有什么水果,就打印吃相关水果

public interface Fruit {
	
	public void eat();
}

public class Apple implements Fruit {
	@Override
	public void eat() {
		System.out.println("吃苹果");

	}
}
public final class Banana implements Fruit {
	@Override
	public void eat() {
		System.out.println("吃香蕉");
	}
}
public class Factory {
	public static void getFruit(String fruitName){
		Fruit fruit=null;
		if(fruitName.equals("apple")){
			fruit=new Apple();
		}
		if(fruitName.equals("banana")){
			fruit=new Banan();
		}	
		fruit.eat();	
	}
}
//调用:
	public static void main(String[] args) {
		Factory.getFruit("apple");		
		Factoryge.getFruit("banan");
	}

弊端:如果有新的水果,就必须在Factory类中添加/更改代码—耦合

修改:通过反射来解耦

public static Fruit getFruit(String className){
		try {
			 //得到类的反射对象
			Class clazz=Class.forName(className);
			//根据反射对象new实例
			Object obj = clazz.newInstance();  // Apple apple=new Apple();
			return (Fruit)obj;
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
		
	}
	
//主函数:
public static void main(String[] args) {
	Fruit fruit = Factory.getFruit("com.woniuxy.fruit.Apple");
     fruit.eat();

我们今后一般使用反射+配置文件的方式来达到真正的解耦:

解耦
1、我们只需要传递类的完整限定名就能够获得该对象
2、如果把类的完整限定名放在文件中,通过读取的手段,避免了再次编译
反射操作构造器
Class clazz=Class.forName(”类的完整限定名“);
clazz.getDeclaredConstructor(Class...parameterTypes)  -- 获得指定的那一个
clazz.getDeclaredConstructors()  -- 获得数组

不带参数:
	//操作构造器
	Constructor  constructor = clazz.getDeclaredConstructor(无参/带参);//public Dog(){}
	//实例化
	Object obj = constructor.newInstance(无参/带参);
	简写:(重点)
	clazz.newInstance()
反射操作属性
clazz.getField(name);  -->返回Field (只能获取public修饰的)
clazz.getDeclaredField(name)     -->返回Field(可以获取除了private修饰的之外)
clazz.getFields() -->返回返回Field[]
clazz.getDeclaredFields()返回返回Field[](可以获取除了private修饰的之外)
//例子:通过反射暴力访问设置private 属性  ,并且 打印
		//获得反射对象
		Class clazz= Dog.class;
		//根据反射对象实例化对象
		Object obj = clazz.newInstance();
		//根据属性名获得属性对象
		Field field = clazz.getDeclaredField("sex");
		//开启暴力访问
		field.setAccessible(true);
		//设置sex属性值
		field.set(obj, "公");
		//打印
		System.out.println(field.get(obj));
		//打印对象.属性
		System.out.println(((Dog)obj).getSex());
反射操作方法(重点)
clazz.getDeclaredMethod(name, Class...parameterTypes)  获得指定方法 Method对象
clazz.getDeclaredMethods() 获得所有方法 Method[]
Method对象.invoke(obj,Object...param)  -->返回object(方法的返回
三种方式获取properties文件的数据
方式1:ResourceBundle
我们需要把properties文件放在src下,相对于src
如果是放在src下的xx包中:
	ResourceBundle.getBundle("xx/db");
	ResourceBundle.getBundle("xx.db");
	ResourceBundle rb = ResourceBundle.getBundle("db");
		String root = rb.getString("user");
		System.out.println(root);
方式2: 通过流+properties对象
properties文件放在项目根目录,相对与项目根目录
如果是直接放在根目录:db.properties
如果是放在根目录下的xx文件夹:xx/db.properties
如果是放在src:    src/db.properties	
		//字符流
		FileReader fr=new FileReader(new File("db.properties"));
		//字节流
//		FileInputStream fis=new FileInputStream(new File("db.properties"));
		//集合
		Properties pro=new Properties();
		pro.load(fr);
		System.out.println(pro.getProperty("user"));
		System.out.println(pro.getProperty("password"));
方式3: 通过类加载器搭配properties
//通过某个类的类加载器去读取流
	InputStream in = Demo3.class.getClassLoader().getResourceAsStream("db.properties");
	//集合
	Properties pro=new Properties();
	pro.load(in);
	System.out.println(pro.getProperty("user"));
	System.out.println(pro.getProperty("password"));

例子 写一个类,类中有一些不带参数的方法,通过反射的方式去执行这些方法,要求,为了解耦,需要用到properties文件

public class Dog {
	public void eat(){
		System.out.println("啃骨头");
	};
	public void run(){
		System.out.println("跑");
	};
}

//Dog的properties:

/*
	className=com.woniuxy.demo2.Dog
	methodName=eat
*/



//Run的properties:

~~~
public static void main(String[] args)throws Exception {
		//获取className和methodName
		InputStream in = Run.class.getClassLoader().getResourceAsStream("xx.properties");
		Properties pro=new Properties();
		pro.load(in);
		String className = pro.getProperty("className");
		String methodName = pro.getProperty("methodName");
		
		//通过反射获得并且执行方法
		Class clazz= Class.forName(className);
		Method m= clazz.getDeclaredMethod(methodName);
		m.invoke(clazz.newInstance());
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值