笔记
码云上的笔记
腾讯文档上的笔记
https://docs.qq.com/pdf/DWklmY0xUUmh2WUxu
代码:
https://gitee.com/hufanglei/study/tree/master/code/reflect-demo
反射
1.根据包名如何获取包下的类的信息并能调用类的信息?
Q: 原理根据文件夹路径获取路径下的文件。
具体操作:
将包名的.替换成/,得到文件路径
获取文件路径下的类的名称
根据名称+.class得到Class对象
根据该类的Class对象获取类的内部信息,然后调用方法,成员变量等。
2.反射有什么好处,并举例说明
Q: new Object获取对象是静态编译,反射是动态编译。反射提高了程序的扩展性。new对象调用的前提(编译需要)是必须内存有这个对象(.class字节码),反射是根据.class字节码文件生成对象,并调用。才可以调用,反射可以没有这个对象甚至没有class文件,用的时候再根据class文件名去查找class文件强制加载这个对象并调用。比较灵活。
我们封装框架的时候,如tomcat并不知道你写了多少个servlet,但是他可以吧你的servlet在程序启动的时候给调用,为啥呢?因为我们可以根据web.xml或者注解方式把文件的包名,类名等给了tomcat,tomcat启动的时候,扫描这些配置,然后加载到内存中就可以使用了。
我们只要关注注解或web.xml对应的类信息(通常是包名+类名),用反射方式获取的信息就可以了。这就是反射扩展性的一个例子。
3.获取字节码对象的三种方式
public static void main(String[] args) throws ClassNotFoundException { // test1(); // test2(); test3(); } /** * 方式3 * 包名 class * 只要通过给定的类的 字符串名称就可获取该类,更为扩展 * 可是用class类中的方法完成, 该方法就是forName */ private static void test3() throws ClassNotFoundException { Class<?> classz = Class.forName("com.example.reflect.bean.Person"); System.out.println(classz); Class<?> classz2 = Class.forName("com.example.reflect.bean.Person"); System.out.println(classz2); System.out.println(classz == classz2); } /** * 方式2: * 类名.class * 任何数据类型都具备一个静态的属性,class类来获取其对应的class对象 * 相对简单,但是还是要明确用到类中的静态成员 * 还是不够扩展 */ private static void test2() { Class<?> classz = Person.class; System.out.println(classz); Class<?> classz2 = Person.class; System.out.println(classz2); System.out.println(classz==classz2); } /** * 方式1: * getClass() * Object类中的getClass()方法。 * 这种方式,必须要明确具体的类并创建对象,麻烦 */ private static void test1() { Person person = new Person(); Class<?> classz = person.getClass(); System.out.println(classz); Class<?> classz2 = person.getClass(); System.out.println(classz2); System.out.println(classz == classz2); }
4.反射获取实例对象的两种方式
早期: new时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存
并创建该字节码文件对象,并接着创建该字节码文件的对应的Person对象。
现在,找寻该名称类文件,并加载进内存,并产生class对象,通过下面产生类的对象
第一种: 通过Class对象的newInstance方法,只能获取无参数的构造函数的实例
第二种:根据Class的属性Construct对象的newIntance(…)方法,获取有参数的构造函数,并实例化
/** * 第二种方式: 根据Construcat对象的newInstance方法获取对象 * 当获取指定名称对应类中的 * * 当获取指定名称对应类中的所体现的对象时,而改对象初始化不适用空参构造该怎么办? * 既然是通过指定的构造函数进行对象的初始化 * 所以应该先获取到该构造函数,通过字节码文件对象即可完成 * 该方法是: getConstructor(paramterTypes) */ private static void createObect2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { String name = "com.example.reflect.bean.Person"; Class<?> clazz = Class.forName(name); Constructor<?> constructor = clazz.getConstructor( String.class, int.class); Person p = (Person) constructor.newInstance("小强",18); p.show(); } //第一种方式 //获取无参的是实例对象 private static void createObect1() throws ClassNotFoundException, IllegalAccessException, InstantiationException { String name = "com.example.reflect.bean.Person"; Class<?> clazz = Class.forName(name); Object o = clazz.newInstance(); Person p = (Person) o; p.show(); }
5.获取Class中的字段
Class类获取字段有4个方法:
演示一个获取可以获取私有字段的单个字段的方法:getDeclaredFiled(String name)
private static void getFieldDemo() throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, InstantiationException { Class<?> clazz = Class.forName("com.example.reflect.bean.Person"); Field field = clazz.getDeclaredField("age"); //对私有字段的访问取消权限检查,暴力访问 field.setAccessible(true); Object object = clazz.newInstance(); //设置值 field.set(object, 19); Object o = field.get(object); System.out.println(o); }
6.获取class中的方法
无参的方法的调用
private static void getMethodDemo() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class<?> clazz = Class.forName("com.example.reflect.bean.Person"); Method[] methods = clazz.getMethods(); //获取的都是公有的方法 for (Method method : methods) { System.out.println(method.getName()); } System.out.println("-----------------------"); //只拿本类中的包括私有的 Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method.getName()); } System.out.println("-----------------------"); //获取空参数一般方法 Method method = clazz.getMethod("show", null); //Object object = clazz.newInstance(); Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class); Object o = constructor.newInstance("小王", 25); method.invoke(o, null); }
有参数的方法的调用
private static void getMethodDemo1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { Class<?> clazz = Class.forName("com.example.reflect.bean.Person"); //获取空参数一般方法 Method method = clazz.getMethod("paramMethod", String.class, int.class); Object object = clazz.newInstance(); method.invoke(object, "小强", 23); }
7.反射调用类,增强扩展性的例子
public interface PCI { void open(); void close(); }
public class SoundPCI implements PCI { @Override public void open() { System.out.println("SoundP open"); } @Override public void close() { System.out.println("SoundP close"); } }
public class NetCard implements PCI{ @Override public void open() { System.out.println("NetCard open"); } @Override public void close() { System.out.println("NetCard close"); } }
public class Mainboard { public void run() { System.out.println("main board run...."); } public void userPCI(PCI p) { if (p != null) { p.open(); p.close(); } } }
public class ReflectTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { Mainboard mainboard = new Mainboard(); mainboard.run(); // mainboard.userPCI(new SoundPCI()); //通过反射调用pci usePCI(mainboard); } private static void usePCI(Mainboard mainboard) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException { // File file = new File("pci.properties"); Properties props = new Properties(); InputStream in = ReflectTest.class.getClassLoader().getResourceAsStream("pci.properties"); props.load(in); for (int i = 0; i < props.size(); i++) { String pciName = props.getProperty("pci" + (i + 1)); Class clazz = Class.forName(pciName); PCI p = (PCI) clazz.newInstance(); mainboard.userPCI(p); } } }
完