9.1 概念

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

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

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

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

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

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

 

JAVA反射机制及其原理实现_反射机制

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

JAVA反射机制及其原理实现_java反射实现原理_02

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

没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧 JAVA反射机制及其原理实现_java字节码_03

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:要为字段设置的值;