反射机制
反射的基本概念
反射就是把Java类中的各种成分映射成相应的java类.
Class类
表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象
字节码 :
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示
三种获得字节码的方式:
类名.class();
对象.getClass();
Class.forName(“类名”);
String str = "asd";
//输出结果为class java.lang.String
System.out.println(String.class);
System.out.println(str.getClass());
System.out.println(Class.forName("java.lang.String"));
九个预定义Class实例对象:
分别为:char,short,byte,int,long,float,double,boolean和void.
Class.isPrimitive可以检查是否为基本类型
Int.class ==Integer.TYPE
Class.isArray()
System.out.println(int.class); //int
System.out.println(int.class.isPrimitive()); //true
System.out.println(int.class == Integer.TYPE); //true<span lang="EN-US">
</span>
Constructor类
Constructor类代表某个类中的一个构造方法
根据构造方法的参数的个数和类型,来获取不同的构造方法.方法的各个参数的类型用Class对象表示
//得到某个类中所有构造方法
Constructor[] con = Class.forName("java.lang.String").getConstructors();
//获得某个类并创建
Constructor constructor = String.class.getConstructor(StringBuffer.class);
String str1 = (String)constructor.newInstance(new StringBuffer("itheima"));
System.out.println(str1);
可以利用泛型提前确定数据类型省去强制类型转换
Constructor<String> constructor2 = String.class.getConstructor(StringBuffer.class);
String str2 = constructor2.newInstance(new StringBuffer("itheima"));
Class类提供货了newInstance()方法用于直接构造没有参数的类
String.class.newInstance();
Field类
Field类代表某个类中的一个成员变量
public static void main(String[] args) throws Exception{
Point p = new Point(1,4);
//获得public属性的值
Field fieldY = p.getClass().getField("y");
//类中的非静态的属性需要一个对象来取出这个对象中的值
System.out.println(fieldY.get(p));
//获取private属性的值
//私有属性不能使用getField()方法
Field fieldX = p.getClass().getDeclaredField("x");
//获得私有属性后,需强制破解才能获得或修改值
fieldX.setAccessible(true);
fieldX.set(p, 12);
//获得类的静态属性
Field fieldZ = p.getClass().getField("z");
System.out.println(fieldZ.get(Point.class));
}
}
class Point {
private int x;
public int y;
public static int z = 0;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"
package com.itheima.reflect;
import java.lang.reflect.Field;
public class replaceChar {
public static void main(String[] args) {
TestClass test = new TestClass();
System.out.println("替换前" + test);
replaceCharB(test);
System.out.println("替换后" + test);
}
private static void replaceCharB(Object obj) {
Field[] fields = obj.getClass().getFields();
for(Field field : fields) {
if(field.getType() == String.class) { //检测是否为String
String str = null;
try {
str = (String)field.get(obj);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
String newStr = str.replace('b', 'a'); //replace方法不会改变str的原值
try {
field.set(obj, newStr); //将str重新设置回去
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
class TestClass {
public String str1 = "bbbbbbbbbbbbb";
public String str2 = "babfbdsbd";
public String str3 = "ball";
public int i = 0;
@Override
public String toString() {
return "TestClass [str1=" + str1 + ", str2=" + str2 + ", str3=" + str3
+ ", i=" + i + "]";
}
}
Method
Method类代表某个类中的一个成员方法
String str = "abc";
//调用String类的chatAt(int) 方法
Method stringCharAt = String.class.getMethod("charAt", int.class);
System.out.println(stringCharAt.invoke(str, 1));
当Method.invoke(null, )方法的第一个参数为null时,代表该Method方法为静态方法.
main方法的反射:
mian方法传递String数组数据的两个解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"});,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
Method reflectMainMethod = Class.forName(args[0]).getMethod("main", String[].class);
reflectMainMethod.invoke(null, (Object)new String[]{"111", "222", "333"});
reflectMainMethod.invoke(null, new Object[]{new String[]{"111", "222", "333"}});
数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;
非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
public static void main(String[] args) {
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 = new int[1][3];
String[] a4 = new String[1];
a4[0] = "sdfs";
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass() == (Object)a3.getClass());
System.out.println((Object)a1.getClass() == a4.getClass());
//不加强制转换会直接报错 不知道为什么 强制转换加到哪是一样的
System.out.println(a1.getClass().getName());
System.out.println(a3.getClass().getSuperclass().getName());
Object o1 = a1;
Object o2= a4;
//Object[] o3 = a1;
Object[] o4 = a3;
Object[] o5 = a4;
//Arrays类
System.out.println(Arrays.asList(a3));
Object obj;
printObject(a1);
}
//利用数组的反射输出数据
private static void printObject(Object obj) {
Class claszz = obj.getClass();
if(claszz.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);
}
}
简单框架实例
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
//根据配置文件来调用配置文件中的Collection的子类
String objectClass;
FileInputStream fis = new FileInputStream("ReflectClass.Properties");
Properties pro = new Properties();
pro.load(fis);
fis.close();
objectClass = pro.getProperty("objectClass");
Class cla = Class.forName(objectClass);
Collection collection = (Collection)cla.newInstance();
}
配置文件的位置
配置文件应尽量写绝对路径,最好在使用时专门设置一个放配置文件的目录
类名.Class.getClassLoader.getResourceAsStream(String name)
可以指定当前包下的文件