反射

反射

反射的主要类

  1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的Class类对象,通过这个类,可以得到类信息,在堆中的Class对象实际上包含了,类的所有信息,也就是把这个类加载到了堆中,然后我们可以通过对应方法得到这个Class类的实例

    //(1) 加载一个class类,返回一个Class类型的对象,Class就是一个类,类名就是Class
    Class aClass = Class.forName(classfullpath);
    //(2) 通过aClass对象,得到加载的类 com.hsp.Cat 的对象实例
    Object o = aClass.newInstance();
    //(3) 通过 aClass 得到加载的类 com.hsp.Cat 的 methodName"hi" 的方法对象
    // 即: 在反射中,可以把方法视为对象(万物皆对象,什么都可以是对象)
  2. java.lang.reflect.Method:代表类的方法

            Method method = aClass.getMethod(methodName);
            //通过 method 这个Method(方法对象) 来实现调用Cat也就是加载类下的方法
            method.invoke(o); // 传统方法调用对象的方法为,对象.方法() ,而反射机制则是 方法.invoke(对象)
  3. java.lang.reflect.Field:代表类的成员变量(属性),Field对象表示某个类的,对应成员变量(属性) Field.get(对象), 可以获得对应属性

    //java.lang.reflect.Field: 代表类的成员变量(属性),Field对象表示某个类的,对应成员变量(属性)
            //getField不能得到私有的属性
            Field name = aClass.getField("name");
            System.out.println(name.get(o));
  4. java.lang.reflect.Constructor:代表类的构造方法

    // ()括号中可以指定构造器参数类型,返回对应构造器
            Constructor constructor = aClass.getConstructor(); //返回无参构造器
            System.out.println(constructor);
    ​
            Constructor constructor1 = aClass.getConstructor(String.class);//这里传入的String.class就是String的Class对象
            System.out.println(constructor1);

     

类加载的三个阶段

  • 代码/编译阶段

    在这个阶段,java会编译源码文件,形成.class的字节码文件,其中包含类的信息

  • Class类阶段(加载阶段)

    在这个阶段会把类对象加载到堆中,在运行阶段调用了new 关键字创建对象时就会先加载类的Class类对象,然后再创建对象实例,在Class类对象中我们的属性、方法、构造器都是一个个对象,如:属性是Field对象,构造器是 Constructor对象,成员方法是 Method对象

    在普通的过程中我们new 一个对象后的到对象实例,这个实例和堆中加载的对象是有关联的,也就是该对象知道它是属于哪个Class类对象

    在反射机制中我们得到的“Class”对象,其中的方法与加载在堆中这个对象名是相同的,但无法直接调用,而是需要使用 “对象.方法名”的方式,来获得对应的,属性、方法、构造器等的对象来,调用类中的成员

    个人理解:

    • 在调用new 和Class.forName时都会通过ClassLoader.loadClass()加载一个Class类对象,但只会加载一次,如果先new以后,ClassforName就不会再调用ClassLoader.loadClass()

    • 在这个Class类实例中有,创建类的成员信息,并且被做成了一个个对象,可以通过对应方法来获取实例

    • Class类对象只会被系统创建,无法通过new来创建

  • Runtime运行阶段

    此阶段主要运行我们写的程序

  • Runtime运行阶段

    这个就是我们运行时的代码,会根据我们写的代码,来运行相关的程序

反射机制的优点和缺点

优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑

缺点:使用反射基本是解释执行,对执行速度有影响

动态加载和静态加载

  1. 静态加载:编译时加载相关类,如果没有则报错,依赖性太强

  2. 动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性

    • 类加载的时机

      1. 当创建对象时(new) //静态加载

      2. 当子类被加载时 //静态加载

      3. 调用类中的静态成员时 //静态加载

      4. 通过反射 //动态加载

类加载的流程图

  • java源码通过javac编译成字节码文件

    com.Cat.java --》com.Cat.class

    字节码文件中存放着类的各种信息,比如访问修饰符,方法代码,方法名,变量名等

  • 字节码文件通过java运行

  • 类加载:

    1. 加载(Loading) :将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程有类加载器完成

    2. 连接(Linking): //将类的二进制数据合并到JRE中

      1. 验证 (verification) 对文件进行安全校验

      2. 准备(Preparation) 完成默认初始化,如静态变量的默认初始化,分配内存并进行默认初始化(对应数据类型的默认初始值,如0、null、false),这些变量所使用的内训都将在方法区中进行分配

        • 实例变量不会分配内存

        • 静态变量如果不是final修饰,会分配成默认的初始值

        • 如果用final修饰,那么再分配过后会无法改变,所以final的静态变量值就是直接定义的值

      3. 解析(Resolution) 将符号引用转换为直接引用,符号引用主要是占位

    3. 初始化(initialization) 完成显示初始化,JVM负责对类进行初始化,主要指静态成员

      1. 此阶段才真正开始执行类中定义的java程序代码,此阶段实质性<clinit>()方法的过程

      2. <clinit>方法是有编译器按语句在源文件中出现的顺序,一次自动手机类中所有静态变量的赋值动作和静态代码块中的语句,惊醒合并

      3. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁、同步,如果多个线程同时初始化一个类,那么只会有一个线程取执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕

  • 以上两个过程是有jvm虚拟机来控制的

  • 类加载后,内存中:

    • 方法区:存在类的字节码二进制数据,其中包括了,定义的方法,访问权限等描述信息

    • 堆区:类的class对象,两者之间还有引用的关系,这里面是一些数据结构,是程序访问的入口

Class的常用方法

  • 1 getName():返回String形式的该类的名称。
  • 2 newInstance():根据某个Class对象产生其对应类的实例,它调用的是此类的默认构造方法(没有默认无参构造器会报错)   
  • 3 getClassLoader():返回该Class对象对应的类的类加载器。   

  • 4 getSuperClass():返回某子类所对应的直接父类所对应的Class对象   

  • 5 isArray():判定此Class对象所对应的是否是一个数组对象   

  • 6 getComponentType() :如果当前类表示一个数组,则返回表示该数组组件的 Class 对象,否则返回 null。   

  • 7 getConstructor(Class[]) :返回当前 Class 对象表示的类的指定的公有构造子对象。   

  • 8 getConstructors() :返回当前 Class 对象表示的类的所有公有构造子对象数组。   

  • 9 getDeclaredConstructor(Class[]) :返回当前 Class 对象表示的类的指定已说明的一个构造子对象。   

  • 10 getDeclaredConstructors() :返回当前 Class 对象表示的类的所有已说明的构造子对象数组。   

  • 11 getDeclaredField(String) :返回当前 Class 对象表示的类或接口的指定已说明的一个域对象。   

  • 12 getDeclaredFields() :返回当前 Class 对象表示的类或接口的所有已说明的域对象数组。   

  • 13 getDeclaredMethod(String, Class[]) :返回当前 Class 对象表示的类或接口的指定已说明的一个方法对象。   

  • 14 getDeclaredMethods() :返回 Class 对象表示的类或接口的所有已说明的方法数组。   

  • 15 getField(String) :返回当前 Class 对象表示的类或接口的指定的公有成员域对象。   

  • 16 getFields() :返回当前 Class 对象表示的类或接口的所有可访问的公有域对象数组。  

  •  17 getInterfaces() :返回当前对象表示的类或接口实现的接口。   

  • 18 getMethod(String, Class[]) :返回当前 Class 对象表示的类或接口的指定的公有成员方法对象。   

  • 19 getMethods() :返回当前 Class 对象表示的类或接口的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。   

  • 20 isInstance(Object) :此方法是 Java 语言 instanceof 操作的动态等价方法。   

  • 21 isInterface() :判定指定的 Class 对象是否表示一个接口类型   

  • 22 isPrimitive() :判定指定的 Class 对象是否表示一个 Java 的基类型。  

  •  23 newInstance() :创建类的新实例

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

Field类

  1. getModifirers: 以int形式返回修饰符

    [说明:默认修饰符 是0,public是1,private 是2,protected 是3,static 是8,final 是16]

  2. getType:以Class形式返回类型

  3. getName:返回属性名

Method类

  1. getModifirers: 以int形式返回修饰符

    [说明:默认修饰符 是0,public是1,private 是2,protected 是3,static 是8,final 是16]

  2. getReturnType:以Class形式获取 返回类型

  3. getName:返回方法名

  4. getParamterTypes:以Class[]返回参数类型数组

Constructor

  1. getModifirers: 以int形式返回修饰符

    [说明:默认修饰符 是0,public是1,private 是2,protected 是3,static 是8,final 是16]

  2. getName:返回构造器名,全类名

  3. getParameterTypes:以Class[]返回参数类型数组

通过方法创建对象

  1. 方式一:调用类中的public修饰的无参构造器

  2. 方式二:调用类中的指定构造器

  3. Class类相关方法

    • newInstance:调用类中的无参构造器,获取对应类的对象

    • getConstructor(Class....clazz):根据参数列表,获取对应的public构造器对象

    • getDecalaredConstructor(Class....clazz):根据参数列表,获取对应的构造器对象

  4. Constructor类相关方法

    • setAccessible:暴破

    • newInstance(Object...obj):调用构造器

    import java.lang.reflect.Constructor;
    ​
    /**
     * 通过反射机制创建实例
     */
    public class ReflecCreateInstance {
        public static void main(String[] args) throws Exception{
            //1.先获取到User类的Class类对象
            Class<?> aClass = Class.forName("com.hsp.User");
            //2.通过public的无参构造器创建实例
            Object o = aClass.newInstance();
            System.out.println(o);
            //3.通过public的有参参构造器创建实例
            Constructor<?> constructor = aClass.getConstructor(String.class);
            Object o1 = constructor.newInstance("aa");
            System.out.println(o1);
            //4.通过非public的有参构造器创建实例
            Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(int.class, String.class);
            declaredConstructor.setAccessible(true);// 暴破【暴力破解】,使用反射可以访问private构造器/方法/属性
            Object o2 = declaredConstructor.newInstance(100, "垃圾");
            System.out.println(o2);
        }
    }
    class User{
        private int age;
        private String name;
    ​
        public User() {
        }
    ​
        public User(String name) {
            this.name = name;
        }
    ​
        private User(int age, String name) {
            this.age = age;
            this.name = name;
        }
    ​
        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }

通过反射访问类中的成员

  1. 根据属性名获取Field对象

    Field f=clazz对象.getDeclareField(属性名);

  2. 暴破:f.setAccessible(true); //f是Field

  3. 访问

    f.set(o, 值);//o表示对象

    f.set(f.get(o));//o表示对象

  4. 注意:如果是静态属性,则set和get中的参数o,可以写成null

    import java.lang.reflect.Field;
    ​
    /**
     * 通过反射访问类中的成员
     */
    public class ReflecAccessProperty {
        public static void main(String[] args) throws Exception{
            //1. 得到Student类对应的Class对象
            Class<?> aClass = Class.forName("com.hsp.Student");
            //2. 创建对象
            Object o = aClass.newInstance();
            System.out.println(o.getClass());
            //3. 使用反射得到age属性 对象
            Field age = aClass.getField("age");
            Object o1 = age.get(o);
            System.out.println(o1);
            //4. 使用反射操作name属性
            Field name = aClass.getDeclaredField("name");
            name.setAccessible(true);
            //Object o2 = name.get(o);
            Object o2 = name.get(null); //因为name是static属性,因此 o 也可以写成null
            System.out.println(o2);
        }
    }
    class Student{
        public int age;
        private static String name;
    ​
        @Override
        public String toString() {
            return "Student{" +
                    "age=" + age + "name" +name+
                    '}';
        }
    }

通过反射访问类中的方法

  1. 根据方法名和参数列表获取Method方法对象:Method m=clazz.getDeclaredMethod(方法名,XX.class);//得到本类的所有方法

  2. 获取对象:Object o = clazz.newInstance();

  3. 暴破:m.setAccessible(true);

  4. 访问:Object returnValue = m.invoke(o,实参列表);

  5. 注意:如果是静态方法,则invoke的参数o,可以写成null!

package com.hsp;
​
import java.lang.reflect.Method;
​
/**
 * 通过反射调用方法
 */
public class ReflecAccessMethod {
    public static void main(String[] args) throws Exception{
​
        //1. 得到Boss类对应的Class对象
        Class<?> aClass = Class.forName("com.hsp.Boss");
        //2. 创建对象
        Object o = aClass.newInstance();
        //3. 调用public的hi方法
        Method hi = aClass.getMethod("hi",String.class);
        hi.invoke(o,"sb");
        //4. 调用private static的say方法
        Method say = aClass.getDeclaredMethod("say", int.class, String.class, char.class);
        say.setAccessible(true);
        Object invoke = say.invoke(o, 1, "2", '3');//在反射中,如果方法有返回值,同一返回Object
        System.out.println(invoke);
        //因为是静态的还可以把传入的对象置空
        //System.out.println(say.invoke(null,1,"2",'3'));
​
    }
}
​
class Boss {
    public int age;
    public static String name;
​
    private static String say(int n, String s, char c) {
        return n + " " + s + " " + c;
    }
​
    public void hi(String s) {
        System.out.println("hi " + s);
    }
}

小结:如果要访问非public的成员一定要使用含有Declared的方法,如果成员设置为私有,那么还需要使用.setAccessible(true),暴破访问,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值