---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
什么是反射?
一,反射就是把java类中的各个成员,提取成其对应的java类对象。
例如:一个java类中用一个Class类的对象来表示,一个类中的组成部分都有对应的类:
Field —— 成员变量;
Method —— 成员方法;
Constructor —— 构造函数;
Package ——包
二,一个类中的每个成员可以用相对应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些
实例对象,就可以执行相应的操作了。
反射的基石 -- Class类
1,java中的各个java类属于同一类事物,描述这类事物的java类名就是Class。
注:对比:众多人用一个Person类表示,众多的java类就用Class表示
a,一个人 -->Person
b,java类 -->Class
2,Class类代表java类,它的实例对象分别对应什么呢?
1,对应各个类在内存中字节码,例如,String类的字节码,Person类的字节码等。。。
2,一个类被类加载器加载到内存中,占用一片存储空间,这个空间的内容就是类的字节码,不同的类的字节码是不同的,
所以它们在内存中的内容是不同的,这一个个空间分别用一个个的对象来表示,这些对象具有相同的类型即Class类型。
3,如何得到各个字节码对应的实例对象(Class类型)
有3中方式:
1:类名.class;例如:System.class
2:对象名.getClass();例如:new Date().getClass()3:Class.forName(类的全名称(包名+类名)字符串);例如:Class.forName("java.util.Date");
4,九个预定义Class实例对象:
1,boolean.class = Boolean.TYPE,
2,char.class = Character.TYPE,
3,byte.class = Byte.TYPE,
4,short.class = Short.TYPE,
5,int.class = Integer.TYPE,
6,long.class = Long.TYPE,
7,float.class = Float.TYPE,
8,double.class = Double.TYPE,
9,void.class = Void.TYPE
5,数组类型的Class对象实例对象
1,Class.isArray()
2,总之,只要好似在源程序中出现的类型,都有各自的Class实例对象,例如,int[]
构造函数的反射应用
一,Constructor类代表某个类中的一个构造方法
二,得到某个类所有的构造方法:
示例:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
三,得到某一个构造方法://获取时要用到参数类型
示例:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
四,创建示例对象:
示例:
1,普通方式:String str = new String(new StringBuffer("abc"));
2,反射方式:
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
String str = (String)constructor.newInstance(new StringBuffer("abc"));
五,Class.newInstance()方法:
1,示例:String obj = (String)Class.forName("java.lang.String").newInstance();
2,该方法内部先得到默认的构造方法,然后用该构造方法来创建实例对象。
3,该方法内部的具体事项代码,用到了缓存机制来保存默认构造方法的示例对象。
构造函数反射的示例代码:
package com.itheima.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 反射操作构造函数
* @author wuyong
*
*/
public class ConstrutorDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Constructor constructor1 = String.class. getConstructor(StringBuffer .class);
String str2 = (String) constructor1.newInstance (/*"abc"*/new StringBuffer("abc"));
System.out .println(str2.charAt (2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
成员变量的反射
Field类表示某个类中的一个成员变量。
语法:
类名 对象名 = new 类();
Field field = 对象名.getClass().getField(属性名字符串);
此时field对象并不代表具体的值,只代表该类下的成员变量的一个对象,
要获取到指定对象里的成员变量的值,语法如下:
field.get(对象名);指明要取的成员变量的值属于哪个对象里的。
注:一,getField()和getDeclareField()方法的区别:
getField()方法只能获取到访问级别为可见的属性,不能获取到不可见的属性,若调用该方法
获取不可见的属性,会报出:notFoundFieldException的异常。
getDeclareField()方法可以获取该类中声明的所有属性,包括private所修饰的。
二,使用Field 对象下的get(对象名)来获取该对象下的属性时,如果属性的访问修饰符为不可见时,
则会报出IllegaAccessException()异常。若要获得不可见的属性的话,可以用以下的方式来强行
获得,Field对象名.setAccessible(true);设置访问级别为可见。(称为暴力反射)
例子:将一个类中的所有String类型中的"b"全部换成"a";
注:字节码比较时,要用"=="来比较,因为是同一份字节码。
成员变量反射的实例代码:
package com.itheima.reflect;
import java.lang.reflect.Field;
/**
* 成员变量的反射
* @author wuyong
*
*/
public class FieldDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
ReflectPoint point = new ReflectPoint(3,5 );
Field fieldY = point .getClass().getField("y" );
//fieldY的值是多少?是5,错!fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
System.out .println("成员变量Y的值:" + fieldY.get (point));
Field fieldX = point .getClass().getDeclaredField("x" );
//由于X是不可见的,所以就要调setAccessible,并设为true的方式。用暴力反射的方式来获得。
fieldX.setAccessible (true);
System.out .println("成员变量X的值:" + fieldX.get (point));
//2)将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
Field name = point.getClass().getField("name");
System.out.println("替换之前:" + name.get(point));
changeStringValue(point);
System.out .println("替换之后:" + name.get(point));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 定义一个将所有字符串的类成员变量中的”a“ 变成 ”b“的方法
* @param obj
* @throws Exception
*/
private static void changeStringValue(Object obj ) {
try {
Field[] fields = obj. getClass().getFields ();
for(Field field : fields ){
//if(field.getType().equals(String.class)){
if(field .getType() == String .class){
String oldValue = ( String)field .get( obj);
String newValue = oldValue.replace("b" , "a" );
field .set( obj, newValue );
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 被反射操作的类
* @author wuyong
*
*/
class ReflectPoint {
private int x;
public int y;
public String name = "a1b2c3d4c3b2a1";
public ReflectPoint(int x,int y){
this.x = x;
this.y = y;
}
public static void main(String[] args){
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
成员方法的反射
Method代表某一个类中的一个成员方法
语法:
Class cls = TestUtil1.class;
TestUtil1 tu1 = (TestUtil1)cls.newInstance();
Method method = cls.getDeclaredMethod("show", null);//指定获取方法名为“show”,并且没有参数的方法
method.invoke(tu1, null);//在对象tu1中执行无参的方法
Method method2 = cls.getDeclaredMethod("show2", null);
method2.invoke(null, null);//当对象参数为null时,表示这是一个静态方法
调用方法的方式:
一,使用对象名.方法名。调用;
二,使用反射来获得对应的方法对象,来调用invoke(Object obj,Object...args)执行方法;
其中obj是指那一个对象上的方法,args是表示该方法的参数列表
注:专家模式:哪个类拥有该方法需要的私有数据,那这个方法就属于哪个类。
用反射调用某个类的main方法
语法:
String className = args[0];//获取第一个参数,首先要设置RunConfigurations中的Arguments为要执行的类的完整类名。
Class cls2 = Class.forName(className);//通过活动到的完整类名,获得该类的字节码
Method main = cls2.getMethod("main", new String[]{}.getClass());//通过字节码,获得该类的main方法
/*
* 因为为了兼容JDK1.4以下的版本,在传入参数的时候,会对参数进行装包;执行时,会对参数进行拆包,如果
* 单纯的传入一个数组的话,则在拆包的时候将每一个元素都拆成一个参数,此时会报出:IllegalArgumentException异常
* 解决方式有以下2种。
*/
//使用一个Object数组,再把传入的数组参数当成Object数组的一个元素在传入。
//main.invoke(null, new Object[]{new String[]{"abc","东方","123"}});
//将传入的参数强转成Object对象,
main.invoke(null, (Object)new String[]{"abc","东方","123"});
成员方法反射的示例代码:
package com.itheima.reflect;
import java.lang.reflect.Method;
/**
* 类成员方法的反射
* @author wuyong
*
*/
public class MethodDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
//执行普通方法
String str1 = "asdewfs21";
//str1.charAt(1)
Method methodCharAt = String .class. getMethod("charAt" , int .class);
System.out .println("字符串的第二个字符是:" + methodCharAt.invoke (str1, 1 ));
System.out .println("字符串的第二个字符是:" + methodCharAt.invoke (str1, new Object[]{2 }));
//执行Person的main方法,及静态方法的演示
// Person.main(new String[]{"111","222","333cc"});
//静态方法由于不用对象就可以调用,所以对象那个参数是null。
//用反射的方式调用ReflectPoint的main方法,
//注:运行前要设置当前主函数的参数步骤:Run Configureations ---> Arguments --->要调用的类的全名称如:com.itheima.reflect.ReflectPoint
String startingClassName = args [0];
Method mainMethod = Class .forName(startingClassName).getMethod ("main", String[].class);
//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
mainMethod.invoke (null, (Object )new String []{"111","222" ,"333"}); //数组,jdk会将数组拆成多个参数,从而出现参数不对异常。
}catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 被反射操作的类
* @author wuyong
*
*/
class ReflectPoint {
private int x;
public int y;
public String name = "a1b2c3d4c3b2a1";
public ReflectPoint(int x,int y){
this.x = x;
this.y = y;
}
public static void main(String[] args){
for (int i = 0; i < args.length; i++) {
System.out.println(args[i] + "aa");
}
}
}
数组的反射,及数组与Object
数组的反射
1,具有相同位数(例如:一维对一维)和元素类型的数组属于同一个类型,即具有相同的Class实例对象
2,代表数字的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
3,基本类型的一维数组可以被当做Object类型使用,不能当作Object[]类型使用;
非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用。
4,Arrays.asList()方法处理int[]和String[]的差异
当处理的是一维的基本数据类型的数组时,其保存的是一维数组的完整名称,即内存中的地址如:[I @a6d7f3。但二维的基本数据类型数组是可以遍历的。
当处理的是引用类型的数组,则会将每一个元素都保存到集合中。
5,Array工具类用于完成对数组的反射操作。
6,通过反射获取元素的步骤:
1,获取数组的字节码:对象.getClass()
2,判断是否是数组:字节码对象.isArray()
3,获取数组的长度:getlength(Object obj)
4,遍历数组元素
5,通过元素.getClass().getName()方法获取元素对应的类型
数组反射的示例代码:
package com.itheima.reflect;
import java.lang.reflect.Array;
import java.util.Arrays;
/**
* 数组的反射,及数组与Object对象
* @author wuyong
*
*/
public class ArrayDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
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"};
System.out .println("a1 = a2" + (a1.getClass () == a2.getClass ())); //true , 相同的类型和相同的维数有相同字节码对象
//JDK1.5之后,编译错误,因为出现了泛型是。类型不同,没有可比性
// System.out .println(a1.getClass () == a4.getClass ());//false
//JDK1.5之后,编译错误维数不同,没有可比性
// System.out .println(a1.getClass () == a3.getClass ());//false
System.out .println("a1的名称" + a1.getClass ().getName());//[I
System.out .println("a1的父类名称" + a1.getClass ().getSuperclass().getName());//java.lang.Object
System.out .println("a4的父类名称" + a4.getClass ().getSuperclass().getName());//java.lang.Object
Object aObj1 = a1 ;
Object aObj2 = a4 ;
//Object[] aObj3 = a1;//不行,因为基本数据类型不是Object
Object[] aObj4 = a3; //二维数组,将一维数组作为一个对象,
Object[] aObj5 = a4; //string本身就是一个对象
System.out .println("a1的toString()形式:" + a1);
System.out .println("a1的toString()形式:" + a4);
//基本类型的一维数组传入asList方法后,会将数组当成一个对象元素存入集合中。
System.out .println("a1的保存集合后的形式:" + Arrays.asList (a1)); //[I@12344 asList(T...t) 作为对象传递进去了。
//引用类型就可以遍历
System.out .println(Arrays.asList (a4)); //[a,b,c] asList(Object[] obj) 兼容jdk1.4,作为数组传递进去
// 数组的反射
//传递数组调用
printObject(a4 );
//传递对象调用
printObject("xyz" );
}
/**
* 数组的反射,如果是数组则遍历,如果是当对象就输出
* @param obj
*/
private static void printObject(Object obj ) {
Class clazz = obj. getClass();
if(clazz .isArray()){ //如果是数组,拆开数组,打印里面的元素
int len = Array.getLength (obj);
for(int i=0;i
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------