黑马程序员--反射

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


反射的概念

            JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。


功能

          Java反射机制主要提供了以下功能:
1. 在运行时判断任意一个对象所属的类;
2. 在运行时构造任意一个类的对象;
3. 在运行时判断任意一个类所具有的成员变量和方法;
4. 在运行时调用任意一个对象的方法;
5. 生成动态代理。


理解反射的概念

反射就是把Java类中的各种成分映射成相应的Java类。

例如:众多的人用一个Person类来表示,那么众多的Java类就用一个Class类来表示。

                       Person 类代表人,它的实例对象就是张三,李四这样一个个具体的人;

           Class类代表Java类,它的各个实例对象对应的就是各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码等等。

Class类用于表示.class文件,是所有加载进内存的字节码对象的父类。所以可以通过Class得到运行时的类。

如何得到某个class文件对应的class对象呢?

                     方法有3种:

1)类名.class 例如,Person.class

2)对象.getClass() 例如,new Data().getClass()。

3)Class.forName("包名.类名"); 例如,Class.forName("java.lang.String");


代码示例:


public class ClassTest{


public static void main(String...args)throws Exception{


String str = "黑马程序员";

Class cls1 =String.class;

Class cls2 = str.getClass();

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

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

System.out.println(cls2 == cls3);//true


}


}


注意:字节码文件是唯一的,所以无论怎么获取,都是同一份字节码文件。

            九个预定义Class实例对象(八大原始类型+void)

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void也表示为 Class 对象。


   反射的基本步骤:

1、获得Class对象,就是获取到指定的名称的字节码文件对象。

2、实例化对象,获得类的属性、方法或构造函数。

3、访问属性、调用方法、调用构造函数创建对象。


构造方法的反射应用(Coustructor类)


Constructor类的实例对象代表类的一个构造方法。

反射公共,私有和保护的构造方法:

反射公共的需要的方法是:getConstructor();

反射私有的需要的方法是:getDeclaredConstructor();

Constructor对象代表一个构造方法,Constructor对象有的方法:得到构造方法名字,得到所属于的类,产生实例对象。

得到某个类空参数构造方法,例:


Constructor constructor = Class.forName("java.lang.String").getConstructor();


得到某个类所有的构造方法,例:


Constructor [] constructors= Class.forName("java.lang.String").getConstructors();


得到某一个带参数的构造方法,例:        


Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class);


利用构造方法创建实例对象:


通常方式:String instance = new String(new StringBuffer("黑马程序员"));


反射方式:String instance = (String)constructor.newInstance("黑马程序员");


             通过Class类中的newInstance()方法也可创建类的实例,其内部工作原理是先得无参的构造方法,再用构造方法创建实例对象。

代码示例

package com.itheima.test;

import java.lang.reflect.Constructor;

public class ReflectTest {

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

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

Constructor constructor =  clszz.getConstructor(StringBuffer.class);

String str = (String) constructor.newInstance(new StringBuffer("黑马程序员"));

char [] chs = str.toCharArray();

for(int x = 0; x<chs.length;x++){

System.out.println(chs[x]);

}

}
}

成员变量的反射(Field类)

Field类代表反射某个类中的一个成员变量。


问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldAge 代表的是Age的定义,而不是具体的Age变量。

(注意访问权限的问题)也就是说,定义的是类对象,而非对象的对象。

当我们需要对其操作的时候,需要确定是那个具体的对象。

                                代码示例:


package com.itheima.test;

public class Person {

public int age;

private int height;

public Person(int age,int height){

this.age=age;

this.height=height;

}

}


package com.itheima.test;

import java.lang.reflect.Field;

public class ReflectTest{

public static void main(String...args) throws Exception{

Person p = new Person(20,30);

Field fieldAge = p.getClass().getField("age");

System.out.println(fieldAge.get(p));

}

}

成员方法的反射(Method类)

Method类代表某个类中的一个成员方法

得到类中的某一个方法:

例子:Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class);

调用方法:

通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1)); 

如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?

说明该Method象对应的是一个静态方法!

代码示例:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。

package com.itheima.test;

import java.lang.reflect.Method;

public class ReflectTest {

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

Class clszz = Class.forName(args[0]);

Method main = clszz.getMethod("main"String[].class);

main.invoke(null,"黑马程序员","黑马论坛","CSDN社区");

}

}

暴力反射

反射里的Constructor,Field,Method三个类都有一个getDeclaredXxx方法,可以不受权限控制的获取类的构造函数,

字段,方法,如果想要私有构造函数创建对象,字段赋值,方法调用的话,会自动的访问类的isAccessable,默认的是false,

所以,要想访问类中的私有成员的时候,就要调用setAccessable方法,将其改为true,

这样,就可以对类中的私有成员进行操作了.

假设有一个类,它有一个私有变量:

package com.itheima.test;
public class ReflectPoint {
	private int  priVar;
	public 	ReflectPoint(int priVar){
	this.priVar =priVar;	
} 	
}

如果我们直接采用.get的方式,是不可能看到私有变量的。 我们可以这样:


package com.itheima.test;
import java.lang.reflect.Field;
public class ReflectTest {
	public static void main(String[] args) throws Exception {
		ReflectPoint pt1 = new ReflectPoint(3);
		Field fieldx = pt1.getClass().getDeclaredField("priVar");
		fieldx.setAccessible(true);
		fieldx.set(pt1, 100);
		System.out.println(fieldx.get(pt1));
	}
}

输出:100;

那么这个Filed是什么呢?他 是一个类,表示属性的类。通过pt1得出ReflectPoint的字节码。然后再调用getDeclaredField()方法,可以获取x属性。再通过setAccessible使得可以访问这个属性。然后可以通过set方法赋值。注意:getClass获得是ReflectPoint的字节码,与具体实例无关,因此,Field也与具体实例无关。在设置和访问的时候,均需要指定具体哪个实例。

数组与Object的关系及其反射类型

            1. 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

                                  
package com.itheima.reflect;

import java.util.Arrays;

public class ReflectTest {

       public static void main(String[] args) throws Exception {
            int [] a1 = new int[3];
            int [] a2 = new int[4];
            int [][] a3 = new int[2][3];
            String[] a4 = new String[3];

            System.out.println(a1.getClass() == a2.getClass());
            //结果:true
            System.out.println(a1.getClass() == a4.getClass());
            //结果:false
            System.out.println(a1.getClass() == a3.getClass());
            //结果:false
       }
}
2. 代表数组的class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。

3. 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,即可以做Object类型使用,又可以当作Object[]类型使用。

         4. Array工具类用于完成数组的反射操作。

public class ReflectTest {

       public static void main(String[] args) throws Exception {
            String[] a1 = new String[]{"a" ,"b" ,"c" };
            String a2 = "xyz";
           
            printObject(a1);
            printObject(a2);
       }
      
       public static void printObject(Object obj){
         Class clazz = obj.getClass();
         if(clazz.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);
         }
       }
}













你可能感兴趣的:(java,反射,黑马程序员)