一.反射
class类是对对象的抽象,而Class是对class的抽象,或者说类是对对象的共性的描述,而Class又是对类的共性的描述,但它本身又是一个类,它的具体实例是内存中某个类的字节码
得到类的字节码的方式有三种:
(1)对象.getClass();(2)类名.class;(3)Class.forName("完整类名“);
Class.forName()的作用:如果有要获得的字节码存在于内存中就直接返回,如果不存在,则通过类加载器将其 .clas文件加载后返回。
九种预定义的Class实例对象:表示八个基本类型和void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即boolean
、byte
、char
、short
、int
、long
、float
和double
。
数组的Class实例对象:类型[ ].class,可以用isArray()方法判断一个Class实例是否为数组
eg:String str = "abc"; Class cls1 = str.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()); System.out.println(int.class.isPrimitive()); System.out.println(Integer.class.isPrimitive()); System.out.println(Integer.TYPE.isPrimitive());
普通方式:String str = new String(new StringBuffer("abc")); 反射方式:String str1 =(String) constructor1.newInstance(new StringBuffer("abc"));
System.out.println(int[].class.isArray());
二.Construtcor类
eg:
Constructor[] constructors=String.class.getConstructors();
Constructor[] constructors=String.class.getDeclaredConstructors();//包括私有的
得到某个类的某个构造方法:会用到参数类型的Class对象
eg:
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
创建对象:
eg:
普通方式:String str = new String(new StringBuffer("abc"));
反射方式:String str1 =(String) constructor1.newInstance(new StringBuffer("abc"));
三.Field类
类的成员变量,它的具体实例代表的的是类中某个字段的定义,而非具体的变量
eg:
public class ReflectPoint{
private int x;
public int y;
public String str1 = "Master";
public String str2 = "Basketball";
public String str3 = "itcast";
public ReflectPoint(int x ,int y){
super();
this.x=x;
this.y = y;
}
}
获取字段值
eg:
ReflectPoint ref = new ReflectPoint(4,8);
Field fieldY =ref.getClass().getField("y");
System.out.println(fieldY.get(ref));
访问私有成员变量
eg:
/暴力反射,用getDeclaredField()取出包括私有在内的所有变量
Field fieldX =ref.getClass().getDeclaredField("x");
//将私有变量设为可访问的
fieldX.setAccessible(true);
System.out.println(fieldX.get(ref));
应用实例:
eg:
private static void changeStringValue(Object obj) throws Exception {
Field [] fields = obj.getClass().getFields();
for(Field field:fields){
if(field.getType()==String.class){//字节码的比较用==更精确
String str =(String) field.get(obj);
String str1 = str.replace('a','w');
field.set(obj, str1);
}
}
}
四.Method类
eg:
String str = "abc";
Method methodCharAt = str.getClass().getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str, 2));//如果方法是静态的,invoke的第一次参数可以写成null
System.out.println(methodCharAt.invoke(str, new Object[]{2}));//JDK1.4的语法,封箱成Integer
反射方式执行某类mian方法
eg:
class TestArguments{
public static void main(String [] args){
for(String arg:args)
{
System.out.println(arg);
}
}
}
String startingClassName =args[0];//注意,要在该程序的运行环境中配置args为TestArguments;
Method mainMethod =Class.forName(startingClassName).getMethod("main",String[].class);
//new Object[]{new String[]{"ball","wife"}
mainMethod.invoke(null,new Object[]{new String[]{"ball","wife"}});//这句为什么?JDK1.5向下兼容1.4版本,而1.4版本中,会把传递进的数组当一个个参数的组成的数组,而进行拆分。这句调用的是1.4的方法。
mainMethod.invoke(null,(Object)new String[]{"ball","wife"});//这句是调用了1.5的方法。
五.数组 Array类
如果两个数组的元素类型和维度相同,则它们用的是同一份字节码
eg:
int[] a1 =new int [3];
int[] a2 = new int [4];
int[][] a3 = new int[2][3];
String[] a4 = new String[]{"111","222","333"};
Class cl1 =a1.getClass();
System.out.println(a1.getClass() == a2.getClass());//true;
// System.out.println(a1.getClass() == a3.getClass());//MyEclipse会报错
// System.out.println(a1.getClass() == a4.getClass());//MyEclipse会报错
System.out.println("a1:"+a1.getClass().getName());
System.out.println("a2:"+a2.getClass().getName());
System.out.println("a3:"+a3.getClass().getName());
System.out.println("a4:"+a4.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
// Object[] obj1 = a1;//这句是错误的
Object obj2 = a2;
Object[] obj3 = a3;
Object[] obj4 = a4;
//Arrays工具类,可以把数组当成集合来打印,但如果数组元素的类型是基本数据类型,那么打印的是数组引用的地址,而不是每个元素的值
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
应用实例:
eg:
private static void printObject(Object obj) {
Class cla = obj.getClass();
if(cla.isArray()){
int len = Array.getLength(obj);
for(int x=0;x<len;x++){
System.out.println(Array.get(obj, x));
}
}else{
System.out.println(obj);
}
}
六.反射作用
实现框架的功能
框架要解决的核心问题:因为在写框架时,你无法知道要调用的类名,所以无法直接new某个类的实例对象,而要用反射的方式来做了
eg:
Collection collection= new ArrayList();//new HashSet();
ReflectPoint p1 = new ReflectPoint(4,4);
ReflectPoint p2 = new ReflectPoint(2,5);
ReflectPoint p3 = new ReflectPoint(7,6);
ReflectPoint p4 = new ReflectPoint(4,4);
collection.add(p1);
collection.add(p2);
collection.add(p3);
collection.add(p1);
//重写ReflectPoint的hashCode和equals方法,让x和y参与其重新方法内。
/*
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override//如果把这句去掉,下边写成equals(ReflectPoint obj)不会报错
public boolean equals(Object obj) {//Object不能改成ReflectPoint,否则就不是重写,而是重载了
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
*/
System.out.println(collection.size())//ArrayList值为4,HashSet则值为3;
p1.y= 8;//HashSet集合中,p1元素的y参与了hashcode值的运算,所以改变y后,导致无法删除和重复加入该元素。
System.out.println(collection.remove(p1));//ArrayList的remove 方法,删除列表中第一次出现的p1
System.out.println(collection.size());//ArrayListz值减少一个为3,HashSet无法删除,所以值不变仍为3;
配置文件的获取操作
eg:
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
Properties prop = new Properties();
prop.load(ips);
ips.close();
String className = prop.getProperty("className");
Collection collection = (Collection) Class.forName(className).newInstance()
//注意:用newInstance时,必须该类有空参构造方法,否则会出现初始化异常;
ReflectPoint p1 = new ReflectPoint(4,4);
ReflectPoint p2 = new ReflectPoint(2,5);
ReflectPoint p3 = new ReflectPoint(7,6);
ReflectPoint p4 = new ReflectPoint(4,4);
collection.add(p1);
collection.add(p2);
collection.add(p3);
collection.add(p1);
System.out.println(collection.size())//配置文件中className为ArrayList时,值为4,为HashSet时值为3;
}
配置文件几种方法
1.相对路径 ClassLoader 获取 资源文件
eg:
//使用类加载器的getResourceAsStream()方法
InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
//上边这句是从执行文件的根目录(bin目录,也即package所在的目录)下去找资源文件。
2.绝对路径 获取资源文件
eg:
//使用类的getResourceAsStream()方法加载时,如果配置文件前没有"/"表示在在包中与.class同级,如果有"/”则表示与包同级,在根目录。
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream("/resource/config.properties");