黑马程序员——反射

来源:互联网 发布:会员卡积分软件 编辑:程序博客网 时间:2024/06/16 15:14

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

1、反射的概念

反射的引入:

Object obj = new Student();

若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:

1.若编译和运行类型都知道,使用 instance of 判断后,强转。

2.编译时根本无法预知该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,这时就必须使用反射了。

3.要是想得到对象真正的类型,就得使用反射。


什么是反射机制?  

        简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。


反射机制的优点与缺点:  

     为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念,  

     静态编译:在编译时确定类型,绑定对象,即通过。  

     动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。  

     一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发

    它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。


Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类。

对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?

人 对应 Person

Java类 对应 Class

对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别对应什么呢?

对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等;

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的;

总结:反射就是将一个类里面的属性映射到该属性的类上面。


2、获得Class对象

获得这个Class对象,有三种方式:
1、根据给定的类名来获得,用于类加载
String classname = "cn.itcast.reflect.Preson";// 来自配置文件。
Class clazz = Class.forName(classname);
2、如果拿到了对象,不知道是什么类型
Object obj = new Person();
Class clazz1 = obj.getClass();
3、如果是明确地获得某个类的class对象 
Class clazz2 = Person.class;

3、Class的九个预定义对象

除了基本的Java数据类型(byte,short,int,long,double,float,boolean,char),还有一个关键字void。

Class类中boolean isPrimitive() :判定指定的 Class 对象是否表示一个基本类型。

包装类和基本数据类型类的的区别:

Class<?> in = int.class;

System.out.println(in);//int

Class<?> in2 = Integer.class;

//包装类都有一个常量TYPE,用来表示其基本数据类型的字节码

Class<?> in3 = Integer.TYPE;

System.out.println(in2);//class java.lang.Integer

System.out.println(in3);//int

System.out.println(in3 == in);//true 包装类都有一个常量TYPE,用来表示其基本数据类型的字节码,所以这里会相等!

System.out.println(in3 == in2);//false

数组类型的Class实例对象

Class<String[]> cls = String[].class 

Class类中 boolean isArray() :判定此 Class 对象是否表示一个数组类型。


4、利用Class获取类的属性信息

class A {}interface B{}interface C{}public class BaseDemo3 extends A implements B,C{//内部类public class C{}public interface D{}public static void main(String[] args) {//类可以,接口也可以Class<BaseDemo3> c = BaseDemo3.class;System.out.println(c);//class junereflect624.BaseDemo3//得到包名System.out.println(c.getPackage());//package junereflect624//得到全限定名System.out.println(c.getName());//junereflect624.BaseDemo3//得到类的简称System.out.println(c.getSimpleName());//BaseDemo3//得到父类/** * Class<? super T> getSuperclass() 此处super表示下限          返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。  */System.out.println(c.getSuperclass().getSimpleName());//A,先获取父类,再获取父类的简称//得到接口System.out.println(c.getInterfaces());//[Ljava.lang.Class;@1b60280Class[] arr = c.getInterfaces();for (Class cla : arr) {System.out.println(cla);//interface junereflect624.B   interface junereflect624.C}//获得public修饰的类/** * Class<?>[] getClasses()      返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 (如果内部类前面没有加上public的话那么得不到!) */Class[] cl = c.getClasses();System.out.println(cl.length);//在内部类没有加上public修饰的时候长度为0,加上就是2(获取的是公共的)for (Class class1 : cl) {System.out.println(class1);}//获得修饰符int i = c.getModifiers();System.out.println(i);//常量值1表示publicSystem.out.println(Modifier.toString(i));//直接打印出public}}

5Class中得到构造方法Constructor方法Method、字段Field

1、Constructor 

Constructor类用于描述类中的构造方法:

Constructor<T> getConstructor(Class<?>... parameterTypes) 

返回该Class对象表示类的指定的public构造方法

Constructor<?>[] getConstructors()

返回该Class对象表示类的所有public构造方法

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

返回该Class对象表示类的指定的构造方法,和访问权限无关;

Constructor<?>[] getDeclaredConstructors() 

返回该Class对象表示类的所有构造方法,和访问权限无关;

2、Method

Method getMethod(String name, Class<?> ... parameterTypes) 

返回该Class对象表示类和其父类的指定的public方法

Method[] getMethods();  

返回该Class对象表示类和其父类的所有public方法

Method getDeclaredMethod(String name, Class<?>... parameterTypes) 

返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;

Method[] getDeclaredMethods(): 获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法;

3、Field

Field  getField(String name)  获取指定的字段

Field[]  getFields() 获取所有字段

Field  getDeclaredField(String name) 

返回该Class对象表示类的指定的字段。和访问权限无关,但不包括继承的方法;

Field[]  getDeclaredFields()

返回该Class对象表示类的所有的字段。和访问权限无关,但不包括继承的方法;


例子:

获取Constructor

public class ConstructorDemo4 {public static void main(String[] args) throws Exception {//得到所有的构造器(先得到类)Class<Emp> c = Emp.class;/** * Constructor<?>[] getConstructors()           返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。  */Constructor[] con = c.getConstructors();//前面的修饰符必须是public才可以在这个方法下获取到for (Constructor cons : con) {System.out.println("c.getConstructors()"+cons);//如果上面的某构造器public去掉,则显示不出/**打印public junereflect624.Emp(java.lang.String,int) */}//得到指定的构造器,也是必须publicConstructor c1 = c.getConstructor(String.class,int.class);System.out.println(c1);//public junereflect624.Emp(java.lang.String,int) System.out.println("====================================");//现在想获得不受public影响的,getDeclaredConstructors(),暴力反射con = c.getDeclaredConstructors();for (Constructor cons : con) { System.out.println("c.getDeclaredConstructors()=="+cons);//此时不受修饰符的影响/**打印 *  public junereflect624.Emp()public junereflect624.Emp(java.lang.String)public junereflect624.Emp(java.lang.String,int) */}}}

获取Method 


<p>package itcastreflect;</p><p> </p><p>import java.lang.annotation.Annotation;</p><p>import java.lang.reflect.Field;</p><p>import java.lang.reflect.Method;</p><p> </p><p>class AB{</p><p>protected String name;</p><p>protected String id;</p><p>}</p><p>@Deprecated</p><p>public class MethodDemo5 extends AB{</p><p>void show(){}</p><p>public void say(){}</p><p>private int age;</p><p>public char c;</p><p>private boolean b;</p><p>public static void main(String[] args) throws Exception {</p><p>Class<MethodDemo5> c = MethodDemo5.class;</p><p>//获取所有的(包含父类的方法)public修饰的方法</p><p>Method[] m = c.getMethods();</p><p>for (Method method : m) {</p><p>System.out.println(method);</p><p>}</p><p>//总结:4个方法,获取全部,获取特定;不受修饰符影响的全部,不受修饰符影响的特定;(前两个都还是受限制的)</p><p></p><p>//获取指定的方法</p><p>Method me = c.getMethod("main", String[].class);</p><p>System.out.println("main "+me);/*main public static void itcastreflect.MethodDemo5.main(java.lang.String[]) </p><p>Throws java.lang.Exception*/</p><p></p><p>//访问所有方法,不受访问权限影响</p><p>m = c.getDeclaredMethods();</p><p>for (Method method : m) {</p><p>System.out.println("不受影响的:"+method);</p><p>}</p><p></p><p>me = c.getDeclaredMethod("show");</p><p>System.out.println(me);//    void.MethodDemo.show()</p><p> </p><p>me = c.getMethod("toString");</p><p>System.out.println(me);//public java.lang.String //java.lang.Object.toString()</p><p></p><p></p><p> /* Method[] getDeclaredMethods() </p><p>          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,</p><p>          包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法,只可以对当前类有效</p><p> */</p><p>/*me=c.getDeclaredMethod("toString");//ERROR,c.getDeclaredMethod()不能得到继承的方法</p><p>System.out.println(me);//public java.lang.String //java.lang.Object.toString()</p><p>*/</p>

获取Field 


<p>package itcastreflect;</p><p> </p><p>import java.lang.annotation.Annotation;</p><p>import java.lang.reflect.Field;</p><p>import java.lang.reflect.Method;</p><p> </p><p>class AB{</p><p>protected String name;</p><p>protected String id;</p><p>}</p><p>@Deprecated</p><p>public class MethodDemo5 extends AB{</p><p>void show(){}</p><p>public void say(){}</p><p>private int age;</p><p>public char c;</p><p>private boolean b;</p><p>public static void main(String[] args) throws Exception {</p><p>Field[] f = c.getFields();</p><p>for (Field field : f) {//只得到了public的</p><p>System.out.println("字段"+field);</p><p>}</p><p></p><p>//特定字段</p><p>Field fi = c.getField("c");//""里面是名称</p><p>System.out.println(fi);//public char itcastreflect.MethodDemo.c</p><p></p><p>//得到不受限定名限定的全部字段</p><p>f = c.getDeclaredFields();</p><p>for (Field field : f) {//得到不受修饰符限定的字段,但是只对当前类有效</p><p>System.out.println("全部字段:"+field);</p><p>/**</p><p> *  全部字段:private int itcastreflect.MethodDemo.age</p><p>全部字段:public char itcastreflect.MethodDemo.c</p><p>全部字段:private boolean itcastreflect.MethodDemo.b</p><p> */</p><p>}</p><p>//注释  Annotation</p><p> Annotation[] a = c.getAnnotations();</p><p> System.out.println(a.length);</p><p> for (Annotation annotation : a) {</p><p>System.out.println(annotation);</p><p>}</p><p> </p><p> //特定注解</p><p> Deprecated d = c.getAnnotation(Deprecated.class);</p><p> System.out.println(d);</p><p>}</p><p>}</p>

6、利用反射创建对象

创建对象:

1使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法。

2使用Class对象获取指定的Constructor对象,再调用ConstructornewInstance()方法创建对象类的实例,此时可以选择使用某个构造方法。如果这个构造方法被私有化起来,那么必须先申请访问,将可以访问设置为true

第二种更强大:

比如,使用指定构造方法来创建对象:

获取该类的Class对象。

利用Class对象的getConstructor()方法来获取指定的构造方法。

调用ConstructornewInstance()方法创建对象。 

AccessibleObject对象的setAccessible(boolean flag)方法,flagtrue的时候,就会忽略访问权限(可访问私有的成员)

其子类有Field, Method, Constructor;

若要访问对象private的成员?

在调用之前使用setAccessible(true),

Xxx x = getDeclaredXxxx();//才能得到私有的类字段.


总之步骤:

1、先获取该类的Class类对象。

2、利用Class对象的getConstructor()方法来获取指定的构造函数。

3、申请访问(设置为可访问)。

4、调用Constructor的newInstance() 方法创建对象。


7、使用反射调用方法

每个Method的对象对应一个具体的底层方法。获得Method对象后,程序可以使用Method里面的invoke方法来执行该底层方法。

Object invoke(Object obj,Object ... args):obj表示调用底层方法的对象,后面的args表示传递的实际参数。

如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。

如果底层方法所需的形参个数为 0,则所提供的 args 数组长度可以为 或 null

若底层方法返回的是数组类型,invoke方法返回的不是底层方法的值,而是底层方法的返回类型;


class Dept{public String show(String name){//用反射的方法来调用正常的方法return name+",您好!";}private void privateshow(){//用反射来实现对私有化方法的调用System.out.println("privateshow");}public static void staticshow(){System.out.println("staticshow");}} public class InvokeDemo9 {public static void main(String[] args) throws Exception {/*传统方式:String name = new Dept().show("吴洁");System.out.println(name);*//** * Method getMethod(String name, Class<?>... parameterTypes)           返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。     name - 方法名parameterTypes - 参数列表  *///想要通过反射来调用Dept中的方法Class<Dept> c = Dept.class;Method m = c.getMethod("show", String.class);Object o = m.invoke(c.newInstance(), "吴洁");System.out.println(o);//私有化的方法m = c.getDeclaredMethod("privateshow");//无参方法m.setAccessible(true);o = m.invoke(c.newInstance());//静态方法的调用m = c.getMethod("staticshow");m.invoke(null);//staticshow为静态方法,不需创建对象,所以这里会是null}
打印结果:

吴洁,您好!

privateshow

staticshow

8、使用反射调用可变参数方法

要把可变参数都当做是其对应的数组类型参数;

如 show(XX... is)作为show(XX[] is)调用; 

若可变参数元素类型是引用类型:

JDK内部接收到参数之后,会自动拆包取出参数再分配给该底层方法,为此我们需要把这个数组实参先包装成一个Object对象或把实际参数作为一个Object一维数组的元素再传递。

若可变参数元素类型是基本类型:

JDK内部接收到参数之后,不会拆包,所以可以不必再封装.不过封装了也不会错.所以建议,不管基本类型还是引用类型都使用Object[]封装一层,保证无误.

class VaryMethod{public static void show(int ...args){System.out.println("基本数据类型传递过来了!");}/*public static void show(int[] args){//这是一样的}*/public static void show(String ...args){System.out.println("引用数据类型传递过来了!");}}public class InvokeVaryDemo10 {public static void main(String[] args) throws Exception{Class<VaryMethod> c = VaryMethod.class;Method m = c.getMethod("show",int[].class);m.invoke(null,new int[]{1,2,3});// m = c.getMethod("show",String[].class);//m.invoke(null,new String[]{"A","B","C"});//ERRORm.invoke(null,(Object)new String[]{"A","B","C"});//YES,强转为Object类型m.invoke(null,new Object[]{new String[]{"A","B","C"}});//推荐写法}}

9、使用反射操作字段

Field提供两组方法操作字段:

xxx getXxx(Object obj):获取obj对象该Field的字段值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,Object get(Object obj);

void setXxx(Object obj,xxx val):obj对象的该Field字段设置成val值,此处的xxx表示8个基本数据类型。若该字段的类型是引用数据类型则使用,void set(Object obj, Object value);

 

package junereflect624;

//获取字符,并且赋值,然后再取出来(对应的去查看api,比如这个是Field,别的比如Constructor,Method)

步骤:

1.获取类

2.获取字段

3.赋值(set(c.newInstance(),””));{如果为私有的话设置可接受}

packge itcastreflect
class Cat{private String name;public int age;private String color;}public class FieldDemo12 {public static void main(String[] args) throws Exception {Class<Cat> clz = Cat.class;Field[] f = clz.getDeclaredFields();for (Field field : f) {System.out.println(field);}Field fi = clz.getDeclaredField("name");System.out.println(fi);//核心开始/** *  void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。  */Cat c = clz.newInstance();fi.setAccessible(true);fi.set(c, "小白");//赋值成功Object o = fi.get(c);System.out.println(o);//取出成功 fi = clz.getDeclaredField("age");fi.setAccessible(true);fi.set(c, 10);int i = fi.getInt(c);//左边的接受类型已经写成了int,右边的返回类型就也必须是intSystem.out.println(i);//获取成功}}
打印结果:

private java.lang.String itcastreflect.Cat.name

public int itcastreflect.Cat.age

private java.lang.String itcastreflect.Cat.color

private java.lang.String itcastreflect.Cat.name

小白

10

10、反射和泛型-反射来获取泛型信息

通过指定对应的Class对象,程序可以获得该类里面所有的Field,不管该Field使用private 方法public。获得Field对象后都可以使用getType()来获取其类型。

Class<?> type = f.getType();//获得字段的类型

但此方法只对普通Field有效,若该Field有泛型修饰,则不能准确得到该Field的泛型参数,Map<String,Integer>;

为了获得指定Field的泛型类型,我们采用:

Type gType = f.getGenericType();得到泛型类型

然后将Type对象强转为ParameterizedType,其表示增加泛型后的类型

Type getRawType()//返回被泛型限制的类型;

Type[]  getActualTypeArguments()//返回泛型参数类型;

 

利用反射来获取泛型的类型

步骤:

1.获取当前类

2.获取目标字段

3.获取包含泛型类型的类型 getGenericType()

4.强转至子类ParameterizedType  因为Type没有任何对应的方法

5.获得泛型真正的类型 getActualTypeArguments()

<p>package itcastreflect;</p><p> </p><p>import java.lang.reflect.Field;</p><p>import java.lang.reflect.ParameterizedType;</p><p>import java.lang.reflect.Type;</p><p>import java.util.HashMap;</p><p>import java.util.Map;</p><p> </p><p>public class GetGenericTypeDemo14 {</p><p>Map<String,Integer> map = new HashMap<String,Integer>();</p><p></p><p>public static void main(String[] args) throws Exception {</p><p>Class c = GetGenericTypeDemo14.class;</p><p>Field f = c.getDeclaredField("map");</p><p>System.out.println(f);</p><p>System.out.println(f.getName());//map</p><p></p><p>// Class<?> getType()  返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。 </p><p>Class cl = f.getType();</p><p>System.out.println("获得其类型:"+cl);</p><p>//获得其类型:interface java.util.Map</p><p></p><p>/**</p><p> *  Type getGenericType() 返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。 </p><p> *  Type是Class的接口;</p><p> */</p><p>Type t = f.getGenericType();//包含泛型的类型</p><p>System.out.println(t);</p><p>//java.util.Map<java.lang.String, java.lang.Integer></p><p></p><p> </p><p>/**</p><p> * Type这个类里面没有任何的方法,所以需要调用子类的方法,那么大的类型转到小的类型,需要强转!</p><p> */</p><p>ParameterizedType pt = (ParameterizedType)t;//强转到其子类</p><p>/**</p><p> *  Type[] getActualTypeArguments() </p><p>          返回表示此类型实际类型参数的 Type对象的数组。 </p><p> Type getOwnerType() </p><p>          返回 Type 对象,表示此类型是其成员之一的类型。 </p><p> Type getRawType() </p><p>          返回 Type 对象,表示声明此类型的类或接口。 </p><p> */</p><p></p><p>t = pt.getRawType();//类型的类或接口</p><p>System.out.println(t);</p><p></p><p>Type[] ts = pt.getActualTypeArguments();</p><p>for (Type type : ts) {</p><p>System.out.println(type);</p><p>/**</p><p> *  class java.lang.String</p><p>    class java.lang.Integer</p><p> */</p><p>}</p><p>}</p><p>}</p><p><span style="color:rgb(63,127,95);">打印:</span></p><p>java.util.Map itcastreflect.GetGenericTypeDemo14.map</p><p>map</p><p>获得其类型:interface java.util.Map</p><p>java.util.Map<java.lang.String, java.lang.Integer></p><p>interface java.util.Map</p><p>class java.lang.String</p><p>class java.lang.Integer</p>


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













 


 






















0 0