java 反射学习:

 

一. 反射机制概念

 

  主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。在java中,只要给定类的名字, 那么就可以通过反射机制来获得类的所有信息。


  反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!


  类中有什么信息,利用反射机制就能可以获得什么信息,不过前提是得知道类的名字。

 

二. 反射机制的作用

 

在运行时判断任意一个对象所属的类;
在运行时获取类的对象;
在运行时访问java对象的属性,方法,构造方法等。

 

三. 反射机制的优点与缺点

 

首先要搞清楚为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。 
静态编译:在编译时确定类型,绑定对象,即通过。 
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。 


反射机制的优点:可以实现动态创建对象和编译,体现出很大的灵活性(特别是在J2EE的开发中它的灵活性就表现的十分明显)。通过反射机制我们可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。


  比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。 


反射机制的缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。

 

四. 反射机制的示例

 

  1. package cfanshe;
  2. class notMainMethord {
  3. private void testForMain() {
  4. System.out.println("demo1");
  5. }
  6. }
  7. public class ReflectTestDemo{
  8. public static void main(String[] args) {
  9. //通过对象获取完整的包名和类名
  10. notMainMethord rtd = new notMainMethord();
  11. System.out.println(rtd.getClass().getName());//cfanshe.notMainMethord
  12. //三种实例化Class类的方法
  13. Class<?> demo1 = null;
  14. Class<?> demo2 = null;
  15. Class<?> demo3 = null;
  16. try {
  17. demo1 = Class.forName("cfanshe.notMainMethord");
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. demo2 = new notMainMethord().getClass();
  22. demo3 = notMainMethord.class;
  23. System.out.println("demo1: "+demo1);
  24. System.out.println("demo2: "+demo2);
  25. System.out.println("demo3: "+demo3);
  26. //通过Class实例化其他类的对象
  27. Class<?> demo = null;
  28. try {
  29. demo = Class.forName("cfanshe.Student");
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. }
  33. Student stu = null;
  34. try {
  35. stu = (Student)demo.newInstance();
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. }
  39. stu.setAge(28);
  40. stu.setName("SELECT_BIN");
  41. System.out.println(stu);
  42. }
  43. }

 

  1. package cfanshe;
  2. public class Student {
  3. private String name;
  4. private int age;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  17. @Override
  18. public String toString() {
  19. return "["+this.name+" "+this.age+"]";
  20. }
  21. }

但是注意一下,当我们把Student中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误:

 

比如定义了一个构造函数:

 

  1. public Student(String name, int age) {
  2. this.age=age;
  3. this.name=name;
  4. }

然后继续运行上面的程序,会出现:
 

java.lang.InstantiationException: cfanshe.Student
	at java.lang.Class.newInstance(Unknown Source)
	at cfanshe.ReflectTestDemo.main(ReflectTestDemo.java:36)
Caused by: java.lang.NoSuchMethodException: cfanshe.Student.<init>()
	at java.lang.Class.getConstructor0(Unknown Source)
	... 2 more
Exception in thread "main" java.lang.NullPointerException
	at cfanshe.ReflectTestDemo.main(ReflectTestDemo.java:40)

所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数。

调用其它类中的构造函数:

  1. package cfanshe;
  2. public class Student {
  3. private String name;
  4. private int age;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  17. public Student() {
  18. }
  19. public Student(int age) {
  20. this.age=age;
  21. }
  22. public Student(String name) {
  23. this.name=name;
  24. }
  25. public Student(String name, int age) {
  26. this.age=age;
  27. this.name=name;
  28. }
  29. @Override
  30. public String toString() {
  31. return "["+this.name+" "+this.age+"]";
  32. }
  33. }
  1. package cfanshe;
  2. import java.lang.reflect.Constructor;
  3. public class ReflectTestDemo{
  4. public static void main(String[] args) {
  5. Class<?> demo = null;
  6. try {
  7. demo = Class.forName("cfanshe.Student");
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. Student stu1 = null;
  12. Student stu2 = null;
  13. Student stu3 = null;
  14. Student stu4 = null;
  15. //取得全部的构造函数
  16. Constructor<?> cons[] = demo.getConstructors();
  17. try {
  18. stu1 = (Student)cons[0].newInstance(28);
  19. stu2 = (Student)cons[1].newInstance("SELECT_BIN");
  20. stu3 = (Student)cons[2].newInstance("SELECT_BIN",28);
  21. stu4 = (Student)cons[3].newInstance();
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. System.out.println(stu1);
  26. System.out.println(stu2);
  27. System.out.println(stu3);
  28. System.out.println(stu4);
  29. }
  30. }

这里获取构造器列表的时候每个构造器使用是由顺序的,至于顺序怎么排的,待会再说,

顺序怎么确定,一个笨一点的办法,用debug可以看到cons里面的内容:

[public cfanshe.Student(int), public cfanshe.Student(java.lang.String), public cfanshe.Student(java.lang.String,int), public cfanshe.Student()]

运行结果:

  1. [null 28]
  2. [SELECT_BIN 0]
  3. [SELECT_BIN 28]
  4. [null 0]

返回实现类的接口:

  1. package cfanshe;
  2. public interface China {
  3. public static final String name = "binguo";
  4. public static int age=16;
  5. public void sayChina();
  6. public void sayHello(String name, int age);
  7. }
  1. package cfanshe;
  2. public class Student implements China{
  3. private String name;
  4. private int age;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  17. @Override
  18. public void sayChina() {
  19. System.out.println("hello ,china");
  20. }
  21. @Override
  22. public void sayHello(String name, int age) {
  23. System.out.println(name+" "+age);
  24. }
  25. }
  1. package cfanshe;
  2. import java.lang.reflect.Constructor;
  3. public class ReflectTestDemo{
  4. public static void main(String[] args) {
  5. Class<?> demo = null;
  6. try {
  7. demo = Class.forName("cfanshe.Student");
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. //保存所有的接口
  12. Class<?> inters[] = demo.getInterfaces();
  13. //取得父类(因为是单继承,所以只有一个)
  14. Class<?> objs = demo.getSuperclass();
  15. for(Class inter : inters) {
  16. System.out.println("实现的接口 :: "+inter);
  17. }
  18. System.out.println("继承的父类为 :: "+objs);
  19. //获取其他类中的全部构造函数
  20. Constructor<?> cons[] = demo.getConstructors();
  21. for(Constructor con : cons) {
  22. System.out.println("构造方法: "+con);
  23. }
  24. }
  25. }
 

控制台输出:

  1. 实现的接口 :: interface cfanshe.China
  2. 继承的父类为 :: class java.lang.Object
  3. 构造方法: public cfanshe.Student(int)
  4. 构造方法: public cfanshe.Student(java.lang.String)
  5. 构造方法: public cfanshe.Student(java.lang.String,int)
  6. 构造方法: public cfanshe.Student()

 

获取其他类的全部属性,并将这些类整理在一起,也就是通过反射获取这些类的全部属性

 

  1. package cfanshe;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Modifier;
  5. public class ReflectTestDemo{
  6. public static void main(String[] args) {
  7. Class<?> demo = null;
  8. try {
  9. demo = Class.forName("cfanshe.Student");
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }
  13. System.out.println("===============本类属性========================");
  14. //取得本类的全部属性
  15. Field[] fields = demo.getDeclaredFields();
  16. for(Field field:fields) {
  17. //权限修饰符
  18. int mo = field.getModifiers();
  19. String priv = Modifier.toString(mo);
  20. //属性类型
  21. Class<?> type = field.getType();
  22. System.out.println(priv + " " + type.getName() + " " + field.getName() + ";");
  23. }
  24. System.out.println("===============实现的接口或者父类的属性========================");
  25. //取得实现的接口和父类的属性
  26. Field field1[] = demo.getFields();
  27. for(Field field:field1) {
  28. //权限修饰符
  29. int mo = field.getModifiers();
  30. String priv = Modifier.toString(mo);
  31. //属性类型
  32. Class<?> type = field.getType();
  33. System.out.println(priv + " " + type.getName() + " " + field.getName() + ";");
  34. }
  35. }
  36. }

控制台输出:

  1. ===============本类属性========================
  2. private java.lang.String name;
  3. private int age;
  4. ===============实现的接口或者父类的属性========================
  5. public static final java.lang.String name;
  6. public static final int age;

通过反射调用其他类的方法

  1. package cfanshe;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Modifier;
  6. public class ReflectTestDemo{
  7. public static void main(String[] args) {
  8. Class<?> demo = null;
  9. try {
  10. demo = Class.forName("cfanshe.Student");
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. try {
  15. //调用Student类中的sayChina方法;
  16. Method method = demo.getMethod("sayChina");
  17. method.invoke(demo.newInstance());
  18. //调用Student类中的sayHello方法;
  19. method = demo.getMethod("sayHello",String.class,int.class);
  20. method.invoke(demo.newInstance(), "binguo",25);
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }

控制台输出:

  1. hello ,china
  2. binguo 25

调用其他类中的get、set方法

  1. package cfanshe;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Modifier;
  6. public class ReflectTestDemo {
  7. public static void main(String[] args) {
  8. Class<?> demo = null;
  9. try {
  10. demo = Class.forName("cfanshe.Student");
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. }
  14. Object obj = null;
  15. try {
  16. obj = demo.newInstance();
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. setter(obj, "Name", "起司", String.class);
  21. getter(obj, "Name");
  22. }
  23. /**
  24. * @param obj
  25. * 操作的对象
  26. * @param att
  27. * 操作的属性
  28. */
  29. public static void getter(Object obj, String att) {
  30. try {
  31. Method method = obj.getClass().getMethod("get" + att);
  32. System.out.println(method.invoke(obj));
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. /**
  38. * @param obj
  39. * 操作的对象
  40. * @param att
  41. * 操作的属性
  42. * @param value
  43. * 设置的值
  44. * @param type
  45. * 参数的属性
  46. */
  47. public static void setter(Object obj, String att, Object value, Class<?> type) {
  48. try {
  49. Method method = obj.getClass().getMethod("set" + att, type);
  50. method.invoke(obj, value);
  51. } catch (Exception e) {
  52. e.printStackTrace();
  53. }
  54. }
  55. }

输出:起司;

注意这里面obj.getClass().getMethod("set" + att, type);是拼的方法名,因此要注意大小写

通过反射操作属性:

  1. package cfanshe;
  2. import java.lang.reflect.Field;
  3. public class ReflectTestDemo {
  4. public static void main(String[] args) {
  5. Class<?> demo = null;
  6. try {
  7. demo = Class.forName("cfanshe.Student");
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. Object obj = null;
  12. try {
  13. obj = demo.newInstance();
  14. Field field = demo.getDeclaredField("name");
  15. //打开通道
  16. field.setAccessible(true);
  17. field.set(obj, "起司");
  18. System.out.println(field.get(obj));
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }

控制台输出:起司

通过反射取得并修改数组的值:

  1. package cfanshe;
  2. import java.lang.reflect.Array;
  3. public class ReflectTestDemo {
  4. public static void main(String[] args) {
  5. int[] temp = { 1, 2, 3, 4, 5 };
  6. Class<?> demo = temp.getClass().getComponentType();
  7. System.out.println("数组类型: " + demo.getName());
  8. System.out.println("数组长度 " + Array.getLength(temp));
  9. System.out.println("数组的第一个元素: " + Array.get(temp, 0));
  10. Array.set(temp, 0, 100);
  11. System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));
  12. }
  13. }

打印:

  1. 数组类型: int
  2. 数组长度 5
  3. 数组的第一个元素: 1
  4. 修改之后数组第一个元素为: 100

 

 

 

很好的贴子推荐:

https://www.cnblogs.com/Eason-S/p/5851078.html

https://blog.csdn.net/sinat_38259539/article/details/71799078