---------------------- android培训、java培训、期待与您交流! ----------------------
黑马程序员-反射机制
反射
Java程序中的各个java类属于同一类事物,描述这里类的java类就叫做Class。
Class类代表Java类,它的各个示例对象分别在对应各个类在内存中的字节码。
什么是字节码?
一个类被加载器加载到内存中占有一片存储空间,这个空间里面的内容就说字节码;
不同类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个空间可以分别用故意个个对象来表示,且这些对象具有相同的类型Class类。
如何得到字节码对应的示例对象(Class类型)?
有三种方式:
1.类名.class
如:Person.class;
2.对象.getclass();
如:得到创建该对象的字节码:new Demo().getClass();
3.Class.forName():
如: Class.forName("Java.util.date");返回字节码。
forName()方法先在内存中判断有没有字节码,有就返回,没有就用类加载器加载一个,然后在返回。
java中一共有九个预定义的Class示例对象:
boolean byte char short int long float double 和 void.
使用Class.isprimitive可以判断指定的class对象是不是基本类型。
int.class==Integer.TYPE比较结果为 true 。因为Integer返回的是int数值的字节码。
数组类型的class示例对象,class.isArray();
总之,只要是在源程序中出现的类型,都各自有各自的Class示例对象,如int[],void .
反射是什么呢?
反射 就是把java类中的各种成分映射到相应的java类,如,一个java类中用一个Class类的对象来表示,一个类中的组成部分,成员变量,方法,构造方法。包等信息,也用一个个的java类来表示
java类的class类提供了一些方法来获取其中的变量方法,构造方法,修饰符,包等信息,这些信息用相应类的示例对象来表示,Fild Method Contructor Package。
Constructor类 代表某个类中的构造方法
得到某个类中所有的构造方法
Constructor[] constructors=Class.forName("Java.lang.String").getConstructors();
得到某一个构造方法,获取参数类型为StringBuffer类型的构造方法。
Constructor constructor=Class.forName("Java.lang.String").getConstructor(StringBuffer.Class);
创建实例对象
通常方法:
String str=new String(new StringBuffer("abc"));
反射方法:
String str1=(String) Constructor.newInstance(new StringBuffer("abc"));
再调用获得的构造方法时,要用到上面相同的实例对象,
Class.newInstance()方法
String obj=() Class.forName("Java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用构造方法创建实例对象。
该方法内部用到了缓存机制,来保存默认构造方法的示例对象。
Field类 代表某个类的一个成员变量
ReflectPoint pt1=new ReflectPoint(3,5);
//在构造方法中传入3.5
Field fieldY=pt1.getClass().getField("y");
//把要去的变量以字符串的方式传入
//fieldY取出来的不是对象身上的变量,而是类上的,要用它去取某个对象对应的值。
sop(field.get(pt1));
//使用对象的.get(pt1);方法取对象的值。
如果要去的对象变量的值是私有的,那么该怎么办呢,不用担心,java为我们提供了获取私有变量的方法:getDeclaredDield().和setAccessible().
//获取X的值,可以取出私有变量
Field fieldX=pt1.getClass().getDeclaredDield("x");
//获取私有变量的值,有称为暴力反射
fieldX.setAccessible(true);
//打印X值
sop(fieldX.get(pt1));
字段反射
如果要把一个类中的所有String类型的成员变量中的b替换为a怎么实现呢?
public static void changeStringValue(Object obj) Throws Exception
{//将所有的成员变量存入数组
Field[] fields=obj.getClass().getFields();
//对数组进行遍历
for(Field field: fields)
{//使用==比较两个类型的字节码
if(field.getTYPE()==String.class)
{
//取出对象的值
String oldValue=() field.get(obj);
//把旧的值用薪的代替,replace用于替换某一个字符
String newValue=oldValue.replace('b','a');
//将对象的值设置为newValue
field.set(obj.newValue);
}
}
}
在对两个字节码进行比较是否相同时,使用==进行比较,因为有肯能是同一个字节码
Mehhod类 代表某个类中的一个成员方法。
//getMethod参数为一个字符串和一个int.class.
Method methodCharAt=String.class.getMethod("charAt",int.class);
//传入一个字符串对象和要取的位置
sop(methodCharAt.invoke(str1,1);
调用方法:
1.通常方法:
sop(str.charAt());
2.反射方法:
sop(charAt.invoke(str,1);
如果传递给Method对象的invoke方法的第一个参数为 null ,说明该Method对象对应的是一个静态方法、
JDK 1.5与JDK 1.4 invoke方法的区别:
JDK 1.5:使用的是可变参数
public Object invoke(Object obj,Object... args);
JDK 1.4:public Object invoke(Object obj,Object[] args);
在这之前没有可变参数,按 1.4的语法需要将一个数组作为可变参数传递给invoke方法时,数组中的每一个元素分别对应被调用方法的一个参数,所以,调用charAt方法的代码也可以用 1.4改写为charAt。invoke("str",new Object[]{1});
反射的方式执行某个类中的main方法。
用静态方法:类名.main(new String[]{})//也可以传入参数
用反射的方法:
//将要启动的类的参数
String stringClassName=args[0];
//根据名字和参数类型获取main
Method mainMethod=Class.forName(stringClassName).getMethod("main",String[].class)
mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
//让他解包一次后还是我们需要的参数或者向下面那么写
mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
//使用强转的方式是参数正确传递
为什么需要使用反射的方式调用main方法呢??
当要调用的main的所在类名字不能确定,或有多个main方法,不能确定调用哪一个main的时候,可以使用反射的方式,使用参数列表来进行选择要调用的main()
在启动Java程序的时候,main方法的参数上一个字符串数组,String[] args,当通过反射的方式调用这个main方法时,如何为invoke方法传递参数呢,按JDK 1.5,整个数组是一个参数,而按JDK 1.4,的语法,数组腮红的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,Java会按照哪种方式进行处理呢??因为1.5要向下兼容1.4的内容,所以会按1.4的语法基尼系那个处理,即把数组打散为若干个单独的参数,所以我们要以1.4的处理方式进行处理。
/*反射 * java程序中的各个java类同属于一类事物,描述这一类事物的java类就是Class * Class代表java类,它的各个实例对象分别对相应各个类在内存中的字节码 * * * */ package fs1; import java.lang.reflect.*; import java.util.*; import java.*; class Person{ private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } } public class fanshe { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub String s1="abc"; sop(Person.class); Person p=new Person("lisi",23); //sop(Person.getClass()); System.out.println(123); //sop(Class.forName("Java.lang.String")); Class c1=String.class; Class c2=s1.getClass(); Class c3=Class.forName("java.lang.String"); sop(c1==c2); sop(c1==c3); sop(c3==c2); sop(new String("abc").getClass().getFields()); List l1=new ArrayList(); Constructor[] cons=Class.forName("java.lang.String").getConstructors(); //获取构造方法。可以通过构造方法构造实例对象,得到方法的时候需要参数列型 Constructor cons1=String.class.getConstructor(StringBuffer.class); //用获取的构造方法创建实例对象,调用活动的方法时,也需要有类型 String str2=(String) cons1.newInstance(new StringBuffer("abc")); sop(str2.charAt(2)); ReflectPoint pt1=new ReflectPoint(3,5); //该方法只有私有的草可以获得 Field fieldY=pt1.getClass().getField("y"); sop(fieldY.get(pt1)); Field fieldX=pt1.getClass().getDeclaredField("x"); fieldX.setAccessible(true); sop(fieldX.get(pt1)); changeStringValue(pt1); sop(pt1); //Method类,代表某一个类中的成员方法 Method methodCharAt=String.class.getMethod("charAt", int.class); sop(methodCharAt.invoke(s1, 1)); //获取main方法 String startingClassName=args[0]; Method mainMethod=Class.forName(startingClassName).getMethod("main", String[].class); //使用一下两种方式调用main方法 //把整个数组打包为一个数组,因为为了兼容1.4要对数组进行依次解包 mainMethod.invoke(null, new Object[]{new String[]{"11","22","33"}}); //生命该数组是一个对象 //mainMethod.invoke(null, (Object)new String[]{"11","22","33"}); int[] a1=new int[]{1,2,3}; int[] a2=new int[4]; int[][] a3=new int[2][3]; String[] a4=new String[]{"a","b","c"}; //对象要得到字节码组要使用方法,不能直接用。class获取 sop(a1.getClass()==a2.getClass()); sop(a1.getClass()==a3.getClass()); sop(a1.getClass()==a4.getClass()); sop(a1.getClass().getName()); sop(a1.getClass().getSuperclass().getName()); Object Obj1=a1; Object Obj2=a4; //基本数据类型的数组是无法装入Object数组中的 //Object[] Obj3=a1; Object[] Obj4=a3; Object[] Obj5=a4; sop(a1); sop(a4); //int类型的数组不能传入Object【】中, sop(Arrays.asList(a1)); sop(Arrays.asList(a4)); Object obj=null; printObject(a1); printObject("abc"); } private static void printObject(Object obj) { Class clazz=obj.getClass(); if(clazz.isArray()){ int len=Array.getLength(obj); for(int i=0;i<len;i++){ sop(Array.get(obj, 1)); } }else{ sop(obj); } } private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException { Field[] fields=obj.getClass().getFields(); for(Field field: fields){ //使用==比较两个字节码,因为两个字节码有可能是同一个 if(field.getType()==String.class){ String oldValue=(String) field.get(obj); String newValue=oldValue.replace('b', 'a'); field.set(obj, newValue); } } } public static void sop(Object obj){ System.out.println(obj); } } class Test{ public static void main(String[] args){ System.out.println(123); } }
---------------------- android培训、java培训、期待与您交流! ----------------------
相关推荐
黑马程序员-SpringCloud-学习笔记-03-Eureka注册中心
黑马程序员-SpringCloud-学习笔记01-认识微服务
黑马QT讲义,适合QT入门,使用,教程很比较基础,新手可以看看,另外视频链接在哔哩哔哩中。
黑马程序员-SpringCloud-学习笔记-02-微服务拆分及远程调用
黑马程序员 - Java基础教学 - 05 - 面向对象(1).doc
黑马程序员 - 毕向东 - Java基础教学 - 第02天 个人观看视频总结文档
黑马程序员-java32期培训视频(基础+就业)黑马程序员-java32期培训视频(基础+就业)
黑马程序员 - Java基础教学 - 04 - 数组、进制转换、二维数组
黑马程序员-梅兰商城项目实战,包含图片素材和编写源码。
黑马程序员 - 毕向东 - Java基础教学 - 第01天 个人观看视频总结文档
day01_Object类、常用API day02_Collection、泛型 day03_List、Set、数据结构、Collections day04_Map,斗地主案例 day05_异常,线程 day06_线程、同步 day07_等待与唤醒案例、线程池、Lambda表达式 ...
C++黑马程序员-演讲比赛流程管理系统记录
黑马程序员 - Java基础教学 - 03 - 变量的作用域、for循环、break、continue、内存结构
黑马程序员--毕向东全套Java视屏,讲解的相当全面,各位如果需要,尽快下载哦! 另外如果视频链接存在打不开的问题,请留言,必定及时更新链接。
黑马程序员 - Java基础教学 - 08 - 面向对象(4)-多态.doc
NULL 博文链接:https://840198532-qq-com.iteye.com/blog/1490901
NULL 博文链接:https://840198532-qq-com.iteye.com/blog/1491352
NULL 博文链接:https://840198532-qq-com.iteye.com/blog/1490903
黑马程序员 - Java基础教学 - 06 - 面向对象(2) - 关于静态static的那些事.doc
NULL 博文链接:https://840198532-qq-com.iteye.com/blog/1490899