------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
万事万物皆对象,类也是可以作为一个对象的,反射就是把Java类中的各种成分映射成相应的java类。一个java类中的各种成分都是可以用类来表示的,java类中的Class类显然要提供一系列的方法,来获取其中的变量、方法、构造方法、修饰符、包等信息,这些信息就是用相应的实例对象来表示,它们是Field、Method、Constructor、Package等等。类是对象,类是java.lang.Class类的实例对象。
假设建立一个类的实例对象:
Foo foo= new Foo();
Foo这个类也是一个实例对象,是Class类的实例对象,任何一个类都是Class的实例对象,这个实例对象有三种表示方式。 Class c1=Foo.Class; //通过类告诉我们任何一个类都有一个隐含的静态成员变量class
Class c2=foo.getClass; // 通过该类的对象用getClass
Class c3=null;
c3=Class.forName(该类的全称)
c1、c2都表示Foo类的类类型,一个类只能是Class类的一个实例对象。我们完全可以通过类的类类型创建该类的对象实例。创建过程中是需要进行类型转换的,有异常的,记得抛异常。
Foo foo=(foo)c1.newInstance()
第三中方法中,forName(类的全称),不仅表示了类的类类型,还代表了动态加载类。编译时刻加载类是静态加载类、运行时刻加载类是动态加载类。我们在new创建对象是静态加载类,在编译时刻就需要加载所有的可能用到的类。通过动态加载类可以解决只使用其中的类。动态加载只在运行时刻加载。
构造方法的反射应用,Constructor类代表了某一个类的构造方法,使用getConstructors()得到的是全部的构造方法,如果想要得到某一个构造方法使用getConstructor,构造方法的参数是识别得到的是哪一个方法。
//通过Constructor对象来创建类实例方法
public static void createPersonClass_2() throws Exception{
//获取Person类的Class对象
String className="cn.itheima.Person";
Class clazz=Class.forName(className);
//Class clazz=Person.class;
//获取指定构造函数的类实例
Constructor con=clazz.getConstructor(String.class,int.class);
Person p=(Person) con.newInstance("lisi",30); //注意类型的转换
System.out.println(p.toString());
}
创建实例对象:
1、创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。
2、newInstance():构造出一个实例对象,每调用一次就构造一个对象。
3、利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象。
Class 也是可以newInstance()无参,占用较多的资源对计算机性能影响大。Class.newInstance()方法先用内部得到默认的构造方法,然后用该构造方法创建实例对象。
Field类代表某个类中的一个成员变量, Field getField(String s);//只能获取公有和父类中公有Field getDeclaredField(String s);//获取该类中任意成员变量,包括私有
setAccessible(ture) //如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。
set(Object obj, Object value);//将指定对象变量上此Field对象表示的字段设置为指定的新值。
Object get(Object obj);//返回指定对象上Field表示的字段的值。下面我们引用一个例子来说明其用法。
//获取Person对象的成员变量
public static void getPersonField() throws Exception{
//如果想要给该变量赋值,必须先要有对象。
Class clazz=Class.forName("cn.itheima.Person");
Person p=(Person)clazz.newInstance();
//获取所以的成员变量
Field[] fs=clazz.getFields();
for(Field f:fs){
System.out.println(f);
}
//获取指定的成员变量
Field fage=clazz.getField("age");
Field fname=clazz.getDeclaredField("name");
//显示改变后的值
fage.set(p, 20);
System.out.println(fage.get(p));
//暴力访问私有变量
fname.setAccessible(true);
fname.set(p, "zhangsan");
System.out.println(fname.get(p));
}
Method类代表某个类中的一个成员方法,得到类中的某一个方法:Method charAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
Method[] getMethods(); //只获取公共和父类中的方法。
Method[] getDeclaredMethods(); //获取本类中包含私有。
Method getMethod("方法名",参数.class(如果是空参可以写null));
Object invoke(Object obj ,参数);
调用的方法:charAt.invoke(str,1);
Method method1=String.class.getMethod("charAt",int.class);
System.out.println(method1.invoke(str1,1))
如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法。
我们使用反射来调用main方法时,因为版本的问题,传入参数的时候,使用的方法是
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"});
我们举例来说明如何获取Method对象对应的例子:
//获取Person类中的方法
public static void getPersonMethod() throws Exception{
//如果想要获取方法,必须先要有对象。
Class clazz=Class.forName("cn.itheima.Person");
Person p=(Person)clazz.newInstance();
//获取所以方法
Method[] mes=clazz.getMethods();//只获取公共的和父类中的。
//mes=clazz.getDeclaredMethods();//获取本类中包含私有。
for(Method me:mes){
System.out.println(me);
}
//获取单个方法
Method me=clazz.getMethod("toString", null);
Object returnVaule=me.invoke(p, null);
System.out.println(returnVaule);
}
我们用一个例子来说明,怎么根据给出的类名,执行其中的main方法。
package cn.itheima;
//定义一个测试类
class Test{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
//用反射方式根据用户提供的类名,去执行该类中的main方法。
import java.lang.reflect.Method;
public class PerformedMain{
public static void main(String[] args) throws Exception {
//普通方式
Test.main(new String[]{"123","456","789"});
System.out.println("-----------------------------");
//反射方式
String className=args[0];
Class clazz=Class.forName(className);
Method methodMain=clazz.getMethod("main",String[].class);
//方式一:强制转换为超类Object,不用拆包
methodMain.invoke(null, (Object)new String[]{"123","456","789"});
//方式二:将数组打包,编译器拆包后就是一个String[]类型的整体
methodMain.invoke(null, new Object[]{new String[]{"123","456","789"}});
}
数组的反射:
我们需要认识到数组其实就是一个对象,无论被声明来承载的是主数据类型或是对象引用,数组永远都是对象。
我们看上面的例子后面三个例子,一个数组里装Object,那么很明显数组String是属于Object的,主数据类型是不属于Object的。所以第三个是不对的。基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异,int[]是当做单个数组打印的是数组的储存地址,String的是当做数组打印元素。
private static void printObject(Object obj) {
Class clazz=obj.getClass();
//如果传入的是数组,则遍历
if(clazz.isArray()){
int len =Array.getLength(obj);//Array工具类获取数组长度方法
for(int x=0;x<len;x++){
System.out.println(Array.get(obj, x));//Array工具获取数组元素
}
}
else
System.out.println(obj);
}
}
通过前面我们知道了反射的实现,那么反射的作用实现框架的作用,框架与工具类的区别在于:框架是调用用户的类而工具类是提供给用户类的。因为框架先于用户类存在,所以无法使用New某个类的实例对象来创建,只能使用反射。
简单框架程序的步骤:
1)右击项目File命名一个配置文件如:config.properties,然后写入配置信息。如键值对:className=java.util.ArrayList,等号右边的配置键,右边是值。
2)代码实现,加载此文件:
①将文件读取到读取流中,要写出配置文件的绝对路径。
如:InputStream is=new FileInputStream(“配置文件”);
②用Properties类的load()方法将流中的数据存入集合。
③关闭流:关闭的是读取流,因为流中的数据已经加载进内存。
3)通过getProperty()方法获取className,即配置的值,也就是某个类名。
4)用反射的方式,创建对象newInstance()。
5)执行程序主体功能
类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。Java中使用全限定类名来辨识,JVM则需要另加类加载器来识别。
下面我们举例来说明:
package cn.itheima.demo;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class OutlineDemo {
public static void main(String[] args) throws Exception{
//应该先直接用ArrayList和HashSet,然后才引入从配置文件读,
Properties props = new Properties();
//先演示相对路径的问题
//InputStream ips = new FileInputStream("config.properties");
/*一个类加载器能加载.class文件,那它当然也能加载classpath环境下的其他文件,既然它有如此能力,它没有理由不顺带提供这样一个方法。
* 它也只能加载classpath环境下的那些文件。注意:直接使用类加载器时,不能以/打头。*/
//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itheima/demo/config.properties");
//Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件
//InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = OutlineDemo.class.getResourceAsStream("/cn/itheima/demo/config.properties");
props.load(ips);
ips.close();
String className = props.getProperty("className");
Class clazz = Class.forName(className);
Collection collection = (Collection)clazz.newInstance();
HashCodeTest hct1=new HashCodeTest(1,2);
HashCodeTest hct2=new HashCodeTest(3,4);
HashCodeTest hct3=new HashCodeTest(1,2);
collection.add(hct1);
collection.add(hct2);
collection.add(hct3);
collection.add(hct1);
//hct1.setX(5);
//collection.remove(hct1);
System.out.println(collection.size());
}
}