一、认识反射机制
1.初识反射
反射是对对象的反向处理操作。
下面我们观察正向创建对象和反向创建对象的操作,进一步加深对反射的理解。
//正向获取Date对象
Date date=new Date();
//反向获取Date对象
Class classz=Date.class;
Object date=claz.newInstance();
正向获取对象就是通过对已知的一个类,直接new出一个对象。而反向创建对象是先获取该类的class对象,然后通过class对象的newInstance()来获取Date对象。那么,这两种方式得到的对象有什么联系吗?看看下面的代码
Date date=new Date();
Class claz=Date.class;
Object date1=claz.newInstance();
System.out.println(date==date1);//false
这段代码运行的结果为false,说明通过正向创建和反向创建获取的不是同一个对象。其实这也不难理解,因为一个类(除单例外)可以有多个对象,我们不断new 和调用class对象的newInstance()得到的对象始终是一个新的对象,和之前创建的对象没有任何关系。但是这并不是说它们没有任何关系,毕竟它们都是Date类,再看下面的代码
public final native Class<?> getClass();//获取类对象
Date date=new Date();
Class claz=Date.class;
Object date1=claz.newInstance();
Class class2=date1.getClass();
Class class1=date.getClass();
System.out.println(class1==class2);//true
这段代码运行的结果为true,说明这两个对象的类对象是相同的。因为它们是由Date类创建的。我们说,在同一个类加载器中,Class类对象有且只有一个。
“反”的本质:通过对象取得对象的来源。
在反射的世界里,看重的不再是一个对象,而是对象身后的组成(类、构造方法、普通方法、属性)。
2.Class类对象的三种获取方式
方式一:通过类名.class获取
方式二:通过Class.forName(类名)获取
方式三:通过对象的getClass()获取
import java.util.Date;
public class TestRefection {
public static void main(String[] args) throws ClassNotFoundException {
Class class1=java.util.Date.class;//方式一
Class class2=Class.forName("java.util.Date");//方式二
Date date=new Date();//方式三
Class class3=date.getClass();
}
}
3.反射与工厂模式
下面将使用通过class对象获取实例化对象改进传统工厂。
package com.com.company.reflection;
import java.util.Date;
public class TestRefection {
public static void main(String[] args) throws ClassNotFoundException {
//传统工厂
FastFood fastFood1= FastFactory1.getInstance("Hamburger");
fastFood1.eat();
//改进工厂(通过class对象的newInstance()获取对象)
try {
FastFood fastFood2=FastFactory2.getInstance("com.com.company.reflection.Pizza");//这里使用类的全限定名(包名.类名)
fastFood2.eat();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
//定义一个制造产品的抽象类
interface FastFood{
void eat();
}
//定义汉堡类实现产品抽象类
class Hamburger implements FastFood{
@Override
public void eat() {
System.out.println("吃汉堡");
}
}
//定义披萨类实现产品抽象类
class Pizza implements FastFood{
@Override
public void eat() {
System.out.println("吃披萨");
}
}
//定义一个工厂生产产品
//传统工厂 缺点:不能直接根据foodName生产产品,需要和已有的产品进行比较。如果新增产品,就需要改变工厂生产产品的方法。
class FastFactory1{
public static FastFood getInstance(String foodName){
FastFood fastFood=null;
if(foodName.equals("Hamburger")){
fastFood=new Hamburger();
}else if(foodName.equals("Pizza")){
fastFood=new Pizza();
}
return fastFood;
}
}
//改进传统工厂 使用class对象的newInstance()获取对象,当新增产品时,不需要对工厂生产产品的方法进行改动。
class FastFactory2{
public static FastFood getInstance(String foodName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
FastFood fastFood=null;
Class cls=Class.forName(foodName);//获取对象的class对象,通过class对象的newInstance()获取对象。
fastFood=(FastFood)cls.newInstance();
return fastFood;
}
}
结果:
二、反射与类操作
1.取得父类信息
在Java中的任何程序中都会有父类Class类中就有通过下面的方法获取父类信息
public Package getPackage();取得类的包名称
public native Class<?super T> getSuperclass();取得父类的Class对象
public Class<?>[] getInterfaces();取得实现的父接口
使用及结果:
package com.com.company.reflection;
import java.util.Date;
public class TestRefection {
public static void main(String[] args){
Son son=new Son();
Class cls=son.getClass();
System.out.println(cls.getPackage());//获取包名
System.out.println(cls.getSuperclass());//获取Son的父对象
Daughter daughter=new Daughter();
Class dcls=daughter.getClass();
Class[] classes=dcls.getInterfaces();
//获取Daughter实现的父接口
for(Class c:classes){
System.out.println(c.getName());
}
}
}
interface A{
void methodA();
}
interface B{
void methodB();
}
class Father {
}
class Son extends Father{ }
class Daughter implements A,B{
@Override
public void methodA() {
}
@Override
public void methodB() {
}
}
结果:
2.反射调用构造
在上面的class对象的newInstance()中,获取的是一个无参构造函数产生的对象。如果想要用有参构造函数,Class类中也提供了有参构造方法及调用。
构造方法:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchmethodException,SecurityException;取得指定参数类型的构造
public Constructor<?>[] getConstructors() throw SecurityException;取得类中的所有构造
获取类中的所有构造方法
package com.com.company.reflection;
import java.lang.reflect.Constructor;
import java.util.Date;
class Person{
private int age;
private String name;
public Person(){}
public Person(int age){
this.age=age;
}
public Person(int age,String name){
this.age=age;
this.name=name;
}
}
public class TestRefection {
public static void main(String[] args) {
Person person = new Person();
Class pclass = person.getClass();
Constructor[] constructor = pclass.getConstructors();
for (Constructor c : constructor) {
System.out.println(c.toString());
}
}
}
结果:
上面两个方法的返回类型都是java.lang.reflent.Constructor类的实例化对象,我们只需要关注一个方法。
public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException;获取实例化对象
Class类中的newInstance()只提供了无参的构造方法实例化对象,如果我们想要通过实例化对象时赋予某种属性,这时我们就可以使用getConstructor(),给构造方法指定参数类型,从而实例化对象。
package com.com.company.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class Person{
private int age;
private String name;
public Person(int age){
this.age=age;
}
public Person(int age,String name){
this.age=age;
this.name=name;
}
public String toString(){
return "姓名:"+this.name+",年龄:"+this.age;
}
}
public class TestRefection {
public static void main(String[] args) {
Class pclass=Person.class;
try {
//取得指定参数类型的构造方法
Constructor constructor=pclass.getConstructor(int.class,String.class);
try {
//通过新得到的构造方法实例化对象
Object person=constructor.newInstance(45, " 张三");
System.out.println(person.toString());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
结果:
3.反射调用普通方法(核心)
3.1 通过Class对象取得全部普通方法
public Method[] getMethods() throws SecurityException;
3.2 通过Class对象取得指定普通方法
public Method getMethod(String name,Class<?>... parameterTypes);
3.3 调用方法(由Method对象调用,第一个参数是设置的对象,后面的是参数列表)
public Object invoke(Object obj,Object... args) throws IllegalAccessException,llegalArgumentException,InvocationTargetException;
使用示例:
package com.com.company.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
class Person{
private int age;
private String name;
public Person(){}
public Person(int age){
this.age=age;
}
public Person(int age,String name){
this.age=age;
this.name=name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString(){
return "姓名:"+this.name+",年龄:"+this.age;
}
}
public class TestRefection {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class pclass=Person.class;
//通过类对象实例化Person对象
Person person=(Person)pclass.newInstance();
//取得类的全部普通方法(包括继承自Object类的方法)
Method[] methods=pclass.getMethods();
for(Method m:methods){
System.out.println(m);
}
//取得指定普通方法
Method setAgeMethod=pclass.getMethod("setAge", int.class);
//调用方法
setAgeMethod.invoke(person,35);
System.out.println(person.getAge());
}
}
结果:
4.反射调用类中属性
public Field[] getFields() throws SecurityException; 取得类中被public修饰的全部属性(包括父类中的)(Class对象调用)
public Field getField(String name) throws NoSuchFieldException,SecurityException
;取得类中指定名称属性(Class对象调用)
public Field[] getDeclaredFields() throws SecurityException;取得本类中全部属性(Class对象调用)
public Method getDeclaredField(String name) throws NoSuchFieldException,SecurityException;
取得本类中指定名称全部属性(Class对象调用)
public void set(Object obj,Object value) throws (IllegalArgumentException,IllegalAccessException;设置属性内容(由获取得到的Field对象调用)
public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException;取得属性内容(由获取得到的Field对象调用)
使用示例:
package com.com.company.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
class Person{
public int age;
public String name;
public String toString(){
return "姓名:"+this.name+",年龄:"+this.age;
}
}
class Student extends Person{
public String teacherName;
}
public class TestRefection {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class pclass=Student.class;
Person student= (Person) pclass.newInstance();
//获取类(包括父类)中被public修饰的全部属性
Field[] fields=pclass.getFields();
for(Field field:fields) {
System.out.println(field.toString());
}
System.out.println("-------------------------------------");
//取得类中指定名称属性(包括父类)
Field myField=pclass.getField("age");
System.out.println(myField.toString());
System.out.println("-------------------------------------");
//取得本类中全部属性
Field[] sfields=pclass.getDeclaredFields();
for(Field f:sfields){
System.out.println(f.toString());
}
System.out.println("-------------------------------------");
//取得本类中指定名称属性(如果指定的是父类中的属性,将不会显示)
Field sfield=pclass.getDeclaredField("teacherName");
System.out.println(sfield.toString());
System.out.println("-------------------------------------");
//设置属性内容(通过获取得到的属性名称)
sfield.set(student,"Peter");
System.out.println(sfield.get(student));
}
}
结果为: