黑马程序员_Java反射机制

来源:互联网 发布:无人机用什么语言编程 编辑:程序博客网 时间:2024/05/21 01:47

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

类的加载:
    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类的初始化。
     加载:
        就是指将class文件读入内存,并为之创建一个Class对象。
        任何类被使用时系统都会建立一个Class对象。
    连接:
        验证 是否有正确的内部结构,并和其他类协调一致
        准备 负责为类的静态成员分配内存,并设置默认初始化值
        解析 将类的二进制数据中的符号引用替换为直接引用
    初始化:
         当一个对象被创建之后,虚拟机会为其分配内存
        在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值。
类初始化时机
    创建类的实例
    访问类的静态变量,或者为静态变量赋值
    调用类的静态方法
    使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    初始化某个类的子类
    直接使用java.exe命令来运行某个主类
类加载器
    负责将.class文件加载到内在中,并为之生成对应的Class对象。
    虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
类加载器的组成
    Bootstrap ClassLoader 根类加载器
    Extension ClassLoader 扩展类加载器   
    Sysetm ClassLoader 系统类加载器
Bootstrap ClassLoader 根类加载器
    也被称为引导类加载器,负责Java核心类的加载
        比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
    负责JRE的扩展目录中jar包的加载。
        在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
反射
    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

我们来看看获取字节码文件的方式。

反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
 Person p = new Person
 p.调用
 
 想要这样使用,首先你必须要得到class文件对象,其实也就是得到Class类的对象。
 Class类:
  成员变量:Field
  构造方法:Constructor
  成员方法:Method


先准备一个Person类以便后面的操作

private String name;int age;public String address;public Person(){}private Person(String name){this.name = name;}Person(String name,int age){this.name = name;this.age = age;}public Person(String name,int age,String address){this.name = name;this.age = age;this.address = address;}public void show(){System.out.println("show");}public void method(String s){System.out.println("method "+s);}public String getString(String s,int i){return s+"---"+i;}private void function(){System.out.println("function");}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";}
下面我们开始操作

<span style="white-space:pre"></span>//方式1Person p = new Person();Class c = p.getClass();//通过Object的getClassPerson p2 = new Person();Class c2  = p.getClass();System.out.println(p == p2);//因为属于不同的对象所以是falseSystem.out.println(c == c2);//他们对应的同一份字节码文件为true//方式2Class c3 = Person.class;//静态的通过类名.class能获得该字节码文件System.out.println(c == c3);//方式3Class c4 = Class.forName("com.reflect.Person");//找到该字符串对应的字节码文件。System.out.println(c == c4);
当我们拿到了一个字节码文件对象后,我们就可以对立面的内容进行操作了

首先来看看怎么样能拿到构造方法。

Construct:

//获取字节码文件Class c = Class.forName("com.reflect.Person");//获取构造方法。//public Constructor[] getConstructors()//返回公共的构造方法、获取到所有的构造方法存入一个数组里面//public Constructor[] getDeclaredConstructors()//返回所有的//Constructor[] cons = c.getDeclaredConstructors();//for(Constructor con:cons)//{//System.out.println(con);遍历整个数组拿到构造方法//}//获取单个构造方法。//public Constructor<T> getConstructor(Class<?>... parameterTypes)//参数表示的是:你要获取的构造方法的参数个数及参数类型的class字节码文件对象。Constructor con = c.getConstructor();//返回的是构造函数对象。//public T newInstance(Object...initargs)//使用此Constructor对象表示的构造方法来创建构造方法的声明的新实例,并用指定的初始化参数初始化该实例。Object obj = con.newInstance();//通过该构造函数对象创建一个实例System.out.println(obj);//创建一个实例对象。
我们发现上诉代码并未拿到私有的方法那我们来看看有参数的,并且私有的构造函数的操作。

//获取字节码文件对象。Class c = Class.forName("com.reflect.Person");//获取带参数的构造方法对象。Constructor con = c.getConstructor(String.class,int.class,String.class);//通过带参构造方法对象创建对象。Object obj = con.newInstance("张三",25,"北京");System.out.println(obj);

Class c = Class.forName("com.reflect.Person");Constructor con = c.getDeclaredConstructor(String.class);//获取到有一个String类型参数的构造函数。con.setAccessible(true);//当出现不能访问的时候就需要强制访问它的私有对象。Object obj = con.newInstance("李四");//我们发现通过getDeclaredConstructor()拿到了所有的构造方法但是却不能访问。System.out.println(obj);


Field:
在我们拿到了构造函数后就需要对各个成员变量进行进一步的访问,

Class c = Class.forName("com.reflect.Person");//Field[] fields = c.getFields();获取所有公共的成员变量。存入数组中//Field[] fields = c.getDeclaredFields();获取所有的成员变量。//for(Field field : fields)//{//System.out.println(field);//通过遍历获得成员变量//}Constructor con = c.getConstructor();//创建一个对象,以便对成员变量进行操作。Object obj = con.newInstance();//获取单个成员变量。//获取address,并对其赋值。Field addressField = c.getField("address");addressField.set(obj, "北京");//给obj对象的addressField字段赋值为"北京"System.out.println(obj);Field nameField = c.getDeclaredField("name");nameField.setAccessible(true);nameField.set(obj, "张三");System.out.println(obj);

剩下最后一个Method了,我们来看看它的各种方法。

Class c = Class.forName("com.reflect.Person");//获取所有的方法//Method[] methods = c.getMethods();获取自己的包括父类的公共方法。存入数组//Method[] methods = c.getDeclaredMethods();//获取自己的所有的方法//for(Method method:methods)//{//System.out.println(method);//}Constructor con = c.getConstructor();Object obj = con.newInstance();//获取单个方法并使用。获取show();//public Method getMethod(String name, Class<?>... parameterTypes)//第一个参数表示方法名,第二个参数表示的是方法参数的clas类型。Method m1 = c.getMethod("show");//public Object invoke(Object obj, Object... args)//返回值是Object接收,第一个参数表示对象是谁,第二个参数表示调用该方法的实际参数。m1.invoke(obj);//调用obj对象的m1方法。Method m2 = c.getMethod("method", String.class);m2.invoke(obj, "hello");Method m3 = c.getMethod("getString", String.class,int.class);Object obj1 = m3.invoke(obj, "张三",26);//String s = (String)m3.invoke(obj, "张三",26);System.out.println(obj1);Method m4 = c.getDeclaredMethod("function");m4.setAccessible(true);m4.invoke(obj);

反射中的三个对象已经了解的差不多了,他们的各类操作几乎都是一样的,我们来看看他们的实际应用。

看看反射对数组的操作:

需求是:我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现。

//创建新集合ArrayList<Integer> array = new ArrayList<Integer>();Class c = array.getClass();//得到集合的class对象Method method = c.getMethod("add", Object.class);//获取到c的add方法,传入的是Object类型method.invoke(array, "hello");//调用array的add方法,传入hellomethod.invoke(array, "world");System.out.println(array);


我们知道反射操作的对象都是我们不知道的一些东西,我们就需要把那些不知道的数据以键值对的方式存入到文档中,以便于别人的查阅和传值,这个文档就是常说的配置文件。我们来看看他们的应用。

创建几个测试需要的学生,老师类。

public class Teacher {public void love(){System.out.println("讲课");}}

package com.reflect.text;public class Student {public void love(){System.out.println("学Java");}}

这就是配置文件的形式


<span style="white-space:pre"></span>Properties prop = new Properties();FileReader fr = new FileReader("class.txt");//读取出配置文件的信息prop.load(fr);//加载搭到prop中fr.close();//获取数据String className = prop.getProperty("className");//根据键获取到值String methodName = prop.getProperty("methodName");//反射Class c = Class.forName(className);Constructor con = c.getConstructor();Object obj = con.newInstance();//创建出对象//调用方法Method m1 = c.getDeclaredMethod(methodName);//获取到方法m1.setAccessible(true);m1.invoke(obj);//调用方法。

最后我们来看看用强大的反射,去设置一个私有的变量的值:

public void setProperty(Object obj,String properName,Object value) throws Exception{//根据对象获取字节码文件Class c = obj.getClass();//拿到该对象的properName成员变量Field field = c.getDeclaredField(properName);//强制访问field.setAccessible(true);//将obj的field对象赋值为valuefield.set(obj, value);}

我们开始操作Tool工具类

public class ToolDemo {public static void main(String[] args) throws Exception {Person p = new Person();Tool t = new Tool();t.setProperty(p, "name", "张三");//用反射机制我们不可思议的操作了一个私有成员,这可见反射的强大之处。t.setProperty(p, "age", 20);System.out.println(p);}}class Person{private String name;public int age;@Overridepublic String toString() {// TODO Auto-generated method stubreturn name + "---" + age; }}



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

详细请查看:www.itheima.com

0 0
原创粉丝点击