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++實現的,具體看這篇:
反射
反射,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(),這樣你就知道這個對象都有些什么域,什么方法了,再通過域的名稱,就可以獲取當前這個對象的域的值,進而可以修改了!!
