黑马程序员——Java基础---反射

时间:2023-02-19 17:14:14
反射
    JAVA反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制。
    动态获取类中信息,就是java反射。可以理解为对类的解剖。
    如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射技术。
    P.S.    所谓的框架就是对外提供一些接口,也就是功能扩展的标准,由实现类按照这个接口标准去实现。框架内部如果需要操纵这些实现类的对象完成某些操作,那么只需要把这些实现类的全名(包名+类名)写在某个配置文件中,框架代码只需要读取这个配置文件,就可以获取这个实现类的字节码文件,然后利用反射技术创建这个实现类的对象并且调用相应的方法完成一些操作。
    用于描述字节码的类就是Class类,创建对象,可以提取字节码文件中的内容,如字段、构造函数、一般函数。该类就可以获取字节码文件中的所有内容,那么反射就是依靠该类完成的。想要对一个类文件进行解剖,只要获取到该类的字节码文件对象即可。实际开发中一些常用,但又容易写错,遗忘的// 使用反射技术得到T的真实类型
// 获取当前new的对象的 泛型的父类 类型
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); 
// 获取第一个类型参数的真实类型
this.clazz = (Class<T>) pt.getActualTypeArguments()[0]; 

使用反射获取Method,可以使用getMethod(String name,  Class<?>... parameterTypes)来获取,第一个参数是方法的名字,第二个是参数类表
但是这个方法没参数呢?比如:
一个简单的Person 类
  1. public class Person {
  2.         public String name="zhangsan";
  3.         public int age;
  4.         public String str1 = "ball";
  5.         public String str2 = "baskecballl";
  6.         public Person(){
  7.                 this.str1=str1;
  8.                 this.str2=str2;
  9.         }
  10.         public String getName() {
  11.                 return name;
  12.         }
  13.         public void setName(String name) {
  14.                 this.name = name;
  15.         }
  16.         public int getAge() {
  17.                 return age;
  18.         }
  19.         public void setAge(int age) {
  20.                 this.age = age;
  21.         }
  22.         @Override
  23.         public String toString() {
  24.                 return "Person [age=" + age + ", name=" + name + ", str1=" + str1
  25.                                 + ", str2=" + str2 + "]";
  26.         }
  27. }
复制代码
如果是调用set方法很简单,根据规则
  1.                 Method methodName = p.getClass().getMethod("setName",String.class);
  2.                 System.out.println(methodName.invoke(p, "张三"));
复制代码
这样就获取了,但是如果是get 方法呢,参数列表是空,怎么做的?
这样可以
  1.                 Method methodName = p.getClass().getMethod("getName",null);
  2.                 System.out.println(methodName.invoke(p,null));
复制代码
这样更好
  1.                 Method methodName = p.getClass().getMethod("getName");
  2.                 System.out.println(methodName.invoke(p));
复制代码



    示例:获取字节码文件对象的3种方式          Person.java
  1. package cn.itcast.bean;

  2. public class Person
  3. {
  4.         private int age;
  5.         private String name;

  6.     public Person(int age,String name){
  7.                 super();
  8.                 this.age = age;
  9.                 this.name = name;

  10.                 System.out.println("Person param run..." + this.name + ":" + this.age);
  11.         }

  12.         public Person(){
  13.                 super();

  14.                 System.out.println("person run");
  15.         }

  16.         public void show(){
  17.                 System.out.println(name + "...show run..." + age);
  18.         }

  19.         private void privateMethod(){
  20.                 System.out.println("method run");
  21.         }

  22.         public void paramMethod(String str,int num){
  23.                 System.out.println("paramMethod run..." + str + ":" + num);
  24.         }

  25.         public static void staticMethod(){
  26.                 System.out.println("static method run...");
  27.         }
  28. }
复制代码

    ReflectDemo.java
  1. import cn.itcast.bean.Person;

  2. //要想要对字节码文件进行解剖,必须要有字节码文件对象。
  3. public class ReflectDemo
  4. {
  5.         public static void main(String[] args) throws ClassNotFoundException {
  6.                 getClassObject_1();
  7.                 System.out.println("--------------------");
  8.                 getClassObject_2();
  9.                 System.out.println("--------------------");
  10.                 getClassObject_3();
  11.         }

  12.    /*
  13.         * 获取字节码对象的方式:
  14.         * 方式一:Object类中的getClass()方法的。
  15.         * 想要用这种方式,必须要明确具体的类,并创建对象。
  16.         * 麻烦。
  17.         */
  18.         public static void getClassObject_1(){
  19.                 
  20.                 Person p = new Person();
  21.                 Class clazz = p.getClass();

  22.                 Person p1 = new Person();
  23.                 Class clazz1 = p1.getClass();

  24.                 System.out.println(clazz == clazz1);
  25.         }

  26.         /*
  27.         * 方式二:任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。
  28.         * 相对简单,但是还是要明确用到类中的静态成员。
  29.         * 还是不够扩展。
  30.         */
  31.         public static void getClassObject_2(){
  32.                 
  33.                 Class clazz = Person.class;
  34.                 Class clazz1 = Person.class;

  35.                 System.out.println(clazz == clazz1);
  36.         }

  37.         /*
  38.         * 方式三:只要通过给定的类的字符串名称就可以获取该类,更为扩展。
  39.         * 可以用Class类中的方法完成。
  40.         * 该方法就是forName。
  41.         * 这种方法只要有名称即可,更为方便,扩展性更强。
  42.         */
  43.         public static void getClassObject_3() throws ClassNotFoundException {
  44.                 
  45.                 //可以把类的字符串名称写到配置文件中,然后读取出来。
  46.                 String className = "cn.itcast.bean.Person";
  47.                 Class clazz = Class.forName(className);

  48.                 System.out.println(clazz);
  49.         }
  50. }
复制代码
    运行结果:
person run
person run
true
--------------------
true
--------------------
class com.test.Person
黑马程序员——Java基础---反射
    示例:获取Class中的构造函数
  1. import cn.itcast.bean.Person;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.InvocationTargetException;

  4. public class ReflectDemo
  5. {
  6.         public static void main(String[] args) throws Exception {
  7.                 createNewObject_1();
  8.                 System.out.println("--------------------");
  9.                 createNewObject_2();
  10.         }

  11.         public static void createNewObject_1() throws ClassNotFoundException,InstantiationException,IllegalAccessException {
  12.                 //早期:new时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存,
  13.                 //并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象。
  14.                 //Person p = new Person();

  15.                 //现在:
  16.                 String name = "cn.itcast.bean.Person";
  17.                 //找寻该文件类文件,并加载进内存,并产生Class对象。
  18.                 Class clazz = Class.forName(name);
  19.                 //如何产生该类的对象呢?
  20.                 Object obj = clazz.newInstance();//调用Person的空参构造函数
  21.         }

  22.         public static void createNewObject_2() throws ClassNotFoundException,InstantiationException,NoSuchMethodException,IllegalAccessException,InvocationTargetException {

  23.                 //Person p = new Person("小强",39);

  24.                 /*
  25.                 * 当获取指定名称对应类中的所体现的对象时。
  26.                 * 而该对象初始化不使用空参数构造函数该怎么办呢?
  27.                 * 既然是通过指定的构造函数进行对象的初始化。
  28.                 * 所以应该先获取到该构造函数,通过字节码文件对象即可完成。
  29.                 * 该方法是:getConstructor(parameterTypes);
  30.                 */

  31.                 String name = "cn.itcast.bean.Person";
  32.                 //找寻该名称类文件,并加载进内存,并产生Class对象。
  33.                 Class clazz = Class.forName(name);
  34.                 //获取到了指定的构造函数对象
  35.                 Constructor constructor = clazz.getConstructor(int.class,String.class);
  36.                 //通过该构造器对象的newInstance方法进行对象的初始化。
  37.                 Object obj = constructor.newInstance(38,"小明");
  38.         }
  39. }
复制代码
    运行结果:
person run
--------------------
Person param run...小明:38
黑马程序员——Java基础---反射
    示例:获取Class的字段
  1. import cn.itcast.bean.Person;
  2. import java.lang.reflect.Field;

  3. public class ReflectDemo
  4. {
  5.         public static void main(String[] args) throws Exception {
  6.                 getFieldDemo();
  7.         }
  8.         
  9.         /*
  10.         * 获取字节码文件中的字段。
  11.         */
  12.         public static void getFieldDemo() throws Exception {

  13.                 Class clazz = Class.forName("cn.itcast.bean.Person");
  14.                 
  15.                 //getField只能获取所有可访问公共字段,private获取不到。
  16.                 //Field field = claszz.getField("age");

  17.                 //getDeclaredField可以获取到公共字段,也可以获取到私有字段。
  18.                 Field field = clazz.getDeclaredField("age");

  19.                 //对私有字段的访问取消权限检查,暴力访问。
  20.                 field.setAccessible(true);

  21.                 Object obj = clazz.newInstance();
  22.                 
  23.                 //为对象的属性赋值
  24.                 field.set(obj,89);
  25.                 
  26.                 //获取某对象的某属性值
  27.                 Object o = field.get(obj);

  28.                 System.out.println(field);
  29.         }
  30. }
复制代码
   运行结果:
person run
private int com.test.Person.age
黑马程序员——Java基础---反射 

   示例:获取Class中的方法
  1. import cn.itcast.bean.Person;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Constructor;

  4. public class ReflectDemo
  5. {
  6.         public static void main(String[] args) throws Exception {
  7.                 getMethodDemo_1();
  8.                 System.out.println("---------------------------");
  9.                 getMethodDemo_2();
  10.                 System.out.println("---------------------------");
  11.                 getMethodDemo_3();
  12.         }
  13.         
  14.         /*
  15.         * 获取指定Class中的公共函数。
  16.         */
  17.         public static void getMethodDemo_1() throws Exception {

  18.                 Class clazz = Class.forName("cn.itcast.bean.Person");
  19.                 
  20.                 Method[] methods = clazz.getMethods();//获取的都是公有的方法

  21.                 methods = clazz.getDeclaredMethods();//只获取本类中所有方法,包括私有。

  22.                 for(Method method : methods){
  23.                         System.out.println(method);
  24.                 }
  25.         }

  26.         public static void getMethodDemo_2() throws Exception {

  27.                 Class clazz = Class.forName("cn.itcast.bean.Person");
  28.                 
  29.                 Method method = clazz.getMethod("show",null);//获取空参数一般方法

  30.                 Object obj = clazz.newInstance();

  31.                 Constructor constructor = clazz.getConstructor(int.class,String.class);
  32.                 obj = constructor.newInstance(37,"小明");

  33.                 method.invoke(obj,null);
  34.         }

  35.         public static void getMethodDemo_3() throws Exception {

  36.                 Class clazz = Class.forName("cn.itcast.bean.Person");
  37.                 
  38.                 Method method = clazz.getMethod("paramMethod",String.class,int.class);//获取空参数一般方法

  39.                 Object obj = clazz.newInstance();

  40.                 Constructor constructor = clazz.getConstructor();
  41.                 obj = constructor.newInstance();

  42.                 method.invoke(obj,"小强",89);
  43.         }
  44. }
复制代码
    运行结果:
public void com.test.Person.show()
public void com.test.Person.paramMethod(java.lang.String,int)
private void com.test.Person.privateMethod()
public static void com.test.Person.staticMethod()
---------------------------
person run
Person param run...小明:37
小明...show run...37
---------------------------
person run
person run
paramMethod run...小强:89
黑马程序员——Java基础---反射
    练习:反射练习

    代码:


    PCI.java
  1. package cn.itcast.reflect.test;

  2. public interface PCI
  3. {
  4.         public void open();
  5.         public void close();
  6. }
复制代码

    SoundCard.java
  1. package cn.itcast.reflect.test;

  2. public class SoundCard implements PCI
  3. {
  4.         public void open(){
  5.                 System.out.println("sound open");
  6.         }

  7.         public void close(){
  8.                 System.out.println("sound close");
  9.         }
  10. }
复制代码

    NetCard.java
  1. package cn.itcast.reflect.test;

  2. public class NetCard implements PCI
  3. {
  4.         public void open(){
  5.                 System.out.println("net open");
  6.         }

  7.         public void close(){
  8.                 System.out.println("net close");
  9.         }
  10. }
复制代码

    Mainboard.java
  1. package cn.itcast.reflect.test;

  2. public class Mainboard
  3. {
  4.         public void run(){
  5.                 System.out.println("main board run...");
  6.         }

  7.         public void usePCI(PCI p){
  8.                 if(p != null){
  9.                         p.open();
  10.                         p.close();
  11.                 }
  12.         }
  13. }
复制代码
    
    pci.Properties
  1. pci1=cn.itcast.reflect.test.SoundCard
  2. pci2=cn.itcast.reflect.test.NetCard
复制代码

    ReflectTest.java
  1. package cn.itcast.reflect.test;

  2. import java.io.File;
  3. import java.io.FileInputStream;

  4. import java.util.Properties;

  5. /*
  6. * 电脑运行
  7. */
  8. public class ReflectTest
  9. {
  10.         public static void main(String[] args) throws Exception {
  11.                 
  12.                 Mainboard mb = new Mainboard();
  13.         
  14.                 mb.run();
  15.                 //每次添加一个设备都需要修改代码传递一个新创建的对象
  16.                 //mb.usePCI(new SoundCard());
  17.                 //能不能不修改代码就可以完成这个动作
  18.                 //不用new完成,而是只获取其class文件,在内部实现创建对象的动作。
  19.                 
  20.                 File configFile = new File("pci.properties");

  21.                 Properties prop = new Properties();
  22.                 FileInputStream fis = new FileInputStream(configFile);

  23.                 prop.load(fis);

  24.                 for(int x = 0; x < prop.size(); x++){

  25.                         String pciName = prop.getProperty("pci" + (x + 1));

  26.                         Class clazz = Class.forName(pciName);//用Class去加载这个pci子类

  27.                         PCI p = (PCI)clazz.newInstance();

  28.                         mb.usePCI(p);
  29.                 }

  30.                 fis.close();
  31.         }
  32. }
复制代码