HengYk Blog

个人站

具有浪漫主义情调的理想主义务实青年


Java Reflection R1

第一章 反射

1. 什么是反射?

       能够分析类能力的程序称之为反射,被视为动态语言的关键。反射机制有以下功能:其一,在运行时判断任意一个对象所属的类;其二,在运行时构造任意一个类的对象;其三,在运行时判断任意一个类所具有的成员变量和方法;其四,在运行时调用任意一个成员变量的类型和方法;其五,生成动态代理。

2. Class类

2.1 什么是Class类

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

       关于Class类,我们需要关注一下信息:        * Class本身也是一个类。        * Class 对象只能由系统建立对象。        * 一个类在 JVM 中只会有一个Class实例 。        * 一个Class对象对应的是一个加载到JVM中的一个.class文件。        * 每个类的实例都会记得自己是由哪个Class实例所生成。        * 通过Class可以完整地得到一个类中的完整结构。

2.2 Class类的四种实例化方法

//1.调用运行时类本身的.class属性
Class clazz = Person.class;
System.out.println(clazz.getName());
//2.通过运行时类的对象获取
Person p = new Person();
Class clazz1 = p.getClass();
System.out.println(clazz1.getName());
//3.通过Class的静态方法获取。通过此方式,体会一下,反射的动态性。
String className = "cn.edu.xidian.ictt.yk.reflection.p3.Person";
Class clazz2 = Class.forName(className);
System.out.println(clazz2.getName());
//4.(了解)通过类的加载器
ClassLoader cl = this.getClass().getClassLoader();
Class clazz3 = cl.loadClass(className);
System.out.println(clazz3.getName());

2.3 Class类的加载过程

       java.lang.Class:是反射的源头。我们创建了一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的)此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例!

       1.每一个运行时类只加载一次!

       2.有了Class的实例以后,我们才可以进行如下的操作:a)创建对应的运行时类的对象;b)获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、…);c)调用对应的运行时类的指定的结构(属性、方法、构造器);d)反射的应用:动态代理。

       关于类加载器:ClassLoader(加载顺序:引导–>扩展–>系统;检查加载顺序:系统–>扩展–>引导)


//系统类加载器
ClassLoader c1 =  ClassLoader.getSystemClassLoader();
System.out.println(c1); // sun.misc.Launcher$AppClassLoader@18b4aac2
//扩展类加载器
ClassLoader c2 = c1.getParent();
System.out.println(c2); // sun.misc.Launcher$ExtClassLoader@8efb846
//引导类加载器
ClassLoader c3 = c2.getParent();
System.out.println(c3); // null

2.4 利用ClassLoader获取指定文件的输入流

//获取工程路径下指定文件的输入流yi
try {
    FileInputStream fis = new FileInputStream(new File("jdbc1.properties"));
    System.out.println(fis.available()); // 29
} catch (Exception e) {
    e.printStackTrace();
}
//获取某类路径下指定文件的输入流er
try {
    ClassLoader c6 = this.getClass().getClassLoader();
    InputStream is = c6.getResourceAsStream("cn\\edu\\xidian\\ictt\\yk\\reflection\\p1\\jdbc.properties");
    Properties properties = new Properties();
    properties.load(is);
    System.out.println(properties.getProperty("username")); // yk
    System.out.println(properties.getProperty("password")); // 123456
} catch (IOException e) {
    e.printStackTrace();
}

3. 利用反射分析类的能力

       import java.lang.reflect.Constructor;


public class TestConstructor {

    @Test
    public void test1() throws Exception {
        String className = "cn.edu.xidian.ictt.yk.reflection.p3.Person";
        Class clazz = Class.forName(className);

        //创建对应的运行时类的对象。使用newInstance(),实际上就是调用了运行时类的空参的构造器。
        //要想能够创建成功:①要求对应的运行时类要有空参的构造器。②构造器的权限修饰符的权限要足够。
        Object obj = clazz.newInstance();
        Person p = (Person) obj;
        System.out.println(p); // Person{name='null', age=0}
    }

    //获取指定的构造器
    @Test
    public void test2() throws ClassNotFoundException {
        String className = "cn.edu.xidian.ictt.yk.reflection.p3.Person";
        Class clazz = Class.forName(className);

        Constructor[] cons = clazz.getDeclaredConstructors();
        for (Constructor c : cons) {
            System.out.println(c);
        }

        /*
        public cn.edu.xidian.ictt.yk.reflection.p3.Person(java.lang.String,int)
        private cn.edu.xidian.ictt.yk.reflection.p3.Person(java.lang.String)
        public cn.edu.xidian.ictt.yk.reflection.p3.Person()
         */
    }

    //调用指定的构造器,创建运行时类的对象
    @Test
    public void test3() throws Exception {
        String className = "cn.edu.xidian.ictt.yk.reflection.p3.Person";
        Class clazz = Class.forName(className);

        Constructor cons = clazz.getDeclaredConstructor(String.class, int.class);
        cons.setAccessible(true);

        Person p = (Person) cons.newInstance("yk", 20);
        System.out.println(p); // Person{name='yk', age=20}
    }
}

       import java.lang.reflect.Field;


public class TestField {

    //获取对应的运行时类的属性
    @Test
    public void test1() {
        Class clazz = Person.class;

        //1.getFields():只能获取到运行时类中及其父类中声明为public的属性
        Field[] fields = clazz.getFields();
        for (int i = 0; i < fields.length; i++) {
            System.out.println(fields[i].getName());
        }

        System.out.println();

        //2.getDeclaredFields():获取运行时类本身声明的所有的属性
        Field[] fields1 = clazz.getDeclaredFields();
        for (Field f : fields1) {
            System.out.println(f.getName());
        }

        /*
        name
        weight

        name
        age
        id
         */
    }

    //权限修饰符 变量类型 变量名
    @Test
    public void test2(){
        Class clazz = Person.class;
        Field[] fields1 = clazz.getDeclaredFields();

        for(Field f : fields1){
            //1.获取每个属性的权限修饰符
            int i = f.getModifiers();
            String str1 = Modifier.toString(i);
            System.out.print(str1 + " ");

            //2.获取属性的类型
            Class type = f.getType();
            System.out.print(type.getName() + " ");

            //3.获取属性名
            System.out.print(f.getName());

            System.out.println();
        }

        /*
        public java.lang.String name
        private int age
         int id
         */
    }

    //调用运行时类中指定的属性
    @Test
    public void test3() throws Exception{

        Class clazz = Person.class;

        //1.获取指定的属性
        //getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
        Field name = clazz.getField("name");

        //2.创建运行时类的对象
        Person p = (Person)clazz.newInstance();
        System.out.println(p);

        //3.将运行时类的指定的属性赋值
        name.set(p,"Jerry");
        System.out.println(p);
        System.out.println("%"+name.get(p));

        //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
        Field age = clazz.getDeclaredField("age");
        //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
        age.setAccessible(true);
        age.set(p,10);
        System.out.println(p);

        //获取静态属性值
        Field num = clazz.getField("num");
        System.out.println(num.get(null));

        /*
        Person{name='null', age=0}
        Person{name='Jerry', age=0}
        %Jerry
        Person{name='Jerry', age=10}
        22
         */
    }
}

       import java.lang.reflect.Method;


public class TestMethod {

    //获取运行时类的方法
    @Test
    public void test1() {
        Class clazz = Person.class;

        //1.getMethods():获取运行时类及其父类中所有的声明为public的方法
        Method[] m1 = clazz.getMethods();
        for (Method m : m1) {
            System.out.println(m);
            System.out.println("#" + m.getDeclaringClass().getSimpleName());
        }

        System.out.println();

        //2.getDeclaredMethods():获取运行时类本身声明的所有的方法
        Method[] m2 = clazz.getDeclaredMethods();
        for (Method m : m2) {
            System.out.println(m);
        }
    }

    //注解 权限修饰符 返回值类型 方法名 形参列表 异常
    @Test
    public void test2() {
        Class clazz = Person.class;

        Method[] m2 = clazz.getDeclaredMethods();
        for (Method m : m2) {
            //1.注解
            Annotation[] ann = m.getAnnotations();
            for (Annotation a : ann) {
                System.out.println(a);
            }

            //2.权限修饰符
            String str = Modifier.toString(m.getModifiers());
            System.out.print(str + " ");
            // System.out.print(Modifier.isPrivate(m.getModifiers()) + " ");

            //3.返回值类型
            Class returnType = m.getReturnType();
            System.out.print(returnType.getName() + " ");

            //4.方法名
            System.out.print(m.getName() + " ");

            //5.形参列表
            System.out.print("(");
            Class[] params = m.getParameterTypes();
            for (int i = 0; i < params.length; i++) {
                System.out.print(params[i].getName() + " args-" + i + " ");
            }
            System.out.print(")");

            //6.异常类型
            Class[] exps = m.getExceptionTypes();
            if (exps.length != 0) {
                System.out.print("throws ");
            }
            for (int i = 0; i < exps.length; i++) {
                System.out.print(exps[i].getName() + " ");
            }
            System.out.println();
        }
    }

    //调用运行时类中指定的方法
    @Test
    public void test3() throws Exception {
        Class clazz = Person.class;

        //关于无参数方法的调用
        //getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法
        //调用指定的方法:Object invoke(Object obj,Object ... obj)
        Method m1 = clazz.getMethod("show");
        Person p = (Person) clazz.newInstance();
        Object returnVal = m1.invoke(p); // I am showing
        System.out.println(returnVal); // null

        //关于重写方法的调用
        Method m2 = clazz.getMethod("toString");
        Object returnVal1 = m2.invoke(p);
        System.out.println(returnVal1); // Person{name='null', age=0}

        //对于运行时类中静态方法的调用
        Method m3 = clazz.getMethod("info");
        m3.invoke(Person.class); // INFO

        //关于带参数方法的调用
        //getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法
        Method m4 = clazz.getDeclaredMethod("display", String.class, int.class);
        m4.setAccessible(true);
        Object value = m4.invoke(p, "CHN", 10); // nation: CHN
        System.out.println(value); // 10
    }
}

       其他。


public class TestOthers {

    //1.获取运行时类的父类
    @Test
    public void test1() {
        Class clazz = Person.class;
        Class superClass = clazz.getSuperclass();

        System.out.println(superClass); // class cn.edu.xidian.ictt.yk.reflection.p2.Creature
    }

    //2.获取带泛型的父类
    @Test
    public void test2() {
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();

        System.out.println(type1); // cn.edu.xidian.ictt.yk.reflection.p2.Creature<java.lang.String>
    }

    //3*.获取父类的泛型
    @Test
    public void test3() {
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();
        System.out.println(type1);

        ParameterizedType param = (ParameterizedType) type1;
        Type[] ars = param.getActualTypeArguments();

        System.out.println(((Class) ars[0]).getName()); // java.lang.String
    }

    //4.获取实现的接口
    @Test
    public void test4() {
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces();

        for (Class i : interfaces) {
            System.out.println(i);
        }

        /*
        interface cn.edu.xidian.ictt.yk.reflection.p2.MyInterface
        interface java.lang.Comparable
         */
    }

    //5.获取所在的包
    @Test
    public void test5(){
        Class clazz = Person.class;
        Package pack = clazz.getPackage();

        System.out.println(pack); // package cn.edu.xidian.ictt.yk.reflection.p3
    }

    //6.获取注解
    @Test
    public void test6(){
        Class clazz = Person.class;
        Annotation[] anns = clazz.getAnnotations();

        for(Annotation a : anns){
            System.out.println(a); // @cn.edu.xidian.ictt.yk.reflection.p2.MyAnnotation(value=yk)
        }
    }

    //7.获取内部类
    @Test
    public void test7() {
        Class clazz = Person.class;
        Class[] cs = clazz.getDeclaredClasses();
        for (Class c: cs) {
            System.out.println(c.getName()); // cn.edu.xidian.ictt.yk.reflection.p3.Person$Bird
        }
    }
}

4. 使用反射编写泛型数组代码

       需求:扩展任意数组对象的容量。


public static Object copyOf(Object obj, int newLength) {
    Class clazz = obj.getClass();
    if (!clazz.isArray()) return null;
    Class componentType = clazz.getComponentType();
    Object object = Array.newInstance(componentType, newLength);
    System.arraycopy(obj, 0, object, 0, Math.min(Array.getLength(obj), newLength));
    return object;
}
代码下载参见https://github.com/HengYk/Reflection