Java 反射 简单介绍 易懂

反射机制:在Java中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

反射使Java这种静态编译型的语言具有了动态性。

反射具有看透类的能力,类的信息在反射面前都是透明的(包括private的属性和方法都是可以调用)。

学习反射的意义:

反射使我们在编译的时候不知道类型,而是延迟到运行时获得对象的属性调用对象的方法,使得java具有动态性。

Hibernate、Sping、MyBatis都是基于反射来实现的,可以说没有反射就没有这些框架。

框架:反射、泛型、注解

我们把自然界中的事物(对象)抽象出共同的特征,封装成一个类,如Person类,Student类等。

我们有没有想过,这些类有没有共同特征,能不能抽象成别的东西?

我们来看一下

不看不知道,一看,可不就是吗,基本上普通的类都是这么构成的,这样,我们把这些类,类的属性,构造方法,普通方法分别抽象出来又组成了新的类。

也就是这样

类比学习一下:面向对象抽象过程

众多的人             ---->     Person类

众多学生             ----->   Stundent类

众多的类             ----->   Class类:任何一个类里面都包含这些东西:Field[]、Constructor[]、Method[]

众多的属性         ------>   Field类

众多构造方法      ------>   Constructor类

众多的普通方法   ------>   Method类

而这些类就是Java提供的反射机制

通过 Class 可以获得类的所有属性Field[]、方法Method[]、构造方法Constructor[]信息。
通过 Field 可以获得属性的名字、类型、修饰符
通过 Method 可以获得方法的名字、参数、返回值。

Java反射机制主要提供一下功能:

1、在运行时判断任意一个对象所属的类。

2、在运行时构造任意一个类的对象。

3、在运行时判断任意一个类所具有的成员变量和方法。

4、在运行时调用任意一个对象的方法。

获取class对象的三种方式(重点):

 1.Class.forName("类路径")
 2.类名.class
 3.对象实例.getClass();
public void test() throws ClassNotFoundException {
        // 1.Class.forName("类路径")
        Class clazz1 = Class.forName("com.situ.day18.Student");
        // 2.类名.class
        Class clazz2 = Student.class;
        // 3.对象.getClass();
        Student student = new Student();
        Class clazz3 = student.getClass();
        
        System.out.println(clazz1 == clazz2);// true
        System.out.println(clazz1 == clazz3);// true
        System.out.println(clazz2 == clazz3);// true
        // 都是同一个
    }

反射中的API:

 这些API看起来多,其实就是一两种的变换。理解了其实很简单。

1.Constructor、Method、Field分别是指的对构造方法,普通方法,属性的获取。

2.每种里边又有get和getDeclsred,前者指只能获取public修饰的,后者则忽略修饰符可以获取全部。

3.这样四条就又分为了两种,每种两条,其实就是有没有s的区别,即获取特定一个还是获取全部。

下面我们通过几个例子来演示一下他们:

1.获取Student类中所有的public构造方法并打印

public void testConstructor() {
        // 类名.class 获取反射对象
        Class clazz = Student.class;
        // 获取所有public构造方法
        Constructor[] constructors = clazz.getConstructors();
        // 遍历输出这些构造方法
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }

2.获取Student类中所有的构造方法并打印,分别打印名字和访问修饰符

public void testConstructor2() {
        Class clazz = Student.class;
        // 获取所有构造方法
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
            // 打印名字
            System.out.println(constructor.getName());
            // 打印修饰符,这里的输出1234代表不同的修饰符
            System.out.println(constructor.getModifiers());
        }
    }

3.获取指定参数列表的构造方法,利用反射创建对象并打印

public void testConstructor3() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz = Student.class;
        // 获得指定参数列表的构造方法
        Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class);
        // 利用获得的构造方法创建对象
        // 虽然反射可以访问private但是访问之前要加这个声明否则会报下面的错
        // java.lang.IllegalAccessException
        constructor.setAccessible(true);
        Student student = (Student) constructor.newInstance(1, "zhangsan");
        System.out.println(student);// student类中重写了toString方法
    }

4.反射调用普通方法

public void testMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Student.class;
        // 当有无参构造方法时可以直接用 class对象.newInstance() 方法创建,而不需获得构造方法
        Student student = (Student) clazz.newInstance();
        // 反射调用setName() 方法
        Method method = clazz.getMethod("setName", String.class);
        method.invoke(student,"lisi");
        System.out.println(student);
    }

剩下的就不一 一演示了,通过这些例子能够学会反射的基本语法,但是看不出反射在java中的作用,下面我们举一个例子来演示反射的作用。


 我们构造以上几个源码。

1.IDB接口,里面只有一个

public abstract void getConnection();

方法

2.MySql、Orical、SqlServer三个类,都实现IDB接口

3.DBDemo是测试类,来测试。

接下来,我们如果想在DBDemo中创建一个对象调用getconnection()方法,可以这么写:

// 声明成接口类型new实现类对象
 IDB db = new Oracle();
// 调用getConnection()方法
db.getConnection();

这样就实现了调用Oracle类中实现的方法获得Oracle的连接,如果我们要获得MySql的连接也就是调用MySql实现的方法,就要new MySql对象。

但是这样我们每次改连接都要改这个代码,有没有一种方法可以让我们在这个代码中不出现具体的子类代码呢?

常用的方法就是反射+配置文件

可以看到我们在图中有一个db.properties文件,这就是配置文件,里面的内容是:

className=com.situ.day18.reflect.Oracle

就这么一句话,也就是我们获得类的class对象的路径,这样我们在DBDemo中就可以这样写:

public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        
        // 反射+配置文件  property  properties
        
        // 字节输入流获取文件
        FileInputStream fileInputStream = new FileInputStream("JavaSE/src/com/situ/day18/reflect/db.properties");
        // new Properties对象
        Properties properties = new Properties();
        // 将流获取的加载到Properties 对象中
        properties.load(fileInputStream);
        // 获取配置文件中的className
        String className = properties.getProperty("className");
        // 创建获取到目标类的class对象
        Class clazz = Class.forName(className);
        // 利用反射构造对象并调用getConnection()方法
        // 两种方式1.无参构造方法
        //Constructor constructor = clazz.getConstructor();
        //IDB db = (IDB) constructor.newInstance();
        
        // 2.如果调用的是无参构造方法,可以不需要获得Constructor,直接clazz.newInstance();
        IDB db = (IDB)clazz.newInstance();
        db.getConnection();
    }

这样我们就会发现,代码中没有出现具体的子类代码,我们想要获取MySql或者SqlService的连接只需要修改配置文件即可。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值