反射

5.7.反射

​ 能够分析反射能力的程序称为反射(reflective)。反射机制可以用来:

  • 在运行时分析类的能力

  • 运行时查看对象,例如,编一个toString方法供所有类是使用

  • 实现通用的数组操作代码

  • 利用Method对象,这个对象很像c++中的函数指针

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

静态编译和动态编译

静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象

反射机制优缺点
优点: 运行期类型的判断,动态加载类,提高代码灵活度。
缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。

5.7.1 Class类

​ 在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。

​ 然而,可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class,这个名字很容易让人混淆。Object类中的getClass()方法将返回一个Class类型的实例。

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;

/**
 * 测试:
    java.lang.Double
 */
public class ReflectionTest {
    public static void main (String[] args) {
        //从命令行或用户输入中读类名
        String name;
        if (args.length > 0) name = args[0];
        else
        {
            Scanner in = new Scanner ( System.in );
            System.out.println ("Enter class name(e.g.java.util.Date):");
            name = in.next ();
        }

        try {
            //打印类名和父类名(if != Object)
            Class c1 = Class.forName ( name );
            //返回Class对象
            Class superc1 = c1.getSuperclass ();
            String modifiers = Modifier.toString ( c1.getModifiers () );
            if (modifiers.length () > 0)
                System.out.println (modifiers + " ");
            System.out.println ("class "+name);
            if (superc1 != null && superc1 != Object.class)
                System.out.println ("extends"+ superc1.getName ());
            System.out.println ("\n{\n");
            printConstructors(c1);
            System.out.println ();
            printMethods(c1);
            System.out.println ();
            printFields(c1);
            System.out.println ("}");
        } catch (ClassNotFoundException e) {
            e.printStackTrace ();
        }
        System.exit ( 0 );
    }

    /**
     * 打印所有域名类
     * @param c1
     */
    private static void printFields (Class c1) {
        Field[] fields = c1.getDeclaredFields ();

        for (Field f : fields){
            Class type = f.getType ();
            String name = f.getName ();
            System.out.println (" ");
            String modifiers = Modifier.toString ( f.getModifiers () );
            if (modifiers.length () > 0)
                System.out.println (modifiers + " ");
            System.out.println (type.getName ()+ " " + name+ ";");

        }

    }

    /**
     * 打印一个类的所有方法
     * @param c1
     */
    private static void printMethods (Class c1) {
        Method[] methods = c1.getDeclaredMethods ();

        for (Method m : methods){
            Class retType = m.getReturnType ();
            String name = m.getName ();

            System.out.println ("  ");
            //打印modifiers,返回类型和方法名
            String modifiers = Modifier.toString ( m.getModifiers ()  );
            if (modifiers.length () > 0)
                System.out.println (modifiers + " ");
            System.out.println (retType.getName ()+ " " + name+ "(");

            Class[] paramTypes = m.getParameterTypes ();
            for (int j=0; j < paramTypes.length; j++){
                if (j>0)
                    System.out.println (", ");
                System.out.println (paramTypes[j].getName ());
            }
            System.out.println (");");

        }
    }

    /**
     * 打印类构造器
     * @param c1
     */
    private static void printConstructors (Class c1) {
        Constructor[] constructors = c1.getConstructors ();

        for (Constructor c : constructors){
            String name = c.getName ();
            System.out.println (" ");
            String modifiers = Modifier.toString ( c.getModifiers () );
            if (modifiers.length () > 0){
                System.out.println (modifiers+" ");
            }
            System.out.println (name + "(");

            //打印Parameter 类型
            Class[] paramTypes = c.getParameterTypes ();
            for (int j=0; j < paramTypes.length; j++){
                if (j>0)
                    System.out.println (", ");
                System.out.println (paramTypes[j].getName ());
            }
            System.out.println (");");
        }
    }
}  

测试用例代入的结果:

java.lang.Double
public final 
class java.lang.Double
extendsjava.lang.Number

{

 
public 
java.lang.Double(
double
);
 
public 
java.lang.Double(
java.lang.String
);

  
public 
boolean equals(
java.lang.Object
);
  
public static 
java.lang.String toString(
double
);
  
public 
java.lang.String toString(
);
  
public 
int hashCode(
);
  
public static 
int hashCode(
double
);
  
public static 
double min(
double
, 
double
);
  
public static 
double max(
double
, 
double
);
  
public static native 
long doubleToRawLongBits(
double
);
  
public static 
long doubleToLongBits(
double
);
  
public static native 
double longBitsToDouble(
long
);
  
public volatile 
int compareTo(
java.lang.Object
);
  
public 
int compareTo(
java.lang.Double
);
  
public 
byte byteValue(
);
  
public 
short shortValue(
);
  
public 
int intValue(
);
  
public 
long longValue(
);
  
public 
float floatValue(
);
  
public 
double doubleValue(
);
  
public static 
java.lang.Double valueOf(
java.lang.String
);
  
public static 
java.lang.Double valueOf(
double
);
  
public static 
java.lang.String toHexString(
double
);
  
public static 
int compare(
double
, 
double
);
  
public static 
boolean isNaN(
double
);
  
public 
boolean isNaN(
);
  
public static 
boolean isFinite(
double
);
  
public static 
boolean isInfinite(
double
);
  
public 
boolean isInfinite(
);
  
public static 
double sum(
double
, 
double
);
  
public static 
double parseDouble(
java.lang.String
);

 
public static final 
double POSITIVE_INFINITY;
 
public static final 
double NEGATIVE_INFINITY;
 
public static final 
double NaN;
 
public static final 
double MAX_VALUE;
 
public static final 
double MIN_NORMAL;
 
public static final 
double MIN_VALUE;
 
public static final 
int MAX_EXPONENT;
 
public static final 
int MIN_EXPONENT;
 
public static final 
int SIZE;
 
public static final 
int BYTES;
 
public static final 
java.lang.Class TYPE;
 
private final 
double value;
 
private static final 
long serialVersionUID;
}

5.7.2 利用反射分析类的能力

反射机制最重要的内容------------检查类的结构

​ 在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有个叫做getName的方法,用来返回描述域所属类型的Class对象。Method和Constructor类能够报告参数类型的方法,Method类还有个可以报告返回类型的方法。这三个类还有个交getModifiers的方法,它将返回一个整形数值,用不同的位开关描述public和static这样修饰符使用状况。另外,可使用Modifier类的静态方法分析getModifiers返回的整形数值。可调用Modifier.toString方法将修饰符打印出来。

Class类中的getFields、getMethods和getConstructors方法分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructor是方法分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。

5.7.3运行时使用反射机制分析对象

查看数据域的实际内容

​ 反射机制默认行为受限于Java的访问控制。然而,如果一个Java程序没有受到安全管理器的控制,就可以覆盖访问控制。为了达到这个目的,需要调用Field、Method或Constructor对象的setAccessible方法。

5.7.4反射案例

package reflection;

import java.lang.reflect.*;
import java.util.Date;
//import java.util.Date;

public class MainClass {
    public static void main (String[] args) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
        String classname = "java.util.Date";

        //加载类java.util.Date并进行实例化
        /**
         * forName方法的工作
         * 1.根据类名到类路径中查找有没有这个类的.class文件
         * 2.如果找到类路径中找到这个字节码文件,则会经过检查加载到虚拟机中
         * 3.在扫描检查的过程中,虚拟机还会得到这个字节码中定义的内容
         * (属性,方法等信息),则forName方法会实例化一个Class对象,把检查到的
         * 字节码信息存入Class对象
         * 4.通过Class对象,可以通过一种比较友好的方式,得到字节码中的信息
         */
        Class c = Class.forName ( classname );
        System.out.println (c.getName ());
//        字段集合
        Field[] fs = c.getFields ();//只能拿到公有字段(属性)
        for (Field f: fs){
            System.out.println (f.getName ()+":"+f.getType ());
        }
        Field[] fs1 = c.getDeclaredFields ();//拿到公,私有字段(属性)
        for (Field f: fs1){
            System.out.println (f.getName ()+":"+f.getType ());
        }

        Constructor[] cs = c.getConstructors ();
        for (Constructor cc : cs ){
            System.out.println (cc.getParameterCount ()+":"+cc.getName ());
        }

        //硬编码  前期绑定
//        Date date = new Date (  );

        //后期绑定(通过反射得到的对象进行实例化)
        Object o = cs[2].newInstance ( );
        System.out.println (o);


        Method[] methods = c.getDeclaredMethods ();
        for (Method m : methods){
            System.out.println (m.getModifiers ()+":"
                    +m.getName ()+":"
                    +m.getReturnType().getName ()
                    +m.getParameters ());
            if ("setYear".equals ( m.getName () )){
                m.invoke ( o,2000 );//改变年份的值
                System.out.println ("定义时间:"+ o);
            }
            Parameter[] ps = m.getParameters ();
            for (Parameter p : ps){
                System.out.println ("***"+p.getName ()+":"+p.getType ().getName ());
            }
        }
    }
}

运行结果:

java.util.Date
gcal:class sun.util.calendar.BaseCalendar
jcal:class sun.util.calendar.BaseCalendar
fastTime:long
cdate:class sun.util.calendar.BaseCalendar$Date
defaultCenturyStart:int
serialVersionUID:long
wtb:class [Ljava.lang.String;
ttb:class [I
6:java.util.Date
1:java.util.Date
0:java.util.Date
1:java.util.Date
3:java.util.Date
5:java.util.Date
Sun Aug 25 16:20:42 CST 2019
1:after:boolean[Ljava.lang.reflect.Parameter;@45ee12a7
***arg0:java.util.Date
1:before:boolean[Ljava.lang.reflect.Parameter;@330bedb4
***arg0:java.util.Date
1:equals:boolean[Ljava.lang.reflect.Parameter;@2503dbd3
***arg0:java.lang.Object
1:toString:java.lang.String[Ljava.lang.reflect.Parameter;@4b67cf4d
1:hashCode:int[Ljava.lang.reflect.Parameter;@7ea987ac
1:clone:java.lang.Object[Ljava.lang.reflect.Parameter;@12a3a380
1:compareTo:int[Ljava.lang.reflect.Parameter;@29453f44
***arg0:java.util.Date
4161:compareTo:int[Ljava.lang.reflect.Parameter;@5cad8086
***arg0:java.lang.Object
2:readObject:void[Ljava.lang.reflect.Parameter;@6e0be858
***arg0:java.io.ObjectInputStream
2:writeObject:void[Ljava.lang.reflect.Parameter;@61bbe9ba
***arg0:java.io.ObjectOutputStream
18:normalize:sun.util.calendar.BaseCalendar$Date[Ljava.lang.reflect.Parameter;@610455d6
***arg0:sun.util.calendar.BaseCalendar$Date
18:normalize:sun.util.calendar.BaseCalendar$Date[Ljava.lang.reflect.Parameter;@511d50c0
9:parse:long[Ljava.lang.reflect.Parameter;@60e53b93
***arg0:java.lang.String
9:from:java.util.Date[Ljava.lang.reflect.Parameter;@5e2de80c
***arg0:java.time.Instant
1:getTime:long[Ljava.lang.reflect.Parameter;@1d44bcfa
1:setTime:void[Ljava.lang.reflect.Parameter;@266474c2
***arg0:long
1:getDate:int[Ljava.lang.reflect.Parameter;@6f94fa3e
1:setYear:void[Ljava.lang.reflect.Parameter;@5e481248
定义时间:Sat Aug 25 16:20:42 CST 3900
***arg0:int
9:UTC:long[Ljava.lang.reflect.Parameter;@66d3c617
***arg0:int
***arg1:int
***arg2:int
***arg3:int
***arg4:int
***arg5:int
26:convertToAbbr:java.lang.StringBuilder[Ljava.lang.reflect.Parameter;@63947c6b
***arg0:java.lang.StringBuilder
***arg1:java.lang.String
18:getCalendarDate:sun.util.calendar.BaseCalendar$Date[Ljava.lang.reflect.Parameter;@2b193f2d
26:getCalendarSystem:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@355da254
***arg0:sun.util.calendar.BaseCalendar$Date
26:getCalendarSystem:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@4dc63996
***arg0:int
26:getCalendarSystem:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@d716361
***arg0:long
1:getDay:int[Ljava.lang.reflect.Parameter;@6ff3c5b5
1:getHours:int[Ljava.lang.reflect.Parameter;@3764951d
58:getJulianCalendar:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@4b1210ee
24:getMillisOf:long[Ljava.lang.reflect.Parameter;@4d7e1886
***arg0:java.util.Date
1:getMinutes:int[Ljava.lang.reflect.Parameter;@3cd1a2f1
1:getMonth:int[Ljava.lang.reflect.Parameter;@2f0e140b
1:getSeconds:int[Ljava.lang.reflect.Parameter;@7440e464
18:getTimeImpl:long[Ljava.lang.reflect.Parameter;@49476842
1:getTimezoneOffset:int[Ljava.lang.reflect.Parameter;@78308db1
1:getYear:int[Ljava.lang.reflect.Parameter;@27c170f0
1:setDate:void[Ljava.lang.reflect.Parameter;@5451c3a8
***arg0:int
1:setHours:void[Ljava.lang.reflect.Parameter;@2626b418
***arg0:int
1:setMinutes:void[Ljava.lang.reflect.Parameter;@5a07e868
***arg0:int
1:setMonth:void[Ljava.lang.reflect.Parameter;@76ed5528
***arg0:int
1:setSeconds:void[Ljava.lang.reflect.Parameter;@2c7b84de
***arg0:int
1:toGMTString:java.lang.String[Ljava.lang.reflect.Parameter;@3fee733d
1:toInstant:java.time.Instant[Ljava.lang.reflect.Parameter;@5acf9800
1:toLocaleString:java.lang.String[Ljava.lang.reflect.Parameter;@4617c264

反射机制的应用场景有哪些?
反射是框架设计的灵魂。

在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。

举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
Java获取反射的三种方法
1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制

public class Get {
    //获取反射机制三种方式
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一(通过建立对象)
        Student stu = new Student();
        Class classobj1 = stu.getClass();
        System.out.println(classobj1.getName());
        //方式二(所在通过路径-相对路径)
        Class classobj2 = Class.forName("fanshe.Student");
        System.out.println(classobj2.getName());
        //方式三(通过类名)
        Class classobj3 = Student.class;
        System.out.println(classobj3.getName());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值