最近看面试题的时候,看到有关反射的面试,由于上课学的时候老师压根没讲反射的内容,所以今天又来补漏洞啦
反射到底是个啥?《Java核心技术》书中给出的解释是:能够分析类能力的程序称为反射。反射机制可以用来:
在运行时分析类的能力在运行时查看对象,例如,编写一个toString实现通用的数组操作代码利用Method对象,这个对象很像C++中的函数指针通俗来说,反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
因此,反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员。
也就是说,每个类加载到内存都对应一个Class对象,每个类有且只有一个Class对象 显示当前程序所加载的类:JVM参数-verbose:class
若类名不存在,则会抛出异常
使用反射获取类的构造方法,创建对象
//使用反射获取类的构造方法,创建对象 public static void reflectOpe2() throws Exception { //(1)获取类对象 Class<?> aClass = Class.forName("com.reflect.Person"); //(2)获取类的构造方法Constructor System.out.println("===========获取类的构造方法============"); Constructor<?>[] cons = aClass.getConstructors(); for (Constructor<?> con : cons) { System.out.println(con); } //(3)获取类中的无参构造 System.out.println("===========获取类的无参构造============"); Constructor<?> con = aClass.getConstructor(); System.out.println(aClass); Person lisi = (Person) con.newInstance(); System.out.println(lisi); //(4)获取类中带参构造方法 System.out.println("===========获取类的带参构造============"); Constructor<?> con1 = aClass.getConstructor(String.class, int.class); Person p2 = (Person) con1.newInstance("李四", 23); System.out.println(p2); }使用反射获取类中的方法,并调用方法
//3使用反射获取类中的方法,并调用方法 public static void reflectOpe3() throws Exception { //(1)获取类对象 Class<?> aClass = Class.forName("com.reflect.Person"); //(2)获取方法Method对象 System.out.println("========获取方法Method对象========"); //Method[] methods = aClass.getMethods(); 获取公开的方法,包括从父类继承的方法 Method[] methods = aClass.getDeclaredMethods(); //获取类中的所有方法,包括私有、默认、保护的、不包含继承的方法 for (Method method : methods) { System.out.println(method); } //(3)获取单个方法 System.out.println("===========获取单个方法==========="); System.out.println("----------不带参----------"); Method eatMethod = aClass.getMethod("eat"); //调用方法 //正常调用方法 Person person = new Person(); person.eat; Person zhangsan = (Person) aClass.newInstance(); eatMethod.invoke(zhangsan); //toString Method toStringMethod = aClass.getMethod("toString"); Object result = toStringMethod.invoke(zhangsan); System.out.println(result); System.out.println("----------带参----------"); Method eatMethod2 = aClass.getMethod("eat", String.class); eatMethod2.invoke(zhangsan, "鸡腿"); //(4)获取私有方法 System.out.println("===========获取私有方法==========="); Method privateMethod = aClass.getDeclaredMethod("privateMethod"); //设置访问权限无效 privateMethod.setAccessible(true); privateMethod.invoke(zhangsan); //(5)获取静态方法 System.out.println("===========获取静态方法==========="); Method staticMethod = aClass.getMethod("staticMethod"); staticMethod.invoke(null); }这里需要注意几点:①getMethods方法是获取公开的方法,包括从父类继承的方法;getDeclaredMethods方法是获取类中的所有方法,包括私有、默认、保护的、不包含继承的方法。②在获取私有方法时,需设置访问权限无效Method setAccessible(true)
使用反射实现一个可以调用任何对象方法的通用方法
//4使用反射实现一个可以调用任何对象方法的通用方法 public static Object invokeAny(Object obj, String methodName, Class<?>[] types,Object...args) throws Exception { //1获取类对象 Class<?> aClass = obj.getClass(); //2获取方法 Method method = aClass.getMethod(methodName, types); //3调用 return method.invoke(obj,args); } public static void main(String[] args) throws Exception { //reflectOpe2(); //reflectOpe3(); Properties properties = new Properties(); invokeAny(properties, "setProperty", new Class[]{String.class,String.class}, "username", "张三"); System.out.println(properties); }使用反射获取类的属性
//5使用反射获取类的属性 public static void reflectOpe4() throws Exception{ //(1)获取类对象 Class<?> aClass = Class.forName("com.reflect.Person"); //(2)获取属性(字段) 公开的字段,父类继承的字段 // Field[] fields = aClass.getFields(); Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } //(3)获取name属性 Field name = aClass.getDeclaredField("name"); name.setAccessible(true); System.out.println(name); //(4)赋值 Person zhangsan = (Person) aClass.newInstance(); name.set(zhangsan, "张三"); //(5)获取值 System.out.println(name.get(zhangsan)); }一套被反复调用、多数人知晓的、经过分类编目的、代码设计经验的总结。
好处:使用设计模式为了可重用代码、让代码更容易被他人理、保证代码可靠性、重用性。添加实现类时只需修改配置文件
方式1:饿汉式(类加载时创建,天生线程安全)
class Singleton { private static final Singleton = new Singleton(); private Singleton(); public static Singleton getInstance(){ return instance; }; }方式2:懒汉式(使用时创建,线程不安全,加同步,效率低)
class Singleton { private static final Singleton = null; private Singleton(); public static synchronized Singleton getInstance(){ if(instance==null){ instance = new Singleton(); } return instance; }; }方式3:懒汉式(使用时创建,线程安全)
class Singleton { private Singleton(); private static class Holder() { static Singleton s = new Singleton(); } public static Singleton instance(){ return Holder.s; }; }枚举是一个引用类型,枚举是一个规定了取值范围的数据类型。
枚举变量不能使用其他的数据,只能使用枚举中常量赋值,提高程序安全性。定义枚举使用enum关键字。枚举的本质: 枚举是一个终止类,并集成enum抽象类枚举中常量是当前类型的静态常量注解是代码里的特殊标记,程序可以读取注解,一般用于替代配置文件。
开发人员可以通过注解告诉类如何运行。
在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。常见注解:@Override、@Deprecated
定义注解使用@interface关键字,注解中只能包含属性
定义一个注解: 使用
元注解:用来描述注解的注解
@Retention:用于指定注解可以保留的域。
RetentionPolicy.CLASS:注解记录在class文件中,运行java程序时,JVM不会保留RetentionPolicy.RUNTIME:注解记录在class文件中,运行java程序时,JVM会保留,程序可以通过反射获取该注释RetentionPolicy.SOURCE:编译时直接丢弃这种策略的注释。@Target:指定注解用于修饰类的哪个成员