0915(053天 反射机制)

0915(053天 反射机制)

每日一狗(田园犬西瓜瓜

反射机制

1. Java反射机制

反射机制无所不能

执行效率低下

什么是反射

Java中提供的自省机制。

  • 动态获取类的信息
  • 动态获调用类的方法

反射:动态获取类的信息一种动态调用对象的方法称为Java的反射Reflection机制。

反射提供了封装程序集、模块和类型的对象。

在Java运行环境中,对于任意一个类的对象,可以通过反射获取这个类的信息,对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

反射的原理

在这里插入图片描述

类的加载流程

  1. 当执行new A();的时候,会触发JVM加载A.class文件
  2. JVM从本地磁盘找到A.class文件并加载到JVM内存文件中
  3. .class文件加载如内存后,JVM会自动创建一个Class对象。一个类文件只会产生一个Class对象

类的加载本质上都是通过类加载器组件来负责完成的,Java的类加载器是通过双亲委托机制

反射的本质就是得到Class对象后,反向获取A对象的各种信息

特点

  • 优点
    • 无所不能
    • 在运行时获得各类的各种内容,进行反编译,对于Java这种先编译后运行的语言,能够很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
  • 缺点
    • 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射
    • 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题

破坏了封装性

用途

  • 反编译

  • IDE工具的提示

  • 一些框架需要知道你定义的东西都有啥

    反射最重要的用途就是开发各种通用框架。比如很多框架Spring都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象
    MyBatis持久层框架 Spring业务层框架 SpringMVC表现层框架

2. 代码实现

前场一下

package com.yang2;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test01 {
	public static void main(String[] args) {
		Class clz = A1.class;
		// 获取属性
		Field[] fs = clz.getDeclaredFields();
		for (Field f : fs) {
			System.out.println(f);
		}
		// 获取方法
		Method[] ms = clz.getDeclaredMethods();
		for (Method m : ms) {
			System.out.println(m);
		}
		// 获取构造器
		Constructor[] cs = clz.getConstructors();
		for (Constructor c : cs) {
			System.out.println(c);
		}
	}
}

class A1 {
	private Long id;
	private String name;

	public A1() {
	}

	public A1(Long id, String name) {
		this.id = id;
		this.name = name;
	}

	public void pp() {
	}
}
/*
private java.lang.Long com.yang2.A1.id
private java.lang.String com.yang2.A1.name
public void com.yang2.A1.pp()
public com.yang2.A1()
public com.yang2.A1(java.lang.Long,java.lang.String)
*/

Class对象

Class对象的一些常见方法
  • getName():获取类的名字,例如java.util.Date
  • getFields():获取类中public类型的属性,自定义或者从父类种继承
  • getDeclaredFields():获取类的所有属性(包括public、protected、default、private),只能获取到当前类种定义,不能获取父类继承的
  • getMethods():获取类中public类型的方法
  • getDeclaredMethods():获取类的所有方法
  • getMethod(String name,Class[] parameterTypes):获取类的指定方法,name:指定方法的名字,parameterType:指定方法的参数类型
  • getConstrutors():获取类中public类型的构造方法
  • getDeclaredConstructors():获取类中所有的构造方法
  • getConstrutor(Class[] parameterTypes):获取类的指定构造方法,parameterTypes:指定构造方法的参数类型
  • getDeclaredConstructor
  • newInstance():通过类的public不带参数的构造方法创建该类的一个对象;

注:在访问私有属性和私有方法时,需要对访问的私有属性或方法设置setAccessible(true)使被反射的类抑制java的访问检查机制。否则会报IllegalAccessException异常。

获取一个Class对象的三种方法

这里需要注意的是A1.class不会执行静态代码块

package com.yang3;

public class Test01 {
	public static void main(String[] args) throws Exception {
        // 1、 使用Class.forName()
		Class<?> clz = Class.forName("com.yang3.A1"); // 静态代码块会被执行
		System.out.println(clz);
		// 2、 使用.class
		Class<?> clz2 = A1.class; // 不会执行加载静态代码块
		System.out.println(clz2);
		// 3、使用getClass方法
		A1 a1 = new A1();
		Class<?> clz3 = a1.getClass();
		System.out.println(clz3);
		System.out.println(clz3 == clz);
		System.out.println(clz3 == clz2);
        // 4、 使用类加载器获取Class对象
        
	}
}

class A1 {
	private static Long id;
	static {
		id = 50L;
		System.out.println(id);
	}

}

针对包装类还可以获取其对应的原生数据类型

Class<Integer> clz = Integer.TYPE;
System.out.println(clz); // int
使用反射创建对象
package com.test;

import java.lang.reflect.Constructor;
import java.util.Date;

public class Test01 {
	public static void main(String[] args) throws Exception {
		Class<?> clz = Date.class;
		// 获取无参的构造方法
		Constructor<?> con = clz.getConstructor();
		// 调用无参的构造方法
		Object dd = con.newInstance();
		System.out.println(dd); // Thu Sep 15 20:59:53 CST 2022
		// 获取带参构造器 public Date(long date) {
		con = clz.getConstructor(long.class);
		// 调用带参构造器
		dd = con.newInstance(1000);
		System.out.println(dd); // Thu Jan 01 08:00:01 CST 1970
	}
}

使用反射机制生成对象的步骤

在这里插入图片描述

属性对象 Field

  • String getName() 获取属性名
  • int getModifiers()获取属性限定词编号
  • Object get(Object obj)获取指定对象的指定属性值
  • Class<?> getType()获取属性的类型
  • Modifier.toString(f.getModifiers())可以将指定限定编号转换为String类型供提高可读性
  • public void setAccessible(boolean flag) 设定属性访问权限
  • public void set(Object obj, Object value) 为obj对象的指定属性设定为value

可见getFields和getDeclaredFields区别:

  • getFields返回的是申明为public的属性,包括父类中定义
  • getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的
package com.yang3;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Test04 {
	public static void main(String[] args) throws Exception, IllegalAccessException {
		A4 a = new A4();
		Class<?> clz = a.getClass();

		// 获取公共属性
		Field[] fs = clz.getFields();
		for (Field f : fs) {
			System.out.println("属性名" + f.getName() + "属性类型" + f.getType() + "属性的类型" + f.getGenericType() + "限定词"
					+ f.getModifiers() + "存储值" + f.get(a));
		}
		fs = clz.getDeclaredFields();
		for (Field f : fs) {
			f.setAccessible(true);
			System.out.println("属性名" + f.getName() + "属性类型" + f.getType() + "属性的类型" + f.getGenericType() + "限定词"
					+ f.getModifiers() + "限定词修饰符" + Modifier.toString(f.getModifiers()) + "存储值" + f.get(a));

		}
	}
}

interface IA41 {
	public String IA41 = "IA41";
}

abstract class A41 implements IA41 {
	public String A41 = "A41";
}

class A4 extends A41 {
	private static final String type = "A4";
	public volatile String name = "测试名";
	private Long id;
}
/*
属性名name属性类型class java.lang.String属性的类型class java.lang.String限定词65存储值测试名
属性名A41属性类型class java.lang.String属性的类型class java.lang.String限定词1存储值A41
属性名IA41属性类型class java.lang.String属性的类型class java.lang.String限定词25存储值IA41
属性名type属性类型class java.lang.String属性的类型class java.lang.String限定词26限定词修饰符private static final存储值A4
属性名name属性类型class java.lang.String属性的类型class java.lang.String限定词65限定词修饰符public volatile存储值测试名
属性名id属性类型class java.lang.Long属性的类型class java.lang.Long限定词2限定词修饰符private存储值null
*/

获取并设定属性
package com.yang3;

import java.lang.reflect.Field;

public class Test03 {
	public static void main(String[] args) throws Exception {
		A3 obj = new A3();
		Class<?> clz = obj.getClass();
		Field[] fs = clz.getDeclaredFields();
		for (Field f : fs) {
			System.out.println(f);
		}
		// 设定编号
		Field f1 = clz.getDeclaredField("id");
		f1.setAccessible(true);
		f1.set(obj, 99L);
		// 设定姓名
		Field f2 = clz.getDeclaredField("name");
		f2.setAccessible(true);
		f2.set(obj, "test");
		System.out.println(obj);
	}
}

class A3 {
	private Long id;
	private String name;

	@Override
	public String toString() {
		return "A3 [id=" + id + ", name=" + name + "]";
	}

}

方法对象 Method

获取类中Method的方法
  • public Method[] getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共
  • public Method getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法
  • public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法
  • public Method[] getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

小结一下

在JDK中,主要由以下类实现Java反射机制:

  • Class类:代表一个类

  • Field类:代表类的成员变量(属性)

  • Method类:代表类的方法

  • Constructor类:代表类的构造方法

  • Array类:提供了动态创建数组,以及访问数组元素的静态方法;(这个就是Java实现数组的方法)

以上类中,Class类在java.lang包,其余位于java.lang.reflect包。

java.lang.Object类(所有类的超类)定义了getClass()方法,任意一个Java对象都可以通过此方法获
得它的class。


3. Java注释

一般注释

一种说明信息,编译器在编译的时候会忽略这些说明信息

// 行内注释
/*
 * 多行注释
 */

/**

注解

一种特殊的注释,会在编译期对指定程序做一些事情

内置注解

自定义注解

一般来说命名规则与接口一致

定义注解的注解

  • Retention 注解的生命周期
  • Target 注解的应用范围
  • Repeatabl 注解可以被使用多次
@Documented  //类和方法的Annotation在缺省情况下是不出现在javadoc中的。如果使用@Documented修饰该 Annotation,则表示它可以出现在javadoc中。定义时@Documented可有可无;若没有定义,则Annotation不会出现在javadoc中。

@Target(ElementType.TYPE) //指定Annotation的类型属性。@Target(ElementType.TYPE)就是指定该Annotation的类型是ElementType.TYPE。这就意味着,MyAnnotation1是来修饰"类、接口(包括注释类型)或枚举声明"的注解。定义Annotation时,@Target可有可无。若有@Target则该Annotation只能用于它所指定的地方;若没有@Target,则该Annotation可以用于任何地方。

@Retention(RetentionPolicy.RUNTIME)//RetentionPolicy是注解的策略属性,而@Retention的作用,就是指定Annotation的策略属性。RetentionPolicy.RUNTIME意思就是指定编译器会将注解信息保留在.class 文件中,并且能被虚拟机读取。定义时@Retention可有可无。若没有@Retention则默认RetentionPolicy.CLASS
    
//@interface表示它实现了java.lang.annotation.Annotation接口,即该注解就是一个Annotation。定义时@interface是必须的。
注意:它和通常的实现接口的方法不同。Annotation接口的实现细节都由编译器完成。通过@interface定义注解后,该注解不能继承其他的注解或接口。

public @interface MyAnnotation {  
  //设置属性,如果只有一个value属性情况下可以省略value名称不写;如果有多个属性,且多个属
  //性没有默认值,则value名称是不能省略的
    String[] value() default "unknown";  
}

扩展小芝士

  • 接口中不能定义静态代码块
  • 接口中的静态成员,无论是在其实现的类,还是在其子接口当中,都是不可以被继承的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值