反射案例

有不懂的可以留言我,虽然我也很菜

Class.forName()读取配置文件举例

以榨汁机榨汁为案例

		配置文件config.properties:
		reflect_examples.Orange
		//注意将配置文件放在src下
		//然后写某个类的相对路径就可以了


		/**
		 * 当我们想喝苹果汁就要注释掉喝橘子汁的代码
		 * 想喝哪种果汁就要注释掉其它果汁的代码,不断修改源代码,不好.
		 *
		 *  当我们通过反射的时候,我们可以只修改配置文件,而不需要动源码
		 */
		public class Example_1_ForName {
		    public static void main(String[] args) throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
		
		        //1. 没有反射,只用到了多态的时候
		
		        /*//这就相当于买了一个榨汁机
		        Juicer juicer = new Juicer();
		        //这就相当于把苹果放进榨汁机,用榨汁机榨了一杯苹果汁
		        //juicer.getJuice(new Apple());
		
		        //这就相当于把橙子放进榨汁机,用榨汁机榨了一杯橙子汁
		        juicer.getJuice(new Orange());*/
		
		
		        //2. 用反射和配置文件
		
		        //先拿到apple类的字节码文件
		        //这样写就写死了,配置文件没有起到作用
		        //Class clazz = Class.forName("main.reflect_examples.Apple");
		        /**
		         * 正确写法应该这样子:
		         *  1. 创建一个流文件,读取一行
		         *  2. 根据流文件中取到的那一行去创建字节码文件对象
		         *  3. 通过字节码文件对象创建该字节码文件对象所表示的类的实例,这里相当于创建一个苹果的实例对象
		         *  4. 我们以后想喝其它水果的果汁,直接改配置文件就可以了
		         */
		        BufferedReader reader = new BufferedReader(new FileReader("src/config.properties"));
		        Class clazz = Class.forName(reader.readLine());
		        Fruit fruit = (Fruit) clazz.newInstance();
		
		        //榨汁机榨汁
		        Juicer juicer = new Juicer();
		        juicer.getJuice(fruit);
		    }
		}
		
		//利用多态把苹果向上抽取为水果
		interface Fruit{
		    public abstract void squeeze();
		}
		class Apple implements Fruit{
		
		    //榨汁功能
		    @Override
		    public void squeeze() {
		        System.out.println("榨了一杯苹果汁!");
		    }
		}
		
		class Orange implements Fruit{
		    //榨汁功能
		    @Override
		    public void squeeze() {
		        System.out.println("榨了一杯橙子汁!");
		    }
		}
		
		//通过榨汁机榨汁
		class Juicer {
		    /*//把苹果放到榨汁机里通过榨汁机榨苹果汁
		    public void getJuice(Apple apple) {
		        apple.squeeze();
		    }
		
		    //把苹果放到榨汁机里通过榨汁机榨苹果汁
		    public void getJuice(Orange orange) {
		        orange.squeeze();
		    }*/
		
		    //利用多态后的写法
		    public void getJuice(Fruit fruit) {
		        fruit.squeeze();
		    }
		}

通过反射获取带参构造函数并使用

我们上面榨汁机例子中使用到了newInstance()这个方法来获取字节码文件所表示的类的实例对象,此时使用的是无参构造函数,当我们想通过带参构造函数创建时,就不能这样子用了,就要用到其他方法.

使用案例及步骤:

		/**
		 * 根据带参构造创建实例举例:
		 * 1. 可以先通过调用Class类的getConstructor(String.class, int.class)获取一个指定的构造函数
		 * 2. 然后再调用Constructor类的newInstance("张三", 18)方法来创建实例对象
		 */
		public class Example_1_ForName2 {
		    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
		        //注意这里的路径是相对路径,idea右键会直接有赋值相对路径的选项
		        Class clazz = Class.forName("reflect_examples.Person");

				//获取该字节码文件对象中所有公共构造方法
		        Constructor[] constructors = clazz.getConstructors();
		        for (int i = 0; i < constructors.length; i++) {
		            System.out.println(constructors[i]);
		        }
		
		        //获取对应构造函数的Constructor对象			
		        Constructor constructor = clazz.getConstructor(String.class, int.class, double.class);
		
		        Person person = (Person) constructor.newInstance("李四", 18, 99);
		
		        System.out.println(person);
		    }
		}
		
		
		//创建一个person类
		class Person {
		    private String name;
		    private int age;
		    private double score;
		
		    public Person(String name, int age, double score) {
		        this.name = name;
		        this.age = age;
		        this.score = score;
		    }
		
		    @Override
		    public String toString() {
		        return "Person{" +
		                "name='" + name + '\'' +
		                ", age=" + age +
		                ", score=" + score +
		                '}';
		    }
		}

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

使用案例及步骤:

		/**
		 * 获取成员变量并使用举例:
		 *  1. 可以先通过调用Class类的getConstructor(String.class, int.class)获取一个指定的构造函数
		 *  2. 然后再调用Constructor类的newInstance("张三", 18)方法来创建实例对象
		 *  3. 通过getFields()获取Class对象所表示的类的所有公共字段
		 *      千万注意这里是公共字段哈,只能是public的,private,默认,propected的它是访问不到的
		 *      如果我们字段是private修饰的,我们可以用getDeclaredFields(),它不管什么修饰符修饰都可以访问到
		 *  4. 通过getField(string name)返回一个Field对象,表示name这个公共字段的对象.
		 *      也要注意这个修改的属性也得是public的哈
		 *      如果不是public修饰的,我们就要去除它的权限

		 * 在反射面前,一切都是赤果果的哈哈哈哈
		 */
		public class Example_1_ForName3 {
		    public static void main(String[] args) throws Exception{
		        //注意这里的路径是相对路径,idea右键会直接有赋值相对路径的选项
		        Class clazz = Class.forName("reflect_examples.Person");
		
		        //获取该字节码文件对象中所有公共构造方法
		        Constructor[] constructors = clazz.getConstructors();
		        for (int i = 0; i < constructors.length; i++) {
		            System.out.println(constructors[i]);
		        }
		
		        //获取对应构造函数的Constructor对象
		        Constructor constructor = clazz.getConstructor(String.class, int.class, double.class);
		
		        Person person = (Person) constructor.newInstance("zhannsan", 18, 99);
		
		        System.out.println(person);
		        System.out.println("------------");
		
		        //获取所有属性字段
		        Field[] fields = clazz.getDeclaredFields();
		        for (int i = 0; i < fields.length; i++) {
		            System.out.println(fields[i]);
		        }
		
		        //获取name属性表示的field对象
		        Field field = clazz.getDeclaredField("name");
		        //去除私有权限
		        field.setAccessible(true);
		        //修改person对象的属性name的值
		        field.set(person,"李四");
		
		        System.out.println(person);
		    }
		}
		
		
		//创建一个person类
		class Person {
		    private String name;
		    int age;
		    protected double score;
		
		    public Person(String name, int age, double score) {
		        this.name = name;
		        this.age = age;
		        this.score = score;
		    }
		
		    @Override
		    public String toString() {
		        return "Person{" +
		                "name='" + name + '\'' +
		                ", age=" + age +
		                ", score=" + score +
		                '}';
		    }
		}

通过反射获取方法并使用

注意

getDeclaredMethods()获取方法的时候不能获取继承的方法和实现的接口的方法.
getMethod()可以获取本类及其继承自父类的所有公共方法
使用案例及步骤:

		/**
		 * 获取方法并使用举例:
		 *  1. 可以通过getDeclaredMethods()获取所有方法
		 *  2. 通过Class.getMethod(String.class...)
		 *      Class.getDeclaredMethod(Srting.class...)方法可以获取类中的指定方法.
		 *  3. 调用method.invoke(Object, Object...)可以调用该方法,表示对象obj调用参数为object的方法method
		 */
		public class Example_1_ForName4 {
		    public static void main(String[] args) throws Exception{
		        //获取Class对象
		        Class clazz = Class.forName("reflect_examples.Person");
		
		        //通过无参构造方法创建实例对象
		        Person person = (Person) clazz.newInstance();
		        System.out.println("------------");
		
		        //查看所有方法
		        Method[] methods = clazz.getDeclaredMethods();
		        for (int i = 0; i < methods.length; i++) {
		            System.out.println(methods[i]);
		        }
		        //生成eat方法对象
		        Method eat = clazz.getDeclaredMethod("eat");
		
		        //调用方法,即person对象调用方法无参方法eat
		        eat.invoke(person);
		
		
		    }
		}
		
		
		//创建一个person类
		class Person {
		    public Person() {
		    }

		    public void eat() {
		        System.out.println("今天吃了大龙虾!");
		    }
		    
		}

通过反射越过泛型检查

要求: ArrayList的一个对象,想在这个集合中添加一个字符串数据,怎么整?

  • 这里我们就要知道泛型起作用是在编译期,在运行期泛型会被擦除掉,也就是说,我们生成.class文件后,泛型就没了.

使用案例及步骤:

		/**
		 * 要求: ArrayList<Integer>的一个对象想在这个集合中添加一个字符串数据
		 */
		public class Example_1_ForName5 {
		    public static void main(String[] args) throws Exception {
		        ArrayList<Integer> list = new ArrayList<>();
		        list.add(1);
		        list.add(2);
		        list.add(3);
		
		        //获取字节码文件对象
		        Class clazz = Class.forName("java.util.ArrayList");
		
		        //拿到add方法对象
		        Method method = clazz.getDeclaredMethod("add", Object.class);
		
		        //执行add方法,表示list调用add方法,且add方法参数为"樊磊"
		        method.invoke(list,"樊磊");
		
		        System.out.println(list);
		    }
		}

通过反射写一个通用的设置某个对象的某个属性为指定的值

要求: public void setProperty(Object obj, String propertyName, Object value){},此方法可将obj对象中名为propertyName的属性值设置为value

使用案例及步骤:

		public class Example_1_ForName6 {
		    public static void main(String[] args) throws Exception {
		        User user = new User();
		        System.out.println(user);
		        Example_1_ForName6 e = new Example_1_ForName6();
		        e.setProperty(user, "name", "fan");
		        System.out.println(user);
		    }
		
		    public void setProperty(Object obj, String propertyName, Object value) throws Exception {
		        //获取字节码文件对象
		        Class clazz = obj.getClass();
		
		        //获取对应的字段对象
		        Field field = clazz.getDeclaredField(propertyName);
		
		        //擦除其权限
		        field.setAccessible(true);
		        //修改值
		        field.set(obj, value);
		    }
		}
		
		class User {
		    private String name;
		
		    @Override
		    public String toString() {
		        return "User{" +
		                "name='" + name + '\'' +
		                '}';
		    }
		}

一个练习

		/**
		 * 要求:
		 *  1. 给定一个类Student,要求自己新建一个property的配置文件写入类的完整名称
		 *     (就是包名.类名,idea选中类然后复制引用就是的)
		 *  2. 然后写一个程序,读取这个properties配置文件,获取类的完整名称并加载这个类,用反射的方式运行run方法
		 */
		public class Example_1_ForName7 {
		    public static void main(String[] args) throws Exception {
		        //读取配置文件
		        BufferedReader br = new BufferedReader(new FileReader("src/Student.properties"));
		        String s = br.readLine();
		        //获取类的字节码文件对象
		        Class clazz = Class.forName(s);
		        //获取方法对象
		        Method run = clazz.getDeclaredMethod("run");
		        //获取该类的构造方法对象
		        Constructor constructor = clazz.getConstructor(String.class);
		        //通过构造方法创建对象
		        Student student = (Student) constructor.newInstance("pipi");
		
		        //调用方法
		        run.invoke(student);
		
		    }
		}
		
		class Student{
		    private String name;
		
		    public Student(String name) {
		        this.name = name;
		    }
		
		    public void run() {
		        System.out.println("学生" + name + "在晨跑!");
		    }
		}

		main方法还可以这么写,就是不需要生成method对象,直接调用
		public static void main(String[] args) throws Exception {
		        //读取配置文件
		        BufferedReader br = new BufferedReader(new FileReader("src/Student.properties"));
		        String s = br.readLine();
		        //获取类的字节码文件对象
		        Class clazz = Class.forName(s);
		        //获取该类的构造方法对象
		        Constructor constructor = clazz.getConstructor(String.class);
		        //通过构造方法创建对象
		        Student student = (Student) constructor.newInstance("pipi");
		
		        //调用方法
		        student.run();
		    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值