当前位置:

首页 > 编程开发 > Java反射机制原理和Class获取方式是什么

Java反射机制原理和Class获取方式是什么

一、Java反射机制是什么?1.1反射原理(1)Java反射机制(JavaReflection)是Java语言中一种动态(运行时)访问、检测&修改它本身的能力,主要作用是动态(运行时)获取类的完整结构信息&调用对象的方法~更简单点的说就是Java程序在运行时(动态)通过创建一个类的反射对象,再对类进行相关操作,比如:获取该对象的成员变量&赋值调用该对象的方法(含构造方法,有参/无参)判断该对象所属的类PS:不过说实话,直接看比较官方的定义还是有点难理解,再来更加通俗点的说吧~(2)一般情况下,我们使用某个

本文目录

    Java反射机制原理和Class获取方式是什么

    一、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的三种方式及区别?

    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;
        }
    
        @Override
        public 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 {
    
        @Test
        public void test1() {
            System.out.println("一、MyClass.class方式=========");
            Class class1 = MyClass.class;
        }
    
        @Test
        public void test2() throws ClassNotFoundException {
            System.out.println("二、Class.forName方式=========");
            Class class2 = Class.forName("com.justin.java.lang.MyClass");
        }
    
    
        @Test
        public void test3() {
            System.out.println("三、new MyClass().getClass方式=========");
            Class class3 = new MyClass().getClass();
        }
    
        @Test
        public 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");
        }
    
        @Test
        public void test13() {
            System.out.println("一、MyClass.class方式=========");
            Class class1 = MyClass.class;
            System.out.println("三、new MyClass().getClass方式=========");
            Class class3 = new MyClass().getClass();
        }
    
        @Test
        public 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();
        }
    
        @Test
        public 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.class和Class.forName内存地址比较是否相同:trueMyClass.class和new MyClass().getClass内存地址比较是否相同:trueClass.forName和new MyClass().getClass内存地址比较是否相同:true

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

    • MyClass.class不会做任何类的初始化工作

    • Class.forName会进行类的静态初始化工作

    • new MyClass().getClass静态初始化和非静态初始化工作都会进行

    • 使用这三种方式任意一种最终在JVM加载到内存中都会是内存地址相同的

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

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

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

    Java反射机制原理和Class获取方式是什么

    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 {
        @Override
        public void show() {
            System.out.println("生产了产品A");
        }}public class ProductB implements Product {
        @Override
        public void show() {
            System.out.println("生产了产品B");
        }}public class ProductC implements Product {
        @Override
        public 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.ProductAProductB = com.justin.java.lang.ProductBProductC = com.justin.java.lang.ProductC

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

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

    public class FactoryTest {
        @Test
        public 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 代理模式中的动态代理实现

    Java反射机制原理和Class获取方式是什么

    3.3.1 什么是代理模式?

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

    什么?还是不太理解?

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

    Java反射机制原理和Class获取方式是什么

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

    3.3.2 什么是静态代理?

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

    3.3.2 什么是动态代理?

    动态代理

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

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

    JDK 原生动态代理

    JDK 原生动态代理,主要利用了JDK API的
    java.lang.reflect.Proxy和java.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{
    
        @Override
        public 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() {
                @Override
                public 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 {
        @Test
        public 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驱动

    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

    	
        
    		
    		
    
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    		
    	

    数据库配置信息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数据库操作

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。