反射
在运行时,将类的各个组成部分封装成其他对象,利用了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());
}