Class类
Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别哦。Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等。学习反射,首先就要明白Class这个类。写如下代码进行对比理解:
Person p1 = new Person("zhangsan");
Person p2 = new Person("lisi");
Class x1 = Vector.class;//Vector类在内存里的字节码
Class x2 = Date.class;//Date类在内存里的字节码
Class对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class类型。另外,类加载的时候会先判断内存中是否已经有了,有了就直接使用,没有才去加载
获得类的字节码文件对应的实例对象有三种方法:
1、通过类的静态属性class获取
Class cls =System.class
2、用对象的getClass方法
Class cls2=p1.getClass();
3、使用Class类中的静态方法forName
Class cls3 =Class.forName(“name”);
String str1 ="abc";有九种预定义的
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
//下面结构都为true,说明用这三种方法得到的字节码文件对象是同一个
System.out.println(cls1==cls2);
System.out.println(cls2==cls3);
Class
对象(参看Class.isPrimitive方法),表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即
boolean
、
byte
、
char
、
short
、
int
、
long
、
float
和
double
。如:int.class==Integer.TYPE。
判断一个Class
对象是否表示一个数组类用Class.isArray方法,如int[].class.isArray()。
反射
反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
为什么要使用反射?
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
通过反射创建一个类的对象
通过反射创建一个类的对象有两种方法
1、通过Class的getConstructor方法得到该类的构造方法,此方法返回一个Constructor对象,然后通过Constructor的newInstance方法返回该类的实例对象,此方法可以指定构造方法进行实例化
Constructor constructor = String.class.getConstructor(StringBuffer.class);// 指定构造方法2、通过Class类的 newInstance方法直接创建对象,但是这里只能通过无参的构造方法进行实例化
String str = (String) constructor.newInstance(new StringBuffer("abc"));// 并使用该构造方法
String obj=(String)Class.forName("java.lang.String").newInstance();
成员变量的反射
Field类代表某个类中的一个成员变量
我们先创建一个类,作为要反射的类
public class ReflectPoint {获取成员变量
private int x;
public int y;
public ReflectPoint(int x, int y) {
this.x = x;
this.y = y;
}
}
ReflectPoint ref=new ReflectPoint(3, 5);//取得属性y的名称fieldY不是对象上的变量,而是类上的变量Field fieldY=ref.getClass().getField("y");//从对象ref的引用中取得y的值System.out.println(fieldY.get(ref));//当成员变量是私有的时候getField会NoSuchFieldException异常,这时候可以用getDeclaredFieldField fieldX=ref.getClass().getDeclaredField("x");//此时取值的时候应先设置变量可以读取(暴力访问)fieldX.setAccessible(true);System.out.println("x="+fieldX.get(ref));练习:获取并修改成员变量的值
//得到类中所有的成员变量
Field[] fields=ref.getClass().getFields();
for(Field field:fields){
if(field.getType()==String.class){//这里要用等号,因为是同一份字节码
//从对象ref的引用中取得某一成员变量的值
String oldValue=(String)field.get(ref);
String newValue=oldValue.replace("b","c");
//设置或修改对象ref的引用中取得某一成员变量的值
field.set(ref, newValue);
}
}
System.out.println(ref);
成员方法的反射
Method类代表某个类中的一个成员方法
String str2="abc";
//方法跟对象没有关系 是类的方法 但是需要通过对象来调用,int.class传递的参数的类型
Method method=String.class.getMethod("charAt", int.class);
//通过invoke调用该方法,但要指定在某一对象上调用,如果对象为null。则说明该方法不需要对象可以调用 即为静态方法
System.out.println(method.invoke(str2, 1));
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方法的反射
class TestMain{
public static void main(String[] args) {
for(String arg:args){
System.out.println(arg);
}
}
}
Method method2=Class.forName("darkhorse.enhance.day01.TestMain").getMethod("main",String[].class);//方法1method2.invoke(null, new Object[]{new String[]{"22","33","44"}});//方法2method2.invoke(null, (Object)new String[]{"22","33","44"});通过getMethods方法可以获取类中所有的方法, 包括该类的父类的所有方法
Method [] methods=Person.class.getMethods();for(Method meth:methods){System.out.println("person:"+meth.getName());}
数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
示例:通过反射遍历数组
public class Reflect4Array {java.lang.reflect.Array工具类用于完成对数组的反射操作。
public static void main(String[] args) {
int[] a1 = new int[]{1,2,3};
printObject(a1);
}
private static void printObject(Object obj) {
Class clazz=obj.getClass();
//如果是数组,就遍历
if(clazz.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);
}
}
}
反射的应用
通过反射加载配置文件信息
配置文件:config.properties
className=java.util.HashSet对配置文件中的信息进行操作
public class ReflectDemo {
public static void main(String[] args) throws Exception{
//InputStream ips = new FileInputStream("config.properties");//绝对路径
//InputStream ips =ReflectDemo.class.getClassLoader().getResourceAsStream("darkhorse/enhance/day01/config.properties");
//InputStream ips =ReflectDemo.class.getResourceAsStream("config.properties");//若文件在当前路径,不用写路径,只需文件名
InputStream ips =ReflectDemo.class.getResourceAsStream("resources/config.properties");//当前路径下的文件夹下
//InputStream ips =ReflectDemo.class.getResourceAsStream("/darkhorse/enhance/day01/resources/config.properties");//相对于项目路径
Properties props=new Properties();
props.load(ips);
ips.close();
//可以对配置文件中的内容进行遍历
Set<Object> keySet=props.keySet();
Iterator<Object> its=keySet.iterator();
while(its.hasNext()){
String key=(String)its.next();
System.out.println(props.get(key));
}
String className=props.getProperty("className");
System.out.println(className);
//通过配置文件中的className使用反射创建对象
@SuppressWarnings("unchecked")
Collection<ReflectPoint> collection=(Collection<ReflectPoint>)Class.forName(className).newInstance();
ReflectPoint pt1=new ReflectPoint(3,3);
ReflectPoint pt2=new ReflectPoint(5,5);
ReflectPoint pt3=new ReflectPoint(3,3);
collection.add(pt1);
collection.add(pt2);
collection.add(pt3);
collection.add(pt3);
System.out.println(collection.size());
}
}