zoukankan      html  css  js  c++  java
  • Java反射 : Declared的作用 ( 例如 : getMethods和getDeclaredMethods )

    import com.tangcheng.learning.service.lock.annotation.KeyParam;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import org.junit.Test;
    import org.springframework.util.ReflectionUtils;
    
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author: tangcheng
     * @description:
     * @since: Created in 2018/08/02 16:29
     */
    public class StringEqualsTest {
    
        @Test
        public void testEquals() throws IllegalAccessException, NoSuchFieldException {
            String a = "abc";
            Field valueFieldString = String.class.getDeclaredField("value");
            valueFieldString.setAccessible(true);
            char[] value = (char[]) valueFieldString.get(a);
            value[2] = '@';
            String b = "abc";
            //a.intern();
            System.out.println(a);
            System.out.println(b);
            System.out.println(a == b); //a和b在内存中的地址是相同的,返回true
            System.out.println("abc" == b);//abc和b在内存中的地址是相同的,返回true
            System.out.println("ab@" == a); //ab@在内存中的地址和a不相同,返回false
            System.out.println(a.equals("ab@"));//"abc"和"ab@"的内存地址不同,但存储的值却是一样的,所以返回 true
            System.out.println(a.equals("abc"));//abc的值和a对应的是同一个内内存地址,所以返回true
            System.out.println("abc".equals("ab@"));//比较的是对象中的值。abc对应String对象的值已经被更改为ab@,所以返回true
        }
    
        @Test
        public void springReflectionUtilsTest1() {
            final List<String> list = new ArrayList<String>();
            ReflectionUtils.doWithFields(TwoLevelChildClass.class, new ReflectionUtils.FieldCallback() {
                public void doWith(Field field) throws IllegalArgumentException,
                        IllegalAccessException {
                    list.add(field.getName());
                }
            });
            System.out.println(list); //[twoLevelChildName, oneLevelChildName, name]
        }
    
        @Test
        public void springReflectionUtilsTest2() {
            final List<String> list = new ArrayList<String>();
            ReflectionUtils.doWithFields(TwoLevelChildClass.class, field -> list.add(field.getName()), field -> {
                KeyParam annotation = field.getAnnotation(KeyParam.class);
                if (annotation != null) {
                    return true;
                }
                return false;
            });
            System.out.println(list); //[twoLevelChildName]
        }
    
        @Data
        public static class ParentClass {
            private String name;
        }
    
        @Data
        @EqualsAndHashCode(callSuper = true)
        public static class OneLevelChildClass extends ParentClass {
            private String oneLevelChildName;
        }
    
        @Data
        @EqualsAndHashCode(callSuper = true)
        public static class TwoLevelChildClass extends OneLevelChildClass {
            @KeyParam
            private String twoLevelChildName;
        }
    
    
    }

    1.方法示例

    Java反射相关类中存在大量Declared方法,例如:

    Class userClass = User.class;
    Method[] methods1 = userClass.getMethods();
    Method[] methods2 = userClass.getDeclaredMethods();
    
    Method getUsrMethod = userClass.getDeclaredMethod("getUsername");
    Annotation[] annotation1 = getUsrMethod.getAnnotations();
    Annotation[] annotation2 = getUsrMethod.getDeclaredAnnotations();

    getXxxxx以及getDeclaredXxxx方法到底有什么区别呢?
    带Declared的返回 当前Class对象的所有公有的方法/Field,包含从父类或父接口继承而来的方法/Field

    不带Declared的返回的是当前Class对象的所有(包括:public,protected,default,private)方法/Field,但是并不包括继承自父类或父接口的方法/Field



    2.源码解读

    先看一下getMethods()的源代码:

    /**
     * Returns an array containing {@code Method} objects reflecting all the
     * public methods of the class or interface represented by this {@code
     * Class} object, including those declared by the class or interface and
     * those inherited from superclasses and superinterfaces.
     *
     * ...省略
     * 
     * @jls 8.2 Class Members
     * @jls 8.4 Method Declarations
     * @since JDK1.1
     */
    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }

    从方法注释可知:getMethods()返回的是当前Class对象的所有公有的方法,包含从父类或父接口继承而来的方法


    在看一下getDeclaredMethods()的源代码:

    /**
     *
     * Returns an array containing {@code Method} objects reflecting all the
     * declared methods of the class or interface represented by this {@code
     * Class} object, including public, protected, default (package)
     * access, and private methods, but excluding inherited methods.
     *
     * ...省略
     * 
     * @jls 8.2 Class Members
     * @jls 8.4 Method Declarations
     * @since JDK1.1
     */
    @CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyMethods(privateGetDeclaredMethods(false));
    }

    从方法注释可知:getDeclaredMethods()返回的是当前Class对象的所有(包括:public,protected,default,private)方法,但是并不包括继承自父类或父接口的方法

    https://blog.csdn.net/hanchao5272/article/details/79402533

    Java反射获取private属性和方法(子类,父类,祖先….)

    先来看一个例子:String可变还是不可变?

    大家都应该知道,或者听过,String类是不可变的,为什么呢?因为String其实使用一个 
    private final char [] value;来保存字符的;final,private,明显就不让你改了啊。但是,大家请看:

        @Test
        public void testEquals() throws IllegalAccessException, NoSuchFieldException {
            String a = "abc";
            Field valueFieldString = String.class.getDeclaredField("value");
            valueFieldString.setAccessible(true);
            char[] value = (char[]) valueFieldString.get(a);
            value[2] = '@';
            String b = "abc";
            //a.intern();
            System.out.println(a);
            System.out.println(b);
            System.out.println(a == b); //a和b在内存中的地址是相同的,返回true
            System.out.println("abc" == b);//abc和b在内存中的地址是相同的,返回true
            System.out.println("ab@" == a); //ab@在内存中的地址和a不相同,返回false
            System.out.println(a.equals("ab@"));//"abc"和"ab@"的内存地址不同,但存储的值却是一样的,所以返回 true
            System.out.println(a.equals("abc"));//abc的值和a对应的是同一个内内存地址,所以返回true
            System.out.println("abc".equals("ab@"));//比较的是对象中的值。abc对应String对象的值已经被更改为ab@,所以返回true
        }

    输出(猜猜看,和你想的一不一样呢?):

    ab@
    ab@
    true
    true
    false//(如果把intern那个注释去掉的话,就是true)
    true
    true
    true

    如果,你都答对了,恭喜你,那请直接跳到 反射用法.

    否则:请看下面分析

    先摘一段官方intern()的注释:

    * Returns a canonical representation for the string object. 
    * <p> 
    * A pool of strings, initially empty, is maintained privately by the 
    * class {@code String}. 
    * <p> 
    * When the intern method is invoked, if the pool already contains a 
    * string equal to this {@code String} object as determined by 
    * the {@link #equals(Object)} method, then the string from the pool is 
    * returned. Otherwise, this {@code String} object is added to the 
    * pool and a reference to this {@code String} object is returned. 

    3个点: 
    * 1.常量池(JVM中就是方法区)是由 String 这个class来私有的管理的。* 
    * 2.调用intern()时,会查看常量池是否存在这个String对象,这是通过equals()方法来判断的。* 
    * 3.如果存在,则返回常量池中那个String对象,否则,加入到常量池,并返回当前对象。*

    ps : equals()方法,首先是看obj1==obj2,也就是引用地址是否相等,如果等则返回true,然后是逐个字符比较。

    ok,有了以上知识。其实到这里你自己也可以分析出来了。

    我们来分析一下 
    首先,String a="abc"; 这句话在内存(常量池)中放入了一个String对象:“abc”(内存地址假设为0x123,由char [] value = [‘a’,’b’,’c’]保存), 
    然后第2~5行,我们先不管语法,只要知道是把“abc”这个字符串的第三个字符’c’替换为’@’, 
    此时char [] value = [‘a’,’b’,’c’] 变为了char [] value = [‘a’,’b’,’@’],String这个class对象管理的还是“abc”这个对象。 
    接下来 String b="abc",这句话会从常量池中寻找“abc”,此时会发现常量池中“abc”是存在的,于是,b就指向了“abc”,b其实就是“abc”的位置的一个引用,a也是一个“abc”位置的引用。 
    那么接下来就是顺理成章了:

            System.out.println(a);//a指向"abc"这个对象,而这个对象,内部存储却是{'a','b','@'}
            System.out.println(b);//同上
            System.out.println(a==b);//都指向0x123
            System.out.println("abc"==b);//都是0x123
            System.out.println("ab@"==a);//"ab@"这个对象在常量池中没有,会先在常量中生成,地址显然不是0x123,所以输出false,但是,当调用了a.intern()后,会调用String的equals()去常量池中新建一个对象"ab@',并返回这个位置。
            System.out.println(a.equals("ab@"));
            System.out.println(a.equals("abc"));
            System.out.println("abc".equals("ab@"));
    
    

    如果仔细想想,其实还是有疑问,在这里就不展开了,不然偏题了,因为我对底层也不懂,intern是c++实现的,具体看这篇:

    http://www.wtoutiao.com/a/1023451.html

    反射

    反射,RTTI,Class对象,这些就不说了,看一段代码:

    static void getClassFieldAndMethod(Class cur_class) {
        String class_name = cur_class.getName();
        Field[] obj_fields = cur_class.getDeclaredFields();
        for (Field field : obj_fields) {
            field.setAccessible(true);
            System.out.println(class_name + ":" + field.getName());
        }
        Method[] methods = cur_class.getDeclaredMethods();
        for (Method method : methods) {
            method.setAccessible(true);
            System.out.println(class_name + ":" + method.getName());
        }
        if (cur_class.getSuperclass() != null) {
            getClassFieldAndMethod(cur_class.getSuperclass());
        }
    }


    或:

    private void listAllFields(Object obj) {
        List<Field> fieldList = new ArrayList<Field>();
        Class tmpClass = obj.getClass();
        while (tmpClass != null) {
            fieldList.addAll(Arrays.asList(tmpClass .getDeclaredFields()));
            tmpClass = tmpClass .getSuperclass();
        }
    }

    https://stackoverflow.com/questions/16295949/get-all-fields-even-private-and-inherited-from-class

     

    这段代码输入是一个Class的对象,递归输出这个类的父类,祖父类直到Object类的所有方法和域。

    再看一段:

        static void getObjField(Object obj) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
    
            Class cur_class = obj.getClass();
            getClassFieldAndMethod(cur_class);
            Field vfield=cur_class.getDeclaredField("value");
            vfield.setAccessible(true);
            char[]value=(char[])vfield.get(obj);
            System.out.println(Arrays.toString(value));
        }

    ok,这段代码输入任意一个对象,调用上面那段代码的方法getClassFieldAndMethod(),这样你就知道这个对象都有些什么域,什么方法了,再通过域的名称,就可以获取当前这个对象的域的值,进而可以修改了!!

  • 相关阅读:
    winform登录成功后打开主窗体的合理写法
    C#将字符串转换为整型的三种方法的总结
    win7下安装Oracle11g
    无法将文件“.exe”复制到“.exe”。文件“.exe”正由另一进程使用,因此该进程无法访问该文件
    GDI+
    “System.Data.SqlClient.SqlConnection”的类型初始值设定项引发异常
    winform中为ComboBox控件添加“请选择”或“全部”选项
    IE8不能上传、插入图片的解决办法!
    asp.net mvc实现上传文件
    winform关闭窗体时确认框提示两次
  • 原文地址:https://www.cnblogs.com/softidea/p/9408294.html
Copyright © 2011-2022 走看看