大家好,我是IT修真院深圳分院第6期的学员,一枚正直纯洁善良的java程序员

今天给大家分享一下,修真院官网Java任务一,深度思考中的知识点——反射的基本使用

1.背景介绍

反射的概述

反射是框架设计的灵魂

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

要想获得类的各种信息,必须先要获取到该类的字节码文件对象。该对象也就是java.lang.Class类,Class类用于表示java程序编译后得到的.class文件。

Class类的理解

Java程序在运行时,系统会对所有的对象进行所谓的运行时类型标识(RTTI,Run-Time Type Identification),其作用是在运行时识别一个对象的类型和类的信息。

传统的”RRTI”,它假定我们在编译期已知道了所有类型(在没有反射机制时,一般都是编译期已确定其类型,如new对象时该类必须已定义好);另外一种是反射机制,它允许我们在运行时发现和使用类型的信息。

在Java中用来表示运行时类型信息的对应类就是Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

JVM为每种类型管理一个独一无二的Class对象。也就是说,无论创建多少个实例对象,在内存中每个类有且只有一个相对应的Class对象,并且基本的java类型和关键字void也都对应一个Class对象。运行程序时,JVM首先检查所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

2.知识剖析

在这里先看一下java为我们提供了哪些反射机制中的类:

java.lang.Class;

java.lang.reflect.Constructor;

java.lang.reflect.Field; 

java.lang.reflect.Method;

java.lang.reflect.Modifier;

反射的基本运用:Class对象获取方式

之前说了,Class对象是jvm用来保存对应实例对象的相关信息的,除此之外,我们完全可以把Class对象看成一般的实例对象。得到一个实例对象对应的Class对象有以下三种方式:

1.通过实例变量的getClass()方法

[java] view plain copy
Test3 test3 =new Test3();  
  
Class c3 =test3.getClass();  

根据已经实例化的对象,利用getClass方法

2.通过类Class的静态方法forName()

[java] view plain copy
Classc2 =Class.forName("com.getClassObject.Test2");  

传入的参数是对应类的全限定名

3.直接给出对象类文件的.class

[java] view plain copy
Class c1 =Test1.class;  

反射的基本运用:判断是否为某个类的实例

一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例,它是一个Native方法

[java] view plain copy
Class cls =Class.forName("com.isInstance.Demo"); //创建了一个Demo类的Class对象  
  
boolean b1 =cls.isInstance(new Integer(37));  

反射的基本运用:创建实例

通过反射来生成对象主要有两种方式:

(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

//获取class对象

[java] view plain copy
Class c1 =Class.forName("com.createInstance.Instance1");  

//第一种方式:根据class对象,创建对应的实例

//调用无参数的构造函数,直接调用Class类中的newInstance

[java] view plain copy
Instance1 instance1 = (Instance1)c1.newInstance();  

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

//获取Instance1所对应的Class对象

[java] view plain copy
Class c2 =Instance1.class;  

//获取Instance1类带一个int参数的构造器

//若想调用有参构造函数,则需要调用Constructor类中newInstance()方法

[java] view plain copy
Constructor constructor =c2.getConstructor(int.class);  

//根据构造器创建实例

[html] view plain copy
Object obj =constructor.newInstance(3213);  

反射的基本运用:获取方法

获取某个Class对象的方法集合,主要有以下几个方法:

(1) getDeclaredMethods() 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

(2) getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

(3) getMethod("add", int.class, int.class) 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

[java] view plain copy
Class c =Class.forName("com.getMethod.methodClass");  

//获取methodClass类的所有公有方法(可获取到父类的方法)

[java] view plain copy
Method[]methods =c.getMethods();  

//获取methodClass类的所有的方法

[java] view plain copy
Method[]declaredMethods =c.getDeclaredMethods();  
//获取指定方法,methodClass类的add方法

[java] view plain copy
Method publicMethod =c.getMethod("add", int.class, int.class);  
//获取指定私有方法,div方法

[java] view plain copy
Method privateMethod =c.getDeclaredMethod("div", int.class, int.class);  

反射的基本运用:获取构造器信息

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例。

[java] view plain copy
public T newInstance(Object ... initargs)  

具体方法同获取对应类方法

反射的基本运用:获取类的成员变量(字段)信息

主要是这几个方法:

getFiled: 访问公有的成员变量

getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量。

getFileds和getDeclaredFields用法同上(参照Method)

反射的基本运用:反射用于工厂模式

先看看传统的工厂模式

主类如下

[java] view plain copy
interface Fruit {  
  
void eat();  
  
}  

[java] view plain copy
class Apple implements Fruit {  
    public void eat(){  
        System.out.println("Eat Apple");  
    }  
}  

[html] view plain copy
class Orange implements Fruit {  
    public void eat(){  
        System.out.println("Eat Orange");  
    }  
}  

工厂类

[java] view plain copy
public class Factory {  
  
public static Fruit getInstance(String fruitName){  
  
Fruit f=null;  
  
        if("Apple".equals(fruitName)){  
  
        f=new Apple();  
  
        }  
  
if("Orange".equals(fruitName)){  
  
f=new Orange();  
  
        }  
  
return f;  
  
    }  
  
}  
可以看到如果我要新增一个实现类,就要在工厂类中改代码,耦合度较高。

利用反射实现工厂

[java] view plain copy
public class ReflexFactory {  
  
public static Fruit2 getInstances(String className) {  
  
Fruit2 f2 =null;  
  
        try {  
  
            //利用反射  
  
            f2 = (Fruit2)Class.forName(className).newInstance();  
  
        }catch (Exception e) {  
  
e.printStackTrace();  
  
        }  
  
return f2;  
  
    }  
  
}  
当如果要新增实现类的话,只要将传入的参数改变就好,无需更改工厂内的代码。

3.常见问题

4.解决方案

5.编码实战

6.扩展思考

理解泛化的Class对象引用

由于Class的引用总是指向某个类的Class对象,利用Class对象可以创建实例,这也就说明Class对象的引用指向的是对象确切的类型。在Java SE5引入泛型后,使我们可以利用泛型来表示Class对象更具体的类型,即使在运行期间会被擦除,但编译期足以确保我们使用正确的对象类型。

7.参考文献

链接:https://blog.csdn.net/javazej...

作者:zejian_

链接:https://blog.csdn.net/sinat_3...

作者:敬业的小码哥

8.更多讨论

反射机制的作用?

1,反编译:.class-->.java

2,通过反射机制访问java对象的属性,方法,构造方法等;

暴力反射?

获取类的私有成员。通过setAccessible(true)方法,设置成可访问。

类加载的过程?

加载:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象。

链接:验证字节码的安全性和完整性,准备阶段正式为静态域分配存储空间,注意此时只是分配静态成员变量的存储空间,不包含实例成员变量,如果必要的话,解析这个类创建的对其他类的所有引用。

初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量。

反射的应用场景?

Java的反射特性一般结合注解和配置文件(如:XML)来使用,这也是大部分框架(Spring等)支持两种配置方式的原因。还有著名的junit测试框架也是利用反射方法名和参数名来进行测试的。

今天的分享就到这里啦,欢迎大家点赞、转发、留言、拍砖~


用户bPbdDlb
422 声望36 粉丝