• 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中默认的加载器。

  • 相关阅读:
    使用物化视图的方式进行表级数据同步示例
    【闲谈】我的大学
    firefox的window.onerror没有详细的出错提示
    完全搞懂傅里叶变换和小波(3)——泰勒公式及其证明
    [VC6 console]调用API获取手机归属地
    灰度直方图及处理“cvQueryHistValue_1D”: 找不到标识符”的问题(上)
    【高级】C++中虚函数机制的实现原理
    jquery小例子
    VBA Promming——入门教程
    Wikidata和SparQL简介
  • 原文地址:https://www.cnblogs.com/iadanac/p/3831516.html
Copyright © 2020-2023  润新知