栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java中的反射机制(详解)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java中的反射机制(详解)

反射机制 Class类

1.在Java中,数据类型分为两大类:基本数据类型,引用数据类型;出来基本数据类型的int,double,float等等,Java的其他类型全部都是class(包括interface),也就是引用数据类型,例如:

  • 字符串:String
  • Object
  • Runnable
  • Exception

    进过思考,我们可以得出结论:class(包括interface)的本质是数据类型(Type),无继承关系的数据类型无法赋值:

例如:

Number n = new Double(123,456); //正确,public final class Doubleextends Number
String s = new Double(123,456); //错误,String类与Double类没有任何关系

    而 class 是由 JVM 在执行过程中动态加载的。
    JVM在第一次读取到一种class 类型时,将其加载进内存。

     每加载一种class(类),JVM就为其创建一个class类型的实例,并关联起来。 注意:这里的Class类型是一个名叫Class的class。如下为Class的样子:

public final class Class{
    private Class(){}
} 

    以String类为例,当JVM加载String类时,它首先读取 String.class 文件到内存,然后为String类创建一个Class实例并关联起来:

Class cls = new Class(String);

    这个Class实例是JVM内部创建的,如果我们查看JDK源码,就可以发现Class类的构造方法时private私有的,只有JVM能创建Class实例,我们自己的Java程序是无法创建class实例的。
    所以说,JVM持有的每个Class实例都指向一个数据类型(class或interface):


    一个Class实例包含了该class的所有完整信息,如下:

    String类的具体信息:

    由于JVM为每个加载的 class 创建了对应的 Class 实例,并在实例中保存了该 class 的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个 Class 实例,我们就可以通过这个 Class 实例获取到该实例对应的 class 的所有信息。

    这种通过 Class 实例获取 class 信息的方法称为反射(Reflection)。

    如何获取一个class的class实例?有四个方法:

1.知道具体类的情况下可以使用: 直接通过一个 class 的静态变量 class 获取

Class cls = String.class;

但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化

2.通过 Class.forName()传入类的路径获取: 如果知道一个 class 的完整类名,可以通过静态方法 Class.forName() 获取

Class cls = Class.forName("java.lang.String");

3.通过对象实例instance.getClass()获取: 如果我们有一个实例变量,可以通过该实例变量提供的 getClass() 方法获取

String s = new String("Hello");
Class cls = s.getClass();

4.通过类加载器xxxClassLoader.loadClass()传入类路径获取:

Class clazz = ClassLoader.loadClass("java.lang.String");

    通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一些列步骤,静态块和静态对象不会得到执行

    因为 Class 实例在JVM中是唯一的,所以,上述方法获取的 Class 实例是同一个实例。可以用== 比较两个 Class 实例:

例如:

package reflect;

//反射 反编译:.class -> .java 通过反射可以直接动态的访问Java对象的属性,方法,构造方法等等
//注意:在运行期间,一个类,只会产生一个class对象,所以下面三种方式判断是否为同一个class对象打印出来的都是true
public class reflect01 {
    //获取class对象的三种方法
    public static void main(String[] args) {
        //第一种方法获取Class对象
        TargetObject to1 = new TargetObject();//这里一new 产生一个TargetObject对象,一个Class类
        Class to1Class = to1.getClass();//获取Class对象
        System.out.println(to1Class.getName());

        //第二种方式获取class对象
        Class to2Class = TargetObject.class;
        System.out.println(to1Class == to2Class);//判断第一种方式获取的Class对象和第二种是否相同

        //第三种方式获取class对象(常用)
        try {
            Class to3class = Class.forName("reflect.TargetObject");//注意这里的字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(to1Class == to3class);//判断第三种方式是否获取的是同一个class对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
class TargetObject{
    private String value;
    public TargetObject() {
        value = "JavaGuide";
    }
    public void publicMethod(String s) {
        System.out.println("I love" + s);
    }
    private void privateMethod(){
        System.out.println("value is" + value);
    }
}

    注意一下class实例比较和instanceof的差别:

Integer n = new Integer(123); 
boolean b1 = n instanceof Integer; // true,因为n是Integer类型 
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类 
boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class 
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class

    用 instanceof 不但匹配指定类型,还匹配指定类型的子类。而用 == 判断 class 实例可以精确地判断数据类型,但不能作子类型比较。

    通常情况下,我们应该用 instanceof 判断数据类型,因为面向抽象编程的时候,我们不关心具体的子类型。只有在需要精确判断一个类型是不是某个 class 的时候,我们才使用 == 判断 class 实例。

    因为反射的目的是为了获得某个实例的信息。 因此,当我们拿到某个 Object 实例时,我们可以通过反射获取该 Object 的 class 信息:

void printObjectInfo(Object obj) { 
    Class cls = obj.getClass(); 
}

    要从 Class 实例获取获取的基本信息,参考下面的代码:

package reflect;

// Class 实例获取获取的基本信息
public class reflect09 {
    public static void main(String[] args) {
        printClassInfo("".getClass()); //String类
        System.out.println("====================================");
        printClassInfo(Runnable.class); //Runnable接口
        System.out.println("====================================");
        printClassInfo(java.time.Month.class);//枚举
        System.out.println("====================================");
        printClassInfo(String[].class); //数组类型
        System.out.println("====================================");
        printClassInfo(int.class); //基本类型
    }
    static void printClassInfo(Class cls) {
        System.out.println("Class name: " + cls.getClass());
        System.out.println("Simple name: " + cls.getSimpleName()); //返回源代码中给出的底层类的简称
        if (cls.getPackage() != null) {
            System.out.println("Package name: " + cls.getPackage().getName());
        }
        System.out.println("is interface: " + cls.isInterface());// 判定指定的 Class 对象是否表示一个接口类型
        System.out.println("is enum: " + cls.isEnum()); //当且仅当该类声明为源代码中的枚举时返回
        System.out.println("is array: " + cls.isArray()); //判定此 Class 对象是否表示一个数组类
        System.out.println("is primitive: " + cls.isPrimitive());//判定指定的 Class 对象是否表示一个基本类型
    }
}

    注意到数组(例如 String[] )也是一种 Class ,而且不同于 String.class ,它的类名是[Ljava.lang.String 。此外,JVM为每一种基本类型如int也创建了 Class ,通过 int.class 访问。

    如果获取到了一个 Class 实例,我们就可以通过该 Class 实例来创建对应类型的实例:

// 获取String的Class实例: 
Class cls = String.class; 
// 创建一个String实例:
String s = (String) cls.newInstance();//newInstance()创建此 Class 对象所表示的类的一个新实例
动态加载

    JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载。例如:

public class Main { 
    public static void main(String[] args) { 
        if (args.length > 0) { 
            create(args[0]); 
        } 
    }
    static void create(String name) { 
        Person p = new Person(name); 
    } 
}

    当执行 Main.java 时,由于用到了 Main ,因此,JVM首先会把 Main.class 加载到内存。然而,并不会加载 Person.class ,除非程序执行到 create() 方法,JVM发现需要加载 Person 类时,才会首次加载 Person.class 。如果没有执行 create() 方法,那么 Person.class 根本就不会被加载。

    这就是JVM动态加载 class 的特性。

    动态加载 class 的特性对于Java程序非常重要。利用JVM动态加载 class 的特性,我们才能在运行期根据条件加载不同的实现类。

​ JVM为每个加载的 class 及 interface 创建了对应的 Class 实例来保存 class 及 interface 的所有信息;

​ 获取一个 class 对应的 Class 实例后,就可以获取该 class 的所有信息;

​ 通过Class实例获取 class 信息的方法称为反射(Reflection);

​ JVM总是动态加载 class ,可以在运行期根据条件来控制加载class。

访问字段(Field 成员变量)

    对任意的一个 Object 实例,只要我们获取了它的 Class ,就可以获取它的一切信息。

    Field 成员变量:每个成员变量有类型和值。java.lang.reflect.Field 为我们提供了获取当前对象的成员变量的类型,和重新设值的方法。

​     我们先看看如何通过 Class 实例获取字段信息。 Class 类提供了以下几个方法来获取字段:

  • Field getField(name):根据字段名(成员变量)获取某个public的filed(包括父类)
  • Field getDeclaredField(name):根据字段名(成员变量)获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
package reflect;

public class reflect10 {
    public static void main(String[] args) throws Exception {
        Class stdClass = Student10.class;
        //获取public字段"score":
        System.out.println(stdClass.getField("score"));
        //获取继承的public字段"name":
        System.out.println(stdClass.getField("name"));
        //获取private字段"grade":
        System.out.println(stdClass.getDeclaredField("grade"));
    }
}

class Student10 extends Person {
    public int score;
    public int grade;
}
class Person {
    public String name;
}

    上述代码首先获取 Student 的 Class 实例,然后,分别获取 public 字段、继承的 public 字段以及 private 字段,打印出的 Field 类似:

一个 Field 对象包含了一个字段的所有信息:

  • getName() :返回字段名称,例如, “name” ;

  • getType() :返回字段类型,也是一个 Class 实例,例如, String.class ;

  • getModifiers() :返回字段的修饰符,它是一个 int ,不同的bit表示不同的含义。

以 String 类的 value 字段为例,它的定义是:

public final class String { 
    private final byte[] value; 
}

我们用反射获取该字段的信息,代码如下:

Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Field f = String.class.getDeclaredField("value");
        f.getName(); // "value"
        f.getType(); // class [B 表示byte[]类型
		//作为整数返回由此 Member 所表示的成员或构造方法的 Java 语言修饰符。应该使用 Modifier 类解码整数中的修饰符。 
        int m = f.getModifiers();//返回:底层成员的 Java 语言修饰符
        Modifier.isFinal(m); // true //如果整数参数包括 final 修饰符,则返回 true,否则返回 false。
        Modifier.isPublic(m); // false //如果整数参数包括 public 修饰符,则返回 true,否则返回 false。
        Modifier.isProtected(m); // false //如果整数参数包括 protected 修饰符,则返回 true,否则返回 false。
        Modifier.isPrivate(m); // true //如果整数参数包括 private 修饰符,则返回 true,否则返回 false。
        Modifier.isStatic(m); // false //如果整数参数包括 static 修饰符,则返回 true,否则返回 false。
获取字段值

    利用反射拿到字段的一个 Field 实例只是第一步,我们还可以拿到一个实例对应的该字段的值。

    例如,对于一个 Person 实例,我们可以先拿到 name 字段对应的 Field ,再获取这个实例的name 字段的值:

package reflect;

import java.lang.reflect.Field;

public class reflect11 {
    public static void main(String[] args) throws Exception {
        Object p = new Person11("Xiao Ming");
        Class c = p.getClass(); //获取Person11类的所有信息
        Field f = c.getDeclaredField("name");//获取私有字段,并调用
        f.setAccessible(true);//暴力反射,解除私有限定
        Object value = f.get(p);//获取到字段值
        System.out.println(value);
    }
}
class Person11 {
    private String name;

    public Person11(String name) {
        this.name = name;
    }
}

    上述代码先获取 Class 实例,再获取 Field 实例,然后,用 Field.get(Object) 获取指定实例的指定字段的值。

​    运行代码,如果不出意外,会得到一个 IllegalAccessException ,这是因为 name 被定义为一个 private 字段,正常情况下, Main 类无法访问 Person 类的 private 字段。要修复错误,可以将 private 改为 public ,或者,在调用 Object value = f.get§; 前,先写一句:

 f.setAccessible(true);//暴力反射,解除私有限定

    调用 Field.setAccessible(true) 的意思是,别管这个字段是不是 public ,一律允许访问。可以试着加上上述语句,再运行代码,就可以打印出 private 字段的值。

    有童鞋会问:如果使用反射可以获取 private 字段的值,那么类的封装还有什么意义?答案是正常情况下,我们总是通过 p.name 来访问 Person 的 name 字段,编译器会根据public 、 protected 和 private 决定是否允许访问字段,这样就达到了数据封装的目的。

​ 而反射是一种非常规的用法,使用反射,首先代码非常繁琐,其次,它更多地是给工具或者底层框架来使用,目的是在不知道目标实例任何信息的情况下,获取特定字段的值。

    此外, setAccessible(true) 可能会失败。如果JVM运行期存在 SecurityManager ,那么它会根据规则进行检查,有可能阻止 setAccessible(true) 。例如,某个 SecurityManager 可能不允许对 java 和 javax 开头的 package 的类调用 setAccessible(true) ,这样可以保证JVM核心库的安全。

设置字段值

    通过Field实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。

    设置字段值是通过 Field.set(Object, Object) 实现的,其中第一个 Object 参数是指定的实例,第二个 Object 参数是待修改的值。示例代码如下:

package reflect;

import java.lang.reflect.Field;

public class reflect12 {
    public static void main(String[] args) throws Exception {
        Person12 p = new Person12("Xiao Ming");
        System.out.println(p.getName());//获取一开始的名字
        Class c = p.getClass();//获取Person12类的所有信息
        Field f = c.getDeclaredField("name");//获取私有字段,并调用
        f.setAccessible(true);//暴力反射,解除私有限定
        f.set(p,"Xiao Hong");//重新更改字段名 set(Object obj,Object value)
        System.out.println(p.getName());
    }
}
class Person12 {
    private String name;

    public Person12(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

    运行上述代码,打印的 name 字段从 Xiao Ming 变成了 Xiao Hong ,说明通过反射可以直接修改字段的值。

    同样的,修改非 public 字段,需要首先调用 setAccessible(true) 。

​ Java的反射API提供的 Field 类封装了字段的所有信息:

​ 通过 Class 实例的方法可以获取 Field 实例: getField() , getFields() , getDeclaredField(), getDeclaredFields() ;

​ 通过Field实例可以获取字段信息: getName() , getType() , getModifiers() ;

​ 通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true) 来访问非 public 字段。

​ 通过反射读写字段是一种非常规方法,它会破坏对象的封装。

实例:

package reflect;

import java.lang.reflect.Field;


public class reflect03 {
    public static void main(String[] args) throws Exception {
        //1.获取Class对象
        Class stuclass = Class.forName("reflect.Student03");
        //2.获取字段
        System.out.println("***********获取所有公有的字段************");
        Field[] fieldArray = stuclass.getFields();
        for(Field f : fieldArray) {
            System.out.println(f);
        }
        System.out.println("***********获取所有字段(包括私有,受保护,默认的)************");
        fieldArray = stuclass.getDeclaredFields();
        for (Field f : fieldArray) {
            System.out.println(f);
        }
        System.out.println("***********获取公有字段,并调用***********");
        Field f = stuclass.getField("name");
        System.out.println(f);
        //获取一个对象(产生student03对象 --> Student03 stu = new Student03)
        Object obj = stuclass.getConstructor().newInstance();
        //为字段设置值
        f.set(obj,"刘德华");
        //验证
        Student03 stu = (Student03) obj;
        System.out.println("验证姓名:" + stu.name);

        System.out.println("***********获取私有字段,并调用************");
        f = stuclass.getDeclaredField("phonenum");
        System.out.println(f);
        f.setAccessible(true);//暴力反射,解除私有限定
        //为字段设置值
        f.set(obj,"1234567899999");
        System.out.println("验证电话:" + stu);
    }
}
class Student03 {
    public String name;
    protected int age;
    char sex;
    private String phonenum;

    public Student03(){

    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + ",sex=" + sex + ",phonenum=" + phonenum + "]";
    }
}
调用方法

    我们已经能通过 Class 实例获取所有 Field 对象(成员变量),同样的,可以通过 Class 实例获取所有 Method 信息。 Class 类提供了以下几个方法来获取 Method :

  • Method getMethod(name, Class...) :获取某个 public 的 Method (包括父类)
  • Method getDeclaredMethod(name, Class...) :获取当前类的某个 Method (不包括父类)
  • Method[] getMethods() :获取所有 public 的 Method (包括父类)
  • Method[] getDeclaredMethods() :获取当前类的所有 Method (不包括父类)

    我们来看一下示例代码:

package reflect;

public class reflect13 {
    public static void main(String[] args) throws Exception {
        Class stdClass = Student13.class;//获取Student13所有的信息
        // 获取public方法getScore,参数为String:
        System.out.println(stdClass.getMethod("getScore",String.class));
        // 获取继承的public方法getName,无参数:
        System.out.println(stdClass.getMethod("getName"));
        // 获取private方法getGrade,参数为int:
        System.out.println(stdClass.getDeclaredMethod("getGrade",int.class));
    }
}
class Student13 extends Person13 {
    public int getScore(String type) {
        return 99;
    }
    private int getGrade(int year) {
        return 1;
    }
}
class Person13 {
    public String getName() {
        return "Person";
    }
}

    上述代码首先获取 Student 的 Class 实例,然后,分别获取 public 方法、继承的 public 方法以及 private 方法,打印出的 Method 类似:


    一个 Method 对象包含一个方法的所有信息:

  • getName() :返回方法名称,例如: “getScore” ;
  • getReturnType() :返回方法返回值类型,也是一个Class实例,例如: String.class ;
  • getParameterTypes() :返回方法的参数类型,是一个Class数组,例如:{String.class, int.class} ;
  • getModifiers() :返回方法的修饰符,它是一个 int ,不同的bit表示不同的含义。
调用方法

    当我们获取到一个 Method 对象时,就可以对它进行调用。我们以下面的代码为例:

String s = "Hello world";
String r = s.substring(6); // "world"

    如果用反射来调用 substring 方法,需要以下代码:

package reflect;

import java.lang.reflect.Method;

public class reflect14 {
    public static void main(String[] args) throws Exception {
        // String对象:
        String s = "Hello world";
        // 获取String substring(int)方法,参数为int:
        Method m = String.class.getMethod("substring",int.class);
        // 在s对象上调用该方法并获取结果:
        String r = (String) m.invoke(s,6);
        // 打印调用结果
        System.out.println(r);
    }
}

    注意到 substring() 有两个重载方法,我们获取的是 String substring(int) 这个方法。思考一下如何获取 String substring(int, int) 方法。

public String substring(int beginIndex, int endIndex)

返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,一直到索引 endIndex - 1 处的字符。

    对 Method 实例调用 invoke 就相当于调用该方法, invoke 的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。

调用方法:
 Method --> public Object invoke(Object obj,Object... args):
 参数说明:             
 	obj : 要调用方法的对象;             
 	args:调用方式时所传递的实参;
调用静态方法

    如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke 方法传入的第一个参数永远为 null 。我们以 Integer.parseInt(String) 为例:

package reflect;

import java.lang.reflect.Method;

public class reflect14 {
    public static void main(String[] args) throws Exception {
        // 获取Integer.parseInt(String)方法,参数为String: 
        Method m = Integer.class.getMethod("parseInt", String.class); 
        // 调用该静态方法并获取结果: 
        Integer n = (Integer) m.invoke(null, "12345"); 
        // 打印调用结果: 
        System.out.println(n);
    }
}

    实例:获取Student类的main方法、不要与当前的main方法搞混了;

package reflect;

import java.lang.reflect.Method;


public class reflect05 {
    public static void main(String[] args) throws Exception {
        //1.获取Class对象
        Class stuclass = Class.forName("reflect.Student05");
        //2.获得main方法 (第一个参数:方法名称,第二个参数:方法形参的类型)
        Method methodmain = stuclass.getMethod("main",String[].class);
        //3.调用main方法 (因为主方法是静态的,所以为空,第二个参数因为主方法里面是String数组类型)
        methodmain.invoke(null,(Object)new String[]{"a","b","c"});//这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
    }
}
class Student05{
    public static void main(String[] args) {
        System.out.println("main方法执行了....");
    }
}
调用非public方法

    和Field(成员变量)类似,对于非public方法,我们虽然可以通过 Class.getDeclaredMethod() 获取该方法实例,但直接对其调用将得到一个 IllegalAccessException 。为了调用非public方法,我们通过Method.setAccessible(true) 允许其调用:

public class Main { 
    public static void main(String[] args) throws Exception { 
        Person p = new Person(); 
        Method m = p.getClass().getDeclaredMethod("setName", String.class); 
        m.setAccessible(true); 
        m.invoke(p, "Bob"); 
        System.out.println(p.name); 
    } 
}
class Person { 
    String name; 
    private void setName(String name) { 
        this.name = name; 
    } 
}

    此外, setAccessible(true) 可能会失败。如果JVM运行期存在 SecurityManager ,那么它会根据规则进行检查,有可能阻止 setAccessible(true) 。例如,某个 SecurityManager 可能不允许对 java 和 javax 开头的 package 的类调用 setAccessible(true) ,这样可以保证JVM核心库的安全。

实例:

package reflect;
import java.lang.reflect.Method;


public class reflect04 {
    public static void main(String[] args) throws Exception {
        //1.获取Class对象
        Class stuclass = Class.forName("reflect.Student04");
        //2.获取所有公有方法
        System.out.println("************获取所有的公有方法*************");
        Method[] methodArray = stuclass.getMethods();
        for (Method m : methodArray) {
            System.out.println(m);
        }
        System.out.println("*************获取所有的方法,包括私有的**************");
        methodArray = stuclass.getDeclaredMethods();
        for (Method m : methodArray) {
            System.out.println(m);
        }
        System.out.println("**************获取公有的show01()方法***************");
        Method m = stuclass.getMethod("show01",String.class);
        System.out.println(m);
        //实例化一个Student对象
        Object obj = stuclass.getDeclaredConstructor().newInstance();
        m.invoke(obj,"刘德华");

        System.out.println("**************获取公有的show04()方法**************");
        m = stuclass.getDeclaredMethod("show04",int.class);
        System.out.println(m);
        //实例化
        m.setAccessible(true);
        Object result = m.invoke(obj,20);
        System.out.println("返回值:" + result);
    }

}
class Student04{
    /
public class reflect02 {
    public static void main(String[] args) throws Exception {
        //1.加载class对象
        Class clazz = Class.forName("reflect.Student0_");

        //2.获取所有的公有构造函数
        System.out.println("**************所有公有构造函数***************");
        Constructor[] conArray = clazz.getConstructors();
        for(Constructor c : conArray) {
            System.out.println(c);
        }

        System.out.println("**************所有的构造方法(包括:私有,受保护,默认,公有)**************");
        conArray = clazz.getDeclaredConstructors();
        for (Constructor c : conArray) {
            System.out.println(c);
        }

        System.out.println("**************获取公有、无参的构造方法***************");
        Constructor con = clazz.getConstructor(null);
        //这里注意null指的是类型参数是空,是参数的类型,不写也可以
        System.out.println("con = " + con);

        //调用构造函数
        Object obj = con.newInstance();//打印公有,无参构造函数

        System.out.println("****************获取私有构造函数,并调用*****************");
        con = clazz.getDeclaredConstructor(char.class);
        System.out.println(con);
        //调用构造函数
        con.setAccessible(true);//暴力访问(忽略访问修饰符)
        obj = con.newInstance('女');
    }
}
class Student{
    //构造方法
    //默认的构造方法
    Student(String str) {
        System.out.println("默认的构造方法s = " + str);
    }
    //无参构造函数
    public Student(){
        System.out.println("公有,无参构造函数");
    }
    //一个参数的构造函数
    public Student(char name){
        System.out.println("姓名:" + name);
    }
    //多个参数的构造函数
    public Student(String name, int age){
        System.out.println("姓名:" + name + "年龄:" + age);
    }
    //受保护的构造函数
    protected Student(boolean n){
        System.out.println("受保护的构造函数 n = " + n);
    }
    //私有构造函数
    private Student(int age) {
        System.out.println("私有的构造函数 年龄:" + age);
    }
}
获取继承关系

    当我们获取到某个 Class 对象时,实际上就获取到了一个类的类型:

Class cls = String.class; // 获取到String的Class

    还可以用实例的 getClass() 方法获取:

String s = ""; 
Class cls = s.getClass(); // s是String,因此获取到String的Class

    最后一种获取 Class 的方法是通过 Class.forName("") ,传入 Class 的完整类名获取:

Class s = Class.forName("java.lang.String");

    这三种方式获取的 Class 实例都是同一个实例,因为JVM对每个加载的 Class 只创建一个Class 实例来表示它的类型。

获取父类的Class
package reflect;

public class reflect17 {
    public static void main(String[] args) {
        Class i = Integer.class;
        //获取 Integer类的父类
        Class n = i.getSuperclass();
        System.out.println(n);
        //获取 Integer类的父类 的父类
        Class o = n.getSuperclass();
        System.out.println(o);
        System.out.println(o.getSuperclass());
    }
}

    运行上述代码,可以看到, Integer 的父类类型是 Number , Number 的父类是 Object , Object 的父类是 null 。除 Object 外,其他任何非 interface 的 Class 都必定存在一个父类类型。

获取interface

    由于一个类可能实现一个或多个接口,通过 Class 我们就可以查询到实现的接口类型。例如,查询 Integer 实现的接口:

package reflect;

//获取interface
public class reflect18 {
    public static void main(String[] args) {
        Class s = Integer.class;//获取Integer类的信息
        Class[] is = s.getInterfaces(); //获取interface
        for (Class i : is) {
            System.out.println(i);
        }
    }
}

    运行上述代码可知, Integer 实现的接口有:java.lang.Comparable ;

    要特别注意: getInterfaces() 只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型:

//获取父类的interface
public class reflect18 {
    public static void main(String[] args) {
        Class s = Integer.class.getSuperclass();//获取Integer类的信息
        Class[] is = s.getInterfaces(); //获取interface
        for (Class i : is) {
            System.out.println(i);
        }
    }
}

    Integer 的父类是 Number , Number 实现的接口是 java.io.Serializable 。

// java.io.FilterInputStream,因为DataInputStream继承自FilterInputStream
System.out.println(java.io.DataInputStream.class.getSuperclass());
// null,对接口调用 getSuperclass()总是返回null,获取接口的父接口要用getInterfaces()
System.out.println(java.io.Closeable.class.getSuperclass());

    如果一个类没有实现任何 interface ,那么 getInterfaces() 返回空数组。

继承关系

    当我们判断一个实例是否是某个类型时,正常情况下,使用 instanceof 操作符:

Object n = Integer.valueOf(123); 
boolean isDouble = n instanceof Double; // false 
boolean isInteger = n instanceof Integer; // true 
boolean isNumber = n instanceof Number; // true 
boolean isSerializable = n instanceof java.io.Serializable; // true

    如果是两个 Class 实例,要判断一个向上转型是否成立,可以调用 isAssignableFrom() :

// Integer i = ? 
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer 
// Number n = ? 
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number 
// Object o = ? 
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object 
// Integer i = ? 
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer

​ 通过 Class 对象可以获取继承关系:

  • Class getSuperclass():获取父类类型;

  • Class[] getInterfaces() :获取当前类实现的所有接口。

通过 Class 对象的 isAssignableFrom() 方法可以判断一个向上转型是否可以实现。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/489200.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号