java反射的深入(二)

雨点打透心脏的1/2处 2022-07-03 12:17 238阅读 0赞

通过反射调用类的方法

在正常情况下一个类的对象功能产生之后,就可以直接调用类中的方法了,若想要调用,则必须知道方法名称,之后用Class类中的 getMethod()方法 ,

public Method getMethod(String name ,Class<?> … parameterTypes)

然后invoke(Method对象)来执行方法,

接上例:

  1. package com.java.reflect;
  2. import java.lang.reflect.Method;
  3. public class InvokesayChinaDemo {
  4. public static void main(String[] args) {
  5. Class<?> c1 = null;
  6. try {
  7. c1 = Class.forName("com.java.reflect.PersonDemo1");// 实例化对象
  8. } catch (ClassNotFoundException e) {
  9. e.printStackTrace();
  10. }
  11. try {
  12. Method met = c1.getMethod("sayChina");// 找到sayChina方法
  13. met.invoke(c1.newInstance());// 调用方法
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }

通过Class类的getMethod()方法根据一个类中的方法名称可以取得Method对象,并通过invoke调用指定的方法,但是在使用invoke()方法的时候必须传入一个类的实例化对象,因为在sayChina()方法上没有任何的参数,所以没有设定参数类型和参数内容。

改进后:

  1. package com.java.reflect;
  2. import java.lang.reflect.Method;
  3. public class InvokesayChinaDemo {
  4. public static void main(String[] args) {
  5. Class<?> c1 = null;
  6. try {
  7. c1 = Class.forName("com.java.reflect.PersonDemo1");// 实例化Class对象
  8. } catch (ClassNotFoundException e) {
  9. e.printStackTrace();
  10. }
  11. try {
  12. Method met = c1.getMethod("sayChina");// 找到sayChina方法
  13. met.invoke(c1.newInstance());// 调用方法
  14. Method meth = c1.getMethod("sayHello", String.class, int.class);
  15. String rv = null;
  16. rv = (String) meth.invoke(c1.newInstance(), "汪兵", 25);//调用方法
  17. System.out.println(rv);
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }

通过反射调用类setter和getter

  1. package com.java.reflect;
  2. import java.lang.reflect.Method;
  3. public class InvokeSetterGetterDemo {
  4. public static void main(String[] args) {
  5. Class<?> c1 = null;
  6. Object obj = null;
  7. try {
  8. c1 = Class.forName("com.java.reflect.PersonDemo1");// 实例化Class对象
  9. } catch (ClassNotFoundException e) {
  10. e.printStackTrace();
  11. }
  12. try {
  13. obj = c1.newInstance();
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. setter(obj, "name", "李兴华", String.class);// 设置setter方法
  18. setter(obj, "age", 25, int.class);// 设置setter方法
  19. System.out.println("年龄:");
  20. getter(obj, "age");
  21. System.out.println("姓名:");
  22. getter(obj, "name");
  23. }
  24. public static void getter(Object obj, String att) {
  25. try {
  26. Method met = obj.getClass().getMethod("get" + initStr(att));
  27. System.out.println(met.invoke(obj));// 调出getter的内容
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. /**
  33. * Object obj:要操作的对象 String att:要操作的属性 Object value:设置属性值
  34. * Class<?>type:设置属性类型
  35. */
  36. public static void setter(Object obj, String att, Object value,
  37. Class<?> type) {
  38. Method met = null;
  39. try {
  40. met = obj.getClass().getMethod("set" + initStr(att), type);// 得到setter方法
  41. // 设置setter内容
  42. met.invoke(obj, value);
  43. } catch (Exception e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. public static String initStr(String old) {// 将单词的首字母大写
  48. String str = old.substring(0, 1).toUpperCase() + old.substring(1);
  49. return str;
  50. }
  51. }

通过反射调用属性

如果现在假设要操作一个类中的属性,则也可以通过Filed完成,而不必麻烦的通过setter和getter方法

得到公共属性:

public Field getField (String name )

得到本类属性:

public Field getDeclaredField (String name )

取得属性内容:

public Object get(Object obj)

设置属性内容:

public void set(object obj,object value)

  1. package com.java.reflect;
  2. import java.lang.reflect.Field;
  3. public class InvokeFieldDemo {
  4. public static void main(String[] args) {
  5. Class<?> c1 = null;
  6. Object obj = null;
  7. try {
  8. c1 = Class.forName("com.java.reflect.PersonDemo1");
  9. } catch (ClassNotFoundException e) {
  10. e.printStackTrace();
  11. }
  12. try {
  13. obj = c1.newInstance();
  14. } catch (InstantiationException e) {
  15. e.printStackTrace();
  16. } catch (IllegalAccessException e) {
  17. e.printStackTrace();
  18. }
  19. Field nameField = null;
  20. Field ageField = null;
  21. try {
  22. nameField = c1.getDeclaredField("name");
  23. ageField = c1.getDeclaredField("age");
  24. nameField.setAccessible(true);// 设置私有属性可见
  25. ageField.setAccessible(true);
  26. nameField.set(obj, "sdfsdfsd");
  27. ageField.set(obj, 23);
  28. System.out.println("姓名:" + nameField.get(obj));
  29. System.out.println("年龄" + ageField.get(obj));
  30. } catch (NoSuchFieldException | SecurityException
  31. | IllegalArgumentException | IllegalAccessException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }

通过反射操作数组

反射机制不光只使用在类中,也可以应用在任意的引用数据类型上,当然包含了数组,数组使用Array类完成

  1. public Class<?> getComponentType()

得到数组指定下标的内容:

  1. public static Object get(Object array,
  2. int index)
  3. throws IllegalArgumentException,
  4. ArrayIndexOutOfBoundsException
  5. public static void set(Object array,
  6. int index,
  7. boolean z)
  8. throws IllegalArgumentException,
  9. ArrayIndexOutOfBoundsException

开辟新的数组:

  1. public static Object newInstance(Class<?> componentType,
  2. int... dimensions)
  3. throws IllegalArgumentException,
  4. NegativeArraySizeException

取得数组的内容:

  1. package com.java.reflect;
  2. import java.lang.reflect.Array;
  3. public class ClassArrayDemo {
  4. public static void main(String[] args) {
  5. int temp[] = { 1, 2, 3 };
  6. Class<?> c = temp.getClass().getComponentType();// 取得数组的Class对象
  7. System.out.println("类型:" + c.getName());// 取得数组类型的名称
  8. System.out.println("长度:" + Array.getLength(temp));
  9. System.out.println("第一个内容是:" + Array.getInt(temp, 1));// 得到第一个数组的内容
  10. Array.set(temp, 2, 20);
  11. System.out.println(temp[2]);
  12. }
  13. }

使用Array类还可以修改数组的大小:(实际上就是新建一个新的数组,将旧的数据复制进去)

  1. package com.java.reflect;
  2. import java.lang.reflect.Array;
  3. public class ChangeArrayDemo {
  4. public static void main(String[] args) {
  5. int temp[] = { 1, 2, 3 };
  6. int newTemp[] = (int[]) arrayInc(temp, 5);// 重新开辟空间
  7. print(newTemp);
  8. }
  9. public static Object arrayInc(Object obj, int len) {// 建立新的数组并拷贝
  10. Class<?> c = obj.getClass();
  11. Class<?> arr = c.getComponentType();// 得到数组的Class
  12. Object newO = Array.newInstance(arr, len);// 拷贝内容
  13. int co = Array.getLength(obj);
  14. System.arraycopy(obj, 0, newO, 0, co);// 拷贝数组
  15. return newO;
  16. }
  17. public static void print(Object obj) {// 数组输出
  18. Class<?> c = obj.getClass();
  19. if (!c.isArray()) {// 判断是否是数组
  20. return;
  21. }
  22. Class<?> arr = c.getComponentType();
  23. System.out.println(arr.getName() + "数组长度是:" + Array.getLength(obj));
  24. for (int i = 0; i < Array.getLength(obj); i++) {
  25. System.out.print(Array.get(obj, i) + "、");// 通过Array输出
  26. }
  27. }
  28. }

代理设计:

一个操作的接口有两个子类,其中一个是真是主题的实现类,另一个是代理类,代理实现类要完成比真实主体实现类更多的内容,而且本身还需要处理一些与具体业务有关的程序代码。

  1. package com.java.reflect;
  2. interface Subject {
  3. public String say(String name, int age);
  4. }
  5. class RealSubject implements Subject {
  6. @Override
  7. public String say(String name, int age) {
  8. return "姓名:" + name + ",年龄:" + age;
  9. }
  10. }
  11. class ProxySubject implements Subject {
  12. private Subject sub = null;
  13. public ProxySubject(Subject sub) {
  14. this.sub = sub;
  15. }
  16. @Override
  17. public String say(String name, int age) {
  18. return this.sub.say(name, age);
  19. }
  20. }
  21. public class DyProxyDemo {
  22. public static void main(String[] args) {
  23. Subject sub = new ProxySubject(new RealSubject());
  24. String info = sub.say("李兴华", 99);
  25. System.out.println(info);
  26. }
  27. }

以上代码称为静态代理,一个代理只能为一个接口服务,

InvocationHandler接口:

public interface InvocationHandler{

  1. Object invoke(Object proxy,
  2. Method method,
  3. Object[] args)
  4. throws Throwable

}

Object proxy:被代理的接口

Method method:要调用的方法

Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject

Proxy类

是专门完成代理的操作类,可以通过此类为一个或者多个接口动态地生成实现类,此类提供如下的方法

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)
  4. throws IllegalArgumentException

loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 得到的InvocationHandle接口子类实例

类加载器:

图1 图1

工厂设计模式:

实例:

  1. package com.java.reflect;
  2. interface Fruit {
  3. public void eat();
  4. }
  5. class Apple implements Fruit {
  6. public void eat() {
  7. System.out.println("eat apple");
  8. }
  9. }
  10. class Orange implements Fruit {
  11. public void eat() {
  12. System.out.println("eat orange");
  13. }
  14. }
  15. // 工厂类
  16. class Factory {
  17. public static Fruit getInstance(String className) {
  18. Fruit fruit = null;
  19. fruit = null;
  20. if ("apple".equals(className)) {
  21. fruit = new Apple();
  22. }
  23. if ("orange".equals(className)) {
  24. fruit = new Apple();
  25. }
  26. return fruit;
  27. }
  28. }
  29. public class FactoryDemo01 {
  30. public static void main(String[] args) {
  31. Fruit f = Factory.getInstance("apple");
  32. if (f != null) {
  33. f.eat();
  34. }
  35. }
  36. }

如果扩充了一个子类,则肯定要修改工厂类,如果希望扩充子类时不用修改工厂类的话,则必须使用反射完成。

例子:

  1. package com.java.reflect;
  2. interface Fruit {
  3. public void eat();
  4. }
  5. class Apple implements Fruit {
  6. public void eat() {
  7. System.out.println("eat apple");
  8. }
  9. }
  10. class Orange implements Fruit {
  11. public void eat() {
  12. System.out.println("eat orange");
  13. }
  14. }
  15. // 工厂类
  16. class Factory {
  17. public static Fruit getInstance(String className) {
  18. Fruit fruit = null;
  19. /**
  20. * if ("apple".equals(className)) { fruit = new Apple(); }
  21. *
  22. * if ("orange".equals(className)) { fruit = new Apple(); } return
  23. * fruit; }
  24. */
  25. try { fruit = (Fruit) Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return fruit; }
  26. }
  27. public class FactoryDemo01 {
  28. public static void main(String[] args) {
  29. Fruit f = Factory.getInstance("com.java.reflect.Apple");
  30. if (f != null) {
  31. f.eat();
  32. }
  33. }
  34. }

以上在扩充子类时,不用修改工厂类,但是输入完整的“包.类”名称,比较麻烦,此时可以通过一些拍之文件的方式保存这些完整的类的路径。

图2图2

  1. package com.java.reflectFactory;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.util.Properties;
  7. interface Fruit {
  8. public void eat();
  9. }
  10. class Apple implements Fruit {
  11. public void eat() {
  12. System.out.println("eat apple");
  13. }
  14. }
  15. class Orange implements Fruit {
  16. public void eat() {
  17. System.out.println("eat orange");
  18. }
  19. }
  20. class Init {
  21. public static Properties getPro() {
  22. Properties pro = new Properties();
  23. File f = new File("F:/JAVA深入学习/fruit.properties");// 找到属性文件
  24. /**
  25. * 配置文件的内容 apple =com.java.reflectFactory.Apple
  26. * orange=com.java.reflectFactory.Orange
  27. * */
  28. if (f.exists()) {// 如果文件存在
  29. try {
  30. pro.load(new FileInputStream(f));
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. }
  34. } else {
  35. pro.setProperty("apple", "com.java.reflectFactory.Apple");
  36. pro.setProperty("orage", "com.java.reflectFactory.Orange");
  37. try {
  38. pro.store(new FileOutputStream(f), "FRUIT CLASS");
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. return pro;
  44. }
  45. }
  46. // 工厂类
  47. class Factory {
  48. public static Fruit getInstance(String className) {
  49. Fruit fruit = null;
  50. try {
  51. fruit = (Fruit) Class.forName(className).newInstance();
  52. } catch (Exception e) {
  53. e.printStackTrace();
  54. }
  55. return fruit;
  56. }
  57. }
  58. public class FactoryDemo02 {
  59. public static void main(String[] args) {
  60. Properties pro = Init.getPro();
  61. Fruit f = Factory.getInstance(pro.getProperty("apple"));
  62. if (f != null) {
  63. f.eat();
  64. }
  65. }
  66. }

配置文件与程序相分离的!

发表评论

表情:
评论列表 (有 0 条评论,238人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java深入浅出

    刚开始接触反射这个概念,感觉反射这个机制很复杂很难懂,所以在这篇文章中对java的反射机制以个人的理解总结归纳。 1. 什么是反射? 什么是反射?在官方文档中是这样说的