Java中反射机制和类加载


简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息.
反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。

Java中类的加载分为三大类:

根加载器【Bootstrap Classloader】【启动类加载器】
这个Classloader装载Java虚拟机提供的基本运行时刻类($JAVA_HOME/jre/lib),还包括放置在系统扩展目录
($JAVA_HOME/jre/lib/ext)内的JAR文件中的类。这个Classloader是java程序最顶层的Classloader,只有它没有
父Classloader。如果你将一个自己写的类或第三方jar包放进$JAVA_HOME/jre/lib/ext目录中,那么它将被Bootstrap Classloader
装载。
扩展类加载器【Extension Classloader】

  该加载器器是用JAVA编写,且它的父类加载器是Bootstrap,是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext

目录中的类库。开发者可以这几使用扩展类加载器。

            我们知道java中系统属性java.ext.dirs指定的目录由ExtClassLoader加载器加载,如果程序中没有指定该系统属性(-Djava.ext.dirs=sss/lib)

那么该加载器默认加载$JAVA_HOME/lib/ext目录下的所有jar文件,通过程序来看下系统变量java.ext.dirs所指定的路径


系统类加载器【System ClassLoader】

System Classloader通常负责装载系统环境变量CLASSPATH中设置的类。由System Classloader装载的类对于Apusic服务器内部的类和部署在Apusic服务器上的J2EE应用(通常打包成ear)都是可见的。%APUSIC_HOME%/lib目录下的jar文件是Apusic应用服务器的核心类,一般把这些jar文件都加在系统CLASSPATH中。另外,一些公用类也可以加在系统CLASSPATH中,如JDBC驱动程序等。

当程序主动使用某个类时,若该类还没加载到内存中,系统会通过加载,链接,初始化3个操作对类进行初始化。
类字面常量”,class”创建Class对象的引用时,不会自动地初始化该Class对象,准备工作包含3个步骤:
1.加载:由类加载器执行,该步骤查找字节码,并从这些字节码中创建一个Class对象
2.链接:在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用。
3.初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块

类的初始化时机
1.创建类的实例
2.访问类或接口的静态变量(static final常量除外,static final变量可以)
3.调用类的静态方法
4.反射(Class.forName(packageName.className))
5.初始化类的子类(子类初始化问题:满足主动调用,即访问子类中的静态变量、方法,否则仅父类初始化)
6.java虚拟机启动时被标明为启动类的类
注:加载顺序:启动类的static block最先加载
(父类静态成员、静态代码块—>子类静态成员、静态代码块—>父类实例成员、代码块——>父类构造函数—>子类实例成员、代码块—>子类构造函数)

我们需要明白在JAVA中任何class都要装载在虚拟机上才能运行,而forClass就是装载类用的,这是要和new不一样,要分清楚哦。
A a = (A)Class.forName(“package.A”).newInstance();和 A a = new A;是等价的。
记住一个概念,静态代码是和class绑定的,class装载成功就表示执行了你的静态代码,而且以后不会再走这套静态代码了。
Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也即是说JVM会执行该类的静态代码段。

JAVA中获取Class对象有3种方式:
1.Class.forName(“包名.类名”)
2.Object.getClass()
3.类字面常量 xx.class

java反射的使用举例:多用于数据库的驱动的加载

package day21.reflect;

 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;

 public class Reflect2Demo {
 public static void main(String[] args) {


try {
//使用Class加载类对象,这里要写全了---> 包名.类名
Class<?> class1=Class.forName("day21.reflect.Student");

//获取构造器,当构造器有参数的时候,这里要吧构造器的参数类型的类给出来,不是Object.lang下的要写全名 
Constructor<?> constructor=class1.getConstructor(String.class,int.class,double.class);

//利用构造器初始化Studnet对象
Student student=(Student)constructor.newInstance("小亮",23,99);
System.out.println(student);
//利用反射获取对象属性
Field field1=class1.getField("name");//该方法只能获取公共属性
   //设置字段内容
field1.set(student, "熊大熊二");//覆盖了构造方法中的name属性值

Field field2=class1.getDeclaredField("age");//该方法可以获取方法,不一定公共的
field2.set(student, 12);
Field field3=class1.getDeclaredField("score");
//该字段是private的,所以要设置访问权限
field3.setAccessible(true);//设置归属性是可以访问的
field3.set(student, 89);
System.out.println(student);

//获取字段属性的值
String name=(String)field1.get(student);
int age=field2.getInt(student);
double score=field3.getDouble(student);
System.out.println("姓名:"+name+" 年龄:"+age+" 分数:"+score);

//反射获取方法并调用
Method method1=class1.getDeclaredMethod("mulip", int.class,int.class);
int sum1=(int)method1.invoke(student, 23,45);
System.out.println("结果:"+sum1);
//这个是公共方法可以下面的方法获取
Method method2=class1.getMethod("mulip", double.class,double.class);
double sum2=(double)method2.invoke(student, 2.3,4.5);
System.out.println("结果:"+sum2);

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 }
 }
 class Student{
public String name;
int age;
private double score;
public Student() {}
public Student(String name,int age,double score) {
this.name=name;
this.age=age;
this.score=score;
}


int mulip(int a,int b) {
return a*b;
}
public double mulip(double a,double b) {
return a*b;
}
public String toString() {
return "name : "+name+" age:"+age+" score:"+score;
}

 }

2.系统加载的使用例子

package day21.reflect;

 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;

 public class ClassLoadDemo {
 public static void main(String[] args) {
//通过系统加载器加载类
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
try {
Class<?> class1=classLoader.loadClass("day21.reflect.LoadClassD");
//可以通过Class的对象初始化,也可以通过构造函数初始化
 // LoadClassD loadClassD=(LoadClassD) class1.newInstance();
 // loadClassD.name="loadclass";
Constructor<?> constructor=class1.getConstructor(String.class);
//不知道具体类是用Object 类的对象接受
Object obj=constructor.newInstance("Object");
//通过强转后用具体类的对象接受
LoadClassD loadClassD=(LoadClassD) constructor.newInstance("LoadClassD");

System.out.println("构造器类的名: "+constructor.getName());
System.out.println("构造器返回对象时候的对象名:"+obj);
loadClassD.name="构造器初始化";
System.out.println("name: "+loadClassD.name);
System.out.println("具体类的对象名: "+loadClassD);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


 }
 }
 class LoadClassD{
String name;
public LoadClassD() {
System.out.println("我是loadclass的构造器");
}
public LoadClassD(String ref) {
System.out.println(ref+" 实现的构造器");
}
 }