----------android培训、java培训、java学习型技术博客、期待与您交流! -----------
1,反射和类的关系
a 通俗的讲反射就是动态的获取类的属性,方法,构造函数,修饰符,包等
-->这种动态获取类的信息以及动态地调用类对象的方法或者属性的功能称为Java语言的反射机制
-->,简单一句话反射就是把类中所有的成分映射成对应的类
b,反射的应用场景:
一个做好的应用程序,没有源代码。但是现在客户端想为这个应用程序添加自己的新功能,怎么办?存在以下两个需要解决的问题:
问题[1]. 也就是已经独立运行的App怎么识别客户端自定义的类?(因为App在开发的时候,并不知道客户端的自定义类是什么样子)
通常情况下,应用程序会提供接口,这样客户端只要实现这个接口就可以了,之后应用程序就可以通过接口引用客户端创建的对象了。
问题[2]. 如果App有办法识别这个了客户端自定义的类,如何使用这个类的对象呢?(因为App的源码不能改变)
通常情况下,客户端需要修改应用程序提供的配置文件,让对应的接口与实现类形成键值对,这样应用程序通过IO流技术再读取相应的配置文件,加载客户端的类,这样应用程序就可以生成客户端的类对象了,
-->这就是反射技术的应用,简单一点就像盖房子,开发商给了毛坯房(门的位置,窗的位置),住户自己装修门,窗等等。
-->反射技术典型的应用就是在web开发中tomcat服务器。tomcat通常是通过读取web.xml配置文件来获取用户的实现servlet接口的类,然后加载字节码到内存,这样用户需要的对象就可以实例化出来了,用户不需要知道tomcat内部实现机制。
c,反射总结:
反射技术提高了应用程序的可扩展性。
反射技术应用起来非常简单。为用户和App之间提供了可以交互的配置文件和接口
2,class类及其作用
class类文件就是内存中的字节码文件,反射的主要操作的就是字节码,
获取Class类对象的三种途径:
public static void main(String[] args)
throws ClassNotFoundException, SecurityException,
NoSuchMethodException, IllegalArgumentException,
InstantiationException, IllegalAccessException,
InvocationTargetException, NoSuchFieldException {
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls3==cls2);
}
验证结果:
分析:从结果可以看出,三种方式获取的class类对象是同一个,
第一种方式:普通类对象.getClass()方法,缺点是,在是哟功能getClass()时对象必须存在,使用起来很不方便。
第二种方法:类的静态成员 类名.class方法,相对来说,对于第一种方式,有了不小的进步,也能满足基本的软件开发应用,但是对于框架层面来说,开发框架,开发像tomcat服务器那样,就起不到很好的效果了。
第三种:Class.forname(类的字符串名称),这种方法是开发框架时必不可少的,因为开发框架时,说不定有些类还没有产生,那么如何知道这个类呢,那就用配置文件给定一个接口,谁实现接口就配置在对应的名字后面(键值对),这样框架读取配置文件就知道你要用什么类了,这是最为有效的方法。
Class类的常用方法
//void.class也是有名字的,就是void
System.out.println(void.class);
System.out.println(int.class);
//isPrimitive判断是否是基本类型
System.out.println(cls1.isPrimitive());
System.out.println(int.class.isPrimitive());
//判断Integer和int类型是否相等,小心
System.out.println(int.class == Integer.class);
System.out.println(int.class == Integer.TYPE);
System.out.println(int[].class.isPrimitive());
//判断是否是数组
System.out.println(int[].class.isArray());
验证结果:
3,反射构造函数
反射获取构造函数主要有四种种方法:
getConstructors(),getDeclaredConstructors(),getConstructor(String.class,int.class),getDeclaredConstructor(String.class)
下面的类作用是为了反射而定义的类,有公有和私有属性,公有和私有构造函数,公有和私有方法
class AnimalClass{
public String name = "cat";
private int age = 2;
public AnimalClass() {}
public AnimalClass(String name,int age){
this.name = name;
this.age = age;
}
private AnimalClass(int age){
this.age = age;
}
private AnimalClass(String name){
this.name = name;
}
public void show1(){
System.out.println("public ::"+name+" : "+age);
}
public void eat1(String food){
System.out.println("public ::" + name + "like eat" +food );
}
private void show2(){
System.out.println("private ::"+name+" : "+age);
}
private void eat2(String food){
System.out.println("private ::" + name + "like eat" +food );
}
}
下面是获取构造函数的代码示例:
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
System.out.println("获取public类型的构造函数");
Constructor[] constructors1 = AnimalClass.class.getConstructors();
for(Constructor item : constructors1){
System.out.println(item);
}
System.out.println("获取private类型的构造函数");
Constructor[] constructors2 = AnimalClass.class.getDeclaredConstructors();
for(Constructor item : constructors2){
item.setAccessible(true);
System.out.println(item);
}
System.out.println("获取指定参数的public类型的构造函数");
Constructor constructors3 = AnimalClass.class.getConstructor(String.class,int.class);
System.out.println(constructors3);
System.out.println("获取指定参数的所有类型的构造函数");
Constructor constructors4 = AnimalClass.class.getDeclaredConstructor(String.class);
System.out.println(constructors4);
}
验证结果:
注意:如果是private的带指定参数的构造函数,用getConstructor方法去获取,结构会抛异常的,
System.out.println("获取指定参数的public类型的构造函数");
Constructor constructors3 = AnimalClass.class.getConstructor(String.class);
System.out.println(constructors3);
4,让单例模式不在孤单
普通方式使用单例,那个单例对象是不是相当的孤单,一辈子就是一个人,自从反射来了,单例不在孤单,它会有个双胞胎对象和它一样陪伴它了。
//s1是独生子女,很可怜
single s1 = single.getInstance();
//反射来了,变出了一个双胞胎
Constructor constructor = single.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
single s2 = (single)constructor.newInstance(null);
//样子有点像,但是确实不是一个人
System.out.println(s1 == s2);
//都是一个妈生的
System.out.println(s1.getClass() == s2.getClass());
System.out.println(s1.getInstance() == s2.getInstance());
验证结果:
分析:类对象肯定是一样的,那为什么后面一句也一样呢,我猜测因为获取的对象在静态数据区,之所以s1和s2不等,可能的原因是,它们在堆内存不一样,但是堆内存中的两个对象所引用的对象是同一个。
5,反射方法
反射方法一共有四种:getMethods,getDeclaredMethods,getMethod,getDeclaredMethod
a,反射获取公共方法
Method[] method1 = AnimalClass.class.getMethods();
for(Method item : method1){
System.out.println(item);
}
验证结果:
b,反射获取只在Animal中声明的方法
Method[] method2 = AnimalClass.class.getDeclaredMethods();
for(Method item : method2){
System.out.println(item);
}
验证结果:
c,反射获取指定public参数类型的方法
Method method3 = AnimalClass.class.getMethod("show1", null);
System.out.println(method3);
Method method4 = AnimalClass.class.getMethod("eat1", String.class);
System.out.println(method4);
验证结果:
d,反射获取所有指定参数类型的方法
Method method5 = AnimalClass.class.getDeclaredMethod("show2", null);
Method method6 = AnimalClass.class.getDeclaredMethod("eat2", String.class);
System.out.println(method5);
System.out.println(method6);
验证结果:
补充:反射获取public参数类型的方法,即getMethod不能指定private类型的方法,会抛出异常的,这里不在赘述。
6,反射方法的调用
a,调用公有非静态方法
AnimalClass animal1 = new AnimalClass();
Method method3 = AnimalClass.class.getMethod("show1", null);
System.out.println(method3);
method3.invoke(animal1, null);
Method method4 = AnimalClass.class.getMethod("eat1", String.class);
System.out.println(method4);
method4.invoke(animal1, "fish");
验证结果:
b,调用公有静态方法
AnimalClass animal1 = new AnimalClass();
Method method3 = AnimalClass.class.getMethod("print1", null);
System.out.println(method3);
method3.invoke(null, null);
验证结果:
c,调用私有方法(公有和私有在一起)
Method method5 = AnimalClass.class.getDeclaredMethod("show2", null);
Method method6 = AnimalClass.class.getDeclaredMethod("eat2", String.class);
Method method7 = AnimalClass.class.getDeclaredMethod("print2", null);
method5.setAccessible(true);
method6.setAccessible(true);
method7.setAccessible(true);
System.out.println(method5);
method5.invoke(animal1,null);
System.out.println(method6);
method6.invoke(animal1, " xia");
System.out.println(method7);
method7.invoke(null, null);
验证结果:
7,反射属性
反射属性有四种方法:getFields,getDeclaredFields,getField,getDeclaredField
Field[] fields1 = AnimalClass.class.getFields();
Field[] fields2 = AnimalClass.class.getDeclaredFields();
for(Field item : fields1){
System.out.println(item);
}
for(Field item : fields2){
System.out.println(item);
}
Field field3 = AnimalClass.class.getField("name");
Field field4 = AnimalClass.class.getDeclaredField("age");
System.out.println(field3);
System.out.println(field4);
验证结果:
注意:getField不能获取private类型的成员变量
8,反射操作字段
Field field3 = AnimalClass.class.getField("name");
Field field4 = AnimalClass.class.getDeclaredField("age");
System.out.println(field3);
field3.set(animal1, "dog");
System.out.println(field3.get(animal1));
System.out.println(field4);
field4.setAccessible(true);
field4.set(animal1, 3);
System.out.println(field4.getInt(animal1));
验证结果:
注意:在正常情况下,field不能修改private类型的成员变量,如果要修改,需要设置setAccessible(true)