JAVA 学习·Class类和反射机制

news/2024/9/25 8:13:49/

Class 类和 Class 对象

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

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返回该类的简单类名
  以下面的代码为例:

java">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对象是一种比较安全的方法。

java">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.ManagerSystem.out.println(clz.getSimpleName()); //产生简单名ManagerClass superClz = clz.getSuperclass(); //获得直接父类型信息System.out.println(superClz.getName()); //产生完全限定名hust.cs.javacourse.ch13.EmployeeSystem.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中获取类的完全限定类名,构建一个相应的类的实例对象。有人可能会这样写:

java">// 从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语句不可能列出所有可能的类型。这时,我们可以利用反射机制,编写以下的代码:

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

  即可成功创建实例对象o

获取类的构造器或方法

获取所有构造器或方法

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

java">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的学生:

java">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的构造器,也就是下面这个构造器:

java">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的学生:

java">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的方法,也就是下面这个方法:

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

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

代码实例

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

java">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 methodclz.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

http://www.ppmy.cn/news/1446966.html

相关文章

构建中小型企业网络-单臂路由

1.给IP地址配置好对应的IP和网关 2.配置交换机 3.路由配置 在交换机ge0/0/1中配置端口为trunk是可以允许多个vlan通过的&#xff0c;但路由器是不能够配置vlan&#xff0c;而交换机和路由器间连接的只有一根线&#xff0c;一个端口又只能配置一个ip地址&#xff0c;只有一个ip地…

深度学习之基于Matlab NN的伦敦房价预测

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 房价预测是房地产领域的一个重要问题&#xff0c;对于投资者、开发商以及政策制定者等都具有重要的指…

从C到Py:Python的异常处理

本篇文章中我们将简单讲解一下有关Python中的异常处理&#xff0c;既有在代码中有显式表达的异常处理语法&#xff0c;还包括Pycharm手动调试的简单操作。 目录 Python异常处理 1、try-except结构 ​编辑 2、try-except-else结构 raise抛出异常 常见异常类型 Pycharm程序…

leetcode18-4Sum

题目 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1a; 0 &l…

vue2 实现echarts图表进入可视区域后再加载动画,以及 使用了resize之后,动画失效问题解决

Intersection Observer API 是一个现代的浏览器 API&#xff0c;用于监测一个或多个目标元素与其祖先元素或视窗&#xff08;viewport&#xff09;之间的交叉状态&#xff08;intersection&#xff09;的变化。它可以有效地监听元素是否进入或离开可视区域&#xff0c;从而实现…

MoonBit 周报 Vol.39:新增 JS 后端、插件和构建系统同步支持多后端开发……

MoonBit 更新 新增JavaScript后端 目前MoonBit已新增对JavaScript的支持并带来前所未有的性能提升&#xff0c;在JS后端实现了超出Json5近8倍性能的优势。更详细的介绍可以看一下这篇文章&#xff1a;IDEA研究院编程语言MoonBit发布JavaScript后端&#xff0c;速度提升25倍 …

9、Flink 用户自定义 Functions 及 累加器详解

1&#xff09;用户自定义函数 1.实现接口 最基本的方法是实现提供的接口。 # 根据提供的接口创建自定义函数 class MyMapFunction implements MapFunction<String, Integer> {public Integer map(String value) { return Integer.parseInt(value); } }# 调用创建的自定…

VSCode+c++/cmake+window配置执行流程

主要为了记录以前学习vscodec/cmakewindow配置执行流程记录 1、创建源码 2、编写一个CMakeLists.txt 3、点击vscode左下角齿轮图标&#xff08;管理&#xff09;然后出现如下图示&#xff1a; 选择cmake配置 4、点击下图中生成图标&#xff08;左下角红色区域&#xff09; 然…