08 - 类加载器和反射 🛰️
1.类的加载
1.1 类加载
当程序要使用某个类时,如果该类还未被加载到内存当中,则系统会通过类加载,类连接,类的初始化三个步骤来对类进行初始化。如果不出现意外情况,jvm 将会连续完成这三个步骤,所有有时候也把这三个步骤称为类加载或类的初始化。
类的加载
- 就是指将 class 文件读入内存,并为之创建一个 java.lang.Class 对象
- 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致(是否对 jvm 有危害)
- 准备阶段:负责为类的类静态变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
对类变量进行初始化,步骤如下:
假如类还未被加载和连接,则程序先加载并连接该类
假如该类的直接父类还未被初始化,则先初始化其父类
假如类中有初始化语句,则系统依次执行这些初始化语句
1.2 类加载器
类加载器的作用:负责将.class 文件加载到内容中,并为之生成对应的 java.lang.Class 对象
ClassLoader 双亲委派机制:用来保护程序的安全
ClassLoader 的三个子类
BoostarpClassLoader (启动类记载器): 用来加载 JDK 中的所有 Class
ExClassLoader(扩展类加载器):用来加载外部引入的一些 jar 包
AppClassLoader(应用类加载器):用来加载我们自己写的 class
2.反射入门
反射概述
是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
获得 Class 对象的三种方式
public class ReflectDemo1 { public static void main(String[] args) throws ClassNotFoundException { //获取class文件的对象方式一:直接通过类的属性class获取 //ctrl+alt+v ~=alt+enter Class c1 = User.class; //获取class文件的对象方式二;通过调用对象的方法getClass() User u = new User(); Class c2 = u.getClass(); System.out.println(c1==c2); //获取class文件的对象方式三:通过调用Class的静态方式forName() Class c3 = Class.forName("com.sfoteem.reflect.demo1.User"); System.out.println(c3==c2); //通过Class对象反射生成对象 jdk9以上newInstance()过时了 Object o = c3.newInstance(); System.out.println(o); } //三种方式都可以获取Class文件对象 //第一种是最简单 //第三种是最灵活 }
获得构造器
/** * 获取构造函数 */ public class ConstructorDemo { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { /* * Constructor<?>[] getConstructors() 返回一个数组包含 Constructor物体反射所有的 类对象表示的类的公共构造函数。 * */ Class clazz = User.class; Constructor[] constructors = clazz.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("============================"); /*Constructor<?>[] getDeclaredConstructors() 返回 Constructor物体反射所有的构造函数通过 类对象表示的类中声明一个数组。 */ Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } System.out.println("============================"); //拿一个构造函数 参数若不写 则拿到的时候默认空参构造函数 //Constructor constructor = clazz.getConstructor(); Constructor constructor = clazz.getConstructor(int.class); Object o = constructor.newInstance(10); System.out.println(o); System.out.println(constructor); //拿到任何单个构造函数 若有参数 则给定指定参数的class对象即可 Constructor c = clazz.getDeclaredConstructor(String.class); //当设置为true的时候 即可越过java虚拟机修饰符检查访问机制 //也称作暴力反射。。。 c.setAccessible(true); Object o1 = c.newInstance("小鲍"); System.out.println(o1); } }
获得字段
/** * 获取字段 */ public class FiledDemo { public static void main(String[] args) throws NoSuchFieldException { /* * Field getField(String name) 拿到一个字段 通过给定的参数拿到指定的字段 返回一个 Field对象反映的类或接口的 类对象表示的指定公共成员。 * */ Class c = User.class; Field field = c.getField("age"); System.out.println(field); //通过调用getType();获取该字段的类型 Class<?> type = field.getType(); System.out.println(type); //获取字段类型的字符串 String str = type+""; str = type.getCanonicalName(); str = type.getName(); System.out.println(str.equals("int")); // int i = field.getModifiers(); //Field getFields() 拿到所有的公共字段 Field[] fields = c.getFields(); for (Field f : fields) { System.out.println(f); } System.out.println("==================="); //拿到任何的字段 调用getDeclaredFiled() Field age = c.getDeclaredField("name"); int num = age.getModifiers(); System.out.println(num); } }
获得方法
** * 获取方法 */ public class MethodDemo { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { //获取Class对象 Class c = User.class; //获取所有公共方法包括父类getMethods() ctrl+alt+v Method[] methods = c.getMethods(); //遍历查看一下是否拿到了 for (Method method : methods) { System.out.println(method); } System.out.println("========================"); //拿单个公共的getMethod(String name,Class<T>... params) //获取所有方法不包括父类 getDeclaredMethods() Method[] m = c.getDeclaredMethods(); //查看一下是否拿到了所有不包括父类 for (Method method : m) { System.out.println(method); } //拿单个公共的getDeclaredMethod(String name,Class<T>... params) Method me = c.getDeclaredMethod("playGame", String.class); //怎么执行这个方法 //实例化一个对象 Object obj = c.newInstance();//存在于堆中 System.out.println(obj); //me代表拿到的方法playGame 通过me对象调用invoke(obj,..) //obj就是谁要执行这个方法 me.invoke(obj,"小鲍"); } }
3. 案例
通过配置文件创建对象
/** * 通过配置文件创建对象 */ public class Test1 { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { /*Cat c = new Cat(); c.bark(); Dog d = new Dog(); d.bark();*/ //第一步 读取配置文件 通过类加载器获得输入流 任何class对象都可以获取类加载器 InputStream inputStream = Test1.class.getClassLoader().getResourceAsStream("class.txt"); //第二步创建properties对象 用来加载流 Properties prop = new Properties(); prop.load(inputStream); String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); //第三步通过Class的静态方法forName()获取该类的Class对象 Class<?> c = Class.forName(className); //通过Class对象拿到该类方法 Method method = c.getDeclaredMethod(methodName); //实例化对象 //Object obj = c.newInstance(); Object obj = c.getDeclaredConstructor().newInstance(); //调用方法 method.invoke(obj); } }
配置文件
#className=com.softeem.reflect.softeem.reflect.test.Dog #methodName=bark className=com.softeem.reflect.softeem.reflect.test.Cat methodName=bark
越过泛型检查
/** * 通过反射越过泛型检查 */ public class Test2 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { //java中的泛型机制是一种伪泛型 只作用于编译期间 List<Integer> list = new ArrayList<>(); /*list.add(1); list.add(2); list.add(3); list.add("呵呵"); System.out.println(list);*/ Class<? extends List> c = list.getClass(); Method method = c.getDeclaredMethod("add", Object.class); method.invoke(list,"呵呵"); method.invoke(list,"哈哈"); method.invoke(list,"嘻嘻"); System.out.println(list); } }
拷贝对象
/** * 对象拷贝 */ public class Test3 { /** * 通过泛型机制返回指定对象 * @param resource * @param t * @param <T> * @return * @throws IllegalAccessException * @throws InstantiationException * @throws NoSuchMethodException * @throws InvocationTargetException */ public static<T> T copy(Object resource,Class<T> t) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //通过原对象获取class对象 Class<?> c = resource.getClass(); //通过给定的类型创建对象 T obj = t.newInstance(); //获取所有字段 Field[] fields = c.getDeclaredFields(); for (Field field : fields) { //获取字段的简洁字符串形式 String f = field.getName(); //System.out.println(f); String sname = "set"+f.substring(0,1).toUpperCase()+f.substring(1); String gname = "get"+f.substring(0,1).toUpperCase()+f.substring(1); if(field.getType().getName().equals("boolean")){ gname = "is"+f.substring(0,1).toUpperCase()+f.substring(1); } //System.out.println(sname); //System.out.println(gname); //获取原对象的具体get方法 Method getMethod = c.getMethod(gname); //调用原对象的具体方法get 获取到一个值 Object value = getMethod.invoke(resource); //获取具体set方法 Method setMethod = c.getMethod(sname, field.getType()); //把通过原对象拿到的值 赋值到新的对象里面 setMethod.invoke(obj,value); } return obj; } /** * 拷贝对象 * @param resource * @return * @throws IllegalAccessException * @throws InstantiationException * @throws NoSuchMethodException * @throws InvocationTargetException */ public static Object copy(Object resource) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //通过原对象获取class对象 Class<?> c = resource.getClass(); //通过反射创建一只新的对象 Object target = c.newInstance(); //获取所有字段 Field[] fields = c.getDeclaredFields(); for (Field field : fields) { //获取字段的简洁字符串形式 String f = field.getName(); //System.out.println(f); String sname = "set"+f.substring(0,1).toUpperCase()+f.substring(1); String gname = "get"+f.substring(0,1).toUpperCase()+f.substring(1); if(field.getType().getName().equals("boolean")){ gname = "is"+f.substring(0,1).toUpperCase()+f.substring(1); } //System.out.println(sname); //System.out.println(gname); //获取原对象的具体get方法 Method getMethod = c.getMethod(gname); //调用原对象的具体方法get 获取到一个值 Object value = getMethod.invoke(resource); //获取具体set方法 Method setMethod = c.getMethod(sname, field.getType()); //把通过原对象拿到的值 赋值到新的对象里面 setMethod.invoke(target,value); } return target; } public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Cat c = new Cat(); c.setAge(2); c.setName("伽菲猫"); Cat cat = copy(c, Cat.class); System.out.println(cat); } }
封装 DBUtils
/** * 封装DBUtils */ public class Test4 { //驱动 private static final String driver = "com.mysql.cj.jdbc.Driver"; //访问路径 private static final String url = "jdbc:mysql:///mybase?serverTimezone=UTC"; //用户名 private static final String username = "root"; //密码 private static final String password = "root"; //声明一个连接池对象 private static DruidDataSource dataSource; static { //静态代码块调用生成连接池方法 dataSource = creatDataSource(); } /** * 创建连接池 * @return */ private static DruidDataSource creatDataSource() { //设置参数 dataSource = new DruidDataSource(); dataSource.setDriverClassName(driver); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } /** * 获取连接 * @return */ public static Connection getConnection(){ Connection conn = null; try{ if(dataSource==null||dataSource.isClosed()){ dataSource = creatDataSource(); } conn = dataSource.getConnection(); }catch (SQLException e){ e.printStackTrace(); } return conn; } /** * 关闭资源 * @param rs * @param stat * @param conn */ public static void close(ResultSet rs, Statement stat,Connection conn){ try{ if(rs!=null){ rs.close(); } if(stat!=null){ rs.close(); } if(conn!=null){ rs.close(); } }catch (SQLException e){ e.printStackTrace(); } } //通用更新操作 // id name age time id:1 ,name:肖翠,age:15 ,time:3 // 1 小鲍 15 3 // 2 肖翠 15 3 /** * 把查询到的结果封装到map集合中 * @param sql * @param params * @return */ public static List<Map<String,Object>> queryToMap(String sql,Object... params){ //创建集合 List<Map<String,Object>> maps = new ArrayList<>(); //获取连接 Connection conn = getConnection(); //获取执行sql语句的平台 PreparedStatement stat = null; ResultSet rs = null; try { stat = conn.prepareStatement(sql); //判断参数是否为空 if(params!=null){ //遍历循环参数 for (int i = 0; i < params.length; i++) { //为占位符设置值 stat.setObject((i+1),params[i]); } } //执行查询并获取结果集 rs = stat.executeQuery(); //获取查询结果的元数据 ResultSetMetaData rsmd = rs.getMetaData(); //获取一条查询结果的总列数 int columnCount = rsmd.getColumnCount(); //遍历结果集 while (rs.next()){ Map<String,Object> map = new HashMap<>(); //遍历一条结果集 for (int i = 1; i <= columnCount ; i++) { //获取每一列的名字 String name = rsmd.getColumnName(i); //通过列名获取对应的值 Object value = rs.getObject(name); //把列名,列值通过键值对存储到map集合中 map.put(name,value); } //把每一行数据对应的map存到maps中 maps.add(map); } return maps; } catch (SQLException e) { e.printStackTrace(); }finally { close(rs,stat,conn); } return null; } /** * 通用查询查一个 * @param sql * @param t * @param params * @param <T> * @return */ public static<T> T queryOne(String sql,Class<T> t,Object... params){ //将查询结果封装到map中 List<Map<String, Object>> maps = queryToMap(sql, params); T obj = null; //判断集合长度 if(maps.size()>0){ Map<String, Object> map = maps.get(0); obj = mapToBean(t,map); } return obj; } public static<T> List<T> queryList(String sql,Class<T> t,Object... params){ //创建一个List List<T> list = new ArrayList<>(); //将查询结果封装到map中 List<Map<String, Object>> maps = queryToMap(sql, params); T obj = null; //判断集合长度 if(maps.size()>0){ for (Map<String, Object> map : maps) { obj = mapToBean(t,map); list.add(obj); } } return list; } /** * 将map转化为对象 * @param t * @param map * @param <T> * @return */ private static <T> T mapToBean(Class<T> t, Map<String, Object> map) { try{ //实例化一个给定类型的对象 T obj = t.newInstance(); //遍历map map.forEach((k,v)->{ try { //获取给定类型的字段 Field field = t.getDeclaredField(k); //暴力反射 field.setAccessible(true); //把值赋值给对象的属性 field.set(obj,v); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }); return obj; } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } return null; } }
封装 BaseServlet