反射机制

反射可以实现在运行时可以知道任意一个类的属性和方法.

类一旦加载进内存,就会变成Class对象(字节码对象)

 

元数据:描述输的的描述数据.

      

反射:得到类的元数据的过程.

在运行时期,动态的去获取某一个类中的成员信息.(构造器/属性/方法/接口/父类等等)

并且把类中的每一种成员都描述成一个新的类

 

Class:                表示所有的类

Constructor:   表示所有的构造方法

Method:          表示所有的方法

Field:                表示所有的字段

 

Class类是设计为泛型,所以Class类可以提供任一类的class文件

例如:

Java.util.Date;       使用Class类表示 class Class<Java.util.Date>;

java.lang.Byte;       使用Class类表示 class Class<java.lang.Byte>;

 

如何获取Class对象

下面举例获取 java.lang.String 的字节码对象

上述是三种获取Class对的方式,基本数据类型不能表示为对象,也就不能使用getClass的方式,基本数据类型没有类名的概念,也不能使用Class.forName的方式,所以:

表示基本数据类型的字节码对象 所有的数据类型都有Class属性

Class  对象名 = 数据类型.class

九大内置Class都使用以上方式创建字节码对象:

byte          short       int   long                 float                  double   char    boolean

数组创建字节码对象的方式

在Object类中说明数组也属于对象,可以调用Object类中的方法.所以创建数组的字节码对象的方式有两种:

1):    Class 对象名 = 数组类型[].class;

2):    Class 对象名 = 数组名.getClass();

注意:每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

所以:同数据类型,同维数的数组的Class对象是相等的.不区分数组内元素的个数,值.

Class:描述所有的类/类型,所以Class类中应该具有所有类型的相同方法

Object:描述所有的对象,所以在Object类中应该具有所有对象的共同的方法.

通过反射来获取某一个类的构造器:

1):获取该类的字节码对象

常规类获取该类的字节码对象有以上的三种方式

2):获取该类的构造器

①: Constructor<?> [] getConstructors();返回表示的类的所有公共构造方法,存放在Constructor数组中。

②: Constructor<?>[] getDeclaredConstructors();返回该类声明的所有构造方法。 存放在Constructor数组中。

使用获取的构造器创建对象

步骤:

1):线找到构造器所在类的字节码对象

2):获取构造器对象

3):使用反射创建对象

对于无参数外界可以直接访问的构造方法推荐使用该类中的该方法:

public T newInstance(Object... initargs);获取该类的构造器对象

对于有参数,则必须要获取该类有参的构造方法,然后在通过Constructor类中的newInstance加参数创建,私有的则需要设置当前的构造器可以被访问才能创建对象.

使用反射来操作类中的方法

步骤:

1):线找到构造器所在类的字节码对象

2):获取构造器对象

3):使用反射创建对象

4):通过对象操作类中的方法

下列第一二种方法获取的方法列表是无序的

Method[] getMethods();        获取该类及其父类和接口的所有公共的方法  

Method[] getDeclaredMethods(); 获取本类的所有的方法. 

Method getDeclaredMethod(形参数据类型.class) ;获取该类指定的方法

获取私有的方法则需要设置当前的方法可以被访问才能创建对象.

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;


class Student {
public Student() {
System.out.println("无参数的公共的构造器方法......");
}

public Student(int a) {
System.out.println("有参数的公共的构造器方法......");
}

private Student(String a) {
System.out.println("有参数的私有的构造器方法......");
}

protected Student(int a, int b) {
System.out.println("受保护的构造器方法......");
}
public void show(){
System.out.println("无参公共的方法");
}
public static void show(int...arr){
System.out.println("int类型数组的参数被调用了,值为:" + Arrays.toString(arr));
}
public static void show(String...arr){
System.out.println("String类型数组的参数被调用了,值为:" + Arrays.toString(arr));
}
private void show(int a){
System.out.println("带int类型的私有的的方法");
}
protected void show(int a,String b){
System.out.println("带int,String为形参受保护的方法");
}
}

public class GetConstructor {

public static void main(String[] args) throws Exception {
//创建需要读取的类的字节码对象
Class<Student> clz = Student.class;
//通过该类的字节码对象获得该类的构造器
Constructor<?>[] tor = clz.getConstructors();//此方法只获取公共的构造方法
for (Constructor<?> constructor : tor) {
System.out.println(constructor);
}
System.out.println("---------------------------------");
tor = clz.getDeclaredConstructors();//获取该类的所有构造方法
for (Constructor<?> constructor : tor) {
System.out.println(constructor);
}
System.out.println("---------------------------------");
//获取单个构造器getConstructor(int.class); 里面不写为读取无参数构造器 ,需要读取有参数的构造器参数里面跟上形参类型的class文件,多个用逗号隔开.
Constructor<Student> ac = clz.getDeclaredConstructor(int.class, int.class);
System.out.println(ac);
System.out.println("---------------------------------");

//使用反射机制创建非私有构造器对象
Student student = ac.newInstance(3, 2);
//使用反射机制创建私有的构造器对象
//1.通过反射获取该类的私有构造器
ac = clz.getDeclaredConstructor(String.class);
//设置当前的构造器可以被访问
ac.setAccessible(true);//暴力破解
student = ac.newInstance("will");//对于私有的构造方法不能直接通过反射机制调用
//如果一个类中的构造器没有参数,外界可以直接访问,可以使用Class类中的newInstance()方法创建对象
student = clz.newInstance();
//操作类中的方法
//读取该类的公共方法包含其父类里面的公共的方法
Method all[] = clz.getMethods();
for (Method method : all) {
System.out.println(method);
}
System.out.println("---------------------------------");
//只返回本类的所有方法 不包含父类接口的方法
all = clz.getDeclaredMethods();
for (Method method : all) {
System.out.println(method);
}
System.out.println("**********************************");
//获取该类指定方法名的无参数的方法
Method acd = clz.getDeclaredMethod("show");
System.out.println(acd);
//获取该类指定方法名带参的方法
acd = clz.getDeclaredMethod("show",int.class,String.class);
acd.invoke(student, 15,"名字");
//操作静态方法形参为数组的方法
Method method = clz.getMethod("show", int[].class);
method.invoke(null, new Object[]{new int[]{1,2,3,4,5}});
method = clz.getMethod("show", String[].class);
method.invoke(null, new Object[]{new String[]{"A","B","C","D","E"}});


}

}

使用反射调用方法:

步骤:

1):获取该方法的字节码对象  

类名.class得到该类的字节码对象

2):通过字节码对象获取该类的构造方法并创建该类的对象

字节码对象.getDeclaredConstructor(参数.class) 获取该类的构造器

对于私有的构造器则需要设置当前的构造器可以被访问

构造器.setAccessible(true);

构造器.newInstance(实参..,)得到该类的实例

3):通过字节码对象获取该类的方法

1.Method  对象名  =  字节码对象. getDeclaredMethod(形参数据类型.class) ;获取该类指定的方法

对于私有的方法使用

2.对象名.setAccessible(true);设置可访问私有成员

4):使用反射调用方法

对象名.invoke(该类的实例,实参);来调用该方法

有返回值类型则接收,默认返回值类型为Object 

针对于静态方法数组作为形参的调用

使用反射调用静态方法

静态方法不属于任何对象,静态方法属于类本身

此时把invoke方法的第一个参数设置为null即可.

使用反射调用数组参数(可变参数):

王道: 调用方法的时候把实际参数统统作为Object数组的元素的即可.

Methad对象.invoke(方法底层所属对象 , new Object[]{ 所有实参 });

针对于读取泛型的方法 默认提升最高级别 Object

把一个数组设置为Object 类型的时候,不能直接操作数组,则只能使用Array类的方法来操作数组.

文件资源路径的加载:

注:加载properties文件只能使用properties类的load方法

   使用方式:使用文件的相对路径,相当于classpath的跟路径(字节码输出目录)

此时使用ClassLoader(类加载器) 类加载器默认是从classpath目录去寻找文件的

推荐使用第二种:

原文地址:https://www.cnblogs.com/it-xiaoBai/p/8081517.html