------- android培训、java培训、期待与您交流! ----------
反射的经典解释:反射就是把Java类中的各种成分映射成相应的java类。
而且反射并不是JDK1.5的新特性,从JDK1.2开始就有了,在框架的开发中应用较多。
反射比较占用性能,反射会导致系统性能下降.
如何得到各个字节码对应的实例对象( Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date");
代码举例:
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
Class类描述哪些信息:类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表等等
Class的实例对象代表内存里面的一份字节码
forName的作用:
如果已经加载过,呆在java虚拟机里面就直接取,如果java虚拟机里还没有这份字节码,则用类加载器去加载,把加载进来的字节码缓存在java虚拟机里面 ,以后要得到这份字节码就不用加载了。
Class cls = void.class;这样写是可以的
构造方法的反射:
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
代码举例:
Constructor constructor1 = String.class.getConstructor(StringBuffer.class/* 类型 */);
// 编译的时候不知道constructor1是String的构造方法,运行的时候才知道,所以要类型转换
String str = (String) constructor1.newInstance(new StringBuffer("abc")/* 同样类型的对象 */);
System.out.println(str.charAt(2));
得到某一个构造方法:
例子:
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
成员变量的反射:
Field类代表某个类中的一个成员变量
定义一个ReflectPoint类,属性:x和y
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
ReflectPoint pt1 = new ReflectPoint(3, 5);
// fieldY 不是对象身上的变量而是类身上的变量,用它去取对象身上的值
Field fieldY = pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1));
需要强调的是:fieldY不是对象上的变量,而是类身上的变量,通过它去获得某个对象身上变量的值。(即上面代码红色的部分)
由于x的访问属性为private,则通过这样的方式会报错。
// 暴力反射
Field fieldX = pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
例题:
作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
定义ReflectPoint类:
public class ReflectPoint {
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "love";
@Override
public String toString() {
return "ReflectPoint [str1=" + str1 + ", str2=" + str2 + ", str3="
+ str3 + "]";
}
}
在ReflectTest类中通过changeStringValue(Object obj)方法来改变值
调用:
changeStringValue(pt1);
System.out.println(pt1);
方法:
private static void changeStringValue(Object obj) throws Exception {
//通过对象的字节码得到对象的字段
Field[] fields = obj.getClass().getFields();
for (Field field : fields) {
//使用==进行判断字段的类型是否和字符串的字节码相同,注意这里用==要比equals好,因为字节码只有一份.
if (field.getType() == String.class) {
String oldValue = (String) field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
方法的反射:
Str1 有 CharAt,str2也有charAt 方法,方法与对象无关,它是属于类的
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子:
Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1));
用反射的方式得到字节码里的方法:
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));
面向对象形象解释:你给门发了个消息,门自己关上了
列车司机刹车,司机给列车发了个信号,列车自己停下了
把变量搞成私有的,如果谁要操作这个变量,那么这个变量在谁身上,这个方法就应该在谁身上
由于静态方法的调用不需要对象,所以methodCharAt.invoke(null, 1),这里为null
Jdk1.4和Jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按Jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
用反射方式执行某个类中的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"}});
mainmethod.invoke(null,(Object)new String[]{"111","222","333"});
每个数组的父类都是Object
mainmethod.invoke(null,(Object)new String[]{"111","222","333"});
跟编译器说这是一个对象,不要拆包。
数组的反射:
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
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.getClass() == a2.getClass());
//System.out.println(a1.getClass() == a3.getClass());
//System.out.println(a1.getClass() == a4.getClass());
System.out.println(a1.getClass().getName());
System.out.println(a4.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a3.getClass().getSuperclass().getName());
Object aObj1 = a1;
Object aObj2 = a3;
Object aObj6 = a4;
//Object[] aObj3 = a1;
Object[] aObj4 = a3;
Object[] aObj5 = a4;
System.out.println(a1);
System.out.println(a4);
如果用反射,每一个数组都属于同一个class
对象得到字节码用方法,getClass()
System.out.println(a1.getClass().getName());
得到[I
[表示数组,I表示整数
数组的维数和类型相同得到的字节码是同一份
基本类型不是Object,所以
int [] a1 = new int[3];
Object[] aObj3 = a1;
这样不行,但是
Object aObj1 = a1;
Object aObj2 = a3;
Object aObj6 = a4;
Object[] aObj4 = a3;
Object[] aObj5 = a4;
都可以
int [] a1 = new int[3];
int [] a2 = new int[4];
int [][] a3 = new int[2][3];
String [] a4 = new String[3];
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"};
赋值之后就不能加长度了
Arrays
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
结果:
[[I@1175422]
[a, b, c]
String 符合了jdk 1.4 的语法
asList
Public static List asList(Object[] a)
但是int不符合,基本类型不是Object,jdk 1.4 就交给1.5来处理识别成下面这样
asList
public static <T> List<T> asList(T... a)
就给当成了一个Object,而不是Object数组
反射的作用:实现框架功能:因为在写才程序时无法知道要被调用的类名,所以在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
综合案例:
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//一定要记住用完整的路径,但完整的路径不是硬编码而是运算出来的
//InputStream ips = new FileInputStream("config.properties");
//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("com/yy/day1/config.properties");
//相对路径,相对当前目录
//InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
//绝对路径,不管相对还是绝对,他们内部都是调用ClassLoader
InputStream ips = ReflectTest2.class.getResourceAsStream("/com/yy/day1/config.properties");
Properties props = new Properties();
props.load(ips);
//不关可能会造成内存泄露,系统资源一直被占用,自己被干掉了,自己指向的操作系统的东西还在
ips.close();
String className = props.getProperty("className");
Collection collections = (Collection)Class.forName(className).newInstance();
//Collection collections = new HashSet();
//Collection collections = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2= new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
/**
* 如果不改pt1.y = 7,取得的结果是1个,如果改了取得的结果是2个,这个就是内存泄露,因为一改,hashCode的值变了,去删的时候,不在原来的那个存储区域了,所以删不掉。
*/
//pt1.y = 7;
//collections.remove(pt1);
System.out.println(collections.size());
}
}