java反射机制
反射是java中的动态机制,它允许我们在程序运行期间再确定对象的实例化,方法的调用,
属性的操作等。使得程序的灵活度大大提升,但是同时也带来了更多的资源开销和较低的
运行效率。
Class 类对象
Class的每一个实例用于表示JVM中加载的一个类,并且每个被JVM加载的类都有且只有一个Class的实例。通过Class我们可以得知其表示的类的一切信息:类名,包名,有哪些构造器,方法属性等。
获取一个类的类对象方式有:
- 类名.class
例如:Class cls = String.class;
注:基本类型只能通过上述方式获取类对象- Class.forName(String className)
使用Class的静态方法forName传入要加载的类的完全限定名(包名.类名)
例如:Class cls = Class.forName("java.lang.String")- 类加载器ClassLoader形式
public class ReflectDemo1 { public static void main(String[] args) throws ClassNotFoundException { // Class cls = String.class;//获取String的类对象 Class cls = Class.forName("java.lang.String"); //通过类对象获取其表示的String的相关信息 String name = cls.getName();//java.lang.String name = cls.getSimpleName();//String //获取包名 System.out.println(cls.getPackage().getName());//java.lang } }
使用反射机制实例化对象
public class ReflectDemo2 { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { //先获取要实例化对象的类所对应的类对象 Class cls1 = Class.forName("reflect.Person"); //类对象提供newInstance()必须有无参且公开的构造器可调用进行实例化 Object o1 = cls.newInstance(); System.out.println(o1); //1加载类对象 Class cls2 = Class.forName("reflect.Person"); //2获取对应的构造器 Person(String name,int age) // cls2.getConstructor();//不传参获取的为无参构造器 Constructor c2 = cls2.getConstructor(String.class,int.class); //3通过构造器实例化对象 new Person("王五",22); Object o2 = c2.newInstance("王五",22); System.out.println(o2); } }
使用反射机制调用含参,无参,私有,条件参数的方法们
public class ReflectDemo4 { public static void main(String[] args) throws Exception { //得到类对象及实例化 Class cls = Class.forName("reflect.Person"); Object o = cls.newInstance();//Person o = new Person(); //1)获取要调用的无参方法 //仅传入方法名时,是获取该无参方法 Method method = cls.getMethod("sayHello");//表示的Person的成员方法sayHello() method.invoke(o);//等同于o.sayHello(),调用o对象反射得到的对应方法 //2)获取要调用的含参方法 Method method = cls.getMethod("dosome",String.class);//dosome(String) method.invoke(o,"玩游戏");//p.dosome("玩游戏"); Method method1 = cls.getMethod("dosome",String.class,int.class); method1.invoke(o,"看电视",5); //3)获取私有方法 Method method = cls.getDeclaredMethod("secret"); //强行打开访问权限 method.setAccessible(true); method.invoke(o); //4)获取当前类对象所表示的类的所有公开方法(包含从超类继承的方法) Method[] methods1 = cls.getMethods(); System.out.println(cls.getSimpleName()+":一共有"+methods1.length+"个公开方法"); //5)获取当前类对象所表示的类自身定义的所有方法(含私有方法,不含从超类继承的方法) Method[] methods2 = cls.getDeclaredMethods(); System.out.println(cls.getSimpleName()+":一共有"+methods2.length+"个本类方法"); for(Method method : methods2){ System.out.println(method.getName()); //6)自动调用与当前类Test2在同一个包下所有类中 方法名含有s的无参公开方法 //定位String类package描述的顶级目u录也是该类编译后存放包的顶级根目录 String.class.getClassLoader().getResource(".").toURI(); String.class.getResource(".").toURI();//定位String类编译后所在的目录(包) //定位Test2所在的目录(包) File dir = new File( Test2.class.getResource(".").toURI() ); File[] subs = dir.listFiles(f->f.getName().endsWith(".class")); //遍历每一个class文件 for(File sub : subs){ String fileName = sub.getName();//获取文件名:例如Test2.class String className = fileName.substring(0,fileName.indexOf(".")); Class cls = Class.forName(Test2.class.getPackage().getName()+"."+className); //实例化对象 Object o = cls.newInstance(); Method[] methods = cls.getDeclaredMethods(); for(Method method : methods){ if(method.getName().contains("s")&& method.getParameterCount()==0&&//int getParameterCount()可以获取其表示的方法的参数个数 method.getModifiers()==Modifier.PUBLIC){ System.out.println("自动调用"+className+"的方法:"+method.getName()); method.invoke(o); } } } } }
变长参数方法
JDK5之后java推出了一个特性:变长参数
编译器支持的操作,编译器在编译时,会将调用的方法改造成对应的参数方法
变长参数只能是方法的最后一个参数且只能有一个变长参数,实际是一个数组类型。public class ArgsDemo { public static void main(String[] args) { doing(1,23,"one"); doing(1,23,"one","two"); doing(1,23,"one","two","three"); doing(1,23,"one","two","three","four"); //doing(1,23,new String[]{"one","two","three","four"});编译器在编译的时候相当于创建了数组 } public static void doing(int age,long a,String... arg){ System.out.println(arg.length); System.out.println(Arrays.toString(arg)); } }