public class ReflectRun {
public static void main(String[] args) throws java.lang.Exception {
/*
* 反射说白了是将java类中的各个部分转换成相对应的java类,比如类中的方法通过反射就转换成Method的一个对象。。。
*/
/*
* java类是描述一类事物的共性,这类事物该有什么属性,不该有什么属性。而属性的值是什么,则是由该类的对象决定的,而不是类;
* 不同的实例对象有不同的值,如人是一个类,该类实例出一个对象的中有一个name=小明的属性,他还可以实例出另一个对象name=小红。
* 同理,java中的类有属性,方法,构造方法等等,那么java中也有描述java类的这些共性的类,这就是Class
* 举个例子,众多的人我们可以用person来代表,众多的java类我们就用Class来代表。
* 适用Class中的方法,可以获得任何类中属性或是方法或是构造方法,所以在反射中Class是最重要的工具
*/
/*
* 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字
* void也表示为 Class对象。
*/
System.out.println("========Class区域========");
/*
* Class是针对字类的节码来操作的,但是Class本身的构造函数是私有的,所以你没法实例化Class本身
* 如果我们要使用Class则需讲一个类的字节码赋给他,这样才能进行操作,Class得到字节码的形式有3种
*/
Class clazz1 = ReflectPoint.class;
Class clazz2 = new ReflectPoint().getClass();
/*
* 如果java虚拟机中存在该调用的类的字节码,则forName直接返回这个字节码;
* 如果没有则通过类加载器加载进来以后缓存起来,并得到该字节码。 做反射用第三种,因为你不知道从外界传入什么。
* 可以看出这是分消耗资源的,因此,反射是很消耗资源。
*/
Class clazz3 = Class.forName("com.holle.ReflectPoint");
// 以上3个class对象对应的是同一个字节码
System.out.println("clazz1 == clazz2: " + (clazz1 == clazz2));
System.out.println("clazz1 == clazz3: " + (clazz1 == clazz3));
// isPrimitive是否是基本类型字节码
System.out.println("clazz1是否是基本类型字节码: " + clazz1.isPrimitive());
System.out.println("int是否是基本类型字节码: "
+ int.class.isPrimitive());
System.out.println("int与Integer的字节码是否是同一个:"
+ (int.class == Integer.class));
// TYPE表示基本类型 int 的 Class 实例。
System.out.println("TYPE表示基本类型 的 Class 实例: "
+ (int.class == Integer.TYPE));
System.out.println("int数组是否是基本类型字节码: "
+ int[].class.isPrimitive());
// 是否是数组字节码
System.out.println("int数组是否是数组字节码: "
+ int[].class.isArray());
/* 总之,在源程序中出现的类型,都有对应的字节码class */
/* 反射-构造函数 */
System.out.println("=======反射-构造函数Constructor区域=========");
/*
* 要想得到一个类的构造函数,我们必须先得到该类的字节码,通过getConstructor方法得到该字节码中的构造函数,
* 因为类中的构造方法可能有多个,所以我们通过传参的形式来判断调用哪一种构造函数;
*/
java.lang.reflect.Constructor cons = clazz3.getConstructor(int.class,
int.class);
/*
* 现在我们想通过得到的构造函数实例对象的话无法直接实例化,因为此时得到的构造函数只是一个类,并不是一个特定的对象,
* 我们要想通过特定的构造方法来创建对象就必须传进该构造方法相应的参数;
*/
ReflectPoint rpCons1 = (ReflectPoint) cons.newInstance(2, 3);
System.out.println("x = " + rpCons1.x);
/* 如果要调用该类字节码中的无参构造,则可以直接适用class中的newInstance就可以了 */
ReflectPoint rpCons2 = (ReflectPoint) clazz3.newInstance();
System.out.println("x = " + rpCons2.x);
/* 反射-属性 */
System.out.println("========反射-属性Field区域==========");
/* 要想得到一个类中的属性,我们同样要先得到该类的字节码,再通过getField方法得到该字节码中的相应属性 */
java.lang.reflect.Field rpField1 = clazz3.getField("x");
/*
* 现在我们得到了该类字节码中的属性,但是我们不能直接调用这个属性,因为他还不是一个明确的对象中的属性,她只是类中的一个属性,因此我们需要指定他是那个对象的属性
*/
/* 调用公用的属性 */
System.out.println("公用属性x = " + rpField1.get(rpCons1));
/*
* 相对的,我们有时候需要调用私有属性,但是java不允许调用私有属性,怎么办?
* Class提供了一个调用私有属性的方法getDeclaredField
*/
java.lang.reflect.Field rpField2 = clazz3.getDeclaredField("y");
/*
* 但是得到私有属性以后我们不能直接调用,因为java不允许访问私有属性,因此我们还需要一个方法setAccessible强行的获取私有属性的值
* 这个方法叫做“暴力反射”
*/
rpField2.setAccessible(true);
System.out.println("私有属性y = " + rpField2.get(rpCons1));
/* 得到静态变量,因为静态成份不需要对象就可以调用,所以在get方法传入null的意思就是取静态变量 */
java.lang.reflect.Field rpField3 = clazz3.getField("z");
System.out.println("静态变量z = " + rpField3.get(null));
// 反射-属性练习
ReflectPoint rp = new ReflectPoint();
System.out.println(rp);
changeValue(rp);
System.out.println(rp);
/* 反射-方法 */
System.out.println("========反射-方法Method区域==========");
/* 要想得到一个类中的方法,我们要先得到该类的字节码,在通过getMethod方法得到该字节码中相应的方法 */
java.lang.reflect.Method rpMethod1 = clazz3.getMethod("showPosition",
int.class, int.class);
/*
* 我们得到了方法的字节码,就要告诉invoke方法调用那个对象的方法,第一个参数就是要传入对象,
* 而我们知道静态方法无需对象就可调用,所以这里我们只需要传入null就可以调用静态方法,后面的参数是这个方法需要的参数
*/
rpMethod1.invoke(null, 5, 9);
ReflectPoint rp2 = new ReflectPoint();
// 调用公共有参方法
java.lang.reflect.Method rpMethod2 = rp2.getClass().getMethod("setX",
int.class);
rpMethod2.invoke(rp2, 3);
java.lang.reflect.Method rpMethod3 = rp2.getClass().getMethod("setY",
int.class);
rpMethod3.invoke(rp2, 5);
// 调用公共无参方法
java.lang.reflect.Method rpMethod4 = rp2.getClass().getMethod("getX",
null);
System.out.println(rpMethod4.invoke(rp2, null));
// 调用私有方法
java.lang.reflect.Method rpMethod5 = rp2.getClass().getDeclaredMethod(
"getY", null);
rpMethod5.setAccessible(true);
System.out.println(rpMethod5.invoke(rp2, null));
/* 专家模式 */
/*
* 司机刹车,踩下刹车,其实是给车一个信号,告诉车要刹车了,则车执行刹车,因此刹车这个方法应该在车身上而不是人身上,人无法刹车只是给车一个刹车的信号
*/
/*
* 把变量搞成私有的,如果外界想操作这个变量,则操作这个变量的方法应该在本身而不是外界 谁拥有数据谁就是操作数据的专家
*/
/* 调用一个类的main方法 */
/*
* 鼠标右键run as->run configuration ->
* 找到Arguments选项卡,把要传进的完整类名写上,这样参数就会自动穿进去,如果不设置则会报数组下标越界
*/
String strattingArgs = args[0];
Class argsClazz = Class.forName(strattingArgs);
java.lang.reflect.Method mainMethod = argsClazz.getMethod("main",
String[].class);
/*
* mainMethod.invoke(null, new String[]{"1","2","3"});
* 这会报参数不匹配,为什么要求我传数组,我传数组后会报错呢?,
* 因为1.5以后java有了可变参数,他要和1.4兼容,可变参数的形式可能是数组(string[]),也有可能是一些独立的参数(String
* str1 , String str2)
* 这样一来,java为了方便程序员就默认的将你所传进来的数组拆分成一个一个的对象,则导致了参数不匹配(
* 针对引用类型,如果是基本类型对象java则不拆分)(只分解一次)
* 如果我们在数组外边在套一个Object数组(因为所有的数组的父类都是Object[]),这样我们就可以讲数组传进main方法了
*/
mainMethod
.invoke(null, new Object[] { new String[] { "1", "2", "3" } });
/*
* 这是另一种方法,讲数组强制转换成Object类型,这样让java看成是一个一个对象而不是数组,他就不进行分解;
* 这只是个人的理解,其根本原因还未知道。
*/
mainMethod.invoke(null, (Object) new String[] { "1", "2", "3" });
// 基本类型数组无需改变
java.lang.reflect.Method testMethod = argsClazz.getMethod("test",
int[].class);
testMethod.invoke(null, new int[] { 1 });
// 引用类型数组必须改变后才能执行
java.lang.reflect.Method IntegerMethod = argsClazz.getMethod(
"testInteger", Integer[].class);
IntegerMethod.invoke(null, (Object) new Integer[] { 1 });
/* 反射-数组 */
int[] a1 = new int[] { 1, 2, 3 };
int[] a2 = new int[] { 4, 5 };
int[][] a3 = new int[2][3];
Integer[] a4 = new Integer[] { new Integer(1), new Integer(2) };
String[] a5 = new String[] { "a", "b", "c" };
/* 维数相同,类型相同,则字节码相同 */
System.out.println(a1.getClass() == a2.getClass());
// 因为a1与a3不属于同一种数组类型,所以字节码是不同的,也不可进行比较,则编译时就报错
// System.out.println(a1.getClass() == a3.getClass());
// //因为a1与a4不属于同一种数组类型,所以字节码是不同的,也不可进行比较,则编译时就报错
// System.out.println(a1.getClass() == a4.getClass());
// System.out.println(a1.getClass() == a5.getClass());
/* 如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '[' 字符加元素类型名I代表int类型 */
System.out.println(a1.getClass().getName());
/* 所有的数组类的父类都是object */
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
/* 因此 */
Object obj1 = a1;
System.out.println("编程Object对象后的自己嘛同样是本身:" + obj1.getClass().getName());
Object obj2 = a4;
Object obj3 = a5;
/*
* a3为2维数组,可以转换成object因为java把他看成数组的数组,又因为一维基本类型数组可以转换成object对象,
* 所以2维数组可以看成是一个数组中装着int类型的数组等效于一个数组装着一个Object对象
*/
Object[] objsa2 = a3;
Object[] objsa3 = a5;
/* 但是 */
/* 有一个数组中装着基本类型对象,而基本类型的父类不是object,所以他不能转换成object数组 */
// Object [] objs1 = a1;
/*
* 我们往往想只管的看见数组中的元素,但是这无法显示其内部元素,则我们只能借助工具类中的Arrays类中的asList(T...args)方法来实现
*/
System.out.println(a1);
System.out.println(a4);
/*
* 问题出现了,当我传入int类型的数组是没有打印出内部元素,而传入Integer类型的数组是打印出来了,
* 这是因为asList方法在1.4之前参数是Object
* [],而Integer类型的数组可以直接转换成Object数组,所以将他转换成了list打印出来。
* 但是int类型的数组无法转换成Object
* [],则他根据1.5的新特性可变参数处理,把int数组当成了一个参数处理。因此打印出了数组中还有一个数组
*/
System.out.println(java.util.Arrays.asList(a1));
System.out.println(java.util.Arrays.asList(a4));
printObject(a1);
printObject(a4);
printObject("COME BABY!!!");
/*
* 如果我们想要知道传进来的对象如果是数组,那么这个数组的类型是什么呢?
* 这个问题没法解决,因为数组可以是Object[],如果是这类数组,那么其里面的元素可以是五花八门,
* 我们只能确定数组中各个元素的类型是什么obj.getClass().getName() 基本类型数组无法适用直接方式得到类型,必须借助
*/
System.out.println(new Object[] { 1 }[0].getClass().getName());
printObjectName(a1);
printObjectName(a5);
}
/**
* 打印参数
*
* @param obj
* 参数有可能是一个对象,也有可能是一个数组
*/
private static void printObject(Object obj) {
if (obj.getClass().isArray()) {
// 用反射包中的array类,得到对象数组的长度
int len = java.lang.reflect.Array.getLength(obj);
for (int i = 0; i < len; i++) {
System.out.print(java.lang.reflect.Array.get(obj, i));
}
System.out.println();
} else {
System.out.println(obj);
}
}
/**
* 打印参数
*
* @param obj
* 参数有可能是一个对象,也有可能是一个数组
*/
private static void printObjectName(Object obj) {
System.out.println(obj.getClass().getName());
if (obj.getClass().isArray()) {
// 用反射包中的array类,得到对象数组的长度
int len = java.lang.reflect.Array.getLength(obj);
for (int i = 0; i < len; i++) {
// 得到元素类型
System.out.println(java.lang.reflect.Array.get(obj, i)
.getClass().getName());
// 判断类型是否是Integer字节码
System.out.println(java.lang.reflect.Array.get(obj, i)
.getClass() == Integer.class);
}
System.out.println();
} else {
System.out.println(obj);
}
}
/**
* 遍历对象中的所有string属性,如果有b则改成a
*
* @param clazz3
*/
private static void changeValue(Object obj) throws Exception {
java.lang.reflect.Field[] fields = obj.getClass().getFields();
for (java.lang.reflect.Field f : fields) {
// 因为是判断字节码,而字节码只有一个,所以这里用==来判断是否字节码相同,而不是用equals
if (f.getType() == String.class) {
String oldValue = (String) f.get(obj);
String newValue = oldValue.replace('b', 'a');
f.set(obj, newValue);
}
}
}
}
/**
* 被调用main方法的类
*
* @author Administrator
*
*/
class TestArguments {
public static void main(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
public static void test(int[] args) {
for (int arg : args) {
System.out.println(arg);
}
}
public static void testInteger(Integer[] args) {
for (Integer arg : args) {
System.out.println(arg);
}
}
}
public class ReflectPoint {
public int x = 0;
private int y = 0;
public String str1 = "ball";
public String str2 = "baby";
public String str3 = "asc";
public static int z = 0;
public ReflectPoint() {
}
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
private int getY() {
return y;
}
public static void showPosition(int x, int y) {
System.out.println("调用静态方法:(" + x + "," + y + ")");
}
@Override
public String toString() {
return str1 + ":" + str2 + ":" + str3;
}
}
java反射
最新推荐文章于 2023-06-06 12:15:12 发布