黑马程序员之java类加载器和java中的反射机制学习

来源:互联网 发布:范冰冰 知乎 编辑:程序博客网 时间:2024/05/31 19:47

------- android培训、java培训、期待与您交流! ----------

学习反射,就应先了解类是如何被加载进内存的,即类加载器的概念。

类加载器:

与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做类加载器。

Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:

BootStrap:JRE/lib/rt.jar

ExtClassLoader:JRE/lib/ext/*.jar

AppClassLoader:CLASSPATH指定的所有jar或目录

可以自定义加载器,作为AppClassLoader加载器类的子类,自定义的加载器需要指定要加载的目录或jar中类加载器也是Java类,因为其他事Java类的类加载器本身也要被类加载器加载,显然必须有第一个加载器,这正是BootStrap,这个加载器不是Java类。

Java虚拟机中的所有类加载器用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或默认采用系统类加载器为其父类加载。

类加载器的委托机制:

1,首先当前线程的类加载器去加载线程中的第一个类;

2,如果类A中引用了类B,Java虚拟机将使用加载类A的加载器来加载B类;

3,还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

选择加载器的顺序:

例如指定了AppClassLoader这个加载器去加载类A,首先他会把这个任务交给它的父级加载器ExtClassLoader,

ExtClassLoader加载器又会把这个任务继续上交给自己的父级加载器BeanStrap,这个过程中每一级加载器是不会执行加载类的操作的。

直到找到顶级父类时,顶级父类才会在自己的负责加载的位置中找可以加载类,如果存在这个类就执行加载操作,

如果没有就会回交给ExtClassLoader这个子加载器,然后ExtclassLoader再去自己负责的位置中找要加载的类,如果找到就加载否则继续向下

回交给AppClassLoader这个子类,到这的时候如果还找不到要加载的类就会抛ClassNotFoundException异常,因为是AppClassLoader提交的任务需求,他不会再继续向下找子类加载器了。

例子:能不能自己写个类叫做java.lang.System?

可以写,但是由于类加载器采用委托机制,要加载这个类会由顶级父加载器Bootstrap加载,在BootStrap加载器中有一个同名System类,所以BootStrap会加载这个类到虚拟机内存中,是不会找到我们自己写的那个System类的;但是我也可以自定义一个类加载器来加载这个System类,

并对这个加载器进行处理让它不采用委托机制,这样就可以加载到我们自己写的System这个类了。

反射(1.2版)

Class:java程序中的各个java类属于同一类事物,描述这类事物的java类就是Class;
获取字节码实例对象:
1,Class cls1 = Date.class;
2,Person p1 = new Person();
Class cls2 = p1.getClass();
3,Class.forName("java.lang.String");
forName获取字节码有两种方式,1,字节码已经加载到java虚拟机中;
9个预定义Class实例对象,Boolean,byte,short,int,char,long,float,double,void;
总之,只要在程序中出现的类型,都有各自的class实例对象。
什么是反射:反射就是把java类中的各种成分映射成相应的java类。
反射要点:
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:Class.forName("java.lang.String").getConstructor(/*对应构造方法参数的class*/StringBuffer.class);//获取String构造函数参数对应StringBuffer的构造函数
创建实例对象方法:Constructor实例对象.newInstance();
调用获得的方法是要用到上面同类型的实力对象,getConstructor中参数类型Class.newInstance()用无惨构造方法创建实例对象
Filed类的对象代表字节码上的成员量,不是某一个对象的成员变量
ReflectPoint rpt1 = new ReflectPoint(3,4);
Field fieldY = rpt1.getClass().getField("y");
System.out.println(fieldY.get(rpt1));//需要用get方法获取具体某一对象的成员变量值
对于私有的成员变量,暴力反射:
Field fieldX = rpt1.getClass().getDeclaredField("x");//获取字节码内的成员变量对象
fieldX.setAccessible(true);//设置成员变量可访问
System.out.println(fieldX.get(rpt1));
getFields()获取类中所有的成员变量对象,返回Field数组对象实例
Method类代表某个类中的一个成员变量
得到类中的某一个方法:getMethod(methodname,参数变量类型.class...);
调用方法:invoke(obj,参数值)
如果传递给Method对象的invoke() 方法的第一个参数是null,说明Method对象对应的是一个静态方法;
jdk1.4和jdk1.5的invoke区别:
Jdk1.5:public Object invoke(Object obj,Object...args)
Jdk1.4:public Object invoke(Object obj, Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法是,数组中的每个元素分别对应被调用方法中的一个参数。

为什么使用反射调用类方法:

当不知道要调用哪个类的时候,类名作为参数传进方法中时;反射主要使用在框架中,你编好一个程序,但是这个程序中所使用的类不一定就是固定的,可以通过配置文件来修改。这个时候就要使用反射了。因为在写程序时无法知道要被调用的类名,所以,在程序中,无法直接new某个类的实例对象了,而要用反射方式来做

数组反射:当数组的数据类型相同,维度(一维,二维,跟数组长度无关)相同时返回的class对象是同一个

练习:

import java.lang.reflect.*;
public class ReflectTest {

public static void main(String[] args) throws Exception{

String str = "abc";

Class cls1 = str.getClass();

Class cls2 = String.class;

Class cls3 = Class.forName("java.lang.String");

System.out.println(cls1==cls2);

System.out.println(cls1==cls3);

System.out.println(cls1.isPrimitive());//是不是基本数据类型class

System.out.println(int.class==Integer.class);

System.out.println(int.class.isPrimitive());

System.out.println(int.class==Integer.TYPE);//返回Integer封装的基本类型的class

System.out.println(int[].class.isPrimitive());//数组的class不是基本数据类型

System.out.println(int[].class.isArray());//数组的class用isArray来判断

Constructor con = String.class.getConstructor(StringBuffer.class);

String str2 = (String)con.newInstance(new StringBuffer("abc"));

System.out.println(str2);

String str3 = (String)String.class.newInstance();

System.out.println(str3.length());

System.out.println(str3.endsWith(""));

ReflectPoint rpt1 = new ReflectPoint(3,4);

ReflectPoint rpt2 = new ReflectPoint(44,65);

Field fieldY = rpt1.getClass().getField("y");

System.out.println(fieldY.get(rpt1));

System.out.println(fieldY.get(rpt2));

//对于私有的成员变量,暴力反射

Field fieldX = rpt1.getClass().getDeclaredField("x");

fieldX.setAccessible(true);//设置成员变量可访问

System.out.println(fieldX.get(rpt1));

System.out.println(fieldX.get(rpt2));

changValue(rpt1);

System.out.println(rpt1);

//利用反射调用String的charAt方法

String string = "abcde";

Method method = String.class.getMethod("charAt", int.class);

System.out.println(method.invoke(string, 1));

//普通形式调用charAt方法

System.out.println(string.charAt(1));

String startingClassName = args[0];

Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);

Object[] args1 = new Object[]{new String[]{"123","456","789"}};

mainMethod.invoke(null, args1);

//数组反射

int[] a1 = new int[]{1,2,3};

int[] a2 = new int[4];

int[][] a3 = new int[3][4];

String[] a4 = new String[]{"a","b","c"};

System.out.println(a1.getClass()==a2.getClass());

System.out.println(a1.getClass()==a3.getClass());

System.out.println(a1.getClass()==a4.getClass());

System.out.println(a1.getClass().getSuperclass().getName());

printObject(a1);

printObject("sdf");

}

public static void printObject(Object obj){

Class objClass = obj.getClass();

if(objClass.isArray()){

int len = Array.getLength(obj);

for(int i=0;i<len;i++){

System.out.println(Array.get(obj, i));

}

}else{

System.out.println(obj);

}

}

public static void changValue(Object obj) throws Exception {

Field[] fields = obj.getClass().getFields();

for(Field field:fields){

if(field.getType()==String.class){//因为字节码对象实例只有一份所以用==进行比较

String oldValue = (String)field.get(obj);

String newValue = oldValue.replace("b", "a");

field.set(obj, newValue);

}

}

}

}

------- android培训、java培训、期待与您交流! ----------

0 0