JAVASE part20_反射

反射

【1】反射概念

反射: 加载类,反射出类的各个组成部分 构造方法,属性(非静态属性和静态属性),方法(非静态方法,静态方法)

java 反射机制: 是在运行状态中(Class对象), 对于任何类,都能能够知道这个类的所有的属性和方法; 对于任意一个对象,能够调用它的任意属性和方法;这种动态获取信息的方式,就称之反射.

【2】类加载

当程序要使用某个类时,如果这个类还没有加载内存中,则系统会通过加载,连接,初始化三个步骤来实现对这个类的初始化。

加载 :

将class 文件加载到内存中,并为之创建一个Class对象(将class文件的内容放到一个对象中,而对象的名字刚好就是Class)

​ 任何类被使用时,系统都会建立一个Class对象.

连接

​ 验证: 是否有正确的内部结构 ,并和其它协调一致

​ 准备 : 负责为类的静态成员分配内存,并设置默认初始化值

​ 解析: 将类的二进制数据中的符号引用替换成直接引用

初始化

​ 就是之前的初始化步骤

2.1类加载的时机

  1. 创建类的实例

  2. 访问类的静态变量或给静态变量赋值

  3. 调用类的静态方法

  4. 初始化某个类的子类

  5. java命令,运行某个类

  6. 使用反射方式强制创建某个类或接口对应的Class对象

2.2类加载器

负责将class 文件加载到内存中,并为之创建一个Class对象,如果了解类加载器的机制,可以的更好的理解程序的运行

类加载器的组成:

根类加载器: bootstrap classLoader

​ 也被称为引导类加载类,负责Java核心类的加载

​ 比如: System, String 等,在 JDK 中的JRE 中 lib 中的 rt.jar文件中

扩展类加载器: extension classLoader

​ 负责jre的扩展目录中的jar的加载

系统类加载器: System classLoader

​ 负责在JVM启动时加载来自java 命令的class文件

2.3获取Class对象的方式

开发: 使用第三种.

​ 为什么? 因为第三种是一个字符 串,而不是具体类名,这种的话就可以将这个值放到配置文件中,方便对它修改.

//方法1:
        Person p = new Person();
        Class c = p.getClass();

        Person p1 = new Person();
        Class c1 = p.getClass();

        System.out.println(p==p1);  //false
        System.out.println(c==c1);  //true
        //方法2:
        Class c2 = Person.class;
        System.out.println(c == c2); //true

        // 方法3
        // Class对象中的静态方法
        //ClassNotFoundException  必须写包名

        Class c3 = Class.forName("com.demo1.Person");

        System.out.println(c==c3);

2.4获取类的加载器

//1. 如何获取 类的加载器
        //1.1 首先得到Class对象
        Class c = Person.class;
        //1.2 获取类的加载器
        ClassLoader classLoader = c.getClassLoader();
        System.out.println(classLoader);

2.5使用类加载器加载其它的文件

注:小心路径问题,最好是将方法放到 resource 目录下

https://blog.csdn.net/weixin_48052161/article/details/115151874

 Class c = User.class;
        //1.2 获取类的加载器
        ClassLoader classLoader = c.getClassLoader();
        System.out.println(classLoader);
        // 类加载器加载其它的文件
        InputStream in = classLoader.getResourceAsStream("jdbc.properties");
        System.out.println(in);

【3】类加载的原理

类加载是Java虚拟机(JVM)对类文件进行加载、验证、准备、解析和初始化等一系列操作的过程。类加载的原理包括以下几个步骤:

1.加载:在类加载过程中,JVM会根据类的全限定名来获取类的字节码文件,并将其读取到内存中。

2.验证:在验证阶段,JVM会对字节码文件进行验证,确保其符合Java语言规范和安全要求。验证包括文件格式验证、元数据验证、字节码验证和符号引用验证等。

3.准备:在准备阶段,JVM会为类的静态变量分配内存,并设置默认初始值(例如0、null等)。

4.解析:在解析阶段,JVM会将类的符号引用转换为直接引用,以便于后续的内存访问。解析过程包括类、接口、字段和方法等的解析。

5.初始化:在初始化阶段,JVM会执行类的初始化代码,包括静态变量的赋值和静态代码块的执行。在这个阶段,类的初始化按照严格的顺序进行,并且只会执行一次。

类加载过程是按需进行的,即当程序需要使用某个类时,JVM才会进行相应的类加载操作。同时,JVM还采用了双亲委派模型来进行类加载,即先委派给父类加载器尝试加载,只有在父类加载器无法加载时,才由子类加载器尝试加载。

通过类加载的原理,Java实现了动态扩展和灵活的类加载机制,使得开发人员可以根据需要动态加载和使用类,实现了面向对象编程的核心特性之一:封装和复用

先做了解,第四阶段着重讲

【4】反射构造器

反射构造器: 通过Class对象,获取构造方法

 Class c = Person.class;
         //获取构造方法
        // Constructor<?>[] getConstructors() : 返回所有的构造方法 Constructor 类的对象  只能获取 public修饰的构造方法
        //返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
//        Constructor[] constructors = c.getConstructors();
        //c.getDeclaredConstructors(); 获取 所有构造方法
//        Constructor[] constructors = c.getDeclaredConstructors();
//        for(Constructor constructor:constructors){
//            System.out.println(constructor);
//        }

        //获取单个构造方法
        // getConstructor(类<?>... parameterTypes): 只能得到public修饰
//        Constructor constructor = c.getConstructor(String.class);
        Constructor constructor = c.getDeclaredConstructor(String.class);
        System.out.println(constructor);


        //Constructor<T> getConstructor(类<?>... parameterTypes)
        //返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。

        // 根据构造方法,创建实例对象
        //java.lang.IllegalAccessException 构造方法是私有的,创建实现,会报非法异常
        // 解决方案:
        constructor.setAccessible(true); //跳过java语法检查
        Object obj = constructor.newInstance("张三");
        System.out.println(obj);

【5】反射属性

反射构造器: 通过Class对象,获取成员属性

   public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class c = Person.class;
        //public Field[] getFields() 返回包含一个数组Field对象反射由此表示的类或接口的所有可访问的公共字段类对象。
//        Field[] fields = c.getFields();
//        for(Field field : fields){
//            System.out.println(field);
//        }

        //public Field[] getDeclaredFields() : 获取所有属性
//        Field[] fields = c.getDeclaredFields();
//        for(Field field : fields){
//            System.out.println(field);
//        }
     //NoSuchFieldException
//        Field field= c.getField("name");
//        System.out.println(field);

//        Field field= c.getDeclaredField("name");
//        System.out.println(field);

        // 没有对象就没有属性
        // 要使用属性,必须 要有对象
        Constructor constructor = c.getDeclaredConstructor();
        Object obj = constructor.newInstance(); //创建对应的对象

        Field namefield = c.getDeclaredField("name");
        Field agefield = c.getDeclaredField("age");
        Field addressfield = c.getDeclaredField("address");

        //IllegalAccessException
        namefield.setAccessible(true);
        namefield.set(obj,"唐姐");
        agefield.set(obj,18);
        addressfield.set(obj,"成都");
        System.out.println(obj);
    }

【6】反射方法

反射构造器: 通过Class对象,获取成员方法

作业:

  1. 写一个setProperty(Object obj, String propertyName, object value){}

  2. 给ArrayList的一个对象,添加一个字符串

  3. curd(增删改查)商品

    商品表(goods)

    ​ g_id,g_name,g_price,g_num

面试题

第一题:JDBC操作数据库的步骤 ?

1)注册数据库驱动。

2)建立数据库连接。

3)创建一个Statement。

4)执行SQL语句。

5)处理结果集。

6)关闭数据库连接

第二题:JDBC中的Statement 和PreparedStatement,CallableStatement的区别?

1)PreparedStatement是预编译的SQL语句,效率高于Statement。

2)PreparedStatement支持?操作符,相对于Statement更加灵活。

3)PreparedStatement可以防止SQL注入,安全性高于Statement。

4)CallableStatement适用于执行存储过程。

第三题:execute,executeQuery,executeUpdate的区别是什么?

· Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpdateCount()方法来获取更新的记录条数。

· Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery来执行查询语句,这样的话如果传进来的是insert或者update语句的话,它会抛出错误信息为 “executeQuery method can not be used for update”的java.util.SQLException。

· Statement的executeUpdate(String query)方法用来执行insert或者update/delete(DML)语句,或者 什么也不返回DDL语句。返回值是int类型,如果是DML语句的话,它就是更新的条数,如果是DDL的话,就返回0。

· 只有当你不确定是什么语句的时候才应该使用execute()方法,否则应该使用executeQuery或者executeUpdate方法。

第四题:PreparedStatement的缺点是什么,怎么解决这个问题?

PreparedStatement的一个缺点是,我们不能直接用它来执行in条件语句;需要执行IN条件语句的话,下面有一些解决方案:

1)分别进行单条查询——这样做性能很差,不推荐。

2)使用存储过程——这取决于数据库的实现,不是所有数据库都支持。

3)动态生成PreparedStatement——这是个好办法,但是不能享受PreparedStatement的缓存带来的好处了。

4)在PreparedStatement查询中使用NULL值——如果你知道输入变量的最大个数的话,这是个不错的办法,扩展一下还可以支持无限参数。

第五题:JDBC的ResultSet是什么?

在查询数据库后会返回一个ResultSet,它就像是查询结果集的一张数据表。

ResultSet对象维护了一个游标,指向当前的数据行。开始的时候这个游标指向的是第一行。如果调用了ResultSet的next()方法游标会下移一行,如果没有更多的数据了,next()方法会返回false。可以在for循环中用它来遍历数据集。

默认的ResultSet是不能更新的,游标也只能往下移。也就是说你只能从第一行到最后一行遍历一遍。不过也可以创建可以回滚或者可更新的ResultSet。

当生成ResultSet的Statement对象要关闭或者重新执行或是获取下一个ResultSet的时候,ResultSet对象也会自动关闭。

可以通过ResultSet的getter方法,传入列名或者从1开始的序号来获取列数据。

  • 29
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值