反射机制是java的动态机制,可以在程序"运行期间"在确定实例化对象,方法调用,属性操作等。
反射机制可以提高代码的灵活度,但是会带来较多的系统开销的较低的运行效率,因此不能过度依赖
Class类
Class类称为类的类对象。
JVM加载一个类的class文件时,就会创建一个Class实例与该类绑定。因此每个被加载的
类都有且只有一个Class实例,这个实例就是该加载的类的类对象。
通过一个类的类对象我们可以获取这个类的一切信息(类名,属性,方法,构造器等)从而在
程序运行期间进行相关的操作
因此反射第一步就是要获取操作的类的类对象。而获取方式有三种:
1:类名.class
Class cls = String.class;
Class cls = int.class;
2:Class.forName(String className)
Class cls = Class.forName("java.lang.String");//参数需要是完全限定名:包名.类名
注意:基本类型不支持此种方式获取类对象
3:ClassLoader类加载器形式获取
代码示例
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要加载的类的名字:");
String className = scanner.nextLine();
Class cls = Class.forName(className);
String name = cls.getName();//获取完全限定名:包名.类名
System.out.println(name);
name = cls.getSimpleName();//仅获取类名
System.out.println(name);
String packageName = cls.getPackage().getName();//获取包名
System.out.println("包名:"+packageName);
//获取String类的所有公开方法(包含从超类继承的方法)
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
System.out.println(method.getName());
}
使用反射机制实例化对象
//2调用Class的newInstance()来调用无参构造器
Object obj = cls.newInstance();
System.out.println(obj);
通过类对象获取指定的有参构造器:Person(String,int)
Constructor c = cls.getConstructor();//不传参获取的就是无参构造器
Constructor c1 = cls.getConstructor(String.class,int.class);
Object obj = c1.newInstance("王五",66);//new Person("王五",66);
System.out.println(obj);
相当于new Person("王五",66);
调用有参方法
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
//public void say(String info){
Method method = cls.getMethod("say",String.class);
method.invoke(obj,"你好!");//p.say("你好!");
//public void say(String info,int count){
Method method2 = cls.getMethod("say",String.class,int.class);
method2.invoke(obj,"嘿嘿",5);//p.say("嘿嘿",5)
若方法是私有的,可以利用setAccessible()方法强行打开访问权限
method.setAccessible(true);//强行打开访问权限
method.invoke(obj);//p.hehe();
method.setAccessible(false);//打开之后要关闭
练习:
/**
* 自动调用Person类中所有无参方法
*
* Method的方法:
* int getParameterCount()
* 返回当前Method表示的方法的参数个数
*/
public class Test {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
//判断是否为无参且公开的方法
if(method.getParameterCount()==0 //没有参数的
&&
method.getModifiers() == Modifier.PUBLIC //公开的
&&
method.getName().contains("a") //名字含有字母"a"
){
System.out.println("自动调用方法:"+method.getName()+"()");
method.invoke(obj);
}
}
}
}
练习2:
public class Test2 {
public static void main(String[] args) throws URISyntaxException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
//定位当前类所在的包(目录)
File dir = new File(
Test2.class.getResource(".").toURI()
);
File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
for(File sub : subs){
//通过每一个class文件的文件名得到类名
String fileName = sub.getName();
String className = fileName.substring(0,fileName.indexOf("."));
//根据Test2这个类的包名来拼接其它同包中的类的完全限定名
className = Test2.class.getPackage().getName()+"."+className;
//加载该类的类对象
Class cls = Class.forName(className);
Object obj = cls.newInstance();//实例化对象
//根据类对象获取该类中所有定义的方法
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
if(method.getParameterCount()==0
&&
method.getModifiers()== Modifier.PUBLIC
){
System.out.println("自动调用:"+method.getName()+"()");
method.invoke(obj);
}
}
}
}
}
通常在定义一个有参方法时,不知道需要传几个参数的时候可以利用下面这个方法
public static void dosome(int d,String... s){
System.out.println(s.length);
System.out.println(Arrays.toString(s));
}
在定义方法时,在数据类型后面加...
变长参数会被编译器改为数组。
变长参数只能是方法的[最后一个]参数