第12章 反射

12.1 反射概述

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以得到任意一个对象所属的类的信息,可以调用任意一个类的成员变量和方法,可以获取任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。

反射机制的优点是可以实现动态创建对象和编译(即动态编译),特别是在J2EE的开发中,反射的灵活性表现的十分明显。例如,一个大型的软件,不可能一次就把程序设计的很完美,当这个程序编译、发布上线后,如果需要更新某些功能,我们不可能要用户把以前的软件卸载,再重新安装新的版本。这时,如果采用静态编译,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制,程序可以在运行时动态的创建和编译对象,不需要用户重新安装软件,即可实现功能的更新。

12.2 认识Class类

Java程序的运行机制,JVM编译.java文件生成对应的.class文件,然后再将.class文件加载到内存中执行。在执行.class文件的时候可能需要用到其他类(其他.class文件内容),这个时候就需要获取其他类的信息(反射)。JVM在加载.class文件时,会产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的信息。因此要想完成反射操作,就必须先认识Class类。

Class是JDK定义的类,它提供了很多方法,通过调用Class类的成员方法可以获取Class对象中的信息(.class文件中的类信息)。Class类的常用方法如下表。

方法描述
public static Class<?> forName(String className) throws ClassNotFoundException传入完整的“包.类”名称实例化Class对象
public Constructor[] getConstructors() throws SecurityException得到一个类中的全部构造方法
public Field[] getDeclaredFields() throws SecurityException得到本类中单独定义的全部属性
public Field[] getFields() throws SecurityException取得本类继承而来的全部属性
方法描述
public Method[] getMethods() throws SecurityException得到一个类中的全部方法
public Method getMethod(String name,Class...parameter Type)throws NoSuchMethodException,SecurityException返回一个Method对象,并设置一个方法中的所有参数类型
public Class[] getInterfaces()得到一个类中所实现的全部接口
public String getName()得到一个类完整的“包.类”名称
public Package getPackage()得到一个类的包
public Class getSuperclass()得到一个类的父类
public Object newInstance() throws InstantiationException,IllegalAccessException根据Class定义的类实例化对象
public Class<?> getComponentType()返回表示数组类型的Class
public boolean isArray()判断此Class是否是一个数组

由于Class对象代表的是.class文件(类),因此可以说所有的类实际上都是Class类的实例,所有的对象都可以转变为Class类型表示。

实例化Class对象共有以下三种方式:

(1)根据类名获取:类名.class;

(2)根据对象获取:对象.getClass();

(3)根据全限定类名获取:Class.forName(“全限定类名”)。

Class类本身没有定义任何的构造方法,所以如果要使用Class类,必须通过上述三种方式进行实例化。接下来,通过一个案例演示Class类的上述三种实例化方式。

class A{
}
class Example01 {
 public static void main(String args[]){
      Class<?> c1 = null;
      Class<?> c2 = null;
      Class<?> c3 = null;
     try{ 
         c1 = Class.forName("cn.itcast.A");
     }catch(ClassNotFoundException e){
          e.printStackTrace();
      }
      c2 = new A().getClass();
      c3 = A.class;
      System.out.println("类名称:"+c1.getName());
      System.out.println("类名称:"+c2.getName());
      System.out.println("类名称:"+c3.getName());
  }
}

上述代码中,第9行代码使用forName()方法实例化Class对象c1,第13行代码使用对象.getClass()的方式实例化Class对象c2,第14行代码使用类名.class的方式实例化Class对象c3。从图12-3的运行结果可以发现,3种实例化Class对象的结果是一样的,但是使用forName()方法实例化Class对象只需要将类的全限定类名以字符串作为参数传入即可,这让程序具备了更大的灵活性,所以使用forName()方法实例化Class对象是较为常用的一种方式,读者应重点掌握。

12.3 Class类的使用

了解了Class类的实例化过程,那么到底该如何去使用Class类呢?实际上Class在开发中最常见的用法就是将Class类对象实例化为自定义类对象,即可以通过一个给定的字符串(类的全限定类名)实例化一个本类的对象。将Class对象实例化为本类对象时,可以通过无参构造完成,也可以通过有参构造完成。

12.3.1 通过无参构造函数实例化对象

如果想通过Class类实例化其他类的对象,则可以使用newInstance()方法,但是必须要保证被实例化的类中存在一个无参构造方法。接下来通过一个案例演示Class类通过无参构造实例化对象。

class Person{
   private String name;
    private int age;
    public String getName() {
        return name;
   }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
   public void setAge(int age) {
       this.age = age;
   }
    public String toString() {
        return "姓名:"+this.name+",年龄:"+this.age;
    }
}
class Example02 {
  public static void main(String args[]){
      Class<?> c = null; 
     try{
          c = Class.forName("cn.itcast.Person");
      }catch(ClassNotFoundException e){
         e.printStackTrace();
      }
     Person per = null;
      try{
        per = (Person)c.newInstance();
      }catch (Exception e){
         e.printStackTrace();
      }
      per.setName("张三");
      per.setAge(30);
      System.out.println(per);
  }
 }

上述代码中,第1~19行代码创建了一个Person类,在Person类中定义了name和age属性。第24行代码是通过Class.forName()方法实例化Class对象,第30行代码是使用Class对象c调用newInstance()方法并传入的完整“包.类”名称,对Person对象进行实例化操作。

在使用newInstance()方法实例化类对象时,被实例化对象的类中必须存在无参构造方法,否则无法实例化对象。

接下来演示没有无参构造方法时,通过newInstance()方法实例化对象。

class Person{
    private String name;
    private int age;
    public Person(String name,int age){			//定义有参构造方法
         this.setName(name);
         this.setAge(age);
    }
    public String getName() {
        return name;
   }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
   public void setAge(int age) {
       this.age = age;
   }
    public String toString() {
        return "姓名:"+this.name+",年龄:"+this.age;
    }
}
class Example03 {
  public static void main(String args[]){
      Class<?> c = null; 
     try{
          c = Class.forName("cn.itcast.Person");
      }catch(ClassNotFoundException e){
         e.printStackTrace();
      }
     Person per = null;
      try{
        per = (Person)c.newInstance();
      }catch (Exception e){
         e.printStackTrace();
      }
  }
}

因为Person类中并没有存在无参构造方法,所以第35行代码对Person对象进行实例化时,无法直接使用newInstance()方法实例化的。由运行结果可知,报错信息提示Person类中没有发现无参构造方法,无法使用newInstance()方法实例化Person对象。因此,在使用Class类实例化对象时一定要在类中编写无参构造方法。

12.3.2 通过有参构造实例化对象

如果类中没有无参构造方法,则可以通过有参构造方法实例化对象。通过有参构造方法实例化对象时,需要明确调用的构造方法,并传递相应的参数。通过有参构造方法实例化对象的操作步骤如下:

(1)通过Class类中的getConstructors()方法获取本类中的全部构造方法。

(2) 向构造方法中传递一个对象数组,对象数组里面包含构造方法中所需的各个参数。

(3)通过Constructor类实例化对象。

上述操作步骤中使用了Constructor类,Constructor类用于存储本类的构造方法。Constructor类的常用方法如下表。

方法描述
publicint getModifiers()得到构造方法的修饰符
public String getName()得到构造方法的名称
public Class<?>[] getParameterTypes()得到构造方法中参数的类型
public String toString()返回此构造方法的信息
public T newInstance(Object...initargs) throws InstantistionException, IllegalAccessException,IllegalArgumentException, InvocationTargetException向构造方法中传递参数,实例化对象
import java.lang.reflect.Constructor;
class Person{
    private String name;
    private int age;
    public Person(String name,int age){
         this.setName(name);
         this.setAge(age);
    }
    public String getName() {
        return name;
   }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
   public void setAge(int age) {
       this.age = age;
   }
    public String toString() {
        return "姓名:"+this.name+",年龄:"+this.age;
    }
}
class Example04 {
  public static void main(String args[]){
      Class<?> c = null; 
     try{
          c = Class.forName("cn.itcast.Person");
      }catch(ClassNotFoundException e){
         e.printStackTrace();
      }
     Person per = null;
     Constructor<?> cons[] = null;
     cons = c.getConstructors();
      try{
        per = (Person)cons[0].newInstance("张三",30);
      }catch (Exception e){
         e.printStackTrace();
      }
     System.out.println(per);
  }
}

上述代码中,第5~8行代码定义了Person类的有参构造方法。第34~35行代码通过Class类取得了Person类中全部构造方法,并以对象数组的形式返回。第27行代码调用了Person类中的构造方法,而在Person类中只有一个构造方法,所以直接取出对象数组中的第一个元素即可(下表为0就表示调用第一个构造方法)。在声明对象数组时,必须考虑到构造方法中参数的类型顺序,所以第一个参数的类型为String,第二个参数的类型为Integer。

12.4 反射的作用

在实际开发中,通过反射可以得到一个类的完整结构,包括类的构造方法、类的属性、类的方法,这就需要使用到java.lang.reflect包中的以下几个类:

(1)Constructor:表示类中的构造方法。

(2)Field:表示类中的属性。

(3)Method:表示类中的方法。

Constructor、Field、Method都是AccessibleObject类的子类。

12.4.1 获取所实现的全部接口

要取得一个类所实现的全部接口,必须使用Class中的getInterfaces()方法。getInterfaces()方法声明如下:

public Class[] getInterfaces();

getInterfaces()方法返回一个Class类的对象数组,调用Class类中的getName()方法可以取得类的名称。

接下来通过一个案例讲解通过getInterfaces()方法获取一个类所实现的全部接口。

interface China{
   public static final String NATION = "CHINA";
   public static final String AUTHOR = "张三";
}
class Person implements China{
    private String name;
    private int age;
    public Person(String name,int age){
         this.setName(name);
         this.setAge(age);
    }
    public String getName() {
        return name;
   }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
   public void setAge(int age) {
       this.age = age;
   }
    public String toString() {
        return "姓名:"+this.name+",年龄:"+this.age;
    }
}
public class Example05 {
  public static void main(String args[]){
     Class<?> c = null;
try{
   c = Class.forName("cn.itcast.Person");
}catch(ClassNotFoundException e){
    e.printStackTrace();
}
Class<?> cons[] = c.getInterfaces();
for (int i = 0;i < cons.length; i++){
    System.out.println("实现的接口名称:"+ cons[i].getName());
}
}
}

上述代码中,第1~4行代码定义了一个China接口,第5~27行代码定义了一个Person类并实现了China接口。因为接口是类的特殊形式,而且一个类可以实现多个接口,所以,第36~39行代码以Class数组的形式将全部的接口对象返回,并利用循环的方式将内容依次输出。由图12-6可知,Person类实现了China接口。

12.4.2 获取全部方法

要取得一个类中的全部方法,可以使用Class类中的getMethods()方法,该方法返回一个Method类的对象数组。如果想要进一步取得方法的具体信息,如方法的参数、抛出的异常声明等,就必须依靠Method类。

Method类的常用方法如下表。

方法描述
public int getModifiers()得到本方法的修饰符
public String getName()得到方法的名称
public Class<?>[] getParameterTypes()得到方法的全部参数的类型
public Class<?> getReturnType()得到方法的返回值类型
public Class<?>[] getExceptionType()得到一个方法的全部抛出异常
public T newInstance(Object...initargs) throws InstantistionException, IllegalAccessException,IllegalArgumentException, InvocationTargetException通过反射调用类中的方法
interface China{
   public static final String NATION = "CHINA";
   public static final String AUTHOR = "张三";
   public void sayChina();
}
class Person{
    private String name;
    private int age;
    public Person(String name,int age){
         this.setName(name);
         this.setAge(age);
    }
    public String getName() {
        return name;
   }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
   public void setAge(int age) {
       this.age = age;
   }
    public String toString() {
        return "姓名:"+this.name+",年龄:"+this.age;
    }
}
class Example06{
  public static void main(String args[]){
     Class<?> c = null;
try{
   c = Class.forName("cn.itcast.Person");
}catch(ClassNotFoundException e){
    e.printStackTrace();
}
Class<?> cons[] = c.getInterfaces();
for (int i = 0;i < cons.length; i++){
    System.out.println("实现的接口名称:"+ cons[i].getName());
}
}
}

上述代码中,第1~5行代码首先定义了一个China接口,并在China接口中定义了两个final修饰的String属性和sayChina()方法。第6~28行代码定义了一个Person类。最后在30~41行的main()方法中定义了一个Class的对象,通过Class对象调用forName()方法获取了“cn.itcast.Person”的所有方法。并定义了一个名称为cons[]的Class集合,用于存储Class的所有接口,最后使用for循还打印consp[]集合。

从运行结果可以发现,程序不仅将Person类的方法输出,也把从Object类中继承而来的方法同样进行了输出。

12.4.3 获取全部属性

在反射操作中也可以获取一个类中的全部属性,但是类中的属性包括两部分,从父类继承的属性,本类定义的属性。因此,在获取类的属性时也有两种不同的方式,分别如下:

(1)获取实现的接口或父类中的公共属性:public Field[] getFields throws SecurityException。

(2)获取本类中的全部属性:public Field[] getDeclaredFields throws SecurityException。

上述两种方法返回的都是Field数组,每一个Field对象表示类中的一个属性。如果要获取属性的详细信息,就需要调用Field类的方法。Field类的常用方法如下表。

方法描述
public int getModifiers()得到本方法的修饰符
public String getName()得到方法的名称
public boolean isAccessible()判断此属性是否可被外部访问
public void setAccessible(Boolean flag)throws SecurityException设置一个属性是否可被外部访问
public String toString()返回此Filed类的信息
public Object get(Object obj)throws IllegalArgument Exception,IllegalAccessException得到一个对象中属性的具体内容
public void set(Object obj, Object value)throws IllegalArgument Exception,IllegalAccessException设置指定对象中属性的具体内容
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class Person{
    private String name;
    private int age;
    public Person(String name,int age){
        this.setName(name);
        this.setAge(age);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String toString() {
        return "姓名:"+this.name+",年龄:"+this.age;
    }
}
public class Example07{
    public static void main(String[] args){
        Class<?> c1 = null;
        try{
            c1 = Class.forName("cn.itcast.Person");
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        {
            Field f[] = c1.getDeclaredFields();       //取得本类属性
            for (int i = 0;i<f.length;i++){            //循环输出
                Class<?> r = f[i].getType();           //取得属性的类型
                int mo = f[i].getModifiers();          //得到修饰符数字
                String priv = Modifier.toString(mo);  //取得属性的修饰符
                System.out.print("本类属性:");
                System.out.print(priv+" ");             //输出修饰符
                System.out.print(r.getName()+" ");     //输出属性类型
                System.out.print(f[i].getName());      //输出属性名称
                System.out.println(" ;");
            }
        }
    }
}

上述代码中,第3~25行代码定义了一个Person类,并在Person类中定义了name和age属性。第30行代码实例化了一个Class对象c1;第35行代码通过调用Class类的getDeclaredFields()方法获取Person类的所有属性,并存入Filed数组中。第36~45行代码通过for循环输出Filed数组中Person类的属性。

【案例12-1】 重写toString()方法

为了方便输出对象,Object类提供了toString()方法。但是该方法的默认值是由类名和哈希码组成的,实用性并不强。通常需要重写该方法以提供更多的对象信息。

本案例要求使用反射重写类的toString()方法,并通过反射输出类的包名、类名、类的公共构造方法、类的公共域和类的公共方法。

(1)通过任务的描述可知,此程序需要利用反射重写toString()方法,因此,需要先创建一个类,并在该类中定义两个方法,一个是toString()方法,用于输出类的包、类的名字、类的公共构造方法、类的公共域和类的公共方法等信息;另一个是main()方法,用来进行测试。

(2)由于是重写Object类的toString()方法,因此需要给toString()方法传递一个Object对象。

(3)由于需要利用反射输出类的包、类的名字、类的公共构造方法、类的公共域和类的公共方法,故需要先通过Object对象.getClass()获得代表该类的Class对象,再通过类的Class对象. getPackage()获得类所在的包,通过类的Class对象.getSimpleName()获得类的简单名称,通过类的Class对象.getDeclaredConstructors()获得所有代表构造方法的Constructor数组,遍历数组,判断如果是访问控制符为“public”即为公共构造方法。通过类的Class对象.getDeclaredFields()获得代表所有域的Field数组,遍历数组,判断如果是访问控制符为“public”即为公共域。通过类的Class对象.getDeclaredMethods()获得代表所有方法的Method[]数组,遍历数组,判断如果是访问控制符为“public”即为公方法。

 1 	import java.lang.reflect.Constructor;
 2 	import java.lang.reflect.Field;
 3 	import java.lang.reflect.Method;
 4 	import java.lang.reflect.Modifier;
 5 	public class StringUtils {
 6 	    @SuppressWarnings("unchecked")
 7 	    public String toString(Object object) {
 8 	        // 获得代表该类的Class对象
 9 	        Class clazz = object.getClass();
 10 	        // 利用StringBuilder来保存字符串
 11 	        StringBuilder sb = new StringBuilder();
 12 	        // 获得类所在的包
 13 	        Package packageName = clazz.getPackage();  
 14 	        // 输出类所在的包
 15 	        sb.append("包名:" + packageName.getName() + "\t"); 
 16 	        String className = clazz.getSimpleName(); // 获得类的简单名称
 17 	        sb.append("类名:" + className + "\n"); // 输出类的简单名称
 18 	        sb.append("公共构造方法:\n");
 19 	        // 获得所有代表构造方法的Constructor数组
 20 	        Constructor[] constructors = clazz.getDeclaredConstructors();
 21 	        for (Constructor constructor : constructors) {
 22 	            String modifier = 
 23 	        Modifier.toString(constructor.getModifiers());// 获得方法修饰符
 24 	            if (modifier.contains("public")) {// 查看修饰符是否含“public”
 25 	                sb.append(constructor.toGenericString() + "\n");
 26 	            }
 27 	        }
 28 	        sb.append("公共域:\n");
 29 	        // 获得代表所有域的Field数组
 30 	        Field[] fields = clazz.getDeclaredFields();
 31 	        for (Field field : fields) {
 32 	            String modifier = Modifier.toString(field.getModifiers());
 33 	            if (modifier.contains("public")) {// 查看修饰符是否含“public”
 34 	                sb.append(field.toGenericString() + "\n");
 35 	            }
 36 	        }
 37 	        sb.append("公共方法:\n");
 38 	        // 获得代表所有方法的Method[]数组
 39 	        Method[] methods = clazz.getDeclaredMethods();
 40 	        for (Method method : methods) {
 41 	            String modifier = Modifier.toString(method.getModifiers());
 42 	        // 查看修饰符是否含有“public”
 43 	        if (modifier.contains("public")) {
 44 	                sb.append(method.toGenericString() + "\n");
 45 	            }
 46 	        }
 47 	        return sb.toString();
 48 	    }
 49 	   public static void main(String[] args) {
 50 	        System.out.println(new StringUtils().toString(new Object()));
 51 	    }
 52 	}

【案例12-2】 速度计算

本案例要求使用反射技术编写一个速度计算程序,计算某种交通工具的行驶速度。现有两种工具:Bike和 Plane,其中Bike的速度运算公式为:A*B/C,Plane的速度运算公式为:A+B+C。

用户可通过输入交通工具名称选择自己想要使用的交通工具,选择交通工具之后,自动计算出该交通工具的行驶速度。此外,在未来如果增加第3种交通工具的时候,不必修改以前的任何程序,只需要编写新的交通工具的程序即可。

(1)通过任务描述可知,有两种交通工具Plane和Bike:Plane类、Bike类。

(2)由于任务要求在未来如果增加第3种交通工具的时候,不必修改以前的任何程序,只需要编写新的交通工具的程序,故还需要编写一个接口Common,且Plane类和Bike类都继承Common接口。

(3)最后编写一个测试类CaculateSpeed,在main()方法中,编写程序,提示用户输入自己想要使用的交通工具,并利用反射来计算交通工具的速度。

 1 	public interface Common {
 2 		 double getSpeed(double a,double b,double c);
 3 	}
 4 	public class Bike implements Common {
 5 		 @Override
 6 		 public double getSpeed(double a, double b, double c) {
 7 		      return a*b/c;
 8 		 }
 9 	}
 1 	public class Plane implements Common {
 2 		 @Override
 3 		 public double getSpeed(double a, double b, double c) {
 4 		      return a+b+c;
 5 		 }
 6 	}
 1 	import java.util.Scanner;
 2 	public class CaculateSpeed {
 3 	     public static void main(String[] args){
 4 	    	Scanner in = new Scanner(System.in);
 5 	    	System.out.println("请输入您要使用的交通工具名称:");
 6 	    	String choice =in.nextLine();
 7 	        String transport = "fanshe."+choice;        //
 8 	        double a = 23, b = 24, c = 25;
 9 	        try {
 10 	            Common newtransport = (Common)
 11 	                             Class.forName(transport).newInstance();
 12 	            System.out.println(choice+" speed is : 
 13 	                             "+newtransport.getSpeed(a,b,c));
 14 	        } catch (InstantiationException e) {
 15 	            e.printStackTrace();
 16 	        } catch (IllegalAccessException e) {
 17 	            e.printStackTrace();
 18 	        } catch (ClassNotFoundException e) {
 19 	            e.printStackTrace();
 20 	        }
 21 	    }
 22 	}

【案例12-3】:利用反射实现通过读取配置文件对类进行实例化

现在有一个项目,项目中创建了一个Person类,在Person类中定义了一个sleep()方法。在工程中还定义了一个Student类继承Person类,在Student类中重写了Person类的sleep()方法。项目有一个配置文件,名称为test.properties,在配置文件中配置了一个className属性和一个methodName属性,className属性值是类的全限定类名,methodName属性值是方法名。

本案例要求通过读取配置文件对类进行实例化,具体如下:

(1)获取test.properties配置文件中的className属性值(类的全限定类名),利用反射对该类进行实例化。

(2)获取test.properties配置文件中的methodName属性值(方法名),利用反射获取对象方法,并执行该方法。

(1)通过任务描述可知,需要先在工程的根目录下创建一个test.properties文件,在配置文件中配置一个className属性和一个methodName属性,className属性值是类的全限定类名,methodName属性值是方法名。

(2)然后创建两个类:Person类和Student类且Student类继承Person类。在Person类中编写一个sleep()方法,在Student类中重写Person类的sleep()方法;

(3)最后编写一个测试类ReflexTest,在main()方法中,编写程序,具体步骤描述如下:

1.利用反射加载配置文件

2.获取配置文件中的数据,获取类的全路径名及方法名

3.根据获取的类的全路径名,利用反射将该类加载进内存

4.创建该类对象

5.根据在配置文件中获取的方法名获取对象方法

6.执行方法

test.properties

 1 	className = fanshe.Person
 2 	methodName = sleep
 1 	public class Person {
 2 		public void sleep() {
 3 			System.out.println("sleep......");
 4 		}
 5 	}
 1 	public class Student extends Person{
 2 	  @Override
 3 	  public void sleep() {
 4 	     super.sleep();
 5 	     System.out.println("呼噜呼噜~~~");
 6 	  }
 7 	  public void s1() {
 8 	     super.sleep();
 9 	     System.out.println("hello");
 10 	}
 11 	}
 1 	import java.io.IOException;
 2 	import java.lang.reflect.InvocationTargetException;
 3 	import java.lang.reflect.Method;
 4 	import java.util.Properties;
 5 	public class ReflexTest {
 6 	    public static void  main(String[] args) throws IOException, 
 7 	ClassNotFoundException, InstantiationException, IllegalAccessException,
 8 	NoSuchMethodException, SecurityException, IllegalArgumentException, 
 9 	InvocationTargetException {
 10 	        /*1、加载配置文件
 11 	         * 用类名.class.getResourceAsStream("/xx")或者
 12 	         * 类名.class.getClassLoader().getResourceAsStream("xx");
 13 	         * 区别在于前者是需要反斜杠,后者不需要
 14 	       * */
 15 	        Properties properties = new Properties();        
 16 	properties.load(RelectTestMain.class.getResourceAsStream("/test.prope
 17 	rties"));
 18 	        //2、获取配置文件中定义的数据
 19 	        String className = properties.getProperty("className");
 20 	        String methodName = properties.getProperty("methodName");
 21 	        //3、加载该类进内存
 22 	        Class cls = Class.forName(className);
 23 	        //4、创建类对象
 24 	        Object obj = cls.newInstance();
 25 	        //5、获取对象方法
 26 	        Method method = cls.getMethod(methodName);
 27 	        //6、执行方法
 28 	        method.invoke(obj);
 29 	    }
 30 	}

只需要修改test.properties配置文件即可。

test.properties

 1 	sclassName = fanshe.Student
 2 	methodName = s1

12.4 本章小结

本章主要介绍了Java的反射机制。首先简单介绍了反射机制;然后介绍了Class类和Class类的应用;最后介绍了反射的应用,包括获取接口、获取方法、获取属性。通过本章的学习,读者对Java的反射会有一定的了解,掌握好这些知识,对以后的实际开发大有裨益。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/392420.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

函数、极限、连续——刷题(6

目录 1.题目&#xff1a;2.解题思路和步骤&#xff1a;3.总结&#xff1a;小结&#xff1a; 1.题目&#xff1a; 2.解题思路和步骤&#xff1a; 相减为0的情况一定要去试一试平方差&#xff0c;不管多复杂&#xff0c;平方差能够将相减为0转化为直接代入 这个题的妙处就在于用…

【开源】SpringBoot框架开发智能教学资源库系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 课程档案模块2.3 课程资源模块2.4 课程作业模块2.5 课程评价模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 课程档案表3.2.2 课程资源表3.2.3 课程作业表3.2.4 课程评价表 四、系统展示五、核心代…

DS:八大排序之堆排序、冒泡排序、快速排序

创作不易&#xff0c;友友们给个三连吧&#xff01;&#xff01; 一、堆排序 堆排序已经在博主关于堆的实现过程中详细的讲过了&#xff0c;大家可以直接去看&#xff0c;很详细,这边不介绍了 DS&#xff1a;二叉树的顺序结构及堆的实现-CSDN博客 直接上代码&#xff1a; …

2023我患上了AI焦虑

2023我患上了AI焦虑 来自&#xff1a;宝玉 原文链接&#xff1a;https://baoyu.io/blog/ai/i-am-suffering-from-ai-anxiety-in-2023 2023 年对我来说是神奇的一年&#xff0c;我意外的从一个程序员变成了一个 AI 资讯届的“网红”&#xff0c;到年底的时候我在 X 平台的阅读量…

SHERlocked93 的 2023 年终总结

工作之后感觉一年一年过的太快&#xff0c;没有个记录连回忆都无从回忆起&#xff0c;之前的年终总结&#xff1a; SHERlocked93 的 2022 年终总结SHERlocked93 的 2021 年终总结SHERlocked93 的 2020 年终总结SHERlocked93 的 2019 年终总结SHERlocked93 的 2018 年终总结SHER…

相机图像质量研究(31)常见问题总结:图像处理对成像的影响--图像差

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

Wheeltec小车的开发实录(3)之 wheeltec小车中配置自己的全局优化算法

我一贯的学习路子就先模仿后创造 所以我找到了哔哩哔哩上的一个up写好的算法放到我的小车中 ros的官方教程链接是&#xff1a; navigation/Tutorials/Writing a Local Path Planner As Plugin in ROS - ROS Wiki 首先去up的github上下载插件 Grizi-ju (xiaoju) GitHub 下…

anaconda安装路径默认在D盘,但安装环境的envs路径跑到C盘,修改为D盘

安装的anaconda环境&#xff0c;路径是在anaconda安装目录下的envs中&#xff08;D:\APPFile\Anaconda3\envs&#xff09;&#xff0c;然而&#xff0c;这次创建的却是在 C:\Users\xxx.conda\envs 中。 首先&#xff0c;找到用户目录下的.condarc文件&#xff08;C:\Users\use…

市场复盘总结 20240208

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 25% 最常用的…

如何用Qt实现一个无标题栏、半透明、置顶(悬浮)的窗口

在Qt框架中&#xff0c;要实现一个无标题栏、半透明、置顶&#xff08;悬浮&#xff09;的窗口&#xff0c;需要一些特定的设置和技巧。废话不多说&#xff0c;下面我将以DrawClient软件为例&#xff0c;介绍一下实现这种效果的四个要点。 要点一&#xff1a;移除标题栏&#…

Spring 事务原理总结六

不知不觉&#xff0c;关于Spring事务的文章已经写了五篇了。老实讲我自己不断质疑过自己&#xff1a;现在写这些文章还有意义吗&#xff1f;当前的市场已经成什么样了&#xff0c;为什么还要固守这落后的技术&#xff1f;但是贝索斯一次接受访谈的回答&#xff0c;让我写下去的…

【大厂AI课学习笔记】【2.1 人工智能项目开发规划与目标】(1)发现与明确问题

抱歉&#xff0c;过春节这几天&#xff0c;没有更新。赶紧续上。 人就是这样&#xff0c;放假之前呢&#xff0c;想着趁着这个假期&#xff0c;把很多之前没有做好的事情&#xff0c;都梳理好&#xff0c;该补的也补上&#xff0c;结果一个假期就这样过去了&#xff0c;很多想…

MATLAB知识点:uniquetol函数(★★☆☆☆)考虑了一定的容差的unique函数

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

Nginx高级课程扩容(四)

Brotli 安装 ● 官网 ● https://github.com/google/ngx_brotli ● https://codeload.github.com/google/brotli/tar.gz/refs/tags/v1.0.9 ● 下载 两个项目 ● 解压缩模块化编译 ./configure --with-compat --add-dynamic-module/root/ngx_brotli-1.0.0rc --prefix/usr/local…

Open CASCADE学习|布尔运算后消除内部拓扑

在CAD建模中&#xff0c;布尔运算是一种逻辑运算方法&#xff0c;通过这种方法&#xff0c;可以创建、修改或组合几何对象。布尔运算主要包括并集&#xff08;UNION&#xff09;、交集&#xff08;INTERSECT&#xff09;和差集&#xff08;SUBTRACT&#xff09;三种运算。 并集…

数学实验第三版(主编:李继成 赵小艳)课后练习答案(十三)(2)

实验十三&#xff1a;数据拟合与数据差值 练习二 1.在飞机的机翼加工时,由于机翼的尺寸很大,所以通常在图纸上只能标出部分关键点的尺寸某型号飞机的机翼上缘轮廓线的部分数据如下: x 0 4.74 9.05 19 38 76 95 114 133 152 171 190 y 0 5.23 8.1 11.97 16.…

《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)

文章目录 10.1 构建微服务架构 - 探索 Go 语言的微观世界10.1.1 基础知识讲解10.1.2 重点案例&#xff1a;订单处理系统订单服务测试服务 10.1.3 拓展案例 1&#xff1a;用户认证服务安装所需的包实现用户模型和存储实现 JWT 生成和验证实现认证服务测试服务 10.1.4 拓展案例 2…

如何清除谷歌浏览器的缓存?这里有详细步骤

如果你想解决加载或格式化问题&#xff0c;以改善你在谷歌Chrome上的浏览体验&#xff0c;那么清除缓存和cookie是一个很好的开始。以下是删除它们的方式和操作。 删除缓存和cookie时会发生什么 当你访问一个网站时&#xff0c;它有时会保存&#xff08;或记住&#xff09;某…

数据结构第十六天(二叉树层序遍历/广度优先搜索(BFS)/队列使用)

目录 前言 概述 接口 源码 测试函数 运行结果 往期精彩内容 前言 从前的日色变得慢&#xff0c;车&#xff0c;马&#xff0c;邮件都慢&#xff0c;一生,只够爱一个人。 概述 二叉树的层序遍历可以使用广度优先搜索&#xff08;BFS&#xff09;来实现。具体步骤如下&…

第11章 GUI

11.1 Swing概述 Swing是Java语言开发图形化界面的一个工具包。它以抽象窗口工具包&#xff08;AWT&#xff09;为基础&#xff0c;使跨平台应用程序可以使用可插拔的外观风格。Swing拥有丰富的库和组件&#xff0c;使用非常灵活&#xff0c;开发人员只用很少的代码就可以创建出…