java中反射机制及其类加载的几种方式的异同
2015-02-15 23:55
253 查看
一、Java中反射机制总结:
反射的定义:通过反射,把Java类中的各个组成部分映射到相应的Java类
反射的优点:
1. 减少对象的依赖,调用方法更灵活,改变属性的值。
2. 通过class对象得到该类的对象,从而获取到方法等。
3. 提高程序的扩展性
首先通过程序来演示一下反射原理:
有一个实体类Student.java
输出结果:
调用有一个参数的构造方法创建对象
Student [name=bbb]
调用有一个参数的构造方法创建对象
Student [name=aaa]
调用无参数构造方法创建对象
Student [name=abcd]
bbb
abcd学习
每个Java程序执行前都必须经过编译、加载、连接和初始化这几个阶段,后三个阶段如下图:
(1)加载是指将编译后的java类文件(.class文件)中的二进制数据读入内存,并将其放在运行时数据区的方法区内,然后再堆区创建一个java.lang.Class对象,用来封装类在方法区的数据结构。即加载后最终得到的是Class对象,并且更加值得注意的是:该Java.lang.Class对象是单实例的,无论这个类创建了对少个对象,它的Class对象是唯一的!而加载并获取该Class对象可以通过三种途径:Class.forName(类的全名称)、实例对象.class(属性)、实例对象getClass()。
(2)在连接和初始化阶段,其实静态变量经过了两次赋值:第一次是静态变量类型的默认值;第二次是我们真正赋给静态变量的值。
(3)Java对类的使用分为两种方式:主动使用和被动使用。其中主动使用的方式有以下6种,
①创建类的实例
②访问某个类或接口的静态变量,或者对该静态变量赋值
③调用类的静态方法
④反射(如Class.forName(“java.lang.String”))
⑤初始化一个类的子类
⑥Java虚拟机启动时被标明为启动类的类
而类的初始化时机正是java程序对类的首次主动使用,除了以上6种方式,其他对类的使用都是被动使用,都不会导致类的初始化。并且应注意以下几个方面:
①调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
②当java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。
l 在初始化一个类时,并不会先初始化它所实现的接口。
l 在初始化一个接口时,并不会先初始化它的父接口。
因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。
在这里可以看出,接口的两重性:可以把接口当做类(因为在接口中有静态变量时,可以被初始化);接口就是接口,和类无关(接口中没有构造方法,所以不能被初始化)。
二、Class.forName、实例对象.class(属性)、实例对象getClass()的异同:
1、相同点
通过这几种方式,得到的都是java.lang.Class对象
例如:
2、区别
① Classc1 = String.class;JVM将使用String类的类装载器,将String类装入内存(前提是String类还没有装入内存),不对String类做类的初始化工作,返回String类的Class对象。
② Class.forName(“类名”);装入类**,并做类的初始化
③ Class= 对象引用o.getClass();返回引用对象o运行时真正所指的对象(因为儿子对象的引用可能会赋给父对象的引用变量中)所属的类的Class对象
从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用Class对象的newInstance()方法的时候,就必须保证:1.这个类已经加载;2.这个类已经连接了。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载java API的那个加载器。
现在可以看出,Class对象的newInstance()实际上是把new这个方法分解为两步,即首先调用Class加载方法加载某个类,然后实例化。这样分布的好处:我们可以在调用Class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。
最后,用最简单的描述来区分new关键字和newInstance()方法的区别:
newInstance:弱类型,低效率,只能调用无参构造;
new:强类型,相对高效,能调用任何Public构造。
反射的定义:通过反射,把Java类中的各个组成部分映射到相应的Java类
反射的优点:
1. 减少对象的依赖,调用方法更灵活,改变属性的值。
2. 通过class对象得到该类的对象,从而获取到方法等。
3. 提高程序的扩展性
首先通过程序来演示一下反射原理:
有一个实体类Student.java
package com.huaxin.reflect; /** * @author 赵芳 * 2015年2月15日 下午5:23:30 */ public class Student { public String name = "abcd"; /** * @param string */ public Student(String name) { System.out.println("调用有一个参数的构造方法创建对象"); this.name = name; } public Student() { System.out.println("调用无参数构造方法创建对象"); } @Override public String toString() { return "Student [name=" + name + "]"; } public void study(){ System.out.println(name+"学习"); } }一个测试类Test.java
package com.huaxin.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * java中反射机制的测试 * @author 赵芳 * 2015年1月24日 上午9:56:15 */ public class Test { public static void main(String[] args) throws Exception{ //通过反射获取构造方法Constructor 创建对象 Class c = Class.forName("com.huaxin.reflect.Student"); Constructor cons = c.getDeclaredConstructor(String.class); // cons.setAccessible(true);//访问权限 Student stu = (Student) cons.newInstance("bbb"); System.out.println(stu); //使用new关键字创建对象 Student stu1 = new Student("aaa"); System.out.println(stu1); // 创建此 Class 对象所表示的类的一个新实例,只能是无参 Student stu2 = (Student) c.newInstance(); System.out.println(stu2); //通过反射获取Field,并且该属性在此类中是public的,否则会抛出NoSuchFieldException Field field_name = c.getField("name"); System.out.println(field_name.get(stu)); //通过反射来调用方法Method Method method = c.getMethod("study", null); method.invoke(stu2, null); } }
输出结果:
调用有一个参数的构造方法创建对象
Student [name=bbb]
调用有一个参数的构造方法创建对象
Student [name=aaa]
调用无参数构造方法创建对象
Student [name=abcd]
bbb
abcd学习
每个Java程序执行前都必须经过编译、加载、连接和初始化这几个阶段,后三个阶段如下图:
(1)加载是指将编译后的java类文件(.class文件)中的二进制数据读入内存,并将其放在运行时数据区的方法区内,然后再堆区创建一个java.lang.Class对象,用来封装类在方法区的数据结构。即加载后最终得到的是Class对象,并且更加值得注意的是:该Java.lang.Class对象是单实例的,无论这个类创建了对少个对象,它的Class对象是唯一的!而加载并获取该Class对象可以通过三种途径:Class.forName(类的全名称)、实例对象.class(属性)、实例对象getClass()。
(2)在连接和初始化阶段,其实静态变量经过了两次赋值:第一次是静态变量类型的默认值;第二次是我们真正赋给静态变量的值。
(3)Java对类的使用分为两种方式:主动使用和被动使用。其中主动使用的方式有以下6种,
①创建类的实例
②访问某个类或接口的静态变量,或者对该静态变量赋值
③调用类的静态方法
④反射(如Class.forName(“java.lang.String”))
⑤初始化一个类的子类
⑥Java虚拟机启动时被标明为启动类的类
而类的初始化时机正是java程序对类的首次主动使用,除了以上6种方式,其他对类的使用都是被动使用,都不会导致类的初始化。并且应注意以下几个方面:
①调用ClassLoader类的loadClass方法加载一个类,并不是对类的主动使用,不会导致类的初始化。
②当java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。
l 在初始化一个类时,并不会先初始化它所实现的接口。
l 在初始化一个接口时,并不会先初始化它的父接口。
因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。
在这里可以看出,接口的两重性:可以把接口当做类(因为在接口中有静态变量时,可以被初始化);接口就是接口,和类无关(接口中没有构造方法,所以不能被初始化)。
二、Class.forName、实例对象.class(属性)、实例对象getClass()的异同:
1、相同点
通过这几种方式,得到的都是java.lang.Class对象
例如:
2、区别
① Classc1 = String.class;JVM将使用String类的类装载器,将String类装入内存(前提是String类还没有装入内存),不对String类做类的初始化工作,返回String类的Class对象。
② Class.forName(“类名”);装入类**,并做类的初始化
③ Class= 对象引用o.getClass();返回引用对象o运行时真正所指的对象(因为儿子对象的引用可能会赋给父对象的引用变量中)所属的类的Class对象
从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用Class对象的newInstance()方法的时候,就必须保证:1.这个类已经加载;2.这个类已经连接了。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载java API的那个加载器。
现在可以看出,Class对象的newInstance()实际上是把new这个方法分解为两步,即首先调用Class加载方法加载某个类,然后实例化。这样分布的好处:我们可以在调用Class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。
最后,用最简单的描述来区分new关键字和newInstance()方法的区别:
newInstance:弱类型,低效率,只能调用无参构造;
new:强类型,相对高效,能调用任何Public构造。
相关文章推荐
- Java基础:JAVA程序类加载及其反射机制
- JAVA程序的类加载及其反射机制
- JAVA程序类加载及其反射机制
- Java加载src目录下文件的几种方式
- 黑马程序员--反射机制和类加载器--java学习日记12(高新技术)
- java中类加载路径和项目根路径获取的几种方式
- java的反射和它的类加载机制
- Java 的加载与反射机制
- java通过反射机制加载类方法和实例方法
- java中类加载路径和项目根路径获取的几种方式
- java中类加载路径和项目根路径获取的几种方式
- Java文件上传的机制与原理以及几种协议方式
- [Java]利用反射机制动态加载并创建包含参数的对象
- 深入理解Java:类加载机制及反射
- java 反射机制及其应用
- java 加载图片的几种方式
- Java高新技术【4】 反射机制 及 Java类加载原理及类加载器
- Java高新技术【4】 反射机制 及 Java类加载原理及类加载器
- Java 加载图片的几种方式
- Java高新技术(二)之类加载机制和反射