9.1 概念JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;public、protected、private。
OO(面向对象),private私有的,不能访问。这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。**
反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
物理:有个反射的概念,通过镜子,可以知道物体的存在。看到一个镜像或名字等,知道物体在哪里。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述) 如图是类的正常加载过程:反射的原理在与class对象。 熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
Student.java--->Student.class 经过编译成了一个字节码文件。
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
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型) Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧
9.3.2 根据一个字符串得到一个类类名方法含义StringgetClass表示此对象运行时类的 Class 对象
ClassforName具有指定名的类的 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(); //对象.getClassSystem.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:要为字段设置的值;