Java基础:反射机制

一、何为反射机制

        1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。

        2、Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

        反射机制的优缺点

        优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

        缺点:

        (1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;

        (2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
        

        反射原理:

        Class对象的由来是将class文件读入内存,并为之创建一个Class对象

        

二、反射机制的用途

        1、反编译

        2、通过反射机制访问java对象的属性,方法,构造方法等

        3、当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。

        4、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。

        5、例如,在使用Strut2框架的开发过程中,我们一般会在struts.xml里去配置Action,比如

<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute">   
    <result>/shop/shop-index.jsp</result>           
    <result name="error">login.jsp</result>       
</action>

 比如我们请求login.action时,那么StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,从action中查找出name为login的Action,并根据class属性创建SimpleLoginAction实例,并用Invoke方法来调用execute方法,这个过程离不开反射。配置文件与Action建立了一种映射关系,当View层发出请求时,请求会被StrutsPrepareAndExecuteFilter拦截,然后StrutsPrepareAndExecuteFilter会去动态地创建Action实例。

比如,加载数据库驱动的,用到的也是反射。        

        Class.forName("com.mysql.jdbc.Driver"); // 动态加载mysql驱动

三、反射机制常用的类:

        Java.lang.Class;

        Java.lang.reflect.Constructor;

        Java.lang.reflect.Field;

        Java.lang.reflect.Method;

        Java.lang.reflect.Modifier;

四、反射的基本使用:

        1、获取Class的三种方法

        (1)Object-->getClass

        (2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性

        (3)通过class类的静态方法:forName(String className)(最常用)

public class dt {

    public static void main(String[] args) {
        //第一种获取方式
        User user = new User();
        Class aClass = user.getClass();
        System.out.println(aClass.getName());
        //第二种
        Class<User> userClass = User.class;
        System.out.println(aClass == userClass);//判断一二是否为一种
        //第三种
        try {
            Class<?> aClass1 = Class.forName("com.cc.user.tset.User");
            System.out.println(aClass == aClass1);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

打印结果都为true,说明三种方式只有一个class对象产生

三种方式中,常用第三种方式,第一种对象已有,其实不太需要反射了。 第二种需要导入类包,依赖太强,不导入包会引起编译错误。一般使用第三种,一个字符串可以传入也可以写在配置文件中等多种方法。

        2、判断是否为某个类的示例

        一般的使用 instanceof 判断是否为某个类的实例。另外我们也能使用反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例。此方法为 native 方法。

public native boolean isInstance(Object obj);

        3、创建实例:通过反射生成对象的主要方法有两种方法

        1、使用 Class 对象的 newInstance() 

                Object o = stringClass.newInstance();

        2、通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法创建对象,优势是此方式可以指定构造器构造类的实例

public  static  void newUser(){
        Class<?> stringClass = String.class;
        try {
            Constructor<?> constructor = stringClass.getConstructor(String.class);
            Object cc = constructor.newInstance("cc");
            System.out.println(cc);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

        4、通过反射获取构造方法并使用

        (1)批量获取方法

        public Constructor<?>[] getConstructors()  //获取所有公有构造方法

        public Constructor[] getDeclaredConstructors() ///获取所有构造方法(公有和非公有)

        (2)单个获取的方法并调用

        public Constructor getConstructor(Class... parameterTypes)  //获取单个的公有构造方法

        public Constructor getDeclaredConstructor(Class... parameterTypes) //获取"某个构造方法"可以是公有的也可是非公有的。      

        (3)调用构造方法

        Constructor —> newInstance(Object... initargs)

        newInstance 是 Constructor 类的方法(管理构造函数的类)

        api 的解释为:newInstance(Object... initargs) ,使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。它的返回值是T类型,所以 newInstance 是创建了一个构造方法的声明类的新实例对象,并为之调用

示例:

package com.cc.user.tset;

import lombok.Data;

/**
 * @author cc
 * @data 2021年11月15日 15:23
 */
@Data
public class User {

    private String id;

    private String name;

    public User() {
        System.out.println("无参构造方法");
    }

    public User(String id) {
        this.id = id;
        System.out.println("一个参数构造方法");
    }

    public User(String id, String name) {
        this.id = id;
        this.name = name;
        System.out.println("多个参数构造方法id:" + id + "name:" + name);
    }

    private User(int age){
        System.out.println("私有构造方法 年龄"+ age);
    }
}

测试类:

public static void test() throws Exception {
        //加载class对象
        Class<?> aClass = Class.forName("com.cc.user.tset.User");
        //获取所有公有构造方法
        System.out.println("=======所有公有构造方法=========");
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        //获取所有构造方法,公有及非公有
        System.out.println("==============获取所有构造方法(公有及非公有)================");
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        //获取公有、无参的构造函数
        Constructor<?> constructor = aClass.getConstructor(null);
        System.out.println("无参构造函数:"+ constructor);
        //调用构造方法
        Object o = constructor.newInstance();
        //获取私有构造方法,并调用
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(int.class);
        System.out.println(declaredConstructor);
        declaredConstructor.setAccessible(true);
        declaredConstructor.newInstance(11);
    }

控制台输出:

        5、获取成员变量并调用

        1、获取批量字段

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

                Field[] getDeclaredFields() //获取所有字段(含公有和非公有)

        2、获取单独字段

                public Field getField(String fieldName) //根据字段名称获取单独一个公有字段

                public Field getDeclaredField(String fieldName) //根据字段名称获取单独一个字段(可以是非公有)

        3、设置字段值

                Field --> public void set(Object obj,Object value)

                参数:obj :要设置的字段所在对象

                          value:要设置的值

        注意:私有设置时需设置暴力接触私有设定

                field.setAccessible(true)

        User类:

package com.cc.user.tset;

import lombok.Data;

/**
 * @author cc
 * @data 2021年11月15日 15:23
 */
@Data
public class User {

    private String id;

    private String name;

    public Integer type;

    char sex;

    public User() {
        System.out.println("无参构造方法");
    }

    public User(String id) {
        this.id = id;
        System.out.println("一个参数构造方法");
    }

    public User(String id, String name) {
        this.id = id;
        this.name = name;
        System.out.println("多个参数构造方法id:" + id + "name:" + name);
    }

    private User(int age){
        System.out.println("私有构造方法 年龄"+ age);
    }


    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", type=" + type +
                ", sex=" + sex +
                '}';
    }
}

        测试类:

public static void fields() throws Exception {
        //加载class对象
        Class<?> aClass = Class.forName("com.cc.user.tset.User");
        //获取字段
        System.out.println("==========获取所有公有的字段============");
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("============获取所有字段(公有及非公有)==================");
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        System.out.println("================获取公有字段并调用====================");
        Field type = aClass.getField("type");
        System.out.println(type);
        Object o = aClass.getConstructor().newInstance();
        type.set(o,111);
        User user = (User) o;
        System.out.println("====type:"+((User) o).getType());
        System.out.println("==================获取私有字段并调用=====================");
        Field name = aClass.getDeclaredField("name");
        System.out.println(name);
        name.setAccessible(true);
        name.set(o,"cc");
        System.out.println("=======name:" + user.getName());
    }

        控制台输出:

        6、获取成员方法并调用

        1、获取批量方法

                public Method[] getMethods() //获取所有公有方法。含父类及 Object 类

                public Method[] getDeclaredMethods() //获取所有的方法,含私有的(不包含继承的)

        2、获取单个方法

                public Method getMethod(String name,Class<?>... parameterTypes) //根据名称获取单独公有方法

                public Method getDeclaredMethod(String name,Class<?>... parameterTypes) //根据名称获取方法,可以是私有的。

                参数:name : 方法名。parameterTypes :形参的class类型对象

        3、调用方法

                Method --> public Object invoke(Object obj,Object... args)

                参数:obj :要调用方法的对象。 args :调用方法时所传递的实参。

        示例:User类

public class User {

    public void  method1(String name){
        System.out.println("调用公有方法,参数name:" + name);
    }

    public void method2(){
        System.out.println("调用私有无参方法");
    }

    void method3(){
        System.out.println("调用默认无参方法");
    }

    private String method4(String name){
        System.out.println("调用私有且有参方法,参数name:" + name);
        return "CC";
    }
}

        测试类:

public static void methods() throws Exception {
        //加载class对象
        Class<?> aClass = Class.forName("com.cc.user.tset.User");
        //获取所有公有方法
        System.out.println("=======获取所有公有方法======");
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        //获取所有方法包含私有
        System.out.println("=======获取所有方法包含私有======");
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        //根据名称获取指定方法
        System.out.println("=======根据名称获取指定方法========");
        Method method1 = aClass.getMethod("method1", String.class);
        System.out.println("公有方法:" + method1);
        Object o = aClass.newInstance();
        method1.invoke(o, "cc");
        //根据名称获取私有方法method4
        System.out.println("==========根据名称获取私有方法method4=============");
        Method method4 = aClass.getDeclaredMethod("method4", String.class);
        System.out.println(method4);
        method4.setAccessible(true);
        Object c2 = method4.invoke(o, "C2");
        System.out.println("===返回值:" + c2);
    }

        控制台输出:

=======获取所有公有方法======
public boolean com.cc.user.tset.User.equals(java.lang.Object)
public java.lang.String com.cc.user.tset.User.toString()
public int com.cc.user.tset.User.hashCode()
public void com.cc.user.tset.User.method1(java.lang.String)
public void com.cc.user.tset.User.method2()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
=======获取所有方法包含私有======
public boolean com.cc.user.tset.User.equals(java.lang.Object)
public java.lang.String com.cc.user.tset.User.toString()
public int com.cc.user.tset.User.hashCode()
public void com.cc.user.tset.User.method1(java.lang.String)
private java.lang.String com.cc.user.tset.User.method4(java.lang.String)
protected boolean com.cc.user.tset.User.canEqual(java.lang.Object)
public void com.cc.user.tset.User.method2()
void com.cc.user.tset.User.method3()
=======根据名称获取指定方法========
公有方法:public void com.cc.user.tset.User.method1(java.lang.String)
调用公有方法,参数name:cc
==========根据名称获取私有方法method4=============
private java.lang.String com.cc.user.tset.User.method4(java.lang.String)
调用私有且有参方法,参数name:C2
===返回值:CC

       7、利用反射创建数组

public  static void newArray() throws ClassNotFoundException {
        Class<?> aClass = Class.forName("java.lang.String");
        Object o = Array.newInstance(aClass, 10);
        Array.set(o, 0, "一");
        Array.set(o, 1, "2");
        Array.set(o, 2, "3");
        Array.set(o, 3, "4");
        Array.set(o, 5, "5");
        System.out.println(Array.get(o, 2));
    }

        8、通过反射运行配置文件内容

        User类:

public class User {

    public void textMethod(){
        System.out.println("运行验证方法");
    }
}

        txt文件:

className = com.cc.user.tset.User
methodName = textMethod

       测试方法:

 public static void main(String[] args) throws Exception {
        //加载class对象
        Class<?> aClass = Class.forName(getValue("className"));
        //获取方法
        Method methodName = aClass.getMethod(getValue("methodName"));
        //调用方法
        methodName.invoke(aClass.newInstance());

    }

    public static String getValue(String key) throws Exception {
        Properties properties = new Properties();
        FileReader in = new FileReader("E:\\mywork\\project-gather\\project-user\\src\\main\\java\\com\\cc\\user\\tset\\test.txt");
        properties.load(in);
        in.close();
        String property = properties.getProperty(key);
        return property;
    }

        9、通过反射越过泛型检查

        泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的

        示例:

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

        ArrayList<String> arrayList  = new ArrayList<>();
        arrayList.add("ccccc1");
        arrayList.add("ccccc2");

        //获取ArrayList的Class对象,反向的调用add()方法,添加数据
        Class<? extends ArrayList> aClass = arrayList.getClass();
        //获取add方法
        Method add = aClass.getMethod("add", Object.class);
        //调用add()方法
        add.invoke(arrayList, 111);
        //遍历集合
        for (Object s : arrayList) {
            System.out.println(s);
        }
    }
}

        控制台输入:

        

        10、反射main方法:

        User 类中的 main 方法

public static void main(String[] args) {
        System.out.println("main方法运行");
    }

        测试类:

public static void getMain() throws Exception {
        //加载class对象
        Class<?> aClass = Class.forName("com.cc.user.tset.User");
        //获取main方法
        Method main = aClass.getMethod("main", String[].class);
        //第一个参数,对象类型,因为方法是static静态的,所以为null可以,第二个参数是String数组,
        //这里要注意在jdk1.4时是数组,jdk1.5之后是可变参数
        //这里拆的时候将  new String[]{"a","b","c"} 拆成3个对象。。。所以需要将它强转。
        main.invoke(null, (Object) new String[]{"q","w","e"});
        //methodMain.invoke(null, new Object[]{new String[]{"a","b","c"}});//方式二	
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cc_南柯一梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值