文章目录
反射
前言
反射是框架设计的灵魂
框架:半成品软件,可以在框架的基础上进行软件开发,简化编码
反射:将类的各个组成部分(类、属性、方法)封装成对象。
概念
1、Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性、方法。
2、对于任意一个对象,都能够调用它的任意一个方法和属性。
3、这种动态获取类的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
总结:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展。
类的对象:基于某个类new出来的对象,也称为实例对象。
类对象:类的加载产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)
要想剖析一个类,首先要拿到这个类的字节码文件对象(.class文件)。
反射的优点和缺点
优点 :
- 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。
- 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力。 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
缺点 :
- 使用反射会有效率问题。会导致程序效率降低。具体参考这里:Java反射效率低的原因
- 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
图解Java程序的三个阶段
反射就是:类加载器(ClassLoader)将.class文件加载进内存,并把java类中的各种成分(成员变量、方法、构造方法、包、父类、父接口)映射成一个个对象。
获取类对象的三种方式
1、通过类的对象,获取类对象
实例对象.getClass()
2、通过类名获取类对象
类名.class
3、通过静态方法forName()获取类对象(推荐使用)
Class.forName("包名.类名")
实例:
//方式一
Person p=new Person();
Class<Person> aClass = (Class<Person>) p.getClass();
//方式二
Class<Person> aClass2 = Person.class;
//方式三
Class<?> aClass3 = Class.forName("com.example.reflect.Person");
与反射相关的类
类名 | 所在包名 | 用途 |
---|---|---|
Class类 | java.lang | 代表类的实体 |
Field类 | java.lang.reflect | 代表类的属性(成员变量) |
Method类 | java.lang.reflect | 代表类的方法 |
Contructor类 | java.lang.reflect | 代表类的构造方法 |
Class类
位于java.lang包下
public final class Class<?>
extends Object implements
Serializable,GenericDeclaration,Type,AnnotateElement
Class类的类表示正在运行的Java应用程序中的类和接口。
枚举是一种类,一个注释是一种界面。
每个数组也属于一个反映为类对象的类,该对象由具有相同元素类型和维数的所有数组共享。
原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为类对象。
Class与class的区别:
(1)Class是一个类
(2)class是一个java关键字,用来声明一个类,如:class Person
Class类中最常见的方法:
public static Class forName(String className):根据全限定类名获取该类的类对象
在JDBC中经常使用这个方法加载数据库连接驱动:
Class.forName(“com.mysql.cj.jdbc.Driver”);
Class类中常用的方法
获取类相关方法:
方法 | 描述 |
---|---|
ClassLoader getClassLoader( ) | 获得类的类加载器 |
static Class forName(String className) | 根据全类名获得该类的类对象 |
T newInstance( ) | 创建类的实例对象 |
String getName( ) | 获得全类名(包名.类名) |
InputStream getResourceAsStream(String name) | 查找具有给定名称的资源 ,返回输入字节输出流 |
URL getResource(String name) | 查找具有给定名称的资源 |
获取成员变量(属性)相关方法:
方法 | 描述 |
---|---|
Field getField(String name) | 获取指定名称的public修饰的成员变量 |
Filed[ ] getFields( ) | 获取所有public修饰的成员变量,存放在Field[ ]数组中 |
Field[ ] getDeclaredFields() | 获取所有成员变量(public、private、protected等修饰的),返回一个Field[ ]数组 |
Field getDeclaredField(String name) | 获取指定名称的成员变量,(public、private、protected等修饰的)不考虑修饰 |
获取构造方法:
方法 | 描述 |
---|---|
Constructor<?>[ ] getConstructors( ) | 返回一个包含Constructor对象的数组,反映由此Constructor对象表示的类的所有公共类函数。 |
Constructor<?> getConstructor(类<?>… parameterTypes) | 返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函数。 |
Constructor<?> getDeclaredConstructor(类<?>… parameterTypes) ) | 返回一个Constructor对象,该对象反映Constructor对象表示的类或接口的指定类函数。 |
Constructor<?> getDeclaredConstructors( ) | 返回反映Constructor对象表示的类声明的所有Constructor对象的数组类 。 |
获取成员方法:
方法 | 描述 |
---|---|
Method[ ] getMethods( ) | 返回包含一个数组方法对象反射由此表示的类或接口的所有公共成员方法类对象,包括那些由类或接口和那些从超类和超接口继承的声明。 |
Method getMethod(String name,类<?>… parameterType) | 返回一个方法对象,它反映此表示的类或接口的指定指定公共成员方法类对象。 |
Method[ ] getDeclaredMethods( ) | 返回包含一个数组方法对象反射的类或接口的所有声明的方法 |
Method getDeclaredMethod(String name,类<?>… parameterType) | 返回一个方法对象,它反映此表示的类或接口的指定声明的方法类对象。 |
利用反射机制获取类名、包名、父类名
Person类:
package cn.example;
public class Person implements Serializable{
public String name;
public int age;
private String gender;
private double height;
public void eat(){
System.out.println("无参eat方法执行.....");
System.out.println("吃..........");
}
public void eat(String food){
System.out.println("有参eat方法执行.....");
System.out.println("吃"+food+"........");
}
public Person() {
System.out.println("无参构造执行.....");
}
public Person(String name, int age, String gender, double height) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
System.out.println("无参构造执行....");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", height=" + height +
'}';
}
}
Class<Person> personClass = Person.class;
//获取类名
String className = personClass.getName();
System.out.println(className);
//获取包名
String packageName = personClass.getPackage().getName();
System.out.println(packageName);
//获取父类名 默认继承Object类
String superClassName = personClass.getSuperclass().getName();
System.out.println(superClassName);
//获取实现的接口名
Class<?>[] interfaces = personClass.getInterfaces();
System.out.println(Arrays.toString(interfaces));
控制台输出结果:
利用反射机制操作类的属性
1.获取属性(成员变量)
(1)获取公共的成员变量
package cn.example.reflect;
import com.wenbin.reflect.Person;
import java.lang.reflect.Field;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException {
//获取Person类的Class类对象
Class<Person> personClass = Person.class;
//获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for(Field e: fields){
System.out.println(e);
}
System.out.println("===========");
//获取指定名称的public修饰的成员变量
Field age = personClass.getField("age");
Field name = personClass.getField("name");
System.out.println(age);
System.out.println(name);
}
}
输出结果:
根据指定名称获取属性时,需要指定的正确的属性名,否则会报错。同时,使用getDeclared()方法无法获取非公共的属性:
(2)获取所有的成员变量(不考虑修饰符)
//获取Person类的Class类对象
Class<Person> personClass = Person.class;
//获取所有成员变量
Field[] fields = personClass.getDeclaredFields();
for(Field e: fields){
System.out.println(e);
}
System.out.println("===========");
//获取指定名称的成员变量
Field age = personClass.getDeclaredField("name");
Field name = personClass.getDeclaredField("age");
Field gender = personClass.getDeclaredField("gender");
Field height = personClass.getDeclaredField("height");
System.out.println(age);
System.out.println(name);
System.out.println(gender);
System.out.println(height);
输出结果:
2、设置和获取属性的值
用到的方法:
java.lang.reflect.Field类中的方法:
public void set(Object obj,Object value):在指定对象的此字段(属性)上设置指定的值。
public Object get(Object obj):获取指定对象在此字段(属性)上的值。
(1)获取公共属性的值:
public class ReflectDemo1 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//获取Person类的Class类对象
Class<Person> personClass = Person.class;
//创建Person对象并赋值
Person person1 = new Person("张三", 24, "男", 178.0);
Person person2 = new Person("李四", 30, "男", 172.5);
//获取公共的成员变量对象
Field name = personClass.getField("name");
Field age = personClass.getField("age");
//获取Person对象的属性值
Object value1 = name.get(person1);
Object value2 = age.get(person1);
System.out.println(value1);
System.out.println(value2);
Object value3 = name.get(person2);t6tg
Object value4 = age.get(person2);
System.out.println(value3);
System.out.println(value4);
}
}
控制台输出结果:
(2)设置公共属性值:
Class<Person> personClass = Person.class;
Field age = personClass.getDeclaredField("age");
Field name = personClass.getDeclaredField("name");
//创建Person对象 不赋值
Person person1=new Person();
Person person2=new Person();
//为Person对象属性赋值
name.set(person1,"张三");
age.set(person1,25);
name.set(person2,"李四");
age.set(person2,30);
System.out.println(person1);
System.out.println(person2);
控制台输出结果:
(3)设置和获取非公共属性值
直接设置和获取非公共属性的值会报非法访问错误:
无法设置和获取非公共属性的值,需要用到Field类中的一个方法:
setAccessible(boolean flag),设置参数为true,表示忽略访问权限修饰符的安全检查(即可以设置和获取非公共属性的值),也称暴力反射。
Field name = personClass.getDeclaredField("name");
Field age = personClass.getDeclaredField("age");
Field gender = personClass.getDeclaredField("gender");
Field height = personClass.getDeclaredField("height");
Person person=new Person();
name.set(person,"张三");
age.set(person,30);
//暴力反射
gender.setAccessible(true);
height.setAccessible(true);
gender.set(person,"男");
height.set(person,182.5);
System.out.println(height.get(person));
System.out.println(gender.get(person));
System.out.println(person);
输出结果:
利用反射机制操作类的构造方法
涉及到的方法:
java.lang.reflect.Constructor类中的newInstance()方法
public T newInstance(Object… initargs):使用由此Constructor对象表示的构造函数,使用指定的初始化参数创建和初始化构造函数的声明类的新实例。
import cn.example.Person;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo4 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Person> personClass = Person.class;
//获取无参构造方法
Constructor<Person> constructor1 = personClass.getConstructor();
System.out.println(constructor1);
//使用无参构造方法创建对象
Person person1 = constructor1.newInstance();
System.out.println(person1);
System.out.println("============================");
//获取全参构造方法
Constructor<Person> constructor2 = personClass.getConstructor(String.class, int.class, String.class, double.class);
System.out.println(constructor2);
//使用全参构造方法创建对象
Person person2 = constructor2.newInstance("张三", 34, "男", 175.5);
System.out.println(person2);
}
}
控制台输出结果:
利用反射机制操作类的成员方法
涉及到的方法:
java.lang.reflect.Method类中的invoke()方法
public Object invoke(Object obj,Object… args):在具有指定参数的方法对象上调用此方法对象表示的基础方法。
public class ReflectDemo5 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<Person> personClass = Person.class;
Person person=new Person();
//获取无参数的eat方法
Method eat1 = personClass.getMethod("eat");
System.out.println(eat1);
//执行无参数的eat方法
eat1.invoke(person);
System.out.println("=====================");
//获取有参数的eat方法
Method eat2 = personClass.getMethod("eat", String.class);
System.out.println(eat2);
//执行有参数的eat方法
eat2.invoke(person,"酸辣粉");
}
}
控制台输出结果:
利用反射机制实例化对象
Class类中的newInstance()方法
public T newInstance():创建由此类对象表示的类的新实例。 该类被实例化为一个具有空参数列表的new表达式。 如果类尚未初始化,则初始化该类。
public class ReflectDemo7 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> aClass = Class.forName("cn.example.Person");
//实例化Person对象
Person person =(Person) aClass.newInstance();
System.out.println(person);
person.eat();
}
}
控制台输出结果: