Java--反射机制原理、几种Class获取方式及应用场景

article/2025/10/30 16:11:20

目录

  • 📢学习背景
  • 🎹一、Java反射机制是什么?
    • 🎸1.1 反射原理
    • 📣1.2 反射例子
  • 🎵二、Java反射机制中获取Class的三种方式及区别?
    • 📀2.1 Class的几种获取方式
    • 🔊2.2 代码演示几种方式的区别
  • 💥三、Java反射机制的应用场景有哪些?
    • 🎶3.1 应用场景
    • 🎧3.2 简单工厂模式优化
      • 📢3.2.1 什么是简单工厂模式?
      • 🎹3.2.2 简单工厂模式有什么用?
      • 🎸3.2.3 如何实现简单工程模式?
      • 📣3.2.4 简单工厂模式优化
      • 🎵3.2.5 简单工厂模式再次优化
    • 📀3.3 代理模式中的动态代理实现
      • 🔊3.3.1 什么是代理模式?
      • 💥3.3.2 什么是静态代理?
      • 🎶3.3.2 什么是动态代理?
      • 🎧3.3.3 动态代理中如何利用Java反射机制?
    • 📢3.4 Java JDBC数据库操作实现
      • 🎹3.4.1 利用反射加载JDBC驱动
      • 🎸3.4.2 Java JDBC连接示例
  • 面试总结

📢学习背景

学习Java的小伙伴,可能听过Java反射机制,但是熟悉又有点陌生,本文主要是通过思考面试中经常被问到的几个Java反射机制的问题,再通过理论知识结合代码实例应用场景进行讲解,加深自己对Java反射机制的认知和理解,也希望能帮助到有需要的小伙伴~
在这里插入图片描述

🎹一、Java反射机制是什么?

🎸1.1 反射原理

(1)Java反射机制(Java Reflection)是Java语言中一种动态(运行时)访问、检测 & 修改它本身的能力,主要作用是动态(运行时)获取类的完整结构信息 & 调用对象的方法~
更简单点的说就是Java程序在运行时(动态)通过创建一个类的反射对象,再对类进行相关操作,比如:

  • 获取该对象的成员变量 & 赋值
  • 调用该对象的方法(含构造方法,有参/无参)
  • 判断该对象所属的类

PS:不过说实话,直接看比较官方的定义还是有点难理解,再来更加通俗点的说吧~

(2)一般情况下,我们使用某个类,都会知道这个类,以及要用它来做什么,可以直接通过new实例化创建对象,然后使用这个对象对类进行操作,这个就属于正射~

(3)而反射则是一开始并不知道要初始化的是什么类,无法使用new来实例化创建对象,主要是通过JDK提供的反射API来实现,在运行时才知道要操作的是什么类,并且可以获取到类的完整构造以及调用对应的方法,这就是反射~

📣1.2 反射例子

代码如下:

package com.justin.java.lang;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;/*** @program: Jdk1.8 Test* @description: 正射、反射简单调用示例* @author: JustinQin* @create: 2021/8/22 13:23* @version: v1.0.0**/
public class Student {private int id;public void setId(int id) {this.id = id;}public int getId() {return id;}public static void main(String[] args) throws Exception{//一、正射调用过程Student student = new Student();student.setId(1);System.out.println("正射调用过程Student id:" + student.getId());//二、反射调用过程Class clz = Class.forName("com.justin.java.lang.Student");Constructor studentConstructor = clz.getConstructor();Object studentObj = studentConstructor.newInstance();Method setIdMethod = clz.getMethod("setId", int.class);setIdMethod.invoke(studentObj, 2);Method getIdMethod = clz.getMethod("getId");System.out.println("正射调用过程Student id:" + getIdMethod.invoke(studentObj));}
}

输出结果:

正射调用过程Student id:1
反射调用过程Student id:2

上述例子反射的调用过程,可以看到获取一个类的反射对象,主要过程为:

  • 获取类的Class实例对象
  • 根据Class实例对象获取Constructor对象
  • 再根据Constructor对象的newInstance方法获取到类的反射对象

获取到类的反射对象后,就可以对类进行操作了~ 例如,上述示例中对类的方法进行调用过程为:

  • 根据Class实例对象获取到类的Method对象
  • 再根据Method对象的invoke方法调用到具体类的方法

前面一点也提到了获取到类的Class实例对象,上面示例反向调用过程中我们是通过Class.forName("类的全局定名")这种方式来获取到类的Class实例对象,除了这种,常用的还有其他两种,往下讲解~

🎵二、Java反射机制中获取Class的三种方式及区别?

在这里插入图片描述

📀2.1 Class的几种获取方式

(1)获取类的java.lang.Class实例对象,常见的三种方式分别为:

  • 通过MyClass.class获取,这里的MyClass指具体类~~
  • 通过Class.forName("类的全局定名")获取,全局定名为包名+类名
  • 通过new MyClass().getClass()获取,这里的MyClass指具体类~

(2)通过MyClass.class获取,JVM会使用ClassLoader类加载器将类加载到内存中,但并不会做任何类的初始化工作,返回java.lang.Class对象

(3)通过Class.forName("类的全局定名")获取,同样,类会被JVM加载到内存中,并且会进行类的静态初始化工作,返回java.lang.Class对象

(4)通过new MyClass().getClass()获取,这种方式使用了new进行实例化操作,因此静态初始化和非静态初始化工作都会进行getClass方法属于顶级Object类中的方法,任何子类对象都可以调用,哪个子类调用,就返回那个子类的java.lang.Class对象

PS: 这3种方式,最终在JVM堆区对应类的java.lang.Class对象都属于同一个,也就是内存地址相同,进行==双等号比较结果为true,原因是JVM类加载过程中使用的是同一个ClassLoader类加载器加载某个类,不论加载多少次,生成到堆区的java.lang.Class对象始终只有一个,除非自定义类加载器,破坏JVM的双亲委派机制,使得同一个类被不同类加载器加载,JVM才会把它当做两个不同的java.lang.Class对象

🔊2.2 代码演示几种方式的区别

创建一个实体类,分别在实体类中创建类的静态代码块动态代码块有参构造方法无参构造方法,方便测试几种方式的区别及内存地址是否相同~

(1)实体类:

public class MyClass {private static final String staticStr = "Hi";private static int staticInt = 2021;private String id;static {System.out.println("静态代码块:staticStr=" + staticStr + ",staticInt=" + staticInt);}{System.out.println("动态代码块~");}public MyClass() {System.out.println("无参构造方法~");}public MyClass(String id) {System.out.println("有参构造方法~");this.id = id;}public String getId() {return id;}public void setId(String id) {this.id = id;}@Overridepublic String toString() {return "MyClass{" +"id='" + id + '\'' +'}';}
}

(2)单元测试类:
通过@Test注解对三种方式分别进行单元测试,再对这三种方式的组合进行单元测试~

package com.justin.java.lang;import org.junit.Test;/*** @program: Jdk1.8Test* @description: Java反射机制中获取类的Class实例对象的常见三种方式及区别对比* @author: JustinQin* @create: 2021/8/22 15:04* @version: v1.0.0**/
public class MyClassTest {@Testpublic void test1() {System.out.println("一、MyClass.class方式=========");Class<?> class1 = MyClass.class;}@Testpublic void test2() throws ClassNotFoundException {System.out.println("二、Class.forName方式=========");Class class2 = Class.forName("com.justin.java.lang.MyClass");}@Testpublic void test3() {System.out.println("三、new MyClass().getClass方式=========");Class class3 = new MyClass().getClass();}@Testpublic void test12() throws ClassNotFoundException {System.out.println("一、MyClass.class方式=========");Class<?> class1 = MyClass.class;System.out.println("二、Class.forName方式=========");Class class2 = Class.forName("com.justin.java.lang.MyClass");}@Testpublic void test13() {System.out.println("一、MyClass.class方式=========");Class<?> class1 = MyClass.class;System.out.println("三、new MyClass().getClass方式=========");Class class3 = new MyClass().getClass();}@Testpublic void test23() throws ClassNotFoundException {System.out.println("二、Class.forName方式=========");Class class2 = Class.forName("com.justin.java.lang.MyClass");System.out.println("三、new MyClass().getClass方式=========");Class class3 = new MyClass().getClass();}@Testpublic void test() throws ClassNotFoundException {System.out.println("四、三种方式内存地址比较=========");Class<?> class1 = MyClass.class;Class class2 = Class.forName("com.justin.java.lang.MyClass");Class class3 = new MyClass().getClass();System.out.println("比较结果=========");System.out.println("MyClass.class和Class.forName内存地址比较是否相同:" + (class1 == class2));System.out.println("MyClass.class和new MyClass().getClass内存地址比较是否相同:" + (class1 == class3));System.out.println("Class.forName和new MyClass().getClass内存地址比较是否相同:" + (class2 == class3));}
}

逐个执行单元,得出测试结果为:

* test1()方法
一、MyClass.class方式=========*  test2()方法
二、Class.forName方式=========
静态代码块:staticStr=Hi,staticInt=2021*  test3()方法
三、new MyClass().getClass方式=========
静态代码块:staticStr=Hi,staticInt=2021
动态代码块~
无参构造方法~*  test12()方法
一、MyClass.class方式=========
二、Class.forName方式=========
静态代码块:staticStr=Hi,staticInt=2021*  test13()方法
一、MyClass.class方式=========
三、new MyClass().getClass方式=========
静态代码块:staticStr=Hi,staticInt=2021
动态代码块~
无参构造方法~*  test23()方法
二、Class.forName方式=========
静态代码块:staticStr=Hi,staticInt=2021
三、new MyClass().getClass方式=========
动态代码块~
无参构造方法~*  test()方法
四、三种方式内存地址比较=========
静态代码块:staticStr=Hi,staticInt=2021
动态代码块~
无参构造方法~
比较结果=========
MyClass.classClass.forName内存地址比较是否相同:true
MyClass.classnew MyClass().getClass内存地址比较是否相同:true
Class.forName和new MyClass().getClass内存地址比较是否相同:true

通过test1test2test3的测试结果验证了2.1 三种方式及区别中黄色标记部分的区别说明,即:

  • MyClass.class不会做任何类的初始化工作
  • Class.forName会进行类的静态初始化工作
  • new MyClass().getClass静态初始化和非静态初始化工作都会进行
  • 使用这三种方式任意一种最终在JVM加载到内存中都会是内存地址相同

test23组合得到的测试结果,说明静态代码块只会被加载一次~

讲了这么多,除了知道基本原理和基本使用之外,更重要的还是要知道它的一些比较实际的应用场景,往下介绍~

💥三、Java反射机制的应用场景有哪些?

在这里插入图片描述

🎶3.1 应用场景

  • 工厂模式中的简单工厂模式优化
  • 代理模式中的动态代理方式实现
  • Java JDBC数据库操作

🎧3.2 简单工厂模式优化

📢3.2.1 什么是简单工厂模式?

Java中主要有23种设计模式,其中工厂模式就是其中一种,而简单工厂模式,顾名思义,也是属于工厂模式中的一种,只不过比较简单。简单工厂模式也可以叫做静态方法模式(因为工厂类一般都是在内部定义了一个静态方法)。
从现实生活角度来理解的话,工厂是专门负责生产产品的,同样在设计模式中,简单工厂模式我们可以理解为专门负责生产对象的一个类,称为“工厂类”。

🎹3.2.2 简单工厂模式有什么用?

简单工厂模式通过创建一个对应的工厂类,将类实例化的操作使用对象的操作进行分开,让使用者不用知道具体参数就可以实例化出所需要的具体产品类,从而避免了在客户端代码中显式指定,实现了解耦。即使用者可直接消费产品而不需要知道其生产的细节~

🎸3.2.3 如何实现简单工程模式?

实现简单工程模式的核心是创建一个工厂类,并且在内部定义了一个静态方法,传入不同的参数标识通过switch进行分组,通过new实例化创建不同的子类对象返回~

实现例子:

步骤1:创建抽象产品类

public interface Product {public abstract void show();
}

步骤2:创建具体产品类:

public class ProductA implements Product {@Overridepublic void show() {System.out.println("生产了产品A");}
}
public class ProductB implements Product {@Overridepublic void show() {System.out.println("生产了产品B");}
}public class ProductC implements Product {@Overridepublic void show() {System.out.println("生产了产品C");}
}

步骤3:创建简单工厂类

public class SimpleFactory {/*** 实现简单工厂模式* @param pName 产品标识* @return 返回具体的产品*/public static Product createProduct(String pName){switch (pName){case "A":return new ProductA();case "B":return new ProductB();case "C":return new ProductC();default:return null;}}
}

步骤4:调用简单工厂类

public class SimpleFactoryTest {public static void main(String[] args) {try {SimpleFactory.createProduct("A").show();} catch (NullPointerException e) {System.out.println("没有A这款产品,无法生产~");}try {SimpleFactory.createProduct("B").show();} catch (NullPointerException e) {System.out.println("没有B这款产品,无法生产~");}try {SimpleFactory.createProduct("C").show();} catch (NullPointerException e) {System.out.println("没有C这款产品,无法生产~");}try {SimpleFactory.createProduct("D").show();} catch (NullPointerException e) {System.out.println("没有D这款产品,无法生产~");}}
}

📣3.2.4 简单工厂模式优化

(1)简单工厂模式弊端

  • 操作成本高:每增加一个接口的子类,必须修改工厂类的逻辑
  • 系统复杂性提高:每增加一个接口的子类,都必须向工厂类添加逻辑

这两点弊端从前面的例子SimpleFactory工厂类的实现,可以看出简单工厂模式中对工厂类SimpleFactory的维护成本有点大,因为实际中可能会很频繁的去更新具体产品类,每一次变更都需要去修改工厂类,此时就可以利用Java反射机制对简单工厂模式进行优化~

(2)简单工厂模式的优化思路
采用Java反射机制,通过传入子类全局定名(包名+类名) 动态的创建不同的子类对象实例,从而使得在不增加产品接口子类和修改工厂类的逻辑的情况下还能实现了工厂类对子类实例对象的统一创建~

(3)简单工厂模式的优化步骤
步骤1:创建工厂类
采用Java反射机制对工厂类进行优化,主要是将className子类全局定名(包名+类名)作为入参,通过Class.forName方式获取类的java.lang.Class实例对象,再通过Class实例对象的getInstance方法获取到具体子类的实例对象~

public class Factory {public static Product getInstance(String className) {Product realProduct = null;try {Class pClass = Class.forName(className);realProduct = (Product) pClass.newInstance();} catch (Exception e) {e.printStackTrace();}return realProduct;}
}

步骤2:调用工厂类

public class FactoryTest {public static void main(String[] args) {try {Product productA = Factory.getInstance("com.justin.java.lang.ProductA");productA.show();} catch (NullPointerException e) {System.out.println("没有A这款产品,无法生产~");}try {Product productB = Factory.getInstance("com.justin.java.lang.ProductB");productB.show();} catch (NullPointerException e) {System.out.println("没有B这款产品,无法生产~");}try {Product productC = Factory.getInstance("com.justin.java.lang.ProductC");productC.show();} catch (NullPointerException e) {System.out.println("没有C这款产品,无法生产~");}try {Product productD = Factory.getInstance("com.justin.java.lang.ProductD");productD.show();} catch (Exception e) {System.out.println("没有D这款产品,无法生产~");}}
}

优化结果:

使用Java反射机制优化简单工厂模式后,可以看到,不论具体产品类更新多频繁,都不需要再修改工厂类,从而解决了普通简单工厂模式操作成本高系统复杂性高的问题~

🎵3.2.5 简单工厂模式再次优化

(1)再次优化背景

简单工厂模式的工厂类采用Java反射机制进行优化后,此时的仍然存在这样一个问题,子类的全局定名(包名+类名)是写死的,但是实际上开发者在写代码时是很难提前预知所有的子类的全局定名(包名+类名)的,因此需要进行二次优化~

(2)再次优化实现思路

通过配置文件方式,统一定义类名对应全局定名(包名+类名),将配置文件存放到资源目录下,程序运行时通过ClassLoader类加载器动态获取到配置文件中定义的子类的全局定名~

(3)再次优化实现步骤

再次优化步骤1:相关优化与第一次优化保持不变~

再次优化步骤2:配置类名对应全局定名(包名+类名)
创建属性配置文件Product.properties

//产品抽象类Product相关子类的全局定名(包名+类名)定义
ProductA = com.justin.java.lang.ProductA
ProductB = com.justin.java.lang.ProductB
ProductC = com.justin.java.lang.ProductC

注意:将Product.properties需要存放在src/main/resources资源目录下,若资源目录不存在则需要手动创建~

再次优化步骤3:修改调用工厂类

public class FactoryTest {@Testpublic void test() throws IOException {ClassLoader classLoader = this.getClass().getClassLoader();Properties prop = new Properties();prop.load(classLoader.getResourceAsStream("Product.properties"));String className = "";try {className = prop.getProperty("ProductA");Product productA = Factory.getInstance(className);productA.show();} catch (NullPointerException e) {System.out.println("没有A这款产品,无法生产~");}try {className = prop.getProperty("ProductB");Product productA = Factory.getInstance(className);productA.show();} catch (NullPointerException e) {System.out.println("没有B这款产品,无法生产~");}try {className = prop.getProperty("ProductC");Product productA = Factory.getInstance(className);productA.show();} catch (NullPointerException e) {System.out.println("没有C这款产品,无法生产~");}}
}

运行结果:

生产了产品A
生产了产品B
生产了产品C

📀3.3 代理模式中的动态代理实现

在这里插入图片描述

🔊3.3.1 什么是代理模式?

代理(Proxy)模式是一种设计模式,通过代理对象来访问目标对象,还可以在不修改目标对象的情况下,对代理对象进行拓展,增强目标对象的功能~

什么?还是不太理解?

更通俗一点的说代理模式,就是想做某件事(买火车票),自己能买(直接去火车站买),却委托别人去买(没空还是代理点买吧),还可以让别人帮自己做其他事(订好酒店)~

在这里插入图片描述

代理模式又分为静态代理、动态代理,往下介绍~

💥3.3.2 什么是静态代理?

(1)静态代理属于代理模式的一种代理方式,需要代理对象目标对象实现相同的接口
(2)静态代理的代理类是由程序员编写源码,编译后即可获取到代理类的class字节码文件,也就是在程序运行前就已经得到实际的代理类class字节码文件了

🎶3.3.2 什么是动态代理?

动态代理

(1)动态代理也属于代理模式的一种代理方式,不过只需要目标对象实现接口,代理对象不需要实现接口~
(2)动态代理的代理类编译后是没有class字节码文件的,而是在运行时利用Java反射机制动态的生成代理类的class字节码文件~

动态代理最常用的是JDK原生动态代理cglib动态代理,往下介绍~

JDK 原生动态代理

JDK 原生动态代理,主要利用了JDK API
java.lang.reflect.Proxyjava.lang.relfect.InnvocationHandler 这两个类来实现~

通过java.lang.reflect.Proxy代理类的newProxyInstance方法,传递3个参数,分别是:
目标对象的加载器 通过MyClass.getClass().getClassLoader方式获取
目标对象的实现接口类型 通过Object.getClass().getInterfaces()方式获取
InnvocationHandler事件处理器 通过new实例化对象并重写invoke方法方式获取

例子:

用户接口类IUserDao

public interface IUserDao {//添加数据public void insert();
}

目标对象类UserDao

/*** @program: DataStructures* @description:* @author: JustinQin* @create: 2021/8/23 23:32* @version: v1.0.0**/
public class UserDao implements IUserDao{@Overridepublic void insert() {System.out.println("添加数据");}
}

动态代理类UserProxy


/*** @program: Jdk1.8Test* @description: 动态代理类* @author: JustinQin* @create: 2021/8/23 23:31* @version: v1.0.0**/
public class UserProxy {private Object target; //目标对象public UserProxy(Object target) {this.target = target;}/*** 利用JDK API获取到代理对象* @return*/public Object getProxyInstance() {//目标对象的加载器ClassLoader loader = target.getClass().getClassLoader();//目标对象的实现接口类型Class<?>[] interfaces = target.getClass().getInterfaces();//InnvocationHandler事件处理器实例对象InvocationHandler h = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("添加数据前:手动开启事务");// 执行目标对象方法Object value = method.invoke(target, args);System.out.println("添加数据后:手动提交事务");return null;}};//传入3个参数,创建代理类的实例对象,并返回return Proxy.newProxyInstance(loader, interfaces,h);}
}

动态代理单元测试类

/*** @program: 动态代理单元测试类* @description:* @author: JustinQin* @create: 2021/8/23 23:42* @version: v1.0.0**/
public class UserProxyTest {@Testpublic void test() {IUserDao target = new UserDao();System.out.println("目标对象信息:" + target.getClass());//获取代理类实例对象IUserDao proxy = (IUserDao) new UserProxy(target).getProxyInstance();System.out.println("代理对象信息:" + proxy.getClass());//执行代理方法proxy.insert();}
}

单元测试执行结果

目标对象信息:class com.justin.java.reflect.UserDao
代理对象信息:class com.sun.proxy.$Proxy2
添加数据前:手动开启事务
添加数据
添加数据后:手动提交事务

cglib动态代理

cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。

Spring AOP结合了cglib动态代理JDK原生动态代理来实现,这里不过多介绍,有兴趣小伙伴可以查阅资料学习下~

🎧3.3.3 动态代理中如何利用Java反射机制?

JDK原生动态代理中,获取代理示例对象过程中,获取目标对象的类加载器,通过target.getClass().getClassLoader(获取到目标对象的类加载器,target.getClass()方式获取目标对象的Class实例对象使用的就是Java反射机制来实现的~

📢3.4 Java JDBC数据库操作实现

🎹3.4.1 利用反射加载JDBC驱动

相信很多小伙伴都知道Java JDBC连接数据库主要分为七大步骤,其中第一步加载JDBC驱动,利用Java反射机制通过传入不同的驱动名称,加载不同数据库的驱动~

Class.forName("com.mysql.jdbc.Driver"); //加载MySQL驱动
Class.forName("oracle.jdbc.driver.OracleDriver"); //加载Oracle驱动

链接:Mysql驱动架包mysql-connector-java-5.1.30.jar 提取码:pc63

链接:Oracle驱动架包ojdbc14-10.2.0.4.0.jar 免提取码

🎸3.4.2 Java JDBC连接示例

创建测试库表及数据

create DATABASE test;
-- DROP TABLE IF EXISTS test.user;
create table test.user(
id int(7) primary key not null auto_increment,
name varchar(255),
sex char(1),
age int(3)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
insert into TEST.user(name,sex,age) values('张一','男',21);
insert into TEST.user(name,sex,age) values('张二','女',22);
insert into TEST.user(name,sex,age) values('张三','男',23);

Java MySQL JDBC连接七大步骤~

    public static void main(String[] args) throws ClassNotFoundException, SQLException {//1.加载JDBC驱动Class.forName("com.mysql.jdbc.Driver");//2.获取数据库的连接(Connection)对象Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/test", //mysql连接url,test表示你要连接的数据库名"root", //数据库用户名"abc@123456"); //密码//3.获取数据库的操作(PrepareStatement)对象PreparedStatement prepareStatement = connection.prepareStatement("select * from TEST.user where id = ?");//4.设置传入参数prepareStatement.setInt(1, 1);//5.上传sql语句到服务器执行(excute),并返回结果集(ResultSet)ResultSet result = prepareStatement.executeQuery();//6.处理返回的ResultSet结果集while (result.next()) {System.out.print(result.getInt("id") + ",");System.out.print(result.getString("name") + ",");System.out.print(result.getString("sex") + ",");System.out.print(result.getInt("age"));System.out.print("\n");}//7.释放相关资源:Connection对象、PrepareStatement对象、ResultSet对象。connection.close();prepareStatement.close();result.close();}

执行结果:

1,张一,,21

Java Oracle JDBC连接七大步骤~

public class JdbcOracleTest {public static void main(String[] args) throws ClassNotFoundException, SQLException {//1.加载JDBC驱动Class.forName("oracle.jdbc.driver.OracleDriver");//2.获取数据库的连接(Connection)对象Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl",	//oracle连接url"root", //数据库用户名"abc@123456"); //密码//3.获取数据库的操作(PrepareStatement)对象PreparedStatement prepareStatement = connection.prepareStatement("select * from TEST.user where id = ?");//4.设置传入参数prepareStatement.setInt(1, 1);//5.上传sql语句到服务器执行(excute),并返回结果集(ResultSet)ResultSet result = prepareStatement.executeQuery();//6.处理返回的ResultSet结果集while (result.next()) {System.out.print(result.getInt("id")+",");System.out.print(result.getString("name")+",");System.out.print(result.getString("sex")+",");System.out.print(result.getInt("age"));System.out.print("\n");}//7.释放相关资源:Connection对象、PrepareStatement对象、ResultSet对象。connection.close();prepareStatement.close();result.close();}
}

PS:上面通过Java JDBC连接数据库并进行操作,这里的连接是单一连接,直接通过DriverManager.getConnection这种Java原生的数据库连接方式建立的连接,现在实际的Java Spring项目当中,都是通过配置mybatis的数据库连接池来实现的,不过原理都是一样的,加载驱动也是利用了Java反射机制指定不同的驱动名称,实现不同数据库驱动的加载~

数据库连接池配置spring-mybatis.xml

	<!-- 基于tomcat jdbc连接池的数据源  --><bean id="dataSource" class="com.justin.datasource.TomcatDataSource" init-method="createPool"><!-- 基于dbcp连接池的数据源<bean id="dataSource" class="com.justin.datasource.DbcpDataSource" destroy-method="close"> --><!-- 基于阿里druid连接池的数据源<bean id="dataSource" class="com.justin.datasource.DruidDataSource" destroy-method="close"> --><property name="driverClassName" value="${app-data-source.driverClassName}" /><property name="url" value="${app-data-source.url}" /><property name="username" value="${app-data-source.username}" /><property name="password" value="${app-data-source.password}" /><!-- 初始化连接大小 --><property name="initialSize" value="${app-data-source.initialSize}" /><!-- 连接池最大数量 --><property name="maxActive" value="${app-data-source.maxActive}" /><!-- 连接池最大空闲 --><property name="maxIdle" value="${app-data-source.maxIdle}" /><!-- 连接池最小空闲 --><property name="minIdle" value="${app-data-source.minIdle}" /><!-- 获取连接最大等待时间 --><property name="maxWait" value="${app-data-source.maxWait}" /></bean>

数据库配置信息jdbc.propertis

#数据库连接驱动
app-data-source.driverClassName=com.mysql.jdbc.Driver
#数据库连接url
app-data-source.url=jdbc:mysql://localhost:3306/test?useSSL=false&characterEncoding=UTF-8
#数据库用户
app-data-source.username=root
#数据库用户密码(加密)
app-data-source.password=abc@123456
#连接池初始化大小
app-data-source.initialSize=10
#连接池最大数量
app-data-source.maxActive=50
#连接池最大空闲
app-data-source.maxIdle=20
#连接池最小空闲
app-data-source.minIdle=5
#获取连接最大等待时间
app-data-source.maxWait=30000

面试总结

一、Java反射机制是什么?
1、Java反射机制(Java Reflection)是Java语言中一种动态(运行时)访问、检测 & 修改它本身的能力,主要作用是动态(运行时)获取类的完整结构信息 & 调用对象的方法~
更简单点的说就是Java程序在运行时(动态)通过创建一个类的反射对象,再对类进行相关操作,比如:

  • 获取该对象的成员变量 & 赋值
  • 调用该对象的方法(含构造方法,有参/无参)
  • 判断该对象所属的类

2、更通俗点的说,我们使用某个类,都会知道这个类,以及要用它来做什么,可以直接通过new实例化创建对象,然后使用这个对象对类进行操作,这个就属于正射~

3、而反射则是一开始并不知道要初始化的是什么类,无法使用new来实例化创建对象,主要是通过JDK提供的反射API来实现,在运行时才知道要操作的是什么类,并且可以获取到类的完整构造以及调用对应的方法,这就是反射~

二、Java反射机制中获取Class的三种方式及区别?
1、获取类的java.lang.Class实例对象,常见的三种方式分别为:

  • 通过MyClass.class获取
  • 通过Class.forName("类的全局定名")获取
  • 通过new MyClass().getClass()获取

2、通过MyClass.class获取,JVM会使用ClassLoader类加载器将类加载到内存中,但并不会做任何类的初始化工作,返回java.lang.Class对象

3、通过Class.forName("类的全局定名")获取,同样,类会被JVM加载到内存中,并且会进行类的静态初始化工作,返回java.lang.Class对象

4、通过new MyClass().getClass()获取,这种方式使用了new进行实例化操作,因此== 静态初始化和非静态初始化工作都会进行 == ,getClass方法属于顶级Object类中的方法,任何子类对象都可以调用,哪个子类调用,就返回那个子类的java.lang.Class对象

5、这3种方式,最终在JVM堆区对应类的java.lang.Class对象都属于同一个,也就是内存地址相同,进行==双等号比较结果为true,原因是JVM类加载过程中使用的是同一个ClassLoader类加载器加载某个类,不论加载多少次,生成到堆区的java.lang.Class对象始终只有一个,除非自定义类加载器,破坏JVM的双亲委派机制,使得同一个类被不同类加载器加载,JVM才会把它当做两个不同的java.lang.Class对象

三、Java反射机制的应用场景有哪些?

  • 工厂模式中的简单工厂模式优化
  • 代理模式中的动态代理方式实现
  • Java JDBC数据库操作

原创不易,觉得有用的小伙伴来个一键三连(点赞+收藏+评论 )+关注支持一下,非常感谢~
在这里插入图片描述


http://chatgpt.dhexx.cn/article/E5w9eTjN.shtml

相关文章

关于线程和进程的区别

进程 &#xff1a; 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间&#xff0c;一个进程可以有多个线程&#xff0c;比如在Windows系统中&#xff0c;一个运行的xx.exe就是一个进程 线程&#xff1a; 进程中的一个执行任务&#xff08;控制单元&#xff09;&…

从内核角度看Linux 线程和进程的区别

多数人都会讲说线程和进程在内核中是相同的&#xff0c;没有严格地做区分。这样讲是没错了&#xff0c;但对于应用开发者来说&#xff0c;这样讲是有点笼统。本文将从内核角度&#xff0c;分析线程和进程之间的区别&#xff0c;希望能对这一块感兴趣的人提供借鉴意义。 1 数据…

java线程与进程的区别是什么?

关于进程与线程的文章早已是非常多了&#xff0c;本文是对我个人过往学习&#xff0c;理解及应用进程与线程的一个总结。此文内容涉及进程线程的区别&#xff0c;什么是进程&#xff0c;什么是线程?希望对大家有所帮助。 java线程与进程的区别是什么? 进程&#xff1a;是并…

Java面试--线程和进程的区别

面试题&#xff1a;线程和进程的区别是什么&#xff08;招银网络科技、阿里巴巴面试题&#xff09; 一、线程和进程的区别是什么&#xff1f; 1、进程是一段正在执行的程序&#xff0c;是资源分配的基本单元&#xff0c;而线程是CPU调度的基本单元。 2、进程间相互独立进程&a…

对线程与进程的区别以及对多线程并发的理解

一、线程与进程的区别 先简单说说线程与进程的概念&#xff1a; &#xff08;1&#xff09;进程是指一个内存中运行的应用程序&#xff0c;比如在Windows系统中&#xff0c;一个运行的exe就是一个进程。 &#xff08;2&#xff09;线程是指进程中的一个执行流程。 区别&…

线程与进程的区别和处理器的调度

&#xff08;1&#xff09;进程的概念&#xff08;Dijkstra&#xff09; 进程是可并发执行的程序在某个数据集合上的一次计算活动&#xff0c;也是操作系统进行资源分配和调度的基本单位。 &#xff08;2&#xff09;进程与程序的联系与区别 ① 程序是指令的有序集合&#x…

多线程(一)线程和进程的区别

目录 &#x1f353; 一&#xff0c;进程线和程的概念&#x1f34c;二&#xff0c;为什么要有线程&#xff08;1&#xff09;首先并发编程成为需求&#xff08;2&#xff09;虽然进程也可以并编程&#xff0c;但是线程更轻量&#xff08;3&#xff09;那么是不是线程创建越多越好…

线程和进程的区别是什么?(2021最新)

首先用一句话概括线程和进程的区别是&#xff1a;进程和线程都是一个时间段的描述&#xff0c;是CPU工作时间段的描述。是运行中的程序指令的一种描述&#xff0c;这需要与程序中的代码区别开来。 做个简单的比喻&#xff1a;进程火车&#xff0c;线程车厢 线程在进程下行进&…

线程和进程的区别和联系

操作系统任何处理线程 1.先描述一个进程(明确出一个进程上面的一些相关的属性) 操作系统里面主要是通过c/c来实现的,此处的描述其实就是用的c语言中的"结构体"(操作系统中描述进程的这个结构体称为"PCB"(process control block)进程控制块。 2.再组…

进程和线程有什么区别?

从用户的角度来看&#xff0c;进程是正在运行的程序实例&#xff0c;而线程是进程中真正执行任务的基本单位。也就是说一个运行的程序至少包含一个进程&#xff0c;一个进程至少包含一个线程&#xff0c;线程不能独立于进程而存在。 进程 进程&#xff08;Process&#xff09…

线程和进程有什么区别(简单介绍)

线程和进程有什么区别&#xff08;简单介绍&#xff09; 简单介绍 一、线程的基本概念 线程是进程中执行运算的最小单位&#xff0c;是进程中的一个实体&#xff0c;是被系统独立调度和分派的基本单位&#xff0c;线程自己不拥有系统资源&#xff0c;只拥有一点在运行中必不可…

线程和进程的区别是什么?

本文转载自知乎 文章目录 1.进程和线程的定义2.二者的区别&#xff08;解释1&#xff09;3.两者的区别&#xff08;解释2&#xff09; 1.进程和线程的定义 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际运作单位。一条线程指的是…

线程和进程的区别

1、线程和进程的区别 进程&#xff1a;是指一个内存中运行的应用程序(已经在内存中运行的程序). 一个进程都有一个独立的内存空间&#xff0c;一个电脑(手机)可以同时运行多个进程&#xff1b;进程也是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff1b; 线…

线程与进程,你真得理解了吗

线程与进程&#xff0c;你真得理解了吗 1 进程与线程的关系和区别2 并行与并发3 线程共享了进程哪些资源 相信大家面试时一定没少被一个问题刁难&#xff0c;那就是进程和线程的区别是什么&#xff1f;这个问题延申开来并不像表面那么简单&#xff0c;今天就来深入一探。 开始…

axure自定义文本框样式

axure中的文本框是我们经常使用的元件&#xff0c;但它本身对样式的设置很有限&#xff0c;不能设置边框样式、阴影等&#xff0c;不能满足我们制作高保真原型的需求&#xff0c;本文给大家介绍一下结合矩形元件自定义文本框样式。&#xff08;PS&#xff1a;此处的“高保真”指…

html文本框左移动怎么设css,html---文本框样式;

一、一个单行文本框的例子 您的姓名&#xff1a; 您的E_mail&#xff1a; 输入口令&#xff1a; 二、检验用户输入的信息 三、制作一个留言簿 留 言 簿 姓名&#xff1a; E_mail: 留 言 html文本框参考样式 输入框景背景透明&#xff1a; 鼠标划过输入框&#xff0c;输入框…

html 文本域和文本框,html的文本框和文本域样式

如果前边几章学习的比较扎实的话,本节教程就相当容易了。下边先说一下文本框,文本框和文本域都是可以用css进行美化的。比如改变边框精细,颜色,添加背景色、背景图像等。请看下边的实例: .text1 { border:1px solid #f60; color:#03C;} .text2 { border:2px solid #390; w…

[微信小程序专题] 配置文本框样式、排版及点击页面跳转

欢迎点击「算法与编程之美」↑关注我们! 本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章。 问题 如何更改小程序页面中的文本框颜色和边框样式? 如何实现多个文本框的排版? 如何实现点击一个文本框即跳转页面? 我们在使用一个小程序…

php输入文本框样式,【js】:检测用户输入、文本框默认样式设置、设计表格样式实现全选反选...

Topic 1 : 检测用户的输入 &#xff1a; 题目要求&#xff1a; 编写一个用户注册页面 检测用户名是否是6位以下&#xff0c;密码是否是8位以上&#xff0c;如果不满足要求高亮显示文本框 &#xff1b; 代码如下 &#xff1a;用户注册页面 .bg {background-color: red; } //编写…

html语言文本框怎么做,HTML文本框参考样式

在网页设计中,常常要使用html文本框来收集一些用户信息或是制作登录页,虽然只是简单的输入框,但是如果加入一些美化设计会使你的页面看起来更加有吸引力,下面就给大家提供了一些html文本框的参考样式和常见的html操作技巧,希望对你的网页制作有帮助。首先我们先看看一个最…