JAVA 学习·Class类和反射机制

文章详细解释了Java中Class类的使用,包括通过类名获取Class对象、使用forName方法、获取对象的运行时类型、构造器和方法的获取以及反射机制的应用实例。展示了如何动态创建对象和调用方法,尤其是在处理未知类名输入时的场景。
摘要由CSDN通过智能技术生成

Class 类和 Class 对象

  Java 虚拟机在运行时,知道一块内存到底指向的是什么对象。比如:

class A{
	public A(){}
	public void method(){
		System.out.println("A's method has been called.");
	}
}
// 定义了 B 是 A 的子类
class B extends A{
	public B(){}
	public void method(){
		System.out.println("B's method has been called.");
	}
}
// 某个代码段:
A obj = new B();
obj.method();

  虚拟机知道,obj指向的内存是一个B对象(运行时类型),而不是A对象(引用类型)。这是通过获得objclass成员得知的。所以,根据多态性,实际运行时obj.method()会调用B类的method方法,从而在控制台打印出B's method has been called.
  虚拟机是怎么知道obj指向的内存是一个B对象的?
  每个类都有一个Class类型的class成员,比如A.class就包含了类A的类型信息。A.class是在A类编译时产生的。
  Class类中提供了很多方法。其中,getName返回该类的完全限定类名getSimpleName返回该类的简单类名
  以下面的代码为例:

package hust.cs.javacourse.ch13;

public class ClassTest {
    public static void main(String[] args){
        System.out.println(ClassTest.class.getName());
        System.out.println(ClassTest.class.getSimpleName());
    }
}

  它的运行结果是:

hust.cs.javacourse.ch13.ClassTest
ClassTest

如何获取 Class 对象

通过类

  上文中已经提到,通过类名.class可以直接获得该类的class对象。
  此外,Class类提供了静态方法forName(String className),可以通过类的完全限定类名className获得这个类的class对象;同时,还提供了实例方法getSuperClass()获取其直接父类的class对象。

forName(String className)无法找到完全限定类名为className的对象时,方法会抛出必检异常ClassNotFoundException。所以通过类名.class获取class对象是一种比较安全的方法。

package hust.cs.javacourse.ch13;

class Person{}

class Employee extends Person{}

class Manager extends Employee{}

public class ClassDemo {
    public static void main(String[] args){
        try {
            Class clz = Class.forName("hust.cs.javacourse.ch13.Manager"); //参数是类完全限定名字符串
            System.out.println(clz.getName()); //产生完全限定名hust.cs.javacourse.ch13.Manager
            System.out.println(clz.getSimpleName()); //产生简单名Manager

            Class superClz = clz.getSuperclass(); //获得直接父类型信息
            System.out.println(superClz.getName()); //产生完全限定名hust.cs.javacourse.ch13.Employee
            System.out.println(superClz.getSimpleName()); //产生简单名Employee
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

  运行上面的代码,得到以下结果:

hust.cs.javacourse.ch13.Manager
Manager
hust.cs.javacourse.ch13.Employee
Employee

通过对象

  任何一个对象在运行时还能够调用getClass()方法获取该对象的运行时类型getClass方法是Object类中的final方法。

反射机制

  看完上面的内容,你可能觉得class对象用处不大。但是反射机制中恰恰能够发挥class对象的用处。

实例化对象

  考虑实现下面的功能:从System.in中获取类的完全限定类名,构建一个相应的类的实例对象。有人可能会这样写:

// 从System.in中获取输入的字符串input
Object o = null;
if( input.equals("java.lang.String") ){
     o = new String("");
}
else if( input.equals("ch13.Student") ){
    o = new Student();
}
// ……更多的else if语句

  这种一个个枚举的方法显然不行,因为事先不知道会输入一个什么类的完全限定名字符串,if语句不可能列出所有可能的类型。这时,我们可以利用反射机制,编写以下的代码:

// 从System.in中获取输入的字符串input
try{
	Class clz = Class.forName();
	Object o = clz.newInstance();// 调用该类的无参构造函数
} catch (Exception e) {
	e.printStackTrace();
}

  即可成功创建实例对象o

获取类的构造器或方法

获取所有构造器或方法

  Class类中提供了实例方法getConstructors()获得某个类的所有构造器,提供了实例方法getMethods()获得某个类的所有方法(包括从父类继承的方法),提供了实例方法getDeclaredMethods()获得某个类中定义的所有方法。比如下面的代码:

package hust.cs.javacourse.ch13;

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

class Student{
    private String name;
    public Student(){
        this.name = "unknown";
    }
    public Student(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String toString() {
        return "Name:" + name;
    }
}

public class ReflectDemo {
    public static void main(String[] args) {
        System.out.println("获取构造器:");
        try {
            Class  clz = Class.forName("hust.cs.javacourse.ch13.Student");
            //获取所有的Constructor对象
            Constructor[] ctors = clz.getConstructors();
            for(Constructor c : ctors){
                System.out.println(c.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("获取方法:");
        try {
            Class clz = Class.forName("hust.cs.javacourse.ch13.Student");
            //获取所有的Method
            //Method[] methods = clz.getMethods(); //会显示所有方法,包括继承的
            Method[] methods = clz.getDeclaredMethods(); //本类定义的方法
            for(Method m: methods){
                System.out.println(m.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  运行结果为:

获取构造器:
public hust.cs.javacourse.ch13.Student()
public hust.cs.javacourse.ch13.Student(java.lang.String)
获取方法:
public java.lang.String hust.cs.javacourse.ch13.Student.getName()
public java.lang.String hust.cs.javacourse.ch13.Student.toString()
public void hust.cs.javacourse.ch13.Student.setName(java.lang.String)

获取某个构造器或方法

获取某个构造器

  前面我们已经知道,可以用一个Class对象的newInstance()方法,调用它的无参构造函数创建一个实例对象。那如果要调用有参构造函数呢?
  Class类还提供了实例方法getConstructor(Class<?>... parameterTypes),按照构造器的参数类型列表parameterTypes获取对应的构造器,返回一个Constructor对象。其中参数类型列表是Class类对象的列表。而Constructor类又提供了实例方法newInstance(Object ... initargs),按照构造器的参数列表initargs传入实参,从而创建一个实例类型。
  例如,按照上面hust.cs.javacourse.ch13.Student的定义,我们可以使用如下代码创建一个名为John的学生:

try {
	Class clz = Class.forName("hust.cs.javacourse.ch13.Student");
	Student s = (Student)clz.getConstructor(String.class).newInstance("John");
} catch (Exception e) {
	e.printStackTrace();
}

  其中,clz.getConstructor(String.class)获取了Student类的参数类型为String.class的构造器,也就是下面这个构造器:

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

  然后对于这个构造器,又调用了newInstance("John")方法,从而将"John"传入形式参数name,构造出了一个Student实例对象。

对于一个Class类对象clz,可以直接使用clz.newInstance()调用该类的无参构造函数,此时newInstanceClass类提供的实例方法;也也可以使用clz.getConstructor().newInstance()调用该类的无参构造函数,此时newInstanceConstructor类提供的实例方法。

获取某个方法

  Class类提供的实例方法getMethod(String name, Class<?>... parameterTypes),按照方法名name以及方法的参数类型列表parameterTypes获取对应的方法,返回一个Method对象。而Method类又提供了实例方法invoke(Object obj, Object... args),调用对象obj的相应方法,并给方法的形参列表传入args实参列表。
  例如,按照上面hust.cs.javacourse.ch13.Student的定义,我们可以使用如下代码创建一个名为Marry的学生:

try {
	Class clz = Class.forName("hust.cs.javacourse.ch13.Student");
	Student s = (Student)clz.getConstructor().newInstance();
	clz.getMethod("setName", String.class).invoke(s, "Marry"); //调用s1对象的setName方法,实参"Marry"
} catch (Exception e) {
	e.printStackTrace();
}

  其中,clz.getMethod("setName", String.class)获取了Student类的参数类型为String.class的、方法名为setName的方法,也就是下面这个方法:

public void setName(String name) {
	this.name = name;
}

  然后对于这个方法,又调用了invoke(s,"Marry")方法,从而将"Marry"传入形式参数name,并调用s对象的该方法。

代码实例

  上面的解释可以通过一段代码的运行来体现:

package hust.cs.javacourse.ch13;

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

class Student{
    private String name;
    public Student(){
        this.name = "unknown";
    }
    public Student(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String toString() {
        return "Name:" + name;
    }
}

public class ReflectDemo {
    public static void main(String[] args) {
        try {
            Class clz = Class.forName("hust.cs.javacourse.ch13.Student");
            //实例化对象
            //1:如有缺省构造函数,调用Class对象的newInstance方法
            Student s1 = (Student)clz.getConstructor().newInstance();
            //2. 调用带参数的构造函数
            Student s2 = (Student)clz.getConstructor(String.class).newInstance("John");
            //invoke method
            clz.getMethod("setName", String.class).invoke(s1, "Marry"); //调用s1对象的setName方法,实参"Marry"
            System.out.println(s1.toString());
            System.out.println(s2.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  运行的结果为:

Name:Marry
Name:John
  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值