1.java提供了一套API
2.提供了检查一对对象内部结构的手段
3.反射的用途
1.java的动态执行 API
a.动态加载类
b.动态创建对象
c.动态访问属性
d.动态调执行方法
2.可以用于实现组件的“解耦”
-可以实现现在组件和未来组件的耦合关系,调用未来的程序组件
重点掌握方法:
1.Class.forName(类名)
2.Method.invole(obj)
3.Class.newInstancea()
4.setAccessible(true)
利用反射可以实现一段程序与未来一个类之间耦合在一起,
这段程序就是与未来的类之间是松耦合的关系,也就是解除耦合了。
如:Eclipse可以开发任何未来的程序,解析任何未来的程序结构
Eclipse的快捷菜单用到了哪些技术?
答:Eclipse利用反射技术实现快捷菜单,可以使用Eclipse被开发的类解耦。
动态加载类
类只加载一次,即便多次调用forName方法
,类也只加载一次,forName返回值是同一个对象的引用
Class cls=Class.forName(String className);
package day01; import java.util.Scanner; /* * 动态加载类到内存中 * Class.forName( */ public class ClassForNameDemo02 { public static void main(String[] args) throws ClassNotFoundException { Scanner in=new Scanner(System.in); System.out.println("输入类名"); //输入的类名需要输入包名加类名(全限定名),例如:day01.Point (day01包下的Point类) String className=in.nextLine(); //动态加载类 //当类名错误的时候会抛出类没有找到的异常 Class cls=Class.forName(className);
Class cls1=Class.forName(classname);
Class cls2=Class.forName(classname);
System.out.println(cls1==cls2);
} }
API方法动态创建对象
a.Instance实例(对象)
b.创建cls代表的类型的实例
c.cls类型上必须包含无参数据构造器(可以是默认构造器)
d.newInstance就是调用这个无参构造器创建对象
e.如果没有无参数构造器则抛出异常
package day01; import java.util.Scanner; /* * 动态加载类到内存中 * Class.forName( */ public class ClassForNameDemo02 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Scanner in=new Scanner(System.in); System.out.println("输入类名"); //输入的类名需要输入包名加类名(全限定名),例如:day01.Point String className=in.nextLine(); //动态加载类 //当类名错误的时候会抛出类没有找到的异常 Class cls=Class.forName(className); System.out.println(cls);
/*
* 若输入java.util.ArrayList 输出class java.util.ArrayList []
* ArrayList无元素toString则输出【】
*
*/
//利用反射API动态创建对象 Object obj=cls.newInstance(); System.out.println(obj); //day01.Point@4aa298b7 //此时Point类没有重写toString,但表示创建成功 } }
例如Eclipse中当我们输入对象名.时,下面提示的方法和变量,就是有利用反射原理。
动态访问属性:
/*
* 在Field类型上定义了get方法,可以用于获取对象的属性值
* ,如果需要获取属性的值:
*
* -利用Class类型对象的方法,才能获取Field类型对象
* -obj.getClass();
* -Class.forName();
* -获取Field类型对象
* -调用get方法
*
*/
package day01; import java.lang.reflect.Field; import java.util.Scanner; public class ClassForNameDemo2 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException { System.out.println("请输入限定名"); Scanner scan=new Scanner(System.in); //加载类 String str=scan.nextLine(); Class cls=Class.forName(str); System.out.println("请输入属性名"); String fieldName=scan.nextLine(); /* * 查找类中的属性 * getDeclaredFields 带s的则获取全部属性(成员变量), * 不加s的获取的是指定名字的一个属性 * 找到就返回属性fid,找不到就抛异常 */ Field fld=cls.getDeclaredField(fieldName); System.out.println(fld); //加载对象 Object obj=cls.newInstance(); //动态获取对象 //动态访问属性 /* * 在Field类型上定义了get方法,可以用于获取对象的属性值 * ,如果需要获取属性的值: * * -利用Class类型对象的方法,才能获取Field类型对象 * -obj.getClass(); * -Class.forName(); * -获取Field类型对象 * -调用get方法 * 属性名是用户运行期间输入的,输入哪个属性 * 名这段程序就会输出哪个属性的值 * 也就意味着,程序和属性之间是松耦合关系 * */ Object val=fld.get(obj);//因为输入的属性类型不确定,故用object接收 System.out.println(val);//获取obj对象中的FieldName值 } }
访问的属性如果为不可见(私有加跨包)属性(被private修饰的属性),则抛java.lang.IllegalAccessException异常。
解决该问题的方法:调用setAccessible(true)方法(故private修饰的属性外部不能访问不正确,利用反射可以访问私有属性)
注:无论是私有属性还有私有方法,都可以利用反射API进行调用,在调用之前使用fld.setAccessible(true);该方法执行之后可以打开原来私有的访问权限。不仅仅如此,该方法还可以打开任何不可见的属性/方法的访问权限。
package day01;
import java.lang.reflect.Field;
import java.util.Scanner;
public class FieldGetDemo {
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException,
NoSuchFieldException, SecurityException {
System.out.println("输入限定名");
Scanner scan=new Scanner(System.in);
String forName=scan.nextLine();
System.out.println("请输入属性名");
String fieldName=scan.nextLine();
Class cls=Class.forName(forName);
Object obj=cls.newInstance();//动态创建对象
Field fl=cls.getDeclaredField(fieldName);//动态访问属性
fl.setAccessible(true);
//如果访问的是私有属性在获取对象值之前用该对象调用setAccessible方法,传入的
//参数为true,则可以访问私有属性
Object val=fl.get(obj);
System.out.println(fl);
System.out.println(val);
scan.close();
}
}
利用反射访问对象的方法
访问方法的核心API
使用步骤
1.获取Class对象,才能利用其方法找到Method对象
-Class.forName(类名)
2.Method getDeclaredMethod(方法名)根据方法名找到Method对象
-Method
3.得到包含方法的对象obj
-cls.newInstances()
4.准备参数(调取的方法有参数)
method.invoke(obj)该方法的返回值为该方法的返回值,如果方法没有返回值,则返回值为null
package day01import java.lang.reflect.Method;
import java.util.Scanner;
/**
* 利用反射API动态的执行对象的方法
* @author TEDU
*
*/
public class InvokeMethodDemo {
public static void main(String[] args)
throws Exception{
/*
*利用反射API动态执行对象的方法
*/
Scanner scan=new Scanner(System.in);
System.out.print("输入类型:");
String className=scan.nextLine();
System.out.println("请输入方法名:");
String methodName=scan.nextLine();
//动态加载类
Class cls=Class.forName(className);
//在类cls中找到需要执行的methodName方法
//如果方法名错误,将抛出没有找到的方法异常
Method method=cls.getDeclaredMethod(methodName);
System.out.println("method:"+method);
//动态创建对象
Object obj=cls.newInstance();
//执行invoke时, obj对象一定包含制定的mothod的对象,否则将抛出异常
Object value=method.invoke(obj);//输出的是方法的返回值
System.out.println("value:"+value);
//public int day01.Point.run()输出的是全限定名
}
}
cls.getDeclaredMethod在当前cls类型上查找当前类中声明的方法
-getDeclaredMethod只查询当前类中的方法
cls.getMethod 在当前cls类型以及全部继承的方法中查找声明的共有方法
-查询当前类和父类中的继承的方法