注:看完我这篇可以看看
http://www.importnew.com/21235.html
https://blog.csdn.net/luanlouis/article/details/18777099
很多书上都说,在java的世界里,一切皆对象。其实从某种意义上说,在java中有两种对象:实例对象和Class对象。实例对象就是我们平常定义的一个类的实例
而Class对象是没办法用new关键字得到的,因为它是jvm生成用来保存对应类的信息的,换句话说,当我们定义好一个类文件并编译成.class字节码后,编译器同时为我们创建了一个Class对象并将它保存.class文件中。我不知道这样描述是否妥当,因为我也见过某些书上直接把.class文件称之为Class对象。同时在jvm内部有一个类加载机制,即在需要的时候(懒加载)将.class文件和对应的Class对象加载到内存中。总之要有这样一个意识,Person.java文件编译成Person.class的同时也会产生一个对应的Class对象。
当程序要使用某个类事,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类的初始化:
加载:就是将class文件读入内存,并为之创建一个class对象。(任何类被使用时,系统都会建立一个class对象)
连接:1)验证:是否有正确的内部结构,并和其他类协调一致;
2)准备:负责为类的静态成员分配内存,并设置默认初始化值(将静态成员变量从类里面单独拿出来,把它放到内存方法区当中的数据共享区当中,再为它标记一个可属,告诉属于哪个类的);
3)解析:将类的二进制数据中的符号引用替换为直接引用;
初始化:给类建立对象(new),这一步是我们做的。
1)创建类的实例(new)
2)类的静态变量,或者为静态变量赋值
3)类的静态方法
4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5)初始化某个类的子类(父类比子类先进内存,所以可以看出Object类是最先进内存的)
6)直接使用java.exe命令来运行某个主类
1)Bootstrap ClassLoader 根加载器:也被称为引导加载器,负责java核心类的加载(比如System,String等)
2)Extension ClassLoader 扩展类加载器:负载JRE的扩展目录中的jar包加载
3)Application ClassLoader负责在JVM启动时加载来自java命令的class文件,已经classpath环境变量所指定的jar包和类路径。
深入类加载器层次结构(三种类加载器)代理加载模式,双亲委托机制
我们首先要知道在java中,类加载器也是分等级的。最高级的一种加载器是加载java中的核心包下的类。比如说java.ang.String类就是通过这种类加载器进行加载的。下一个等级的就是额外的类加载器。也是加载一些类的。再下一级的就是应用程序的类加载器。再下一级的就是自定义的类加载器了。但是这几种加载器之间不是继承关系的,而是组合关系的。这是要注意的。
在java中,类加载采用的是代理模式。所谓的代理模式,可以简单的理解为,看起来是这个类加载器进行加载,但其实并不是这个加载器进行加载。这就是代理模式。
在代理模式中,有一个比较重要的一种是双亲委托模式。所谓的双亲委托模式就是:某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次追溯,直到最高的爷爷辈的。如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
双亲委托机制的最大的好处就是可以确保是安全的。这种机制保证了不会出现用户自己能定义java.lang.Object类的情况出现。类加载器除了用于加载类,也是安全的最基本屏障。
比如说,我举一个例子:
我首先定义可java.lang包。然后我在这个package的下面定义了一个String类。由于String类是java中的核心包,理应是由最高等级的那个类加载器进行加载的。我们采用了双亲委托的模式: 由上往下,一层一层进行判断,是否能够加载。首先是在最高等级的那个类加载器当中,发现String类是核心包下的一个类。那么就不会对我们自定义的一个类进行加载了。所以即使我们定义了那个类,我们也不能使用这个类。这就是双亲委托模式的好处,可以确保了安全。但是也并不是所有的类加载的过程都是采用双亲委托模式的。比如Tomcat服务器采用的就不是双亲委托模式,它的加载的过程和双亲委托模式是正好相反的。但是也是代理的模式。
关于把calss文件加载到内存的知识我们差不多了解了。那么,我们仅仅站在class文件的角度,我们如何使用class文件中的内容呢?所以现在我们来研究反射
java反射机制是在运行状态中(不是编译时),对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java的语言的反射机制。
要想解刨一个类,必须先要获取到该类的字节码文件对象。而解刨使用的就是class类中的方法。所以先要获取到每一个字节码文件对应的class类型的对象。
Class没有公共构造方法。Class对象是在加载类时由java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。(java.lang.Class是描述文件对象的类)
基本的概念都讲完了,具体api的方法:
一:创建类的类对象
// 1.对象的引用.getClass(); // 已经知道该类的对象通过getClass方法
Class> cls1 = person.getClass();
// 2.类名.class--->实际在告诉我们任何一个类都有一个隐含的静态成员变量class
Class> cls2 = Person.class;
// 3.Class.forName(URL);
Class> cls3 = Class.forName("com.newer.cn.Person");
// 通过加载器加载类
Class> cls4 = TestPerson.class.getClassLoader().loadClass("com.newer.cn.Person");
//不管c1 or c2都代表了Foo类的类类型,一个类只可能是Class类的一个实例对象
System.out.println(c1 == c2);
结果:true