JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;public、protected、private。
OO(面向对象),private私有的,不能访问。这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。**
反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
物理:有个反射的概念,通过镜子,可以知道物体的存在。看到一个镜像或名字等,知道物体在哪里。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述) 如图是类的正常加载过程:反射的原理在与class对象。 熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
Student.java--->Student.class 经过编译成了一个字节码文件。
在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。
反射是框架设计的灵魂
(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))
9.2.1 反编译:.class-->.java
9.2.2通过反射机制访问java对象的属性,方法,构造方法等;
User user=new User();--》形成的java文件-->XXX.class
将来赋值的时候,不是User类,是不是就报错了啊。存在紧耦合的状态,我们做OO的目的就是高内聚、松耦合,说白了,就是模块内部实现特定功能,模块与模块之间,关联度不大。
这种方式,是编译时
我们以后写程序,更多的应该是运行时给值。
与Java反射相关的类如下:
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型) Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧
类名 | 方法 | 含义 |
---|---|---|
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);
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();
}
提问?最常用哪种?一般用第三个。松耦合方式。
调用方法:
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);
}
}
获取成员变量并调用:
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:要为字段设置的值;