黑马程序员_基础加强(反射)

来源:互联网 发布:我的世界0.15.4枪械js 编辑:程序博客网 时间:2024/05/20 17:28

---------------------- ASP.Net+Android+IO开发S、.Net培训、期待与您交流! ----------------------

反射

反射基石--->Class类

Java程序中的各个Java类同属于同一类事物,描述这类事物的Java类名就是Class。


对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类来表示。

人----->Person

Java类----->Class

对比提问:Person类代表人,他 的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,它的各个实例对象又分别是什么呢?

它的实例对象对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以他们在内存中的内容是不同的,这一个个的空间

可分别用一个个对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?

Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。

Java程序中的各个Java类,它们是否属于同一类事物?是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class的区别。Class类描述了那些方

面的信息呢?类的名字,类的访问属性,类所属的包名,字段名称的列表,方法名称的列表,等等。学习反射,首先要明白Class类。

Java中类的实例化是:

Person p1 = new Person();

那么Class如何实例化呢?

Class cls1 = 字节码1;

如何得到各个字节码对应的实例对象(Class类型)

1.类名.class。例如:System.class。

2.对象.getClass()。例如:new Date().getClass。

3.Class.forName(“类名”)。例如:Class.forName(“java.util.Date”)//主要用这种方法。

返回这个类的字节码有两种情况:

第一种,这个类的字节码已经加载到内存中,直接返回字节码;第二种,如果JVM中没有这个字节码,类加载器先加载,然后再返回字节码。


九种预定义的Class实例对象:表示八个基本类型和void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即booleanbytecharshortintlongfloat 和double

数组的Class实例对象:类型[ ].class,可以用isArray()方法判断一个Class实例是否为数组。

总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void...


反射就是把Java类中的各种成分映射成相应的Java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Filed、method、Contructor、Package等等。

 一个类中的每个成员都可用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。


Constructor类

Constructor类代表某个类中的一个构造方法。

得到某个类中所有的构造方法:

举例:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();

得到某个构造方法:

举例:Constructor constructors = Class.forName("java.lang.String").getConstructor(StringBuffer.class).//通过参数类型识别构造方法。

创建实例对象:

通常方法:String str = new String(new StringBuffer(“abc”));

反射方式:String str = (String)constructor.newInstance(new StringBuffer(“abc”))

Class.newInstance()方法:

举例:String obj = (String)Class.forName(“java.lang.String”).newInstance()

该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

该方法内部内部的具体代码是怎样写成的呢?用到了缓存机制来保存默认构造方法的实例对象。

File类

File类代表某个类中的一个成员变量。

问题:得到的Filed对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?

所以字段filedX代表的是X的定义,而不是具体的X变量。


练习:将任意一个对象中的所有的String类型的成员变量所对应的字符串内容的“b”改成“a”。


Method类

Method类代表某个类中的一个成员方法。

得到类中的某个方法:

                   MethodcharAt = Class.forName(“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方法的区别:

                   Jdk1.5:publicObject invoke(Object obj,Object...args)

                   Jdk1.4:publicObject invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用jak1.4改写为charAt.invoke(“str”,new Object[]{1});

用反射方式执行某个类中的main方法。

目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。

用反射的用处是:将类名也作为参数传递。


问题:

启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),

通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按照jdk1.5的语法,整个数组的一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给mian方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”});

Javac 只是把它当做jdk1.4的语法进行理解,而不是把它当做jdk1.5的语法解释。因此会出现参数类型不对的问题。

 

解决办法:

mainMethod.invoke(null,new Object[]{“xxx”});

mainMethod.invoke(null,(Object)newStirng[]{“XXX”}),编译器会作特殊处理,编译时不把参数当作数组看待,也就不会讲数组打散成若干个参数了。

 

数组的反射

         具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

         代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。

         基本类型的一维数组可以被当作Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Objcet[]类型使用。

         Arrays.asList()方法处理int[]和String[]时的差异:

public static List asList(Object[]  a)//接受一个Object类型的数组。

如果是int类型的数组,把它当成一个Object对象。

而String类型的数组,可以当做Objcet类型的数组使用。

 

Array工具类用于完成对数组的反射操作。

思考题:怎么得到数组中的元素类型?

只能获取非基本类型数组或Objcet类型数组中某个元素的类型:a[0].getClass().getName()。

不能获取基本类型数组中某个元素的类型。(因为基本数据类型只会被作为一个Object对象传递,而不是一个数组。)

能获取非基本类型数组(例如String类型)中某个元素的类型,并依此判断数组的类型。

如果是Object类型的数组,只能获取其某个元素的类型。

 

import java.lang.reflect.*;import java.util.*;class ReflectPoint {private int x;public int y;public String str1 = "ball";public String str2 = "bag";public String str3 = "it";ReflectPoint(int x ,int y){//super();this.x = x;this.y = y;}public String toString(){return str1 + ":" + str2 + ":" + str3;}}class ReflectDemo{public static void main(String[] args) throws Exception{ReflectPoint pt1 = new ReflectPoint(3,5);Field fieldY = pt1.getClass().getField("y");/*field 不是对象上的变量,而是类上,而要用它去取某个对象对应的值。getField()只能发现公有的参数。getDeclaredField()可以发现所有的参数。*/Field fieldX = pt1.getClass().getDeclaredField("x");fieldX.setAccessible(true);//设置x可以被获取。System.out.println(fieldY.get(pt1));System.out.println(fieldX.get(pt1));System.out.println(pt1);changeStringValue(pt1);System.out.println(pt1);//method类调用String str1 = "abc";//实例化一个String对象str1 引用字符串abc。//调用string类中的CharAt方法。Method methodCharAt = String.class.getMethod("charAt",int.class);//通过Method类实例化对象调用CharAt方法。System.out.println(methodCharAt.invoke(str1,1));//通过String实例化对象调用String中的CharAt方法。System.out.println(str1.charAt(1));System.out.println(methodCharAt.invoke(str1,new Object[]{2}));System.out.println(str1.charAt(2));//用静态方法调用main函数中并遍历传递的字符串数组的值。TestArguments.main(new String[]{"111","222","333"});//通过反射的方式。//String startingClassName = args[0];//Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class);//mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});//将数组封装成一个数组对象作为参数传递。//数组的反射int[] a1 = new int[3];int[] a2 = new int[4];int[][] a3 = new int[2][3];String[] a4 = new String[3];int[] a5 = new int[]{1,2,3};String[] a6 = new String[]{"a","b","c"};Object[] a7 = new Object[]{"a",1};System.out.println(a1.getClass() == a2.getClass());System.out.println(a1);System.out.println(a1.getClass());System.out.println(a3.getClass());//System.out.println(a1.getClass() == a3.getClass());System.out.println(a1.getClass().getName());System.out.println(a1.getClass().getSuperclass().getName());System.out.println(Arrays.asList(a5));System.out.println(Arrays.asList(a6));printObject(a5);printObject("xyz");System.out.println(a5[1]);       // System.out.println(a5[1].getClass().getName());}//打印语句,如果打印的是数组,运用了数组的反射。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++ ){System.out.println(Array.get(obj,i));}}else{System.out.println(obj);}}/*作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容的“b”改成“a”。*/private static void changeStringValue(Object obj)throws Exception{Field[] fields = obj.getClass().getFields();for (Field field : fields ){if (field.getType() == String.class)//比较是否为String类型的字节码{String oldValue = (String)field.get(obj);String newValue = oldValue.replace('b','a'); field.set(obj,newValue);//将制定对象obj变量上的值换成newValue。}}}}class TestArguments{public static void main(String[] args){for (String arg : args ){System.out.println(arg);}}}

反射的作用

实现框架功能。框架要解决的核心问题:因为在写框架时,你无法知道要调用的类名,所以无法直接new某个类的实例对象,而要用反射的方式来做了

ArrayList 和HashSet:一个无序,一个有序(位置顺序)

注意:HashCode作用。

配置文件几种方法
1.相对路径 ClassLoader 获取 资源文件

          //使用类加载器的getResourceAsStream()方法InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");//上边这句是从执行文件的根目录(bin目录,也即package所在的目录)下去找资源文件。

2.绝对路径 获取资源文件

//使用类的getResourceAsStream()方法加载时,如果配置文件前没有"/"表示在在包中与.class同级,如果有"/”则表示与包同级,在根目录。InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");InputStream ips = ReflectTest2.class.getResourceAsStream("/resource/config.properties");


---------------------- ASP.Net+Android+IO开发S、.Net培训、期待与您交流! ----------------------

原创粉丝点击