反射的基础——Class
Java当中的类用来表示具有相同属性和方法的对象的集合,是抽象的概念。对象是类创建的,同一个类的不同对象具有不同的属性值。
Java当中定义的所有类都属于同一类事物,可以Class来表示。
类型 | 访问方法 | 返回值类型 | 说明 |
包路径 | getPackage() | Package 对象 | 获取该类的存放路径 |
类名称 | getName() | String 对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class 对象 | 获取该类继承的类 |
实现接口 | getlnterfaces() | Class 型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() | Constructor 型数组 | 获取所有权限为 public 的构造方法 |
getDeclaredContruectors() | Constructor 对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() | Methods 型数组 | 获取所有权限为 public 的方法 |
getDeclaredMethods() | Methods 对象 | 获取当前对象的所有方法 | |
成员变量 | getFields() | Field 型数组 | 获取所有权限为 public 的成员变量 |
getDeclareFileds() | Field 对象 | 获取当前对象的所有成员变量 |
对比理解:
不同的人可以用Person类来表示。
人->Person
Java当中定义的不同类可以用Class来表示。
Java当中的类->Class
Class类的对象-字节码:不同类编译生成的字节码。
Person p1 = new Person();
Class class = new Class() (×)
三种方式:
- 对象名.getClass()
Person p1 = new Person(); p1.getClass();
- 类名.class
Date.class
- Class.forName()方法(此方式用的较多)
Class.forName(“java.lang.String”);
package com.apesource.demo;
//class对象的三种创建方式
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException {
//方法1:通过类名访问class
Class stringCls = String.class;
//方法2: 通过实例访问getclass();
String s = "";
Class stringCls2 = s.getClass();
//方法3:通过class类的静态方法forName(类名)
Class stringCls3 = Class.forName("java.lang.String");
System.out.println(stringCls.getName());
System.out.println(stringCls2.getName());
System.out.println(stringCls3.getName());
}
}
Java当中的基本数据类型有:
Boolean int short byte char long float 和 double 他们也有各自的Class对象。
例如:int.class
Void 也有自己对应的Class对象 void.class
基本数据类型对应的封装类有属性TYPE,这个属性代表了封装所封装的基本数据类型的Class对象。
反射就是把Java类中的各个组成部分映射成相应的Java类。
一个类的组成部分包括:属性,方法,构造方法,包等。这些组成部分都会被映射成相应的类。
Class类定义了一系列方法来获取java类的属性,方法,构造方法,包等信息,这些信息都有相应的类来表示,分别是Field,Method,Constryctor,Package等。
注意:表示java类组成部分的各种类位于java.lang.reflect包中
Constructor类
Constructor类用来描述类中所定义的构造方法。
(1)得到类所有的构造方法
Constructor constructors[]=
Class.forName(“java.lang.String”).getConstructors();
- 得到类中某个具体的构造方法,在getConstructors中传入参数类型所对应的字节码
Constructor constructor=
Class.forName(“java.lang.String”).getConstructor(String.class)
使用Constructor类的目的是用来创建响应类的对象
正常情况:
String str = new String(“hello”);
反射情况:
Constructor con = String.class.getConstructor(String.class)
//拿到string类型的构造方法
String str = (String)con.newlnstance(“hello”)
通过反射方式创建对象的过程
Class---->Constructor---->某个类的对象
只有当要用某个无参构造方法创建该类对象时,可以省略创建Constructor类对象的这个过程。
Date d = (Date)Class.forName(“java.util.Date”).newlnstance();
注意:java运行环境的缓存中保存了类的无参构造方法所对应的Constructor对象。
Field类
Field类用来表示类中的属性(字段)。
(1) Class.getPields0:得到Class对象的所有字段,返回的是riela数组。
(2) Class. getField (String: name)返回个 Field 对象,它反映此 Class对象所表示的类或接口的指定公有成员字段。
(3) Field的对象所代表的某-一个类的属性,而不是那个类的某一个对象的属性。要得到某个对象对应的属性值,需要通过get (0b ject obj)方法与某个对象具体关联
(4)对于非公有属性只能通过Class的getDec laredField (String fieldName)方法得到。
(5)对于私有属性要得到它所关联到的对象的值,需通过Field的setAccessible (boolean boolean)方法设置。
(6) Field类的getType Q方法用来得到字段所属的类型
动态加载机制
public class Main {
static {
System.out.println("Main被加载");
}
public static void main(String[] args) {
int rand = new Random().nextInt(10);
if (rand > 5) {
create(rand);
}
}
static void create(int no) {
Person p = new Person(no);
}
}
class Person{
static {
System.out.println("Person类被加载");
}
public Person(int no) {
System.out.println("Person类的有参构造方法");
}
}
当执行Main.java时,由于用到了Main,因此,JVM首先会把Main.class加载到内存。然而,并不会加载Person.class,除非程序执行到create()方法,JVM发现需要加载Person类时,才会首次加载Person.class。如果没有执行create()方法,那么Person.class根本就不会被加载。
Method类
Method用来表示类中的方法。通过Class对象的如下方法得到Method对象:
- Method getMethod(String name,Class<?>…parameterTypes)按名称得到某个特定的public方法(包括从父类或者接口继承的方法);
- Method[] getMethods():得到public方法(包括从父类或者接口继承的方法);
- Method[] getDeclaredMethods():得到所有的方法(不包括继承的方法);
- Method getDeclaredMethod(String name,Class<?>…parame-
terTypes):按名称得到某个特定的方法(不包括继承)。
得到某个方法对应的Method对象后,需要调用如下方法来在某个对象上执行该方法:
- invoke(Object obj,Object…obj)方法用来调用Method所表示的方法。其中,第一个参数表示此方法作用于哪一个对象。
- 如果调用的时静态方法,那么invoke()方法中的第一个参数用null表示。
//获得共有方法
Method method[] = class1.getMethods();
for (Method mm : method){
System.out.println("共有方法的名称是"+mm.getName());
}
当参数为数组时
Method method4 = class1.getDeclaredMethod("showMessages", String[].class);
System.out.println("得到的这个方法是:"+method4.getName());
String[] strs = {"你好","咸阳师范学院","计算机学院"};
// 1,调用数组的参数方法,把整个实际的数组最为一个Object数组的唯一元素进行调用
method4.invoke(person1,new Object[] {strs});
// 2,吧实际数组转换为Object对象
method4.invoke(person1,(Object)strs);
- 得到Method对象的最终目的还是为了去调用这个方法;
- 如果要执行应该私有访问权限的方法,调用该方法执行要执行setAccessible方法,设置为true;
- 调用数组的参数方法,把整个实际的数组最为一个Object数组的唯一元素进行调用;
- 最后将把实际数组转换为Object对象;
- 数组也是符合数据类型。
-
Method类-关于方法参数的操作
- int getParameters():获取该Method对象所表示方法的参数数量;
- Parameter[] getParameters():返回一个Parameter对象的数组,表示该Method对象所表示方法返回的所有参数;
- Class<?>[] getParameterTypes():获取该Method对象所表示方法的所有参数类型的数组;
- Type[] getGenericParameterTypes():获取该Method对象所表示方法的所有参数类型的数组,一般情况下,同getParameterTypes()方法的返回值一样,如果存在泛型,则该方法会返回泛型的类型。
(5)public class MethodReflect {
public static void main(String[] args) {
Class<? extends MyClass1> aClass = MyClass1.class;
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods){
//获取该方法的参数值
int parameterCount = method.getParameterCount();
//获取参数类型数组,
Class<?>[] parameterTypes = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (int i = 0;i < parameterTypes.length; i++){
System.out.println(method.getName()+","+parameterCount+","+parameterTypes[i].getTypeName()+","+genericParameterTypes[i].getTypeName()+"\t");
}
System.out.println();
}
}
}
class MyClass1{
public void m1(int i){
}
public void m2(Boolean bool){
}
public void m3(String str,int i){
}
public void m4(List<String> list){
}
public void m5(Integer... nums){
}
}
Method类-关于方法返回值的操作
- Class<?> getReturnType():返回应该Class对象,该对象表示此Method对象表示的方法的正式返回值类型;
- Type getGenericReturnType():返回一个Type对象,该对象表示此Method对象表示的方法的正式返回值类型。(如果返回值类型不是泛型,那么两个方法的返回值是一致的;如果是泛型,getGenericReturnType()方法会返回泛型的类型。)
(3)public class MethodReflect2 {
public static void main(String[] args) {
Class<? extends MyClass2> aClass = MyClass2.class;
Method[] methods = aClass.getDeclaredMethods();
for (Method method : methods){
Class<?> returnType = method.getReturnType();
Type genericReturnType = method.getGenericReturnType();
System.out.println(returnType+","+genericReturnType);
}
}
}
class MyClass2{
public int getInt(){
return 1;
}
public Boolean getBoolean(){
return Boolean.TRUE;
}
public String getString(){
return "ddc";
}
public List<String> getList(){
return new ArrayList<>();
}
public Map<String,Object> getMap(){
return new HashMap<>();
}
}
Method类-关于修饰符
- int getModifiers():得到方法前面所定义的修饰符,返回类、接口、变量、方法等以2整数编码的Java语言修饰符,访问修饰符编码列表。Modifier类应该提供了12种修饰符的编码常量;
- java.lang.reflect.Modifier类提供解码类和访问修饰符的静态方法和常量,系师傅集合别表示为具有表示不同的修饰符的不同位置的整数。
Method类-关于修饰符
- int getModifiers():得到方法前面所定义的修饰符,返回类、接口、变量、方法等以2整数编码的Java语言修饰符,访问修饰符编码列表。Modifier类应该提供了12种修饰符的编码常量;
- java.lang.reflect.Modifier类提供解码类和访问修饰符的静态方法和常量,系师傅集合别表示为具有表示不同的修饰符的不同位置的整数。
Method类-关于所抛出的异常
- Class[] method.getExceptionTypes():方法在定义的时候可以通过throws关键字声明抛出异常,应该方法可以抛出多个异常。
数组的反射操作
在Java中数组属于符合数据类型,具有相同元素类型和维数的数组属于同一个类型,即具有相同的字节码对象。
代表数组的字节码对象调用getSuperClass()得到它的父类Object的字节码对象。
基本数据类型的一维数组可以当做Object类型使用,但不能当作Object[]类型使用;基本数据类型的二维数组可以当做Object[]类型使用;复合数据类型的一维数组既可以当做Object类型使用,也可以当作Object[]类型使用。
Java.lang.Array用于完成对数组的反射操作。
Object[]来使用
//数组也是复合类型
Object obj3 = new String[4];//一维复合数据类型数组当作Object使用
Object[] obj4 = new String[4];//一维复合数据类型数组当作Object[]使用
Object[][] obj5 = new String[4][5];//二维复合数据类型数组当作Object,Object[][].或者Object[]使用
int a1[] = new int[3];
int a2[] = new int[100];
//只要维数相等,类型相等,那么我们就认为驻足的class对象相等(和元素个数无关)
System.out.println(a1.getClass() == a2.getClass());
- 只要维数相等,类型相等,那么我们就认为驻足的class对象相等(和元素个数无关);
- 二维复合数据类型数组当作Object,Object[][].或者Object[]使用;