Java反射

反射技术

  • 其实就是动态加载一个指定的类,并获取该类中的所有的内容。

  • 而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,它允许程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作

  • 简单说:反射技术可以对一个类进行解剖。

反射的好处:大大的增强了程序的扩展性。

反射的基本步骤

  1. 获得Class对象,就是获取到指定的名称的字节码文件对象。

  2. 实例化对象,获得类的属性、方法或构造函数。

  3. 访问属性、调用方法、调用构造函数创建对象。

类类型  class Class  

用于描述程序中的各个类属于同一类事物的Java类,它封装了类的很多信息。

查看JDK中的源码:

public final
    class Class<T> implements java.io.Serializable,
                              java.lang.reflect.GenericDeclaration,
                              java.lang.reflect.Type,
                              java.lang.reflect.AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;

    private static native void registerNatives();
    static {
        registerNatives();
    }

    /*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.
     */
    private Class() {}

发现:Class类有构造器,并且它的构造方法是private的(可能是为了禁止开发者去自己创建Class类的实例)。

看到注释我们知道,这个类是JVM来创建的。如果我们拿到一个类的类型信息,就可以利用反射获取其各种成员以及方法了。

那么我们怎么拿到一个类的信息呢?【获取Class对象可以有3种方式】

如果没有对象实例的时候,主要有两种办法可以获取类类型:

Class cls1 = Show.class;//每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
Class cls2 = Class.forName("Show");//【推荐这种方法】这种方式的扩展性最强,只要将类名的字符串传入即可。如果类是在某个包中,要带上包名。
	public static void main(String[] args) throws Exception {
		Class<Show> cls1 = Show.class;
		Object obj1 = cls1.newInstance();
		System.out.println(obj1);

		Class<?> cls2 = Class.forName("Show");
		Object obj2 = cls2.newInstance();
		System.out.println(obj2);
	}

这样就创建了一个对象,缺点是:

第一,我们只能利用默认构造函数,因为Class的newInstance是不接受参数的;

第二,如果类的构造函数是private的,比如Class,我们仍旧不能实例化其对象。

如果有对象实例的话,除了上面的两种方法来获取类的信息外,还有第三种方法:对象.getClass()。弊端:必须要创建该类对象,才可以调用getClass方法。

反射就是把Java类中的各种成分映射成相应的Java类:

例如,一个Java类用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示【就像汽车是一个类,汽车中的发动机,变速箱也是一个个的类】。表示Java类的Class类中提供了一系列的方法来获取其中的变量(Field),方法(Method),构造方法(Contructor),修饰符(Modifiers),包(Package)等信息。

反射的用法

1.需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:

  • Class.forName(classname) 用于做类加载
  • obj.getClass() 用于获得对象的类型
  • 类名.class     用于获得指定的类型,传参用

2.反射类的成员方法:

  • public Method getMethod(String name,Class<?>... parameterTypes)//返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
  • public Method[] getMethods()//返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口的公共 member 方法。

3.反射类的构造函数:

  • public Constructor<?>[] getConstructors()      返回类中所有的public构造器集合,默认构造器的下标为0
  • public Constructor<T> getConstructor(Class<?>... parameterTypes)   返回指定public构造器,参数为构造器参数类型集合
  • public Constructor<?>[] getDeclaredConstructors()  返回类中所有的构造器,包括私有
  • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回任意指定的构造器

发现带上Declared的都是获得所有的构造方法,包括私有,这下我们就可以调用原本不允许调用的私有构造器了^_^

eg:		Class cls1 = Show.class;

		// 获取所有的构造方法集合
		Constructor[] con1 = cls1.getDeclaredConstructors();
		con1[1].setAccessible(true);// 设置可访问的权限
		Object obj1 = con1[1].newInstance(new Object[] { "adanac" });
		System.out.println(obj1);

		// 指定参数列表获取特定的方法
		Constructor con = cls1.getDeclaredConstructor(new Class[] { String.class });
		con.setAccessible(true);
		Object obj2 = con.newInstance(new Object[] { "lfz" });
		System.out.println(obj2);

4.获取类的成员变量:

  • public Field getDeclaredField(String name)  获取任意指定名字的成员
  • public Field[] getDeclaredFields()             获取所有的成员变量
  • public Field getField(String name)           获取任意public成员变量
  • public Field[] getFields()                          获取所有的public成员变量
		Class cls1 = Show.class;

		// 获取所有的构造方法集合
		Constructor[] con1 = cls1.getDeclaredConstructors();
		con1[1].setAccessible(true);// 设置可访问的权限
		Object obj1 = con1[1].newInstance(new Object[] { "adanac" });

		Field nameField = cls1.getDeclaredField("name");
		nameField.setAccessible(true);
		System.out.println(nameField.get(obj1));// 打印结果:adanac

现在私有变量也可以访问到了哈~~

获取了字节码文件对象后,最终都需要创建指定类的对象,创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):

  1. 调用空参数的构造函数:使用了Class类中的newInstance()方法。
  2. 调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。

注意:第二种方式必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。

Object obj = clazz.newInstance();//该实例化对象的方法调用就是指定类中的空参数构造函数,给创建对象进行初始化。

当指定类中没有空参数构造函数时,该如何创建该类对象呢?

Class<?> clazz = Class.forName("heimablog.Person");

// 既然类中没有空参数的构造函数,那么只有获取指定参数的构造函数,用该函数来进行实例化。
// 获取一个带参数的构造器。
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// 想要对对象进行初始化,使用构造器的方法newInstance();
Object obj = constructor.newInstance("zhagnsan", 30);
		
// 获取所有构造器。
Constructor[] constructors = clazz.getConstructors();// 只包含公共的
constructors = clazz.getDeclaredConstructors();// 包含私有的
for (Constructor<?> con : constructors) {
	System.out.println(con);
}        

【案例】

通过class取得一个类的全部框架

package heimablog;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

class hello {
    public static void main(String[] args) {
        Class<?> demo = null;
        try {
            demo = Class.forName("heimablog.Person");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("===============本类属性========================");
        // 取得本类的全部属性
        Field[] field = demo.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 权限修饰符
            int mo = field[i].getModifiers();//返回此类或接口以整数编码的 Java 语言修饰符。
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + field[i].getName() + ";");
        }
        System.out.println("===============实现的接口或者父类的属性========================");
        // 取得实现的接口或者父类的属性
        Field[] filed1 = demo.getFields();
        for (int j = 0; j < filed1.length; j++) {
            // 权限修饰符
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " "
                    + filed1[j].getName() + ";");
        }
    }
}

通过反射操作属性

package heimablog;

import java.lang.reflect.Field;

class hello {
	public static void main(String[] args) throws Exception {
		Class<?> cls1 = Class.forName("heimablog.Person");
		Object object = cls1.newInstance();
		
		Field field = cls1.getDeclaredField("name");
		field.setAccessible(true);
		field.set(object, "男");
		System.out.println(field.get(object));//打印结果:男
	}
}

通过反射取得并修改数组的信息:

package heimablog;

import java.lang.reflect.Array;

class hello {
	public static void main(String[] args) throws Exception {
		int[] temp={1,2,3,4,5};
        Class<?>demo=temp.getClass().getComponentType();
        System.out.println("数组类型: "+demo.getName());
        System.out.println("数组长度  "+Array.getLength(temp));
        System.out.println("数组的第一个元素: "+Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("修改之后数组第一个元素为: "+Array.get(temp, 0));
	}
}
/**
 * 数组类型: int
 *数组长度  5
 *数组的第一个元素: 1
 *修改之后数组第一个元素为: 100
 */

通过反射修改数组大小

package heimablog;

import java.lang.reflect.Array;

class hello{
    public static void main(String[] args) {
        int[] temp={1,2,3,4,5,6,7,8,9};
        int[] newTemp=(int[])arrayInc(temp,12);
        print(newTemp);
        System.out.println("^^^^^");
        String[] atr={"a","b","c"};
        String[] str1=(String[])arrayInc(atr,5);
        print(str1);
    }
     
    /**
     * 修改数组大小
     * */
    public static Object arrayInc(Object obj,int len){
        Class<?>arr=obj.getClass().getComponentType();
        Object newArr=Array.newInstance(arr, len);
        int co=Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }
    /**
     * 打印
     * */
    public static void print(Object obj){
        Class<?>c=obj.getClass();
        if(!c.isArray()){
            return;
        }
        System.out.println("数组长度为: "+Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i)+" ");
        }
    }
}
/**
 *数组长度为: 10
 *1 2 3 4 5 6 7 8 9 0 ^^^^^
 *数组长度为: 5
 *a b c null null 
 */

获得类加载器:

package heimablog;

class test{
     
}
class hello{
    public static void main(String[] args) {
        test t=new test();
        System.out.println("类加载器  "+t.getClass().getClassLoader().getClass().getName());
    }
}
//类加载器  sun.misc.Launcher$AppClassLoader

反射小例子:

package heimablog;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.Calendar;

public class ClassTest {

	public static void main(String[] args) {
		Calendar birthday = Calendar.getInstance();
		birthday.set(1966, 9, 11, 0, 0, 0);
		Student s1 = new Student("007", "Jack Brawer", true, new Timestamp(
				birthday.getTimeInMillis()), 1.80);

		ClassInfo classInfo = new ClassInfo();
		System.out.println(classInfo.getClassInfo(s1));
	}

}

// 该类可以获取任何类的元数据信息
class ClassInfo {
	public String getClassInfo(Object obj) {
		StringBuffer result = new StringBuffer();
		// 得到参数类变量的Class引用变量
		Class cls = obj.getClass();

		// 得到参数类变量的属性信息
		Field[] fields = cls.getDeclaredFields();

		// 得到参数类变量的完整类名(含有包的名称)
		String fullName = cls.getName();

		// 得到去除包名称的类名
		String className = fullName.substring(fullName.lastIndexOf('.') + 1);

		// 如果有包的定义,可以得到包的名称
		int packagePosition = fullName.lastIndexOf('.');
		String packageName = null;
		if (packagePosition < 0)
			packageName = "";
		else
			packageName = fullName.substring(0, fullName.lastIndexOf('.'));

		// 输出包名和类名
		result.append("包的名称为:" + packageName + "
");
		result.append("类的名称为:" + className + "
");

		// 输出类中所有的属性信息
		for (Field field : fields) {
			// 允许访问私有成员
			field.setAccessible(true);

			try {
				// 输出私有属性信息
				if (field.getModifiers() == Modifier.PRIVATE)
					result.append("私有属性" + field.getName() + ":值为"
							+ field.get(obj) + "
");

				// 输出受保护属性信息
				if (field.getModifiers() == Modifier.PROTECTED)
					result.append("受保护属性" + field.getName() + ":值为"
							+ field.get(obj) + "
");
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
		}
		return result.toString();
	}
}

// 学生类
class Student {
	// 学号
	private String number = null;

	// 姓名
	private String name = null;

	// 性别
	private boolean sex = false;

	// 生日
	private Timestamp birthday = null;

	// 身高
	private double height = 0;

	// 默认构造函数
	public Student() {
	}

	// 该构造函数可以对学生的属性进行初始化
	public Student(String number, String name, boolean sex, Timestamp birthday,
			double height) {
		setNumber(number);
		setName(name);
		setSex(sex);
		setBirthday(birthday);
		setHeight(height);
	}

	// 学号的读取函数
	public String getNumber() {
		return number;
	}

	// 学号的设置函数
	public void setNumber(String number) {
		this.number = number;
	}

	// 姓名的读取函数
	public String getName() {
		return name;
	}

	// 姓名的设置函数
	public void setName(String name) {
		this.name = name;
	}

	// 性别的读取函数
	public boolean isSex() {
		return sex;
	}

	// 性别的设置函数
	public void setSex(boolean sex) {
		this.sex = sex;
	}

	// 生日的读取函数
	public Timestamp getBirthday() {
		return birthday;
	}

	// 生日的设置函数
	public void setBirthday(Timestamp birthday) {
		this.birthday = birthday;
	}

	// 身高的读取函数
	public double getHeight() {
		return height;
	}

	// 身高的设置函数
	public void setHeight(double height) {
		this.height = height;
	}

	// 格式化字符信息输出
	public String toString() {
		return "学号:" + number + "/n姓名:" + name + "/n是否为男生:" + sex + "/n生日:"
				+ birthday + "/n身高:" + height;
	}
}
/*
 包的名称为:heimablog
类的名称为:Student
私有属性number:值为007
私有属性name:值为Jack Brawer
私有属性sex:值为true
私有属性birthday:值为1966-10-11 00:00:00.267
私有属性height:值为1.8
*/

在java中有三种类类加载器:

  1. Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。

  2. Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类

  3. AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。

原文地址:https://www.cnblogs.com/iadanac/p/3831516.html