Java 的Reflection(反射)
Reflection 反射
反射是被视为动态语言的关键,反射机制允许程序在执行期间借助于
Reflection Api 取得任何类的内部信息,并能直接操作任对象的内部属性及其方法
反射是什么:反射就是可以获取到所有在运行时的类,并且可以获取到他们的方法,属性,构造函数
加载完类之后,在JVM堆内存中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。
这个对象就就像一面镜子,可以通过这个镜子看到类的结构,所以,我们形象的称之:反射
区别
正常的方式:引入 Java 类的路径 → new 一个类 → 获取到实例化对象 反射获取类:实例化对象
→ getClass() → 得到完整的包类名称
动态语言 VS 静态语言
动态语言
是一个类在运行时可以改变其结构的语言:列入:新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗来说就是在运行时代码可以根据某些条件改变自身的类结构
主要的动态语言有:Object-C,C#,JavaScript,PHP,Python,Eriang,Vue
静态语言
与静态语言相对应的。运行结构不可变的语言就是静态语言。如Java,C,C++
注:Java 是准动态语言,因为Java 有一定的动态性,我们可以利用反射机制,字节码操作获取类型动态语言的特性。Java 的动态性让编程的时候更加灵活
Java 反射机制提供的功能
1.在运行是判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法
4.运行是获取泛型信息
5.在运行时调用任意一个对象的成员变量和方法
6.在运行时处理注解
7.生成动态代理
在运行时调用任意一个对象的成员变量和方法
实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author FengRui
* @Title: 人
* @date 2021/07/10 18:17
*/
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
public Integer age;
public Person(String name){
this.name = name;
}
public Person(Integer age){
this.age = age;
}
public void show(){
System.out.println("姓名为:"+this.name+",年龄为:"+this.age);
}
private String showNation(String nation){
System.out.println("我的国籍是:"+nation);
return nation;
}
}
测试类代码
package reflection;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author FengRui
* @Title: 反射测试
* @date 2021/07/10 18:15
*/
@Slf4j
public class ReflectionTest {
/** methodsEffect
* @Description: 反射之前 person 的操作
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/11
*/
public static void test1(){
//正常情况下,不能调用类的私有的方法或者属性
log.info("1.创建Person 类的对象");
Person person = new Person(
"封瑞"
);
log.info("2.通过对象,调用其内部的属性方法");
person.setAge(20);
person.show();
}
/** methodsEffect
* @Description: 通过反射调用调用私有的方法
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/11
*/
public static void test3() throws Exception{
log.info("1.获取到 类的class");
Class aClass=Person.class;
log.info("2.给看类的构造函数设设置类型");
Constructor cons = aClass.getConstructor(String.class);
log.info("3.设置 为 true 可以调用私有方法, 设置为false 不可以调用私有方法,默认为false");
//如果构造函数是公共的就不用设置
cons.setAccessible(true);
log.info("4.将值塞入到 class 的构造函数中");
Person obj =(Person)cons.newInstance("封瑞");
log.info("5.根据方法名获取到方法");
//aClass.getDeclaredMethod() 其中 第一个参数是方法名,第二个参数是 方法名中参数的类型
Method showNation = aClass.getDeclaredMethod("showNation", String.class);
log.info("6.将私有方法设置为可以可以调用的");
//如果方法是公共的就不用设置
showNation.setAccessible(true);
log.info("7.将参数塞到名称为 showNation 方法中");
showNation.invoke(obj,"中国");
log.info("8.根据字段名称来获取字段");
Field name = aClass.getDeclaredField("name");
log.info("9.将私有的字段设置为可以赋值的");
name.setAccessible(true);
log.info("10.将值设置到name字段中");
name.set(obj,"封瑞");
log.info("11.输出");
System.out.println(obj.toString());
}
public static void main(String[] args)throws Exception {
ReflectionTest.test3();
}
public static void test100(){
int list[]= new int[]{1,2,3,4,5,6,-10,7};
int listLength = list.length-1;
for (int i = listLength;i>=list.length/2;i--){
int value =list[listLength-i];
list[listLength-i] =list[i];
list[i] = value;
}
System.out.println(Arrays.toString(list));
}
public static void test101(){
int list[]= new int[]{1,2,3,4,5,6,-10,7};
for (int i = 0; i < list.length / 2; i++) {
int temp = list[i];
list[i] = list[list.length - i - 1];
list[list.length - i - 1] = temp;
}
for (int i = 0; i < list.length; i++) {
System.out.print(list[i ]+ "\t");
}
}
}
输出的结果
疑问
问题一:通过直接new 的方式或者反射的方式都可以调用公共的方法,开发中到底用那个?
问题二:反射机制与面向对象中的封装是不是矛盾?如何看待这2个技术?
问题一的解答
如果能确定一个类 需要实现 哪一个类的话建议直接用 new 既可
如果不能确定需要实现某个类 的话,建议使用 反射的方式
反射的特性:动态性
问题二的解答
不矛盾的!因为Java 中本来就是推荐是用封装后的方法去赋值,但是你就是不想用封装后的方法去赋值也是可以的;也就相当于在开发一个项目中,某些功能我可以不用,但是你一定要有
注释:
关于Java。Lang.Class 类的理解
1.类的加载
程序经过Java.exe 命令以后,会生成一个或多个字节码文件(.class 结尾),接着我们使用 java.exe 命令对某个字节码文件允许解释既可,也就相当于将某个文件加载到内存当中
加载到内存中的类,我们就称之为运行时类,此运行时类,就作为Class 的一个实例
2.换句话说,Class 的实例就对应着一个运行时类.
3. 反射主要是想要获动态性
获取运行时类的注解,方法(方法的注解),构造函数,字段(字段的注解)
package com.test;
import lombok.Data;
import java.io.Serializable;
/**
* @author FengRui
* @Title: 父类
* @date 2021/07/12 17:20
*/
@Data
public class Creaturn<T> implements Serializable {
private char gener;
public double weight;
private void breath(){
System.out.println("弟弟开始吃哦里给");
}
public void eat(){
System.out.println("弟弟开始呀屎");
}
}
package com.test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
/**
* @author FengRui
* @Title: 测试注解
* @date 2021/07/12 19:39
*/
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE, ElementType.MODULE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
package com.test;
/**
* @author FengRui
* @Title: 测试接口
* @date 2021/07/12 19:30
*/
public interface MyInferface {
void info()throws Exception;
}
package com.test;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* @author FengRui
* @Title: 测试类
* @date 2021/07/12 17:19
*/
@AllArgsConstructor
@NoArgsConstructor
@ToString
@MyAnnotation(value = "注解:弟弟爱吃哦里给")
public class Person extends Creaturn<String> implements Comparable<String>,MyInferface{
@MyAnnotation(value = "注解:弟弟爱吃哦里给")
public int id;
int age;
private String name;
@MyAnnotation(value = "person的构造函数,形参名称:name")
private Person(String name){
this.name= name;
}
Person(String name,int age){
this.name = name;
this.age=age;
}
@MyAnnotation
private String show(String name)throws Exception{
System.out.println("我的是:"+name);
return name;
}
public String display(String interests)throws IndexOutOfBoundsException,NullPointerException{
return interests;
}
@Override
public int compareTo(String s) {
return 0;
}
@Override
public void info()throws Exception {
System.out.println("我叫河蚌猪,每天爱呀屎");
}
}
测试类
package com.test2;
import com.test.Person;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import javax.print.PrintService;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Arrays;
/**
* @author FengRui
* @Title: 获取运行时类的所有属性,方法,注解,构造函数等等
* @date 2021/07/12 19:48
*/
@Slf4j
public class ClassTest {
/**
* methodsEffect
*
* @Description: 获取当前运行时类的变量
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/12
*/
@Test
public void test1() {
Class<Person> clazz = Person.class;
log.info("获取到所有参数的名称");
// 可以获取到当前运行时类及其父类的所有public的属性
for (Field field : clazz.getFields()) {
System.out.println(field);
}
log.info("获取到当前运行时类所有的属性(包含私有的)(不包含父类中的属性)");
for (Field declaredField : clazz.getDeclaredFields()) {
System.out.println(declaredField);
}
}
/**
* methodsEffect
*
* @Description: 获取当前运行时类的 属性 的 权限修饰符,数据类型,变量名
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/12
*/
@Test
public void test11() {
Class<Person> clazz = Person.class;
log.info("获取到所有参数的名称");
// 可以获取到当前运行时类及其父类的所有public的属性
// for (Field field : clazz.getFields()) {
// for (Annotation annotation : field.getAnnotations()) {
// // 属性的注解
// System.out.println(annotation);
// }
// System.out.println(Modifier.toString(field.getModifiers())+" "+field.getType()+" "+field.getName()+"\n");
// }
log.info("获取到当前运行时类所有的属性(包含私有的)(不包含父类中的属性)");
for (Field field : clazz.getDeclaredFields()) {
for (Annotation annotation : field.getAnnotations()) {
// 属性的注解
System.out.println(annotation);
}
System.out.println(Modifier.toString(field.getModifiers()) + " " + field.getType() + " " + field.getName() + "\n");
}
}
/**
* methodsEffect
*
* @Description: 获取当前运行时类的方法 ,方法名,权限修饰符,返回类型,形参
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/12
*/
@Test
public void test2() {
Class<Person> clazz = Person.class;
log.info("获取到所有的方法(不包含私有的)(包含父类的public 方法)");
// 获取到所有的方法(不包含私有的,但是包含父类的public 方法)
// for (Method declaredMethod : clazz.getMethods()) {
// for (Annotation annotation : declaredMethod.getAnnotations()) {
// //获取到方法上的注解
// System.out.println(annotation);
// }
// System.out.print(Modifier.toString(declaredMethod.getModifiers())+" "+declaredMethod.getReturnType().getName()+" "+declaredMethod.getName());
// System.out.print("(");
// for (Parameter parameter : declaredMethod.getParameters()) {
// //获取形参和形参名称
// System.out.print(parameter.getType()+" "+parameter.getName());
// }
// System.out.print(")");
// //开始获取方法抛出的异常
// if (declaredMethod.getExceptionTypes()!=null &&declaredMethod.getExceptionTypes().length>0) {
// System.out.print("throws ");
// for (Class<?> exceptionType : declaredMethod.getExceptionTypes()) {
// System.out.print(exceptionType.getTypeName());
// if (declaredMethod.getExceptionTypes().length>1) {
// System.out.print(", ");
// }
// }
// }
// System.out.println();
// }
log.info("获取到当前运行时类的所有的方法(不包含父类的方法)");
//可以获取到 当前运行时类的的所有方法包括私有的(不包含父类的方法 (但是包含重写的方法)
for (Method declaredMethod : clazz.getDeclaredMethods()) {
for (Annotation annotation : declaredMethod.getAnnotations()) {
//获取到方法上的注解
System.out.println(annotation);
}
System.out.print(Modifier.toString(declaredMethod.getModifiers()) + " " + declaredMethod.getReturnType().getName() + " " + declaredMethod.getName());
System.out.print("(");
for (Parameter parameter : declaredMethod.getParameters()) {
//获取形参和形参名称
System.out.print(parameter.getType() + " " + parameter.getName());
}
System.out.print(")");
//开始获取方法抛出的异常
if (declaredMethod.getExceptionTypes() != null && declaredMethod.getExceptionTypes().length > 0) {
System.out.print("throws ");
for (Class<?> exceptionType : declaredMethod.getExceptionTypes()) {
System.out.print(exceptionType.getTypeName());
if (declaredMethod.getExceptionTypes().length > 1) {
System.out.print(", ");
}
}
}
System.out.println();
}
}
/**
* methodsEffect
*
* @Description: 通过反射获取到父类
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/13
*/
@Test
public void test3() {
Class<Person> clazz = Person.class;
log.info("获取到所有的方法(不包含私有的)(包含父类的public 方法)");
Class aClass = clazz.getSuperclass();
for (Field field : aClass.getDeclaredFields()) {
//后续操作统一和上面的方法是一样的
System.out.println(field);
}
}
/**
* methodsEffect
*
* @Description: 调运运行时类的指定属性
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/13
*/
@Test
public void test4() throws Exception {
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(person, "弟弟爱吃哦里给");
System.out.println(person.toString());
}
/**
* methodsEffect
*
* @Description: 调运运行时类的指定构造函数
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/13
*/
@Test
public void test5() throws Exception {
Class<Person> clazz = Person.class;
Constructor<Person> constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Person person = constructor.newInstance("河蚌猪是傻逼");
System.out.println(person.toString());
}
/**
* methodsEffect
*
* @Description: 调运运行时类的指定方法
* @Param:
* @return:
* @Author: FengRui
* @Date: 2021/07/13
*/
@Test
public void test6() throws Exception {
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
show.invoke(person,"中国");
}
}