Java反射实践
2016-07-04 22:33
387 查看
在讲Java的反射机制之前必须先了解Java的Class类和Class对象:
一、Java中的Class类是一个什么样的类
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。
二、Java中的Class对象和实例对象
java中把生成Class对象和实例对象弄混了,更何况生成Class对象和生成instance都有多种方式。所以只有弄清其中的原理,才可以深入理解。首先要生成Class对象,然后再生成Instance。那Class对象的生成方式有哪些呢,以及其中是如何秘密生成的呢?
Class对象的生成方式如下:
Class.forName(“类名字符串”) (注意:类名字符串必须是全称,包名+类名)
类名.class
实例对象.getClass()
通过一段小程序,来观察一下Class对象的生成的原理。
测试的结果如下:
—静态的参数初始化—
testForName—class TestClassType
testTypeClass—class TestClassType
—-非静态的参数初始化—
—-构造函数—
testGetClass—class TestClassType
根据结果可以发现,三种生成的Class对象一样的。并且三种生成Class对象只打印一次“静态的参数初始化”。
我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是new类实例对象的时候加载。
因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断内存中是否已经加载。
所以,生成Class对象的过程其实是如此的:
当我们编写一个新的Java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。
三、认识Java的Class类
对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意一个方法和属性;
java.lang.reflect.Constructor:提供关于类的单个构造方法的信息以及对它的访问权限。
java.lang.reflect.Field:提供有关类或接口的单个字段的信息,以及对它的动态访问权限
java.lang.reflect.Method:提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
java.lang.reflect.Modifier:提供了类、类的成员、static方法、常量的修饰符。
c.getField(name) ;
c.getFields()
c.getDeclaredFields()
c.getDeclaredField(name)
**注意:getField()方法返回这个类或其超类的公有字段(不包括private和protect修饰的字段)
getDeclaredField方法返回这个类的全部字段(包括private和protect修饰的字段)**
c.getDeclaredConstructors()
c.getConstructors()
c.getDeclaredConstructor(parameterTypes)
c.getDeclaredConstructor(parameterTypes)
c.getMethods();
c.getMethod(name, parameterTypes)
c.getDeclaredMethods();
c.getDeclaredMethod(name, parameterTypes)
输出结果如下:
高级软件工程师 10000.0
10000.0
高级软件工程师
输出结果如下:
10000.0
15000.0
一、Java中的Class类是一个什么样的类
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。
二、Java中的Class对象和实例对象
java中把生成Class对象和实例对象弄混了,更何况生成Class对象和生成instance都有多种方式。所以只有弄清其中的原理,才可以深入理解。首先要生成Class对象,然后再生成Instance。那Class对象的生成方式有哪些呢,以及其中是如何秘密生成的呢?
Class对象的生成方式如下:
Class.forName(“类名字符串”) (注意:类名字符串必须是全称,包名+类名)
类名.class
实例对象.getClass()
通过一段小程序,来观察一下Class对象的生成的原理。
public class TestClass { public static void main(String[] args) { try { //测试Class.forName() Class testTypeForName = Class.forName("TestClassType"); System.out.println("testForName---" + testTypeForName); //测试类名.class Class testTypeClass = TestClassType.class; System.out.println("testTypeClass---" + testTypeClass); //测试Object.getClass() TestClassType testGetClass = new TestClassType(); System.out.println("testGetClass---" + testGetClass.getClass()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class TestClassType { //构造函数 public TestClassType() { System.out.println("----构造函数---"); } //静态的参数初始化 static { System.out.println("---静态的参数初始化---"); } //非静态的参数初始化 { System.out.println("----非静态的参数初始化---"); } }
测试的结果如下:
—静态的参数初始化—
testForName—class TestClassType
testTypeClass—class TestClassType
—-非静态的参数初始化—
—-构造函数—
testGetClass—class TestClassType
根据结果可以发现,三种生成的Class对象一样的。并且三种生成Class对象只打印一次“静态的参数初始化”。
我们知道,静态的方法属性初始化,是在加载类的时候初始化。而非静态方法属性初始化,是new类实例对象的时候加载。
因此,这段程序说明,三种方式生成Class对象,其实只有一个Class对象。在生成Class对象的时候,首先判断内存中是否已经加载。
所以,生成Class对象的过程其实是如此的:
当我们编写一个新的Java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。
三、认识Java的Class类
Java反射机制原理:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。这种反射机制在Java当中给我们带来了极大的方便,被大量地应用于JavaBean中Java反射机制在我的理解当中就是下面几点
对一个给定的类名(以字符串形式提供)能动态构建一个对象实例对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意一个方法和属性;
在Java的反射中我们主要用到以下几个类,下面将逐个说明
java.lang.Class类:用于保存每个对象所属类的相关信息,在Java中,Object类中的getClass()方法将会返回一个Class类型的实例,(注意:记住这是一个类,和关键字class完全是两码事)java.lang.reflect.Constructor:提供关于类的单个构造方法的信息以及对它的访问权限。
java.lang.reflect.Field:提供有关类或接口的单个字段的信息,以及对它的动态访问权限
java.lang.reflect.Method:提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
java.lang.reflect.Modifier:提供了类、类的成员、static方法、常量的修饰符。
理论看上去非常简单,下面我们用代码来展示下如何正确使用它们,在此之前,我们先创建两个类,后面需要用到
public class Person { // Field private String person_name; protected int person_age; public boolean person_sex; // Construct public Person() { super(); } public Person(String name, int age, boolean sex) { super(); this.setPerson_name(name); this.person_age = age; this.person_sex = sex; } // Method public void AddAge(int year) { this.person_age += this.person_age + year; } public String getPerson_name() { return person_name; } public void setPerson_name(String person_name) { this.person_name = person_name; } } public class Employee extends Person{ private double saray; protected String gangwei; public Employee() { super(); } public Employee(double saray, String gangwei) { super(); this.saray = saray; this.gangwei = gangwei; } public double getSaray() { return saray; } public void setSaray(double saray) { this.saray = saray; } public String getGangwei() { return gangwei; } public void setGangwei(String gangwei) { this.gangwei = gangwei; } public void addSaray(double money){ this.saray+=money; } }
1. 根据类名构造一个实例
// 根据类名获取Class对象 Class c = Class.forName("Employee"); System.out.println(c.getName()); // 此处打印:Employee // 获取父类Class对象 Class cp = c.getSuperclass(); System.out.println(cp.getName());// 此处打印:Person // 获取类的访问修饰符 String modifiers = Modifier.toString(c.getModifiers()); System.out.println(modifiers); // 此处打印:public // 实例化 Object obj = c.newInstance(); //通过无参的构造函数进行实例化
2. 获取类的Field,主要有以下四个方法
Class c = Class.forName(“Employee”);c.getField(name) ;
c.getFields()
c.getDeclaredFields()
c.getDeclaredField(name)
**注意:getField()方法返回这个类或其超类的公有字段(不包括private和protect修饰的字段)
getDeclaredField方法返回这个类的全部字段(包括private和protect修饰的字段)**
/* * 获取类Employee的所有Field * * c.getField(name) c.getFields() c.getDeclaredFields() * c.getDeclaredField(name) */ Field[] fields = c.getDeclaredFields(); for (Field f : fields) { // 获取该Field的类型 Class type = f.getType(); // 获取该Field的名称 String name = f.getName(); // 获取该Field的访问修饰符 String sFieldModifiers = Modifier.toString(f.getModifiers()); System.out.println(sFieldModifiers + " " + type.getName() + " " + name + ";"); }
3. 获取类的构造函数
同Field一样,获取构造函数也有4个方法c.getDeclaredConstructors()
c.getConstructors()
c.getDeclaredConstructor(parameterTypes)
c.getDeclaredConstructor(parameterTypes)
/* * 获取类的构造函数 * c.getDeclaredConstructors() * c.getConstructors() * c.getDeclaredConstructor(parameterTypes) * c.getDeclaredConstructor(parameterTypes) */ Constructor[] constructors = c.getDeclaredConstructors(); for (Constructor cr : constructors) { // 构造函数名称 String name = cr.getName(); // 构造函数修饰符 String sConstructorModifiers = Modifier.toString(cr.getModifiers()); // 构造函数参数 Class[] paramTypes = cr.getParameterTypes(); String sParam = ""; for (int j = 0; j < paramTypes.length; j++) { if (j > 0) sParam += ", "; sParam += paramTypes[j].getName(); } System.out.println(sConstructorModifiers + " " + name + "(" + sParam + ");"); }
4. 获取类的Method
同Field一样,有4个获取方法的方法,另外多了一个方法,用于获取返回类型c.getMethods();
c.getMethod(name, parameterTypes)
c.getDeclaredMethods();
c.getDeclaredMethod(name, parameterTypes)
Method[] methods = c.getDeclaredMethods(); for (Method m : methods) { // 方法返回类型 Class returnType = m.getReturnType(); String sReturnType = returnType.toString(); // 方法名称 String name = m.getName(); // 方法修饰符 String sMethodModifiers = Modifier.toString(m.getModifiers()); // 方法参数 Class[] paramTypes = m.getParameterTypes(); String sParam = ""; for (int j = 0; j < paramTypes.length; j++) { if (j > 0) sParam += ", "; sParam += paramTypes[j].getName(); } System.out.println(sMethodModifiers + " " + sReturnType + " " + name + "(" + sParam + ");"); }
5. 利用反射动态给对象属性赋值,比如我们需要从配置文件中读取某个员工的信息,并赋值给某个对象,我们可以这样写:
Class c2 = Class.forName("Employee"); Object e = c2.newInstance(); Field f = c2.getDeclaredField("saray"); // 因为属性saray是private的,所有需要调用setAccessible方法才能给该属性赋值,否则报错 f.setAccessible(true); f.set(e, 10000); Field f2 = c2.getDeclaredField("gangwei"); f2.set(e, "高级软件工程师"); System.out.println(((Employee) e).getGangwei() + " " + ((Employee) e).getSaray()); // 取值 Field f3 = c2.getDeclaredField("saray"); Field f4 = c2.getDeclaredField("gangwei"); f3.setAccessible(true); System.out.println(f3.get(e)); System.out.println(f4.get(e));
输出结果如下:
高级软件工程师 10000.0
10000.0
高级软件工程师
6. 利用反射动态地调用对象的方法,比如Employee类中有个addsaray的方法,我们看下要怎么调用:
Class c3 = Class.forName("Employee"); Object e3 = c3.newInstance(); Field fSaray = c3.getDeclaredField("saray"); fSaray.setAccessible(true); fSaray.set(e3, 10000); System.out.println(fSaray.get(e3)); //输出10000 // 获取Method Method m = c3.getDeclaredMethod("addSaray", double.class); /* * Method m = c3.getDeclaredMethod("addSaray"); Method m2 = * c3.getDeclaredMethod("addSaray", double.class); * 如果有多个重名的方法,要添加参数让编译器知道应该调用哪个方法,这里只有一个参数,所以上面两种写法都可以 */ // 调用方法 m.invoke(e3, 5000); System.out.println(fSaray.get(e3)); //输出15000,因此证明方法调用成功
输出结果如下:
10000.0
15000.0
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树