您的位置:首页 > 职场人生

黑马程序员———反射机制

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

代码示例:

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());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: