一、Java的反射机制
每个Java程序执行前都必须经过编译、加载、连接、和初始化这几个阶段,后三个阶段如下图:
其中![]()
![]()
![](https://img-blog.csdn.net/20140326130535703?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVjaGFvZGVjaHVudGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
i、加载是指将编译后的java类文件(也就是.class文件)中的二进制数据读入内存,并将其放在运行时数据区的方法区内,然后再堆区创建一个Java.lang.Class对象,用来封装类在方法区的数据结构。即加载后最终得到的是Class对象,并且更加值得注意的是:该Java.lang.Class对象是单实例的,无论这个类创建了多少个对象,他的Class对象时唯一的!!!!。 而 加载并获取该Class对象可以通过三种途径:
Class.forName(类的全路径)、类.class(属性)、实例对象getClass()。关于他们的区别将在下面讲到!!!
###另外 ,类加载时类中的静态代码块会得到执行(详见前一篇博客:Class.forName()加载JDBC驱动)
ii、在 连接和初始化阶段,其实静态变量经过了两次赋值:第一次是静态变量类型的默认值;第二次是我们真正赋给静态变量的值。
iii、Java对类的使用分为两种方式:主动使用和被动使用。其中主动使用如下图:
![](https://img-blog.csdn.net/20140326130711046?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVjaGFvZGVjaHVudGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
而类的初始化时机正是java程序对类的首次主动使用,除了以上6中方式,其他对类的使用都是被动使用,都不会导致类的初始化。 并且应该注意以下几个方面:
![](https://img-blog.csdn.net/20140326130839312?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWVjaGFvZGVjaHVudGlhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
在这里可以看出;
二、Class.forName、实例对象.class(属性)、实例对象getClass()的区别
1、相同点:
通过这几种方式,得到的都是Java.lang.Class对象(这个是上面讲到的 类在加载时获得的最终产物)
例如:
- package demo;
- public class A
- {
- public static void main(String[] args) throws Exception
- {
- System.out.println(A.class);
-
- A a = new A();
- System.out.println(a.getClass());
- System.out.println(Class.forName("demo.A"));
- System.out.println(".................................");
- System.out.println(a);
- System.out.println(A.class.newInstance());
- System.out.println(a.getClass().newInstance());
- System.out.println(Class.forName("demo.A").newInstance());
- }
- }
结果:class demo.A
class demo.A
class demo.A (这里也可以得到一个类的Class对象是唯一的)
.......................................
demo.A@de6ced
demo.A@c17164
demo.A@1fb8ee3
demo.A@61de33
2、区别:
1)Class cl=A.class; JVM将使用类A的类装载器,将类A装入内存(前提是:类A还没有装入内存),不对类A做类的初始化工作.返回类A的Class的对象
2)Class cl=对象引用o.getClass();返回引用o运行时真正所指的对象(因为:儿子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象
3)Class.forName("类名"); JAVA人都知道.装入类A,并做类的初始化
附:
从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用Class对象的newInstance()方法的时候,就必须保证:1、这个 类已经加载;2、这个类已经连接了。而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。
现在可以看出,Class对象的newInstance()(这种用法和Java中的工厂模式有着异曲同工之妙)实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。
最后用最简单的描述来区分new关键字和newInstance()方法的区别:
newInstance: 弱类型。低效率。只能调用无参构造。
new: 强类型。相对高效。能调用任何public构造。