目录
1. 反射的定义
Java 反射机制是在程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。
简单来说,Java 反射机制就是把类的属性,方法以及构造器封装成对象供调用者使用。
2. 为什么要使用反射
在理解为什么要用反射之前,我们首先看一个例子:
为了对实例化具体动物这个动作做统一规范,我们把该动作交由一个实例化工厂去完成,该工厂会根据使用者的需求实例化对应的动物对象。
/**
* 动物
*/
public abstract class Animal {
/**
* 动物吃的方法
*/
public abstract void eat();
}
/**
* 猫
*/
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
/**
* 狗
*/
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
public class Factory {
//实例化对象
public static Animal getInstance(String className){
Animal animal = null;
if(className.equals("Dog")){
animal = new Dog();
}else if(className.equals("Cat")){
animal = new Cat();
}
return animal;
}
}
下面我们通过这个工厂来创建一个猫的实例化对象并调用它吃的方法。
public static void main(String[] args) {
Animal animal = Factory.getInstance("Cat");
animal.eat();
}
结果:猫吃鱼
通过这种方法虽然实现了实例化动作的统一规范,但是如果此时我们又声明了一个新的动物类 —— 仓鼠,那么 Factory 中的代码就必须再加上对仓鼠的条件判断。这样的话我们每新声明一个动物类,就必须改变一次 Factory 中的代码,这样岂不是很麻烦?此时,Java 的反射机制就派上了用场。
其实 Java 反射机制最重要的用途就是用来开发各种通用框架。开发者通过使用框架,就可以在极大程度上减少代码量,提高开发效率,提高产品的性能。
3. Java代码在计算机中经历的阶段
① javac 把 .java 文件编译为 .class 字节码文件。
② .class 文件由类加载器转变为 Class 类对象。
③ 通过 Class 类对象在内存中开辟空间创建对象。
4. Class 类的使用
Java 的反射机制均是借助 Class 这个泛型类来完成的,因此对于如何使用反射机制,我们就需要学习 Class 类的 API 。
public class Person {
private String name;//私有属性
public Integer age;//公共属性
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void method(String param) {
System.out.println("参数:" + param + " 方法执行了");
}
}
(1)获取 Class 对象的三种方式
Class 类没有公共构造方法,Class 对象是通过 JVM 调用类加载器来进行构造的。而 Java 代码在计算机中经历了三个阶段,因此对于每个阶段,都有一种方式来把相应阶段的 java 代码转变为 Class 对象。
① Source 源码阶段:找到字节码文件 (.class 文件) 加载进内存创建 Class 对象。
static Class<?> | forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。 |
public static void main(String[] args) {
try {
//通过此方法获取的Class并不能确定它的泛型,因此用通配符接受
Class<?> aClass = Class.forName("com.java.day01.Person");//获取Class对象
System.out.println(aClass);
} catch (ClassNotFoundException e) {
System.out.println("该类不存在");
}
}
结果:
class com.java.day01.Person
② Class 类对象阶段:通过类的属性 class 获取 Class 对象。
public static void main(String[] args) {
Class<?> aClass = Person.class;
System.out.println(aClass);
}
结果为:
class com.java.day01.Person
③ Runtime 运行时阶段:通过基类 Object 类的 getClass ( ) 方法获取。
public static void main(String[] args) {
Person person = new Person("张三");
Class<?> aClass = person.getClass();
System.out.println(aClass);
}
结果:
class com.java.day01.Person
(2)getName ( ) 方法与 toString ( ) 方法
String |
|
String | toString() 将对象转换为字符串。字符串的表示形式为字符串 "class" 或 "interface" 后面紧跟一个空格,然后是该类的完全限定名 |
public static void main(String[] args) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> aClass = Person.class;
String s1 = aClass.getName();//获取类名
String s2 = aClass.toString();//打印类信息
System.out.println(s1);
System.out.println(s2);
}
结果为:
com.java.day01.Person
class com.java.day01.Person
(3)isInstance ( ) 方法与 newInstance ( ) 方法
boolean | isInstance(Object obj) 判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。此方法是 instanceof 运算符的动态等效方法。 |
T | newInstance() 创建此 Class 对象所表示的类的一个新实例。如同用一个空参数列表的 new 表达式实例化该类(空参构造)。 |
public static void main(String[] args) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> aClass = Person.class;
//新建一个Person实例,只能调用空参构造方法
//多用Constructor对象创建实例(可带参构造)而不用此方法
Object o = aClass.newInstance();
Person person = null;
//判断Object是否与aClass兼容
if(aClass.isInstance(o)){
person = (Person)o;//向下转型
}
//instanceof写法
if(o instanceof Person){
person = (Person)o;
}
}