反射
1 反射基本理解
1.2 反射意义:
1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
Class对象的由来是将.class文件读入内存,并为之创建一个Class对象,所以一个类在内存中只存在一个 Class 对象。
反射的用途
1、反编译:.class–>.java
2、通过反射机制访问java对象的属性,方法,构造方法等
3、反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
例如:Class.forName(“com.mysql.jdbc.Driver”); // 动态加载mysql驱动,就是使用反射机制
1.2 反射机制常用的类:
Java.lang.Class; |
---|
Java.lang.reflect.Constructor; |
Java.lang.reflect.Field; |
Java.lang.reflect.Method; |
Java.lang.reflect.Modifier; |
2 反射的常用使用
2.1 Class三种获得方法
1 Object–>getClass
2 任何数据类型(包括基本的数据类型)都有一个“静态”的class属性,getClass();
3 通过class类的静态方法:forName(String className)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
//第一种方式获取Class对象
User user = new User();//new一个 User 对象,一个 Class 对象
Class c1 = user.getClass();//获取Class对象
System.out.println(c1.getName()); //com.hncj.User
//第二种方式获取Class对象
Class c2 = User.class;
System.out.println(c1.getName());//com.hncj.User
//第三种方式获取Class对象
Class c3 = Class.forName("com.hncj.User");
System.out.println(c3.getName());//com.hncj.User
System.out.println(c1==c2&&c1==c3); //true
}
}
在运行期间,一个类,只有一个Class对象产生,所以打印结果都是true
第一种方式,对象都有了还要反射干什么?
第二种方式,需要导入类包,依赖太强,不导包就抛编译错误。
第三种(常用)方式,一个字符串可以传入也可以写在配置文件中等多种方法。需要抛出异常
2.2 判断是否为某个类的实例
使用instanceof 关键字来判断是否为某个类的实例
public native boolean isInstance(Object obj);
2.3 创建实例
通过反射来生成对象主要有两种方法
- 使用Class对象的newInstance()方法来创建Class对象对应类的实例
Class<?> c = String.class;
Object str = c.newInstance();
- 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例
//获取Class对象
Class<?> str = String.class;
//通过Class对象获取指定的Constructor构造器对象
Constructor constructor=c.getConstructor(String.class);
//根据构造器创建实例:
Object obj = constructor.newInstance(“hello reflection”);
3 反射深入使用(获取类的运行时结构)
3.1 获取构造方法
1 批量获取
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2 单个获取
public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
调用构造方法:
Constructor–>newInstance(Object… initargs)
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
User user2 = (User) constructor.newInstance("李中祥", 23);
System.out.println(user2);
3.2 获取类名
Class c1 = Class.forName("com.hncj.User");
//获取类名
System.out.println(c1.getName()); //获取包名+类名 com.hncj.User
System.out.println(c1.getSimpleName()); //获取类名 User
getName() 获取包名+类名
getSimpleName() 获取简单的类名称
3.3 获取成员变量
//批量获取类属性
Field[] fields = c1.getFields(); //获取public属性
for(Field field : fields){
System.out.println(field);
}
System.out.println("==========");
fields = c1.getDeclaredFields(); //找到全部属性(private,public)
for(Field field : fields){
System.out.println(field);
}
//单个 获取指定属性的值
Field b = c1.getField("b"); //获取指定字段(private,public)
System.out.println(b);
System.out.println("++++++++++++++++++");
Field b1 = c1.getDeclaredField("b"); //获取指定字段(private,public)
System.out.println(b1);
1 批量获取属性
Field[] getFields(); 获取所有public属性
Field[] getDeclaredFields(); 获取所有属性(private.public)
2 单个获取属性
Field getField(String fieldName)
Field getDeclaredField(String field) 获取指定字段
3.4 获取成员方法
Method[] methods = c1.getMethods(); //获得本类及其父类所有全部public方法
for(Method method : methods){
System.out.println("方法:" + method);
}
System.out.println("++++++++++++++++++");
methods = c1.getDeclaredMethods(); //本类所有方法(包含public,private)
for(Method method : methods){
System.out.println("方法:" + method);
}
1.批量的:
Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
2.获取单个的:
Method getMethod(String name,Class<?>… parameterTypes):
参数:
name : 方法名;
Class … : 形参的Class类型对象
Method getDeclaredMethod(String name,Class<?>… parameterTypes)
**调用方法**
Method --> public Object invoke(Object obj,Object... args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;
4 主动引用和被动引用
类的主动引用(一定会发生类的初始化)
1.当虚拟机启动,先初始化main方法所在的类
2.new一个类的对象
3.调用类的静态成员(除了final常量)和静态方法
4.使用java.lang.reflect包的方法对类进行反射调用
5.当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动调用(不会发生类的初始化)
1.当访问一个静态域时,只有真正申明这个域的类才会被初始化
(通过子类引用父类的静态变量,不会导致子类初始化)
2.通过数组定义类引用,不会触发此类的初始化
3.引用常量不会触发此类的初始化(常量再连接阶段就存入调用类的常量池了)