java中的反射与注解

注解与反射

注解

什么是注解

  • Annotation的作用:

    • 不是程序本身,可以对程序进行解释
    • 可以被其他程序读取
  • Annotation格式:@注释名,如:@SuppressWarnings(“all”)

    package annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    public class Test01 {
    	public static void test(){
    		System.out.println("a");
    	}
    	public static void main(String[] args) {
    		test();
    	}
    }
    
    //可以作用在那些地方
    @Target(value={ElementType.METHOD,ElementType.TYPE})
    
    //表示注解在上面地方还有效
    //RUNTIME>class>sources
    @Retention(value=RetentionPolicy.RUNTIME)
    
    //表示是否可以将我们的注解生成在javadoc中
    @Deprecated 
    
    //子类看继承父类的注解
    @Inherited
    @interface myAnotation{
    	
    }
    

    内置注解

在这里插入图片描述

如何自己手写一个注解

  1. @interface 注解名称
  2. 加入元注解,如@Target({ElementType.TYPE,ElementType.METHOD}),@Retention(RetentionPolicy.RUNTIME),@Document,@Inherited
  3. 在注解中加入参数
package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class Test02 {
	
	@MyAnnotation2(age=18)//没有默认值的参数,不加参数会报错
	public void test01(){
		
	}
	
	@MyAnnotation3(name="许浩")//除非参数为values,否则只有一个参数的时候,参数名不可省略
	public void test02(){
		
	}
}


@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
	//注解的参数 :参数类型+参数名();
	String name() default "";
	int age();
	int id() default -1;//如果默认为-1,则代表不存在
	
	String[] schools() default {"湖南城市学校","城院"};
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
	String name();
}

java反射机制

动态语言和静态语言

动态语言

在运行时可以改变其结构的语言,如:C#,JavaScript

静态语言

运行时结构不可变的语言就是静态语言,如java,c

java不是动态语言,但java可以称之为“准动态语言”。即java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。

Java Reflection

Class c=Class.forName("java.lang.String");

加载完类之后,在堆内存的方法区就产生了一个Class类型的对象,这个对象包含了完整的类的信息。

  • 正常方式:引入需要的“包类”名称 -> 通过new实例化 -> 取得实例化对象
  • 反射方式:实例化对象 -> getClass -> 得到完整的包类名称

反射优缺点

优点:可以实现动态创建对象和编译

缺点:对性能有影响

主要API

在这里插入图片描述

package reflection;

public class test01{
	public static void main(String[] args) throws ClassNotFoundException {
		//通过反射获取类的class对象
		Class c1=Class.forName("reflection.test01");	//参数:包名+类名
		System.out.println(c1);
		
		/*
		 * 一个类在内存中只有一个class对象
		 * 一个类被加载后,类的整个结构都会被封装在class对象中
		 */
		Class c2=Class.forName("reflection.test01");
		System.out.println(c2.hashCode());
		Class c3=Class.forName("reflection.test01");
		System.out.println(c3.hashCode());
	}
}

class User{
	private String name;
	private int id;
	private int age;
	
	public User() {
	}
	
	public User(String name, int id, int age) {
		super();
		this.name = name;
		this.id = id;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

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

Class类

在这里插入图片描述

Class类的常用方法

在这里插入图片描述

package reflection;

//测试class类创建方式有哪些
public class Test02 {
	public static void main(String[] args) throws ClassNotFoundException {
		Person person = new Student();
		
		//方式一:通过对象获得
		Class c1=person.getClass();
		
		//方式二:通过对象获得
		Class c2=Class.forName("reflection.Test02");
		
		//方式三:通过类名.class获得
		Class c3=Student.class;
		
		//方式四:基本内置类型的包装类都有一个Type属性
		Class c4=Integer.TYPE;
		
		//获得父类类型
		Class c5=c1.getSuperclass();
	}
}

class Person{
	String name;

	public Person(String name) {
		super();
		this.name = name;
	}

	public Person() {
		super();
	}

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

class Student extends Person{
	public Student(){
		this.name="学生";
	}
}

class Teacher extends Person{
	public Teacher(){
		this.name="老师";
	}
}

在这里插入图片描述

那些类型可以有Class对象

在这里插入图片描述

package reflection;

import java.lang.annotation.ElementType;

//所有类型的class
public class Test03 {
	public static void main(String[] args) {
		Class c1=Object.class;		//类
		Class c2=Comparable.class;	//接口
		Class c3=String[].class;	//一维数组
		Class c4=int[][].class;		//二维数组
		Class c5=Override.class;	//注解
		Class c6=ElementType.class;	//枚举
		Class c7=Integer.class;		//基本数据类型
		Class c8=void.class;		//void
		Class c9=Class.class;		//Class
		
		System.out.println(c1);
		System.out.println(c2);
		System.out.println(c3);
		System.out.println(c4);
		System.out.println(c5);
		System.out.println(c6);
		System.out.println(c7);
		System.out.println(c8);
		System.out.println(c9);
		
		//只要元素类型与维度相同,就是同一个Class
		int[] a=new int[10];
		int[] b=new int[100];
		System.out.println(a.getClass().hashCode());
		System.out.println(b.getClass().hashCode());
	}
}

在这里插入图片描述

Java内存分析

    • 存放new的对象和数组
    • 可以被所有线程共享,不会存放别的对象引用
    • 存放基本变量类型(会包含这个基本类型的具体数值)
    • 引用对象的遍历(会存放这个引用在堆里面的具体地址)
  • 方法区

    • 可以被所有的线程共享
    • 包含了所有的class和static遍历

类的加载过程

类的加载(Load) -> 类的链接(Link)->类的初始化(Initialize)

在这里插入图片描述

在这里插入图片描述

package reflection;

public class Test04 {
	public static void main(String[] args) {
		A a=new A();
		System.out.println(a.m);
		
		/*
		 * 1.方法区加载到内存,会产生一个类对应的Class对象
		 * 2.链接,链接结束后m=0,此时类刚刚加载,静态代码块还未执行
		 * 3.初始化
		 * 		<clinit>(){
		 * 			System.out.println("A类静态代码块初始化");
		 *			m=300;
		 *			m=100;
		 * 		}
		 * 
		 * 	m=100;
		 * 
		 */
	}
}

class A{
	static{
		System.out.println("A类静态代码块初始化");
		m=300;
	}
	//静态代码块和静态变量的执行先后顺序取决于代码的编写顺序
	static int m=100;
	
	
	public A(){
		System.out.println("A类的无参构造初始化");
	}
}
输出结果:
    A类静态代码块初始化
	A类的无参构造初始化
	100

什么时候发生类初始化

在这里插入图片描述

package reflection;

public class Test05 {
	static{
		System.out.println("Main类被加载");
	}
	
	public static void main(String[] args) throws ClassNotFoundException {
		//1.类主动引用
		//new	一个类的对象,调用静态成员(除了final常量)和静态方法
//		Son son=new Son();
		
		//反射调用
//		Class c=Class.forName("reflection.Son");
		
		//2.类的被动引用(不会产生类的引用的方法)
		//当访问一个静态域时,只有真正声明这个域的类才会被初始化
		//如:通过子类引用父类的静态变量,不会导致子类初始化。
//		System.out.println(Son.b);
		
		//通过数组定义类的引用,不会触发此类的初始化,数组只是一个名字,一个空间
//		Son[] array=new Son[5];
		
		//引用常量不会触发此类的初始化, 常量在链接层就存入调用类的常量池中
		System.out.println(Son.M);
	}
}

class Father{
	
	static int b=2;
	
	static{
		System.out.println("父类被加载");
	}
}

class Son extends Father{
	
	static{
		System.out.println("子类被加载");
		m=300;
	}
	
	
	static int m=100;
	static final int M=1;
}

类加载器的作用

简单来说类加载器的作用就是把类(class)装载进内存的。

在这里插入图片描述

双亲委派机制:在加载类时,一层层往上找,如果找到相应的加载器,则优先使用上层的加载器,没有则一层层返回

在这里插入图片描述

java平台核心库rt.jar,位于jdk1.8.0_211\jre\lib

package reflection;

public class Test06 {
	public static void main(String[] args) throws ClassNotFoundException {

		// 获取系统类的加载器
		ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
		System.out.println(systemClassLoader);

		// 获取系统类加载器的父类加载器->拓展类加载器
		ClassLoader parent = systemClassLoader.getParent();
		System.out.println(parent);

		// 获取拓展类加载器的父类加载器->根加载器(c++)
		ClassLoader parent1 = parent.getParent();
		System.out.println(parent1);

		// 测试当前类是哪个加载器加载的
		ClassLoader classLoader = Class.forName("reflection.Test06")
				.getClassLoader();
		System.out.println(classLoader);

		// 测试jdk内置类是哪个加载器加载的
		classLoader = Class.forName("java.lang.Object").getClassLoader();
		System.out.println(classLoader);

		// 如何获取系统类加载器可以加载的路径
		System.out.println(System.getProperty("java.class.path"));
	}
}

结果:
    sun.misc.Launcher$AppClassLoader@73d16e93
	sun.misc.Launcher$ExtClassLoader@15db9742
	null
	sun.misc.Launcher$AppClassLoader@73d16e93
	null
	D:\eclipse\Reflect-Study\bin

如何获取类的运行时结构

  1. 获取class对象
  2. 通过getMethods。getDeclaredMethods,getDeclaredMethod,getDeclaredConstructor来获取属性,方法和构造器
package reflection;

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

public class Test07 {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException {
		Class c1=Class.forName("reflection.User");
		
		//获得类的名字
		System.out.println(c1.getName());	//获得包名+类名
		System.out.println(c1.getSimpleName());   //获得类名
		
		//获得类的属性
		Field[] fields=c1.getFields();	//只能找到public属性
		
		fields=c1.getDeclaredFields();	//找到全部属性
		for (Field field : fields) {
			System.out.println(field);
		}
		
		//获得指定属性的值
		Field name=c1.getDeclaredField("name");
		System.out.println(name);
		
		//获得类的方法
		Method[] methods=c1.getMethods();	//获得本类及其父类的所有方法
		for (Method method : methods) {
			System.out.println("正常的:"+method);
		}
		methods=c1.getDeclaredMethods();	//获得本类的所有方法
		for (Method method : methods) {
			System.out.println("getDeclaredMethods"+method);
		}
		
		//获得指定方法
		//加入参数,避免由于重载发生错误
		Method getName=c1.getDeclaredMethod("getName", null);
		Method setName=c1.getMethod("setName", String.class);
		System.out.println(getName);
		System.out.println(setName);
		
		//获得指定的构造器
		Constructor[] constructors=c1.getConstructors();
		for (Constructor constructor : constructors) {
			System.out.println(constructor);
		}
		
		constructors=c1.getDeclaredConstructors();
		for (Constructor constructor : constructors) {
			System.out.println("-"+constructor);
		}
		
		//获得指定的构造器
		Constructor declaredConstructor=c1.getDeclaredConstructor(String.class,int.class,int.class);
		System.out.println("指定"+constructors);
	}
}

setAccessible

当需要频繁的使用反射时,可以关闭检测以提高效率

在这里插入图片描述

package reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test08 {
	//普通方法调用
	public static void test01(){
		User user=new User();
		
		long start=System.currentTimeMillis();
		
		for (int i = 0; i <10_0000_0000; i++) {
			user.getName();
		}
		
		long end=System.currentTimeMillis();
		
		System.out.println("普通方法执行1000000000需要的时间:"+(end-start)+"ms");
	}
	
	//反射方法调用,invoke执行方法,Accessible默认是打开检测
	public static void test02() throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		Class c=Class.forName("reflection.User");
		
		Method getName=c.getDeclaredMethod("getName", null);
		
		long start=System.currentTimeMillis();
		
		for (int i = 0; i <10_0000_0000; i++) {
			//invoke默认是false关闭
			getName.invoke(new User(), null);	//调用方法,(从底层方法被调用的对象 ,参数)
		}
		
		long end=System.currentTimeMillis();
		
		System.out.println("反射调用执行1000000000需要的时间:"+(end-start)+"ms");
	}
	
	//反射方法调用,Accessible关闭检测
	public static void test03() throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		Class c=Class.forName("reflection.User");
		
		Method getName=c.getDeclaredMethod("getName", null);
		getName.setAccessible(true);
		
		long start=System.currentTimeMillis();
		
		for (int i = 0; i <10_0000_0000; i++) {
			//invoke默认是false关闭
			getName.invoke(new User(), null);	//调用方法,(从底层方法被调用的对象 ,参数)
		}
		
		long end=System.currentTimeMillis();
		
		System.out.println("关闭检测执行1000000000需要的时间:"+(end-start)+"ms");
	}
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		test01();
		test02();
		test03();
	}
}
结果:
    普通方法执行1000000000需要的时间:3ms
	反射调用执行1000000000需要的时间:1846ms
	关闭检测执行1000000000需要的时间:1044ms

invoke

public Object invoke(Object obj,Object...args)

在具有指定参数的方法对象上调用此方法对象表示的基础方法。个别参数自动解包以匹配原始形式参数,原始参考参数和参考参数都需要进行方法调用转换。

如果底层方法是静态的,则指定的obj参数将被忽略。 它可能为null。

如果底层方法所需的形式参数的数量为0,则提供的args数组的长度为0或为空。

如果底层方法是一个实例方法,它将使用动态方法查找来调用。

如果方法正常完成,则返回的值将返回给调用者; 如果值具有原始类型,则首先将其适当地包装在对象中。 但是,如果该值具有基本类型的数组的类型,则该数组的元素不会包含在对象中; 换句话说,返回一个原始类型的数组。 如果底层方法返回类型为void,则调用返回null。

public static void test04() throws NoSuchMethodException,
		SecurityException, ClassNotFoundException, IllegalAccessException,
		IllegalArgumentException, InvocationTargetException {
	User user = new User();
	Class c = user.getClass();

	Method getName = c.getDeclaredMethod("getName", null);
	Method setName = c.getDeclaredMethod("setName", String.class);
	setName.invoke(user, "鲁迅");
	System.out.println(getName.invoke(user, null));
}

获取泛型信息

在这里插入图片描述

package reflection;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

//通过反射获取泛型
public class Test09 {
	public void test01(Map<String, User> map, List<User> list) {
		System.out.println("test01");
	}

	public Map<String, User> test02() {
		System.out.println("test02");
		return null;
	}

	public static void main(String[] args) throws NoSuchMethodException,
			SecurityException {
		Method method = Test09.class.getMethod("test01", Map.class, List.class);

		Type[] genericParameterTypes = method.getGenericParameterTypes(); // 获取方法的泛型参数类型
		for (Type genericParameterType : genericParameterTypes) {
			System.out.println("-" + genericParameterType);
			if (genericParameterType instanceof ParameterizedType) { // 判断参数泛型的类型是否为结构化参数化类型
				Type[] acutalTypeArguments = ((ParameterizedType) genericParameterType) // 将参数类型转化为结构化参数化类型
						.getActualTypeArguments(); // 获得真实参数信息
				for (Type acutalTypeArgument : acutalTypeArguments) {
					System.out.println(acutalTypeArgument);
				}
			}
		}

		method = Test09.class.getMethod("test02", null);

		Type genericParameterType = method.getGenericReturnType(); // 获取返回值的泛型参数类型

		if (genericParameterType instanceof ParameterizedType) { // 判断参数泛型的类型是否为结构化参数化类型
			Type[] acutalTypeArguments = ((ParameterizedType) genericParameterType) // 将参数类型转化为结构化参数化类型
					.getActualTypeArguments(); // 获得真实参数信息
			for (Type acutalTypeArgument : acutalTypeArguments) {
				System.out.println(acutalTypeArgument);
			}
		}
	}
}

结果:
    -java.util.Map<java.lang.String, reflection.User>
	class java.lang.String
	class reflection.User
	-java.util.List<reflection.User>
	class reflection.User
	class java.lang.String
	class reflection.User

反射操作注解

package reflection;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;


//反射操作注解
public class Test10 {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
		Class c=Class.forName("reflection.Student2");
		
		//通过反射获得注解
		Annotation[] annotations=c.getAnnotations();
		for (Annotation annotation : annotations) {
			System.out.println(annotation);
		}
		
		//获取注解value的值
		Table table=(Table) c.getAnnotation(Table.class);
		String value=table.value();
		System.out.println(value);
		
		//获得类指定的注解
		Field f=c.getDeclaredField("id");
		FieldHao annotation=f.getAnnotation(FieldHao.class);
		System.out.println(annotation.columnName());
		System.out.println(annotation.type());
		System.out.println(annotation.length());
	}
}

@Table("db_student")
class Student2 {	//此处只用于演示反射获取注解,为了方便就不连接数据库
	@FieldHao(columnName="db_id",type="int",length=10)
	private int id;
	@FieldHao(columnName="db_age",type="int",length=10)
	private int age;
	@FieldHao(columnName="db_name",type="int",length=10)
	private String name;

	public Student2() {
		super();
	}

	public Student2(int id, int age, String name) {
		super();
		this.id = id;
		this.age = age;
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

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

//类创建注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
	String value();
}

//属性的注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface FieldHao{
	String columnName();
	String type();
	int length();
}

结果:
    @reflection.Table(value=db_student)
    db_student
    db_id
    int
    10

    

反射API使用

package reflection;

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

import test.bean.User;

/**
 * 通过反射API动态的操作:构造器,方法,属性
 * 
 * @author 浅墨
 *
 */
@SuppressWarnings("all")
public class ReflectionText3 {
	public static void main(String[] args) {
		String path = "test.bean.User";

		try {
			Class<User> clazz = (Class<User>) Class.forName(path);

			// 通过动态调用构造方法,构造对象
			User u = clazz.newInstance();// 其实是调用了User的无参构造方法
			System.out.println(u);

			Constructor<User> c = clazz.getDeclaredConstructor(int.class,
					int.class, String.class);
			User u2 = c.newInstance(1000, 18, "许浩");
			System.out.println(u2.getUname());

			// 通过反射API调用普通方法
			User u3 = clazz.newInstance();
			Method method = clazz.getDeclaredMethod("setUname", String.class);
			method.invoke(u3, "许浩二");// 两行等同于u3.setUname("许浩二") invoke:激活

			System.out.println(u3.getUname());

			// 通过反射API操作属性
			User u4 = clazz.newInstance();
			Field f = clazz.getDeclaredField("uname");
			f.setAccessible(true);// 这个属性不需要安全检查,可以直接访问
			f.set(u4, "许浩三");// 通过反射直接写属性
			System.out.println(u4.getUname());// 通过反射直接读属性的值
			System.out.println(f.get(u4));

		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

结果:
    test.bean.User@15db9742
    许浩
    许浩二
    许浩三
    许浩三

猜你喜欢

转载自blog.csdn.net/qq_45879460/article/details/106529154