1、获取Class的三种方法
/* * 1、反射机制 * 通过Java语言中反射机制可以操作字节码文件(可以读和修改字节码文件) * 通过反射机制可以操作代码片段(class文件) * 2、反射机制的相关类在哪个包下:java.lang.reflect.* * 3、反射机制相关的类有哪些? * java.lang.Class 代表整个字节码 * java.lang.reflect.Method 代表字节码中方法字节码 * java.lang.reflect.Construct 代表字节码中的构造方法字节码 * java.lang.reflect.Field 代表字节码中的属性字节码,代表类中的成员变量 * * * */ /* * 要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Close实例? * 三种方式: * 第一种:Class c = Class.forName("完整类名带包名"); * 第二种:Class c = 对象.geClass() * 第三种:java语言中任何一种类型,包括基本数据类型,它都有class属性,Class c = "任何类型“.class * Class.forName() * 1、静态方法 * 2、方法的参数是一个字符串 * 3、字符串需要的是一个完整的类名 * 4、完整类名必须带有包名,java.lang包不能省略 * * */ Class c1 = null; //代表String.Class类型 Class c2 = null; Class c3 = null; try { c1 = Class.forName("java.lang.String"); c2 = Class.forName("java.lang.Integer"); //代表Integer类型 } catch (ClassNotFoundException e) { e.printStackTrace(); } //java中任何一个对象都有一个方法:getClass() String s = "abc"; Class x = s.getClass(); //x代表的String.class字节码文件,x代表String类型 System.out.println(c1 == x); //==比较的是字节码的内存地址,字节码文件装载到JVM的时候,值装载一份 //java语言中任何一种类型,包括基本数据类型,它都有class属性 Class z = String.class; //代表String类型 Class f = Date.class; //代表Date类型 Class e = double.class; //代表double类型 Class r = int.class; //代表int类型
2、通过反射实例化对象
//普通创建 User user = new User(); System.out.println(user); try { //重点:通过反射机制,获取Class,通过Class来实例化对象 Class c = Class.forName("User"); //会调用USer的无参构造方法,完成User的创建; //重点是:newInstance调用的是无参构造,必须保证无参构造是存在的 Object obj = c.newInstance(); System.out.println(obj); //User@4554617c } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); }
3、反射机制的灵活性
* * 获取Class,能干什么? * 操作字节码文件 * java 代码写一遍,再不改变java源代码的基础之上,可以做到不同对象的实例化 * 非常之灵活,符合OCP原则,对修改关闭,对扩展开放 * * 后期要歇息高级框架,而工作中需要使用高级框架,而高级框架都是使用反射机制,有利于学习框架底层的东西 * */
//这种就写死了,只能创建一个User类型的对象 User user = new User(); //一下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改后,可以创建出不同的实例对象 try { //通过IO流读取classinfo.properties文件 FileReader fr = new FileReader("src/classinfo.properties"); //创建属性类对象 Properties pro = new Properties(); //加载 pro.load(fr); //通过key获取value String className = pro.getProperty("className"); //通过反射机制实例化对象 Class c1 = Class.forName(className); Object obj = c1.newInstance(); System.out.println(obj); //关闭流 fr.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); }
4、Class.forName()发生了什么
/* * 研究一下:Class.forName()发生了什么 * 重点:只希望一个类的静态代码块执行,其他一律不执行 * 你可以使用:Class.forName("完整类名") * 这个方法的执行会导致类加载,类加载时,静态代码块执行 * * 提示: * 后面JDBC技术的时候我们还需要 * */ public class ReflectTest01 { public static void main(String[] args) { //静态代码块在类加载时加载,并且只执行一次 try { Class.forName("MyClass"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class MyClass{ //静态代码块在类加载时执行,并且只执行一次 static{ System.out.println("静态代码块执行了"); } }
5、获取路径的绝对路径
//这种路径的缺点就是:移植性差,在idea中默认的路径是project的根 // 这个代码假设离开了idea,换到其他的位置,可能当前路径就不是project的根了,这个路径就无效了 // FileReader reader = new FileReader("classinfo.properties"); //接下来说一种通用的路径表示方法,这种编写是通用的是 //注意:试用一下通用方式的前提是:这个文件必须在类路径下,什么是类路径下,方式在src下都是类路径 src类的根路径 /* * Thread.currentThread():当前线程 * getContextClassLoader():是线程对象的方法,可以获取当前线程的类加载器 * getResource:这是类加载器对象的方法,当前线程的类加载器默认从类的根目录获取资源 * 可以拿到一个文件的绝对路径:/D:/java_base/out/production/chapter15/classinfo.properties * * */ String path = Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath(); System.out.println(path); //获取db.properties的绝对路径(从类的根路径下作为起点开始) String path2 = Thread.currentThread().getContextClassLoader().getResource("integer/db.properties").getPath(); System.out.println(path2);
6、类加载器
/* * java.util下提供一个资源绑定器,便于获取属性配置文件中的内容 * 但是以这种方式的时候,属性配置文件xxx.properties必须放到类路径下 * * */ //资源管理器,只能绑定xxx.properties文件,并且这个文件必须在类路径下,在写文件名的时候,路径后面的扩展名不能写 ResourceBundle bundle = ResourceBundle.getBundle("classinfo"); String className = bundle.getString("className");