什么是反射?
java的内存模型,我们关注的点,一个方法区,一个栈,一个堆

首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。
假如你写了一段代码:Object o=new Object(); 运行了起来!
- JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中
- 你的类Object加载到方法区中,方法区中就存在了这个类的所有描述/资料。
- 然后创建了Object类的Class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。
- jvm检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化 = new Object()。
为什么要讲这个呢?因为要理解反射必须知道它在什么场景下使用
题主想想上面的程序对象是自己new的,程序相当于写死了给jvm去跑。假如一个服务器上突然遇到某个请求哦要用到某个类,哎呀但没加载进jvm,是不是要停下来自己写段代码,new一下,再启动一下服务器?
反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以之前没有加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻
举例子
-
项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化
通过Class tc =Class.forName("com.java.dbtest.TestConnection"); 让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了! -
如果接触过spring,会发现当你配置各种各样的bean时,是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,你的程序就能健壮地运行。
Java程序运行分为两种状态:
1、编译时:类编译为 .class 字节码文件时。
2、运行时:将生成的 .class 字节码文件加载到内存中。
泛型和反射的区别
1、不知道在程序的运行中哪些类型要用——>泛型
2、不知道哪个类在程序运行中会被实例化——反射
Class 是开启反射的源头
加载进JVM的类,JVM会先创建对应类的Class实例
当获取了jvm 创建的Class 实例,则可调用这个类的所有描述/资料。
如何获取 Class 实例?
-
使用运行时类的 class 属性
Class clazz1 = Person.class; System.out.println(clazz1);
-
使用运行时类对象的 getClass() 方法
Person p = new Person(); Class clazz2 = p.getClass(); System.out.println(clazz2);
-
使用 Class 类的静态方法 forName(String className)
String className = "com.atguigu.java.Person"; Class clazz3 = Class.forName(className) System.out.println(clazz3);
-
(了解)使用类加载器
String className = "com.atguigu.java.Person"; ClassLoader cl = this.getClass().getClassLoader(); Class clazz4 = cl.loadClass(className); System.out.println(clazz4);
反射的使用
在程序运行时获取类信息
- Fields
Field[] getFields() :获取所有 public 修饰的属性,包括父类的
Field[] getDeclaredFields():获取当前类中所有的属性,包括私有的,不包括父类的
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//1 修饰符
int mod = field.getModifiers();
String strMod = Modifier.toString(mod);
System.out.print(strMod + "\t");
//2 数据类型
Class type = field.getType();
System.out.print(type.getName() + "\t");
//3 属性名
System.out.println(field.getName());
- Methods:
Method[] getMethods() : 获取所有 public 修饰的方法,包括父类的
Method[] getDeclaredMethods() : 获取当前类中所有的方法,包括私有的,不包括父
Class clazz = Person.class;
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
//0 获取方法注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//1 获取修饰符
String mod = Modifier.toString(method.getModifiers());
System.out.print(mod + "\t");
//2 获取返回值类型
Class returnType = method.getReturnType();
System.out.print(returnType.getName() + "\t");
//3 获取方法名
System.out.print(method.getName() + "(");
//4 获取参数列表
Class[] paramTypes = method.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
//5 获取方法异常
Class[] exceptionTypes = method.getExceptionTypes();
for (Class class1 : exceptionTypes) {
System.out.println(class1.getName());
}
- 获取类的一些信息
1) 获取 运行时类 的父类
方法:getSuperclass()
2)获取带泛型父类的类型
方法:getGenericSuperclass()
4 )运行时获取运行时类的接口
方法:getInterfaces()
5 )在运行时获取运行时类的内部类
方法:getClasses()
6 )在运行时获取运行时类的注解
方法:getAnnotations()
7) 获取自定义注解value
MyAnnotation mya = (MyAnnotation) annotation;
System.out.println(mya.value());
8)在运行时获取运行时类的包
方法:getPackage()
9) 获取类的名字
String name = clazz.getName();
String simpleName = clazz.getSimpleName();
- 在运行时获取运行时类带泛型父类的 泛型类型
public void test3(){
Class clazz = Person.class;
//1 获取带泛型父类类型,返回值为类型
//com.atguigu.java.Creature<java.lang.String>
Type type = clazz.getGenericSuperclass();
//2参数化类型。在java中,长的像参数列表<java.lang.String>
ParameterizedType pt = (ParameterizedType) type;
//3 参数化获取的是Class中的泛型属性,因Calss实现了Type接口, 要获取真实类型数组
Type[] types = pt.getActualTypeArguments();
//4 类型类强转
Class cl = (Class) types[0];
System.out.println(cl.getName());
}
改变类信息或者调用运行时类方法
-
创建 运行时类对象
方法:newInstance()
① 默认调用无参构造器
②结合泛型使用:可不用强转 -
操作属性
public void test3() throws Exception{
//1 使用forname()获取Person的大Class实例
Class clazz=Class.forName("ReflectionGte/Person");
//2 创建Person 实例
Person person=(Person) clazz.newInstance();
//3获取属性
Field age=clazz.getField("age");
//4得到这个属性实例进行操作
age.set(person, 18);
//5反射获取运行时类的属性值
Object obj=age.get(person);
System.out.println(obj);
//6 私有属性的访问
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);//忽略访问权限
// 私有属性的操作
name.set(person, "张三");
//查看
Object obj2 = name.get(person);
System.out.println(obj2);
}
- 操作方法:
public void test4() throws Exception{
//获取类的Class
Class clazz=Class.forName("ReflectionGte.Person");
// 创建Person 实例
Person person=(Person) clazz.newInstance();
//无参无返回的
Method m1=clazz.getMethod("eat");
Object obj2 = m1.invoke(person);
System.out.println(obj2);
//有参有返回的
Method m2 = clazz.getMethod("setName", String.class, double.class, int.class);
Object obj = m2.invoke(person, "张三", 99.99, 18);
System.out.println("--" + obj);
//私有的!
//Method m3 = clazz.getMethod("sleep");
Method m3 = clazz.getDeclaredMethod("sleep");
m3.setAccessible(true);//忽略访问权限
m3.invoke(person);
}
反射+泛型结合
目的
不确定要用什么类,返回哪个对象
解释
public <T> T get(Class<T> clazz){
//查询表数据,封装对象,返回
// return clazz.newInstance();
return clazz.cast(obj);
}
- public <T> T
未知数据类型,类中泛型也不适用,使用方法泛型
- Class<T> clazz
未知返回创建什么对象,使用反射
- return clazz.newInstance();
使用时确定传入什么泛型类型,就返回什么对象的实例
示例
@GetMapping("/infoByCart/{productId}")
@ApiOperation("PC端:获取购物车商品详情")
@ApiImplicitParam(name = "productId", value = "商品id", paramType = "query")
public Result<CartProductInfoRes> infoByCart(@PathVariable @Min(1L) Long productId) {
Long languageId = getLanguageId();
RedisProductInfoRes redisProductInfoRes = getRedisProductInfoRes(productId, languageId);
//数据过滤
Class<CartProductInfoRes> cartProductInfoResClass = CartProductInfoRes.class;
CartProductInfoRes cartProductInfo = productInfoFilter(redisProductInfoRes, cartProductInfoResClass, languageId);
return succeed(cartProductInfo);
}
/**
* 组装pc端的商品详情数据
*
* @param redisProductInfoRes redis的商品详情数据
* @param languageId 语言id
* @return com.olight.omall.product.dto.res.CartProductInfoRes
*/
private <E> E productInfoFilter(RedisProductInfoRes redisProductInfoRes, Class<E> clazz, Long languageId) {
E instance = null;
try {
instance = clazz.newInstance();
} catch (Exception e) {
throw new OcloudBusinessException(ProductErrorCodeEnum.ERROR_CODE_10006);
}
if (redisProductInfoRes == null) {
return instance;
}
//基本数据多语言过滤
List<ProductLangRes> productLangRes = redisProductInfoRes.getProductLangRes();
for (ProductLangRes productLang : productLangRes) {
if (languageId.equals(productLang.getLanguageId())) {
OcloudBeanUtils.copy(productLang, instance);
break;
}
}
OcloudBeanUtils.copy(redisProductInfoRes, instance);
return instance;
}
网友评论