JAVA反射机制及其原理实现

 

9.1 概念

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;public、protected、private。

OO(面向对象),private私有的,不能访问。这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。**

反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。

物理:有个反射的概念,通过镜子,可以知道物体的存在。看到一个镜像或名字等,知道物体在哪里。

(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述) 如图是类的正常加载过程:反射的原理在与class对象。 熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

Student.java--->Student.class 经过编译成了一个字节码文件。

 

img

9.2 作用

在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。

反射是框架设计的灵魂

(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

9.2.1 反编译:.class-->.java

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

User user=new User();--》形成的java文件-->XXX.class

将来赋值的时候,不是User类,是不是就报错了啊。存在紧耦合的状态,我们做OO的目的就是高内聚、松耦合,说白了,就是模块内部实现特定功能,模块与模块之间,关联度不大。

这种方式,是编译时

我们以后写程序,更多的应该是运行时给值。

 

9.3 反射机制的相关类

与Java反射相关的类如下:

类名 用途
Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(成员变量也称为类的属性)
Method类 代表类的方法
Constructor类 代表类的构造方法

9.3.1 查看Class类在java中的api

img

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型) Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧 img

9.3.2 根据一个字符串得到一个类

类名 方法 含义
String getClass 表示此对象运行时类的 Class 对象
Class forName 具有指定名的类的 Class 对象
包装类 属性Type

参考代码:

         String str="今天是反射课程";
         Class clz=str.getClass();//得到当前正在运行的类;
         System.out.println(clz);
         Class clz2=Integer.TYPE; //包装类型,不同;包装类.Type
         System.out.println(clz2);
         System.out.println(Boolean.TYPE);
         System.out.println(Double.TYPE);
         System.out.println(Character.TYPE);
 ​

9.3.3 获取Class对象的 三种方式

  • Object:getClass

  • 任何数据类型(包含基本数据类型)都有一个"静态"的class属性,这时候可以通过类名.属性访问.

  • 通过Class类的静态方法:forName(string className路径)

参考代码

//1.使用第一种方式来获取User的Class对象;
         User user=new User(); //弄了一个User对象,在内存里面;
         Class clz1=user.getClass(); //对象.getClass
         System.out.println(clz1); //clz1:是什么类呢?com.aaa.chapter07.User;路径+类名;
         //2.使用第二种方式;
         Class clz2=User.class;      //类名.class 这个静态属性.
         System.out.println(clz2);
         //这时候,我们是不是考虑一下,之前讲的那个原理图。证明原理图,里面,正在运行的Class是一个。
         System.out.println(clz1==clz2);
         //3.Class.forName(类路径方式)
         try {
             Class clz3=Class.forName("com.aaa.chapter07.User");
             System.out.println(clz3);
             System.out.println(clz2==clz3);
         } catch (ClassNotFoundException e) {
             e.printStackTrace();
         }

提问?最常用哪种?一般用第三个。松耦合方式。

9.3.4 通过反射来获取构造方法

调用方法:

1.获取构造方法:

1).批量的方法: public Constructor[] getConstructors():所有"公有的"构造方法 public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

2).获取单个的方法,并调用: public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法: public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

例如:

调用构造方法: Constructor-->newInstance(Object... initargs)

package com.aaa.chapter07;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;

/**
 * Created by 张晨光 on 2020/3/10 10:24
 */
public class Constructors {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clz=Class.forName("com.aaa.chapter07.User");
        //2.获取所有公共字段;
//        Field[] fields = clz.getFields();
//        for(Field f:fields){
//            System.out.println(f);
//        }
        //2.获取所有共有 私有字段;
//        Field[] fields = clz.getDeclaredFields();
//        for(Field f:fields){
//            System.out.println(f);
//        }
        Field field=clz.getField("country");
        System.out.println(field);
        Object obj=clz.getConstructor().newInstance();
        field.set(obj,"中国");
        User u=(User)obj;
        System.out.println(u.getCountry());
    }
}

2、newInstance是 Constructor类的方法(管理构造函数的类) api的解释为: newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用

 

package com.aaa.chapter07;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * Created by 张晨光 on 2020/3/10 22:29
 */
public class InstanceDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clz=Class.forName("com.aaa.chapter07.User");
        //1.调用第一个默认构造方法,没有参数,创建实例之后,再次使用setter赋值。
//        Constructor constructor = clz.getConstructor();//Alt+Enter,
//        Object obj=constructor.newInstance();
//        User user=(User)obj;
//        user.setName("张老师");
//        System.out.println(obj);
        //2.调用第二个有3个参数的构造方法,公共的构造方法,注意里面参数的使用方式.
//        Constructor constructor2 = clz.getConstructor(String.class,char.class,Integer.class);
//        Object obj2=constructor2.newInstance("张晨光",'男',18);//类似于之前的构造方法,填充值;
//        User user2=(User)obj2;
//        System.out.println(user2);
        //3.调用第三个私有构造方法,这个构造方法,我们说外部无法访问.
        Constructor declaredConstructor = clz.getDeclaredConstructor(String.class);
        //设置私有构造方法,可以访问,强制(暴力)访问.
        declaredConstructor.setAccessible(true);
        Object obj=declaredConstructor.newInstance("登徒子");

        User user3=(User)obj;
        System.out.println(user3);
    }
}

9.3.5 获取成员变量并调用

获取成员变量并调用:

1.批量的

1).Field[] getFields():获取所有的"公有字段"

2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;

2.获取单个的:

1).public Field getField(String fieldName):获取某个"公有的"字段;

2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)

设置字段的值:

Field --> public void set(Object obj,Object value):

参数说明:

1.obj:要设置的字段所在的对象;

2.value:要为字段设置的值;

你可能感兴趣的:(Java)