关于java:Java反射详解

文章和代码曾经归档至【Github仓库:https://github.com/timerring/java-tutorial 】或者公众号【AIShareLab】回复 java 也可获取。

反射(reflection)

一个需要引出反射

  1. 依据配置文件 re.properties 指定信息,创立Cat对象并调用办法hi

    classfullpath = com.hspedu.Cat
    method = hi
  2. 这样的需要在学习框架时特地多,即通过内部文件配置,在不批改源码状况下。来控制程序,也合乎设计模式的ocp准则(开闭准则:不批改源码,扩容性能)。
package com.hspedu.reflection.question;

import com.hspedu.Cat;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 反射问题的引入
 */
@SuppressWarnings({"all"})
public class ReflectionQuestion {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        //依据配置文件 re.properties 指定信息, 创立Cat对象并调用办法hi
        //传统的形式 new 对象 -》 调用办法
//        Cat cat = new Cat();
//        cat.hi(); ===> cat.cry() 批改源码.

        //咱们尝试做一做 -> 明确反射

        //1. 应用Properties 类, 能够读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
        String methodName = properties.get("method").toString();//"hi"
        System.out.println("classfullpath=" + classfullpath);
        System.out.println("method=" + methodName);

        //2. 创建对象 , 传统的办法,行不通 =》 反射机制
        //new classfullpath(); // classfullpath 这是一个字符串,而正规应该new 类名()

        //3. 应用反射机制解决
        //(1) 加载类, 返回Class类型的对象cls
        Class cls = Class.forName(classfullpath);
        //(2) 通过 cls 失去你加载的类 com.hspedu.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型=" + o.getClass()); //运行类型
        //(3) 通过 cls 失去你加载的类 com.hspedu.Cat 的 methodName"hi"  的办法对象
        //    即:在反射中,能够把办法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
        //(4) 通过method1 调用办法: 即通过办法对象来实现调用办法
        System.out.println("=============================");
        method1.invoke(o); //传统办法 对象.办法() , 反射机制 办法.invoke(对象)
    }
}

反射机制

Java Reflection

  1. 反射机制容许程序在执行期借助于 Reflection API 获得任何类的外部信息(比方成员变量,结构器,成员办法等等),并能操作对象的属性及办法。反射在设计模式和框架底层都会用到。
  2. 加载完类之后,在堆中就产生了一个Class类型的对象( 一个类只有一个Class对象),这个对象蕴含了类的残缺构造信息。通过这个对象失去类的构造。这个Class对象就像一面镜子,透过这个镜子看到类的构造,所以,形象的称之为:反射。

类比:

p对象 —> 类型Person类

Class对象cls —> 类型Class类

Java 反射机制原理示意图

Java 反射机制能够实现

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时结构任意一个类的对象
  3. 在运行时失去任意一个类所具备的成员变量和办法
  4. 在运行时调用任意一个对象的成员变量和办法
  5. 生成动静代理

反射相干的次要类

  1. java.lang.Class:代表一个类,Class对象示意某个类加载后在堆中的对
  2. java.lang.reflect.Method:代表类的办法, Method对象示意某个类的办法
  3. java.lang.reflect.Field:代表类的成员变量,Field对象示意某个类的成员变量
  4. java.lang.reflect.Constructor:代表类的构造方法,Constructor对象示意
    结构器

这些类在java.lang.reflection

package com.hspedu.reflection;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

public class Reflection01 {

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

        //1. 应用Properties 类, 能够读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
        String methodName = properties.get("method").toString();//"hi"

        //2. 应用反射机制解决
        //(1) 加载类, 返回Class类型的对象cls
        Class cls = Class.forName(classfullpath);
        //(2) 通过 cls 失去你加载的类 com.hspedu.Cat 的对象实例
        Object o = cls.newInstance();
        System.out.println("o的运行类型=" + o.getClass()); //运行类型
        //(3) 通过 cls 失去你加载的类 com.hspedu.Cat 的 methodName"hi"  的办法对象
        //    即:在反射中,能够把办法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
        //(4) 通过method1 调用办法: 即通过办法对象来实现调用办法
        System.out.println("=============================");
        method1.invoke(o); //传统办法 对象.办法() , 反射机制 办法.invoke(对象)

        // java.lang.reflect.Field: 代表类的成员变量, Field对象示意某个类的成员变量
        // getField不能失去公有的属性
        Field nameField = cls.getField("age"); //
        System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射: 成员变量对象.get(对象)

        //java.lang.reflect.Constructor: 代表类的构造方法, Constructor对象示意结构器
        //()中能够指定结构器参数类型, 返回无参结构器
        Constructor constructor = cls.getConstructor();
        System.out.println(constructor);//Cat()


        Constructor constructor2 = cls.getConstructor(String.class); //这里老师传入的 String.class 就是String类的Class对象
        System.out.println(constructor2);//Cat(String name)
    }
}

反射长处和毛病

  1. 长处:能够动静的创立和应用对象(也是框架底层外围),应用灵便,没有反射机制,框架技术就失去底层撑持。
  2. 毛病:应用反射根本是解释执行,对执行速度有影响.

反射调用优化-敞开拜访查看

  1. Method 和 Field、Constructor对象都有 setAccessible() 办法
  2. setAccessible作用是启动和禁用拜访安全检查的开关
  3. 参数值为true示意反射的对象在应用时勾销拜访查看,进步反射的效率。参数值为false则示意反射的对象执行拜访查看。
package com.hspedu.reflection;

import com.hspedu.Cat;

import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 测试反射调用的性能,和优化计划
 */
public class Reflection02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        m1();
        m2();
        m3();
    }

    //传统办法来调用hi
    public static void m1() {

        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 90; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("m1() 耗时=" + (end - start));
    }

    //反射机制调用办法hi
    public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Class cls = Class.forName("com.hspedu.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(o);//反射调用办法
        }
        long end = System.currentTimeMillis();
        System.out.println("m2() 耗时=" + (end - start));
    }

    //反射调用优化 + 敞开拜访查看

    public static void m3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Class cls = Class.forName("com.hspedu.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");
        hi.setAccessible(true);//在反射调用办法时,勾销拜访查看
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(o);//反射调用办法
        }
        long end = System.currentTimeMillis();
        System.out.println("m3() 耗时=" + (end - start));
    }
}

Class 类

根本介绍

  1. Class也是类,因而也继承Object类[类图]
  2. Class类对象不是new进去的,而是零碎创立的[演示]
  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次[演示]
  4. 每个类的实例都会记得本人是由哪个Class 实例所生成
  5. 通过Class对象能够残缺地失去一个类的残缺构造,通过一系列API
  6. Class对象是寄存在堆的
  7. 类的字节码二进制数据,是放在办法区的,有的中央称为类的元数据(包含办法代码, 变量名,办法名,拜访权限等等) https://www.zhihu.com/question/38496907【示意图】
package com.hspedu.reflection.class_;

import com.hspedu.Cat;

import java.util.ArrayList;

/**
 * 对Class类特点的梳理
 */
public class Class01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //看看Class类图
        //1. Class也是类,因而也继承Object类
        //Class
        //2. Class类对象不是new进去的,而是零碎创立的
        //(1) 传统new对象
        /*  ClassLoader类
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, false);
            }
         */
        //Cat cat = new Cat();
        //(2) 反射形式, 方才老师没有debug到 ClassLoader类的 loadClass, 起因是,我没有登记Cat cat = new Cat(); 而类只会加载一次,刚刚加载过上面就不会再次加载
        /*
            ClassLoader类, 依然是通过 ClassLoader类加载Cat类的 Class对象
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, false);
            }
         */
        Class cls1 = Class.forName("com.hspedu.Cat");

        //3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次!!!!!!!!!!
        Class cls2 = Class.forName("com.hspedu.Cat");
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode()); // 相等
        Class cls3 = Class.forName("com.hspedu.Dog");
        System.out.println(cls3.hashCode());
    }
}

Class 类的罕用办法

package com.hspedu.reflection.class_;

import com.hspedu.Car;

import java.lang.reflect.Field;

/**
 * 演示Class类的罕用办法
 */
public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {

        String classAllPath = "com.hspedu.Car";
        //1 . 获取到Car类 对应的 Class对象
        //<?> 示意不确定的Java类型
        Class<?> cls = Class.forName(classAllPath);
        //2. 输入cls
        System.out.println(cls); //显示cls对象, 是哪个类的Class对象 com.hspedu.Car
        System.out.println(cls.getClass());//输入cls运行类型 java.lang.Class
        //3. 失去包名
        System.out.println(cls.getPackage().getName());//包名 com.hspedu
        //4. 失去全类名
        System.out.println(cls.getName()); // com.hspedu.Car
        //5. 通过cls创建对象实例
        Car car = (Car) cls.newInstance();
        System.out.println(car);//car.toString()
        //6. 通过反射获取属性 brand
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));//宝马
        //7. 通过反射给属性赋值
        brand.set(car, "飞驰");
        System.out.println(brand.get(car));//飞驰
        //8 我心愿大家能够失去所有的属性(字段)
        System.out.println("=======所有的字段属性====");
        Field[] fields = cls.getFields();
        for (Field f : fields) {
            System.out.println(f.getName());//名称
        }
    }
}

获取Class 类对象

1.前提:已知一个类的全类名,且该类在类门路下,可通过Class类的静态方法 forName() 获取,可能抛出 ClassNotFoundException,实例:

Class cls1 = Class.forName("java.lang.Cat");

利用场景:多用于配置文件,读取类全门路,加载类.

2.前提:若已知具体的类,通过类的 class 获取,该形式最为安全可靠,程序性能最高,实例:

Class cls2 = Cat.class

利用场景:多用于参数传递,比方通过反射失去对应结构器对象.

3.前提:已知某个类的实例,调用该实例的 getClass() 办法获取Class对象,实例:

Class clazz =对象.getClass();//运行类型

利用场景:通过创立好的对象,获取Class对象.

4.其余形式

ClassLoader cls =对象.getClass(.getClassLoaderO;
Class clazz4 = cl.loadClass(“类的全类名”);

5.根本数据 (int, char,boolean.float,double,byte,long,short) 按如下形式失去Class类对象:

Class cls =根本数据类型.class

6.根本数据类型对应的包装类,能够通过.TYPE失去Class类对象:

Class cls = 包装类.TYPE

package com.hspedu.reflection.class_;

import com.hspedu.Car;

/**
 * 演示失去Class对象的各种形式(6)
 */
public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {

        //1. Class.forName
        String classAllPath = "com.hspedu.Car"; //通过读取配置文件获取
        Class<?> cls1 = Class.forName(classAllPath);
        System.out.println(cls1);

        //2. 类名.class , 利用场景: 用于参数传递(例如后面通过反射失去结构器对象 传入参数就是 类名.class)
        Class cls2 = Car.class;
        System.out.println(cls2);

        //3. 对象.getClass(), 利用场景,有对象实例
        Car car = new Car();
        Class cls3 = car.getClass();
        System.out.println(cls3);

        //4. 通过类加载器【有4品种加载器】来获取到类的Class对象
        //(1)先失去类加载器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
        //(2)通过类加载器失去Class对象
        Class cls4 = classLoader.loadClass(classAllPath);
        System.out.println(cls4);

        //cls1 , cls2 , cls3 , cls4 其实是同一个对象,因为一个类只能有一个class对象
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        System.out.println(cls3.hashCode());
        System.out.println(cls4.hashCode());

        //5. 根本数据(int, char,boolean,float,double,byte,long,short) 按如下形式失去Class类对象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
        System.out.println(integerClass);//int

        //6. 根本数据类型对应的包装类,能够通过 .TYPE 失去Class类对象
        Class<Integer> type1 = Integer.TYPE;
        Class<Character> type2 = Character.TYPE; //其它包装类BOOLEAN, DOUBLE, LONG,BYTE相似
        System.out.println(type1);

        System.out.println(integerClass.hashCode());// 是同一个
        System.out.println(type1.hashCode());// 是同一个
    }
}

哪些类型有Class 对象

如下类型有Class 对象

  1. 外部类,成员外部类,动态外部类,部分外部类,匿名外部类
  2. interface:接口
  3. 数组
  4. enum:枚举
  5. annotation:注解
  6. 根本数据类型
  7. void
package com.hspedu.reflection.class_;

import java.io.Serializable;

/**
 * 演示哪些类型有Class对象
 */
public class AllTypeClass {
    public static void main(String[] args) {

        Class<String> cls1 = String.class;//外部类
        Class<Serializable> cls2 = Serializable.class;//接口
        Class<Integer[]> cls3 = Integer[].class;//数组
        Class<float[][]> cls4 = float[][].class;//二维数组
        Class<Deprecated> cls5 = Deprecated.class;//注解
        Class<Thread.State> cls6 = Thread.State.class;//枚举
        Class<Long> cls7 = long.class;//根本数据类型
        Class<Void> cls8 = void.class;//void数据类型
        Class<Class> cls9 = Class.class;//

        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}

类加载

根本阐明

反射机制是java实现动静语言的要害,也就是通过反射实现类动静加载

  1. 动态加载:编译时加载相干的类,如果没有则报错,依赖性太强。
  2. 动静加载:运行时加载须要的类,如果运行时不必该类,即便不存在该类,也不报错,升高了依赖性。

类加载机会

  1. 当创建对象时(new) //动态加载
  2. 当子类被加载时,父类也加载 //动态加载
  3. 调用类中的动态成员时 //动态加载
  4. 通过反射 //动静加载

Class.forName(“com.test.Cat”);

类加载过程图

加载后办法区存储的是类的字节码二进制文件,而堆区就创立了相应的类的Class对象。

类加载各阶段实现工作

留神:这里是针对类的加载阶段,并不是new的阶段,因而是针对动态成员。

加载阶段

JVM在该阶段的次要目标是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象

连贯阶段-验证

  1. 目标是为了确保 Class文件的字节流中蕴含的信息合乎以后虚拟机的要求,并且不会危害虚拟机本身的平安。
  2. 包含:文件格式验证(是否以魔数oxcafebabe结尾)、元数据验证、字节码验证和符号援用验证[举例说明]。
  3. 能够思考应用 -Xverify:none 参数来敞开大部分的类验证措施,缩短虚拟机类加载的工夫。

连贯阶段-筹备

  1. JVM会在该阶段对动态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0、OL、null、false等)。这些变量所应用的内存都将在办法区中进行调配。
package com.hspedu.reflection.classload_;

public class ClassLoad02 {
    public static void main(String[] args) {

    }
}
class A {
    //属性-成员变量-字段
    //剖析类加载的链接阶段-筹备 属性是如何解决
    //1. n1 是实例属性, 不是动态变量,因而在筹备阶段,是不会分配内存
    //2. n2 是动态变量,分配内存 n2 是默认初始化 0 ,而不是20。初始化子阶段才会是20。
    //3. n3 是 static final 是 常量, 他和动态变量不一样, 因为一旦赋值就不变 因而n3 = 30
    public int n1 = 10;
    public static  int n2 = 20;
    public static final  int n3 = 30;
}

连贯阶段-解析

虚拟机将常量池内的符号援用替换为间接援用的过程。这个过程是由jvm机主动实现的。

Initialization初始化

  1. 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()办法的过程。
  2. <clinit>()办法是由编译器按语句在源文件中呈现的程序,顺次主动收集类中的所有动态变量的赋值动作和动态代码块中的语句,并进行合并。
  3. 虚构机会保障一个类的<clinit>()办法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>0)办法,其余线程都须要阻塞期待,直到流动线程执行<clinit>()办法结束

    正因为有这个机制,能力保障某个类在内存中, 只有一份Class对象。

package com.hspedu.reflection.classload_;

/**
 * 演示类加载-初始化阶段
 */
public class ClassLoad03 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1. 加载B类,并生成 B的class对象
        //2. 链接 num = 0
        //3. 初始化阶段
        //    顺次主动收集类中的所有动态变量的赋值动作和动态代码块中的语句,并合并
        /*
                clinit() {
                // 依照程序合并
                    System.out.println("B 动态代码块被执行");
                    //num = 300;
                    num = 100;
                }
                合并: num = 100

         */

        //new B();//类加载
        //System.out.println(B.num);//100, 如果间接应用类的动态属性,也会导致类的加载

        //看看加载类的时候,是有同步机制管制
        /*
        protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
        {
            //正因为有这个机制,能力保障某个类在内存中, 只有一份Class对象
            synchronized (getClassLoadingLock(name)) {
            //....
            }
            }
         */
        B b = new B();
    }
}

class B {
    static {
        System.out.println("B 动态代码块被执行");
        num = 300;
    }

    static int num = 100;

    //如果间接调用类的动态变量,而没有new对象,则结构器不会被执行
    public B() {//结构器
        System.out.println("B() 结构器被执行");
    }
}

通过反射获取类的构造信息

第一组: java.lang.Class 类

  1. getName:获取全类名
  2. getSimpleName:获取简略类名
  3. getFields:获取所有public润饰的属性,蕴含本类以及父类的
  4. getDeclared Fields:获取本类中所有属性
  5. getMethods:获取所有public润饰的办法,蕴含本类以及父类的
  6. getDeclaredMethods:获取本类中所有办法
  7. getConstructors:获取本类所有public润饰的结构器
  8. getDeclaredConstructors:获取本类中所有结构器
  9. getPackage:以Package模式返回包信息
  10. getSuperClass:以Class模式返回父类信息
  11. getlnterfaces:以Class[模式返回接口信息
  12. getAnnotations:以Annotation0模式返回注解信息

第二组: java.lang.reflect.Field 类

  1. getModifiers: 以int模式返回修饰符
    [阐明:默认修饰符是0,public是1,private是2,protected是4,static是8, final是16], public(1) + static (8) =9
  2. getType:以Class模式返回类型(属性对应类的class对象)
  3. getName:返回属性名

第三组: java.lang.reflect.Method 类

  1. getModifiers: 以int模式返回修饰符
    [阐明:默认修饰符是0,public是1,private是2,protected是4,static是8, final是16]
  2. getReturnType: 以Class模式获取返回类型
  3. getName: 返回办法名
  4. getParameterTypes: 以Class[]返回参数类型数组

第四组: java.lang.reflect.Constructor 类

  1. getModifiers: 以int模式返回修饰符
  2. getName: 返回结构器名(全类名)
  3. getParameterTypes: 以Class[]返回参数类型数组
package com.hspedu.reflection;

import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 演示如何通过反射获取类的构造信息
 */
public class ReflectionUtils {
    public static void main(String[] args) {

    }

    @Test
    public void api_02() throws ClassNotFoundException, NoSuchMethodException {
        //失去Class对象
        Class<?> personCls = Class.forName("com.hspedu.reflection.Person");
        //getDeclaredFields:获取本类中所有属性
        //规定 阐明: 默认修饰符 是0 , public  是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有属性=" + declaredField.getName()
                    + " 该属性的修饰符值=" + declaredField.getModifiers()
                    + " 该属性的类型=" + declaredField.getType());
        }

        //getDeclaredMethods:获取本类中所有办法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有办法=" + declaredMethod.getName()
                    + " 该办法的拜访修饰符值=" + declaredMethod.getModifiers()
                    + " 该办法返回类型" + declaredMethod.getReturnType());

            //输入以后这个办法的形参数组状况
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该办法的形参类型=" + parameterType);
            }
        }

        //getDeclaredConstructors:获取本类中所有结构器
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("====================");
            System.out.println("本类中所有结构器=" + declaredConstructor.getName());//这里老师只是输入名

            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该结构器的形参类型=" + parameterType);
            }
        }
    }

    // 第一组办法API
    @Test
    public void api_01() throws ClassNotFoundException, NoSuchMethodException {

        //失去Class对象
        Class<?> personCls = Class.forName("com.hspedu.reflection.Person");
        //getName:获取全类名
        System.out.println(personCls.getName());//com.hspedu.reflection.Person
        //getSimpleName:获取简略类名
        System.out.println(personCls.getSimpleName());//Person
        //getFields: 获取所有public润饰的属性,蕴含本类以及父类的
        Field[] fields = personCls.getFields();
        for (Field field : fields) {//加强for
            System.out.println("本类以及父类的属性=" + field.getName());
        }
        //getDeclaredFields: 获取本类中所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有属性=" + declaredField.getName());
        }
        //getMethods: 获取所有public润饰的办法,蕴含本类以及父类的
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类以及父类的办法=" + method.getName());
        }
        //getDeclaredMethods:获取本类中所有办法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有办法=" + declaredMethod.getName());
        }
        //getConstructors: 获取所有public润饰的结构器,蕴含本类
        Constructor<?>[] constructors = personCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("本类的结构器=" + constructor.getName());
        }
        //getDeclaredConstructors:获取本类中所有结构器
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("本类中所有结构器=" + declaredConstructor.getName());//这里老师只是输入名
        }
        //getPackage:以Package模式返回 包信息
        System.out.println(personCls.getPackage());//com.hspedu.reflection
        //getSuperClass:以Class模式返回父类信息
        Class<?> superclass = personCls.getSuperclass();
        System.out.println("父类的class对象=" + superclass);//
        //getInterfaces:以Class[]模式返回接口信息
        Class<?>[] interfaces = personCls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("接口信息=" + anInterface);
        }
        //getAnnotations:以Annotation[] 模式返回注解信息
        Annotation[] annotations = personCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("注解信息=" + annotation);//注解
        }


    }
}

class A {
    public String hobby;

    public void hi() {

    }

    public A() {
    }

    public A(String name) {
    }
}

interface IA {
}

interface IB {

}

@Deprecated
class Person extends A implements IA, IB {
    //属性
    public String name;
    protected static int age; // 4 + 8 = 12
    String job;
    private double sal;

    //结构器
    public Person() {
    }

    public Person(String name) {
    }

    //公有的
    private Person(String name, int age) {

    }

    //办法
    public void m1(String name, int age, double sal) {

    }

    protected String m2() {
        return null;
    }

    void m3() {

    }

    private void m4() {

    }
}

通过反射创建对象

  1. 形式一:调用类中的public润饰的无参结构器
  2. 形式二:调用类中的指定结构器
  3. Class类相干办法

    • newlnstance:调用类中的无参结构器,获取对应类的对象
    • getConstructor(Class..clazz):依据参数列表,获取对应的public结构器对象
    • getDecalaredConstructor(Class..clazz):依据参数列表,获取对应的所有结构器对象
  4. Constructor类相干办法

    • setAccessible:暴破 (应用反射能够拜访private结构器/办法/属性, 反射背后,都是纸老虎)

      constructor1.setAccessible(true);
    • newlnstance(Object…obj):调用结构器

案例演示

测试1:通过反射创立某类的对象,要求该类中必须有public 的无参结构

测试2:通过调用某个特定结构器的形式,实现创立某类的对象

package com.hspedu.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 演示通过反射机制创立实例
 */
public class ReflecCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        //1. 先获取到User类的Class对象
        Class<?> userClass = Class.forName("com.hspedu.reflection.User");
        //2. 通过public的无参结构器创立实例
        Object o = userClass.newInstance();
        System.out.println(o);
        //3. 通过public的有参结构器创立实例
        /*
            constructor 对象就是
            public User(String name) {//public的有参结构器
                this.name = name;
            }
         */
        //3.1 先失去对应结构器
        Constructor<?> constructor = userClass.getConstructor(String.class);
        //3.2 创立实例,并传入实参
        Object hsp = constructor.newInstance("hsp");
        System.out.println("hsp=" + hsp);

        //4. 通过非public的有参结构器创立实例
        //4.1 失去private的结构器对象
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
        //4.2 创立实例
        //暴破【暴力破解】 , 应用反射能够拜访private结构器/办法/属性, 反射背后,都是纸老虎
        constructor1.setAccessible(true);

        Object user2 = constructor1.newInstance(100, "张三丰");
        System.out.println("user2=" + user2);
    }
}

class User { //User类
    private int age = 10;
    private String name = "教育";

    public User() {//无参 public
    }

    public User(String name) {//public的有参结构器
        this.name = name;
    }

    private User(int age, String name) {//private 有参结构器
        this.age = age;
        this.name = name;
    }

    public String toString() {
        return "User [age=" + age + ", name=" + name + "]";
    }
}

通过反射拜访类中的成员

拜访属性

  1. 依据属性名获取Field对象
    Field f = clazz对象.getDeclaredField(属性名);
  2. 暴破:f.setAccessible(true); //f是Field
  3. 拜访

    f.set(o, 值);//o示意对象
    syso(f.get(o));//o示意对象
  4. 留神:如果是动态属性,则set和get中的参数o,能够写成null
package com.hspedu.reflection;

import java.lang.reflect.Field;

/**
 * 演示反射操作属性
 */
public class ReflecAccessProperty {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {

        //1. 失去Student类对应的 Class对象
        Class<?> stuClass = Class.forName("com.hspedu.reflection.Student");
        //2. 创建对象
        Object o = stuClass.newInstance();//o 的运行类型就是Student
        System.out.println(o.getClass());//Student
        //3. 应用反射失去 age 属性对象
        Field age = stuClass.getField("age");
        age.set(o, 88);//通过反射来操作属性
        System.out.println(o);//
        System.out.println(age.get(o));//返回age属性的值

        //4. 应用反射操作name 属性
        Field name = stuClass.getDeclaredField("name");
        //对name 进行暴破, 能够操作private 属性
        name.setAccessible(true);
        //name.set(o, "老");
        name.set(null, "老~");// 因为name是static属性,在类加载的时候就曾经有了,因而 o 也能够写出null
        System.out.println(o);
        System.out.println(name.get(o)); // 获取属性值
        System.out.println(name.get(null));// 获取属性值, 必须要求name是static
    }
}

class Student {//类
    public int age;
    private static String name;

    public Student() {//结构器
    }
    public String toString() {
        return "Student [age=" + age + ", name=" + name + "]";
    }
}

拜访办法

  1. 依据办法名和参数列表获取Method办法对象:

    Method m = clazz.getDeclaredMethod(办法名, X.class);//失去本类的所有办法
  2. 获取对象:Object o=clazz.newlnstance();
  3. 暴破:m.setAccessible(true);
  4. 拜访:Object returnValue = m.invoke(o,实参列表);//o就是对象
  5. 留神:如果是静态方法,则invoke的参数o,能够写成null!
package com.hspedu.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 演示通过反射调用办法
 */
public class ReflecAccessMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

        //1. 失去Boss类对应的Class对象
        Class<?> bossCls = Class.forName("com.hspedu.reflection.Boss");
        //2. 创建对象
        Object o = bossCls.newInstance();
        //3. 调用public的hi办法
        //Method hi = bossCls.getMethod("hi", String.class);//OK
        //3.1 失去hi办法对象
        Method hi = bossCls.getDeclaredMethod("hi", String.class);//OK
        //3.2 调用
        hi.invoke(o, "教育~");

        //4. 调用private static 办法
        //4.1 失去 say 办法对象
        Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
        //4.2 因为say办法是private, 所以须要暴破,原理和后面讲的结构器和属性一样
        say.setAccessible(true);
        System.out.println(say.invoke(o, 100, "张三", '男'));
        //4.3 因为say办法是static的,还能够这样调用 ,能够传入null
        System.out.println(say.invoke(null, 200, "李四", '女'));

        //5. 在反射中,如果办法有返回值,对立返回Object , 然而他运行类型和办法定义的返回类型统一
        Object reVal = say.invoke(null, 300, "王五", '男');
        System.out.println("reVal 的运行类型=" + reVal.getClass());//String


        //在演示一个返回的案例
        Method m1 = bossCls.getDeclaredMethod("m1");
        Object reVal2 = m1.invoke(o);
        System.out.println("reVal2的运行类型=" + reVal2.getClass());//Monster


    }
}

class Monster {}
class Boss {//类
    public int age;
    private static String name;

    public Boss() {//结构器
    }

    public Monster m1() {
        return new Monster();
    }

    private static String say(int n, String s, char c) {//静态方法
        return n + " " + s + " " + c;
    }

    public void hi(String s) {//一般public办法
        System.out.println("hi " + s);
    }
}

本章作业

练习1 :通过反射批改公有成员变量

  1. 定义PrivateTest类,有公有name属性,并且属性值为hellokitty
  2. 提供getName的私有办法
  3. 创立PrivateTest的类,利用Class类失去公有的name属性,批改公有的name属性值,并调用getName()的办法打印name属性值

    class PrivateTest{
    private String name = "hellokitty";
    public String getName(){
    return name;
    }}
package com.hspedu.reflection.homework;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Homework01 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        /**
         * 定义PrivateTest类,有公有name属性,并且属性值为hellokitty
         * 提供getName的私有办法
         * 创立PrivateTest的类,利用Class类失去公有的name属性,批改公有的name属性值,并调用getName()的办法打印name属性值
         */
        //1. 失去 PrivateTest类对应的Class对象
        Class<PrivateTest> privateTestClass = PrivateTest.class;
        //2. 创建对象实例
        PrivateTest privateTestObj = privateTestClass.newInstance();
        //3. 失去name属性对象
        Field name = privateTestClass.getDeclaredField("name");//name属性是private
        //4. 暴破name
        name.setAccessible(true);
        name.set(privateTestObj, "天龙八部");
        //5. 失去getName办法对象
        Method getName = privateTestClass.getMethod("getName");
        //6. 因为getName() 是public,所有间接调用
        Object invoke = getName.invoke(privateTestObj);
        System.out.println("name属性值=" + invoke);//天龙八部

    }
}

class PrivateTest {
    private String name = "hellokitty";
    //默认无参结构器
    public String getName() {
        return name;
    }
}

练习2 :利用反射和File实现以下性能

  1. 利用Class类的forName办法失去File类的class对象
  2. 在控制台打印File类的所有结构器
  3. 通过newlnstance的办法创立File对象,并创立E:\mynew.txt文件
    提醒:创立文件的失常写法如下:
    File file = new File(“d:1laa.txt”);//内存
    file.createNewFile0;//办法,能力真正的创立文件
package com.hspedu.reflection.homework;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Homework02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /**
         * 利用Class类的forName办法失去File类的class 对象
         * 在控制台打印File类的所有结构器
         * 通过newInstance的办法创立File对象,并创立E:\mynew.txt文件
         */
        //1. Class类的forName办法失去File类的class 对象
        Class<?> fileCls = Class.forName("java.io.File");
        //2. 失去所有的结构器
        Constructor<?>[] declaredConstructors = fileCls.getDeclaredConstructors();
        //遍历输入
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("File结构器=" + declaredConstructor);
        }
        //3. 指定的失去 public java.io.File(java.lang.String)
        Constructor<?> declaredConstructor = fileCls.getDeclaredConstructor(String.class);
        String fileAllPath = "e:\\mynew.txt";
        Object file = declaredConstructor.newInstance(fileAllPath);//创立File对象

        //4. 失去createNewFile 的办法对象
        Method createNewFile = fileCls.getMethod("createNewFile");
        createNewFile.invoke(file);//创立文件,调用的是 createNewFile
        //file的运行类型就是File
        System.out.println(file.getClass());
        System.out.println("创立文件胜利" + fileAllPath);
    }
}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理