黑马程序员———反射机制
2015-08-04 02:55
603 查看
——- android培训、java培训、期待与您交流! ———
反射就是把java类中的各种成分映射成相对应的java类。
Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”。
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。
Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
反射的基石 Class类
java程序中的各个类属于同一种事物,描述着类事物的java类名就是Class。
一个类编译放在硬盘后就是一堆二进制代码,要把二进制代码加载到内存。 每一份字节码就是Class的实例对象。
获取字节码的三种方法:
1:类名.class;例如:System.class//得到这份类的字节码。在写程序的时候已经把System写上了
2:getClass,例如:new Date().getClass()
//有了一个对象,这个对象一定是字节码创建出来的,得到创建对象的字节码,也就是通常说的类
3:Class.forNmae(“类名”),例如:ClassforName(“java.util.Date”);
(静态方法)(主要用第三种)由于写源程序的时候还不知道类的名字
,运行的时候传递给我一个字符串,字符串里面包含一个类名
写源程序的时候把它当成一个字符串类型的变量,程序运行后,变量的值从一个配置文件里面装载进来。
类的名字在写源程序的时候不需要知道,等运行的时候临时送进来。
该方法可以指定一个类的完整的名称。得到这个类的字节码
1.这个类字节码已经加载内存。不需要再加载,找到字节码即可
2.这个类字节码还没有加载金虚拟机。用类加载器去加载。把字节码缓存起来。该方法返回刚加载进来的字节码
三种方法的示例:
String str1=”abc”;
Class cls1=String.class;
Class cls2=str1.getClass();
Class cls3=Class.forName(“java.lang.String”);
//”cls1==cls2==cls3” 获得的字节码内容为同一个
如何得到各个字节码对应的实例对象(Class类型)
8个基本数据类型分别对应了8个Class示例对象。(有9个预定义的class对象,8个基本类型,一个void)
boolean byte char short int long float double
还有void
void.class;
int class==Integer.TYPE
数组类型的Class实例对象
Class.isArray()
只要是在源程序中出现的类型,都有各自的Class实例对象 例如int[] void
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
Class类:代表一个类。
Constructor 类:代表类的构造方法。
Field 类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
constructor类
该类代表某个类中的一个构造方法
得到了某个类所有的构造方法代码示例:
Constructor[] constructors=
Class.forName(“java.lang.String”).getConstructors();//得到所有的构造方法撞在数组里面
得到某一个单独的构造方法:
例子: Constructor constructor=
Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
解析:Class.forName(“java.lang.String”)==String.Class
constructor代表的是一个构造方法,可以得到所属的类,得到前面的修饰符,
StringBuffer.Class返回StringBuffer的字节码
最重要的是 newInstance 构造出一个实例(String)对象
constructor.newInstance
class–>constructor–>new object
代码示例:
成员变量的反射 Field类
Field类代表某个类中的一个成员变量
成员 变量反射的综合示例
将任意一个对象中所有String类型的成员变量所对应的字符串内容中的“b”改成“a”;
成员方法的反射:Method类
method类代表的是某个类中的一个成员方法
得到类中的某一个方法:
如 得到String类中charAt方法
Mathod charAt=
ClassforName(“java.lang.String”).getMethod(“charAt”.int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
如果传递给Method对象的invoke()方法的一个参数为null.这有着什么样的意义呢
说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:(1.4没有可变参数)
jdk1.5;public Object invoke(Object obj,Object…args)//打包成一个Object数组
jdk1.4: public Object invoke(Object obj,Object[] args)//封装成一个Object对象
即按照jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时
数组中的每个元素分别应被
代码示例:
Method类应用
写一个程序,这个程序能够根据用户提供的类名。去执行该类中的main方法。
问题:
启动java程序的main()方法参数是一个字符串数组。即public static void main(String[] args)
通过反射的方式来调用这个main方法时。如何为invode方法传递参数呢。按照jdk1.5的语法。整个数组是一个参数。
而按照jdk1.4的语法,数组中的每个元素对应一个参数。当吧一个字符串数组作为参数传递给invoke方法时,
javac会按照哪种语法进行处理呢?
jdk1.5肯定要兼容jdk1.4的语法。会按照1.4的语法进行处理
即把数组打散称为若干个单独的参数,所以,在给main方法传递参数时,不能使用代码
mainMethod.invoke(null.new String[]{“xxx”});java只把他当做jdk1.4的语法进行理解
而不把它当做jdk1.5的语法理解,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{“xxx”}});
mainMethod.invoke(null,(Object)new String[]{“xxxx});,编译器会做特殊处理,
编译时不把参数当做数组看待,也就不会将数组打散成若干个参数了。
代码示例:
数组和Object的关系及其反射类型
具有相同维数和元素类型的数组属于同一类型,
即具有相同的Class对象,反射得到的字节码是同一个
代表数组的Class示例对象的getSuperClass()方法
返回父类的Object类对应的Class
基本类型的一维数组可以被当做Object类型使用,不能
当做Object[]类型使用,非基本类型的一维数组,既可以
当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。
思考。怎么得到数组中的元素类型。
没有办法得到数组元素的类型
int[] a=new int[3];
OBject[] a=new Object
a[0].getClass.getName();
//通过具体的元素,得到每个具体元素的类型,然后得到数组的类型。
代码示例:
反射实现框架功能:
把我们要调用的类放在配置文件里面
而在源程序里面没有出现类名
通过反射来获取这个类
反射就是把java类中的各种成分映射成相对应的java类。
Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”。
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。
Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。
反射的基石 Class类
java程序中的各个类属于同一种事物,描述着类事物的java类名就是Class。
一个类编译放在硬盘后就是一堆二进制代码,要把二进制代码加载到内存。 每一份字节码就是Class的实例对象。
获取字节码的三种方法:
1:类名.class;例如:System.class//得到这份类的字节码。在写程序的时候已经把System写上了
2:getClass,例如:new Date().getClass()
//有了一个对象,这个对象一定是字节码创建出来的,得到创建对象的字节码,也就是通常说的类
3:Class.forNmae(“类名”),例如:ClassforName(“java.util.Date”);
(静态方法)(主要用第三种)由于写源程序的时候还不知道类的名字
,运行的时候传递给我一个字符串,字符串里面包含一个类名
写源程序的时候把它当成一个字符串类型的变量,程序运行后,变量的值从一个配置文件里面装载进来。
类的名字在写源程序的时候不需要知道,等运行的时候临时送进来。
该方法可以指定一个类的完整的名称。得到这个类的字节码
1.这个类字节码已经加载内存。不需要再加载,找到字节码即可
2.这个类字节码还没有加载金虚拟机。用类加载器去加载。把字节码缓存起来。该方法返回刚加载进来的字节码
三种方法的示例:
String str1=”abc”;
Class cls1=String.class;
Class cls2=str1.getClass();
Class cls3=Class.forName(“java.lang.String”);
//”cls1==cls2==cls3” 获得的字节码内容为同一个
如何得到各个字节码对应的实例对象(Class类型)
8个基本数据类型分别对应了8个Class示例对象。(有9个预定义的class对象,8个基本类型,一个void)
boolean byte char short int long float double
还有void
void.class;
int class==Integer.TYPE
数组类型的Class实例对象
Class.isArray()
只要是在源程序中出现的类型,都有各自的Class实例对象 例如int[] void
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
Class类:代表一个类。
Constructor 类:代表类的构造方法。
Field 类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
constructor类
该类代表某个类中的一个构造方法
得到了某个类所有的构造方法代码示例:
Constructor[] constructors=
Class.forName(“java.lang.String”).getConstructors();//得到所有的构造方法撞在数组里面
得到某一个单独的构造方法:
例子: Constructor constructor=
Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
解析:Class.forName(“java.lang.String”)==String.Class
constructor代表的是一个构造方法,可以得到所属的类,得到前面的修饰符,
StringBuffer.Class返回StringBuffer的字节码
最重要的是 newInstance 构造出一个实例(String)对象
constructor.newInstance
class–>constructor–>new object
代码示例:
public class PreflectTest { public static void main(String[] args) throws Exception { String str1="abc"; Class cls1=str1.getClass(); Class cls2=String.class; Class cls3=Class.forName("java.lang.String"); System.out.println(cls1==cls2); System.out.println(cls1==cls3); System.out.println(cls1.isPrimitive());//false,是否为原始类型 System.out.println(int.class.isPrimitive()); System.out.println(int.class==Integer.class);//fasle System.out.println(int.class==Integer.TYPE);//true.TYPE代表他们的基本类型 new String(new StringBuffer("abc")); Constructor constructor1=String.class.getConstructor(StringBuffer.class); String str2=(String)constructor1.newInstance(new StringBuffer("abc"));//表示用这个构造方法并传一个对象进去 System.out.println(str2.charAt(2));// 打印c } }
成员变量的反射 Field类
Field类代表某个类中的一个成员变量
public class ReflectPoint { private int x; public int y; public String str1="ball";//将所有的字符串类型成员变量中的所有b该改为a public String str2="basketball"; public String str3="itcast"; public ReflectPoint(int x,int y) { super(); this.x=x; this.y=y; } //代码块 ReflectPoint pt1=new ReflectPoint(3,5); Field fieldY= pt1.getClass().getField("y");//得到一个字段 //fieldY的值是是多少,它不代表变量在某个对象上具体的值,代表类上的一个变量,要用它去取某个对象上对应的值fieldY.get(对象)只能得到可见的 //y是公有的成员变量。 x是私有的成员变量 System.out.println(fieldY.get(pt1));//取出这个对象在某个对象(pt1)获取的值 Field fieldX= pt1.getClass().getDeclaredField("x");//x是私有的,getDeclaredField()可见不可见都可以获取 fieldX.setAccessible(true);//表示可以访问。暴力反射 System.out.println(fieldX.get(pt1));
成员 变量反射的综合示例
将任意一个对象中所有String类型的成员变量所对应的字符串内容中的“b”改成“a”;
public class ReflectPoint { private int x; public int y; public String str1="ball";//将所有的字符串类型成员变量中的所有b该改为a public String str2="basketball"; public String str3="itcast"; public ReflectPoint(int x,int y) { super(); this.x=x; this.y=y; } public String toString() { return str1+";"+str2+";"+str3; } public class PreflectTest { public static void main(String[] args) throws Exception { changeStringValue(pt1); System.out.println(pt1);//打印:aall;aasketaall;itcast } private static void changeStringValue(Object obj)throws Exception { // TODO Auto-generated method stub //扫描这个类这个对象所有String类型的变量 Field[] fields=obj.getClass().getFields(); for(Field field : fields) { //if(field.getType().equals(String.class))//字节码之只有一份用==号比较 if(field.getType()==(String.class))//如果获取的是String类的字节码 { String oldValue=(String)field.get(obj); String newValue=oldValue.replace('b','a'); field.set(obj, newValue); } } } }
成员方法的反射:Method类
method类代表的是某个类中的一个成员方法
得到类中的某一个方法:
如 得到String类中charAt方法
Mathod charAt=
ClassforName(“java.lang.String”).getMethod(“charAt”.int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
如果传递给Method对象的invoke()方法的一个参数为null.这有着什么样的意义呢
说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:(1.4没有可变参数)
jdk1.5;public Object invoke(Object obj,Object…args)//打包成一个Object数组
jdk1.4: public Object invoke(Object obj,Object[] args)//封装成一个Object对象
即按照jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时
数组中的每个元素分别应被
代码示例:
String str1="abc"; //str1.charAt(1);调用str1的charAt()方法 Method methodCharAt=String.class.getMethod("charAt",int.class); System.out.println("iovote=-----"+methodCharAt.invoke(str1,1));//b //invoke是methodCharAt对象的方法,专家模式 //{2}为元素列表一个元素(jdk1.4用法) //new Object[]{new String("abc"),new String("xyz"),new String("123")};
Method类应用
写一个程序,这个程序能够根据用户提供的类名。去执行该类中的main方法。
问题:
启动java程序的main()方法参数是一个字符串数组。即public static void main(String[] args)
通过反射的方式来调用这个main方法时。如何为invode方法传递参数呢。按照jdk1.5的语法。整个数组是一个参数。
而按照jdk1.4的语法,数组中的每个元素对应一个参数。当吧一个字符串数组作为参数传递给invoke方法时,
javac会按照哪种语法进行处理呢?
jdk1.5肯定要兼容jdk1.4的语法。会按照1.4的语法进行处理
即把数组打散称为若干个单独的参数,所以,在给main方法传递参数时,不能使用代码
mainMethod.invoke(null.new String[]{“xxx”});java只把他当做jdk1.4的语法进行理解
而不把它当做jdk1.5的语法理解,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{“xxx”}});
mainMethod.invoke(null,(Object)new String[]{“xxxx});,编译器会做特殊处理,
编译时不把参数当做数组看待,也就不会将数组打散成若干个参数了。
代码示例:
//调用代码块 String startingClassName=args[0]; Method mainMethod=Class.forName(startingClassName).getMethod("main",String[].class); //mainMethod.invoke(null, new String[]{"123","abc","96hebf"}); //mainMethod.invoke(null, new Object[]{new String[]{"123","abc","96hebf"}});//将它打包为一个Objcet数组。 mainMethod.invoke(null, (Object)new String[]{"123","abc","96hebf"});//封装成一个Object对象 class TestArguments { public static void main(String[] args) { for(String arg :args) { System.out.println(arg); } } }
数组和Object的关系及其反射类型
具有相同维数和元素类型的数组属于同一类型,
即具有相同的Class对象,反射得到的字节码是同一个
代表数组的Class示例对象的getSuperClass()方法
返回父类的Object类对应的Class
基本类型的一维数组可以被当做Object类型使用,不能
当做Object[]类型使用,非基本类型的一维数组,既可以
当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。
思考。怎么得到数组中的元素类型。
没有办法得到数组元素的类型
int[] a=new int[3];
OBject[] a=new Object
a[0].getClass.getName();
//通过具体的元素,得到每个具体元素的类型,然后得到数组的类型。
代码示例:
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 TR1=a1.getClass(); Class TR2=a2.getClass(); Class TR3=a3.getClass(); Class TR4=a4.getClass(); System.out.println(TR1== TR2); System.out.println(TR1== TR4); System.out.println(TR1== TR3); System.out.println(a1.getClass().getName());//[I:[表示数组,I表示整数 System.out.println(a1.getClass().getSuperclass().getName()); //父类java.lang.Object System.out.println(a4.getClass().getSuperclass().getName()); //java.lang.Object Object aObj1=a1; Object aObj2=a4; //Object[] aObj3=a1;//**基本类型的一维数组不是Object类型,数组里面装的是int类型的 Object[] aObj4=a3;//a3当成一个数组,一个数组数组里面装的是Object的一维数组 Object[] aObj5=a4; //System.out.println(a1);//[I@7852e922 System.out.println(Arrays.asList(a1));//输出[地址值,而不是内容] //原因:1.4版本asList(Object[] a)接受的是一个Object类型的数组 //1.5版本asList(T... a)等于一个Object的,一个参数 System.out.println(Arrays.asList(a4));//输出[a,b,c] Object obj=null; printObject(a1); printObject(a4); printObject("xyz"); private static void printObject(Object obj) { Class clas=obj.getClass(); if(clas.isArray()) { int len=Array.getLength(obj);//得到长度 for(int i=0;i<len;i++) { System.out.println(Array.get(obj,i)); } } else { System.out.println(obj); } }
反射实现框架功能:
把我们要调用的类放在配置文件里面
而在源程序里面没有出现类名
通过反射来获取这个类
package test; import java.io.*; import java.util.*; public class ReflectTest2 { public static void main(String[] args)throws Exception { InputStream ips=new FileInputStream("config.properties"); Properties props=new Properties(); props.load(ips); ips.close(); String className=props.getProperty("className"); Collection col=(Collection)Class.forName(className).newInstance(); //Collection col=new HashSet();//反射的方式这个地方不出现具体类的名字,从配置文件读出的 ReflectPoint pt1=new ReflectPoint(3,3); ReflectPoint pt2=new ReflectPoint(5,5); ReflectPoint pt3=new ReflectPoint(3,3); col.add(pt1); col.add(pt2); col.add(pt3); col.add(pt1); //pt1.y=7;//改完了hash值被修改,导致内存泄露 //col.remove(pt1);//移除不掉 System.out.println(col.size()); } }
相关文章推荐