Java注解和反射

Java注解[Annotation]和反射

注解

JDK5.0引入

不是程序本身,可以对程序作出解释,这一点和注释[comment]没什么区别

可以被其他程序(比如:编译器)读取

@Override 就是个重写注解

内置注解

@Override 修辞方法 打算重写

@Deprecated 修辞方法 不赞成不鼓励使用,因为有危险或者有更好的办法

​ 被它修饰的东西,在使用时,会有一道横线画在名字上

@SuppressWarnings 抑制编译时的警告信息,需要选择一个参数才能正确使用

​ @SuppressWarnings(“all”)

​ @SuppressWarnings(“unchecked”)

​ @SuppressWarnings(value={“unchecked”,“deprecation”})

元注解 meta-annotation

@Target 描述注解使用范围

@Retention 表示需要在什么级别保存该注释信息,用于描述注解的生命周期

​ (SOURCE < CLASS < RUNTIME)

@Documented 说明该注解将被包含在javadoc中

@Inherited 说明子类可以继承父类的该注解

自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

Annotation在哪里使用?

可以附加在package、class、method、field等上面,相当于给他们添加了额外的辅助信息

我们可以通过反射机制编程实现对这些元数据的访问

Java.Reflection 反射机制

Java不是动态语言,但可以称为准动态语言,即Java具有一定的动态性、可以用反射机制来实现。

反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法(比如获得private修饰的方法)

优点:可以实现动态创建对象和编译,具有很大的灵活性

缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

//什么叫反射
public class Test01 extends Object{
	public static void main(String[] args) throws ClassNotFoundException {
		// 通过反射获取类的Class对象
		// 快捷键 shift+command+L 查看所有指令快捷键
		//       option+command+L 自动生成变量接收函数返回的对象
		Class c1 = Class.forName("Reflection.User");
		Class c2 = Class.forName("Reflection.User");
		Class c3 = Class.forName("Reflection.User");
		Class c4 = Class.forName("Reflection.User");
		
		System.out.println(c1);
		
		//一个类在内存中只有一个class对象
		//一个类被加载后,整个类的结构都会被封装在class对象中
		System.out.println(c2.hashCode());
		System.out.println(c3.hashCode());
		System.out.println(c4.hashCode());

		System.out.println(c2.getClass());
	}
}

获得Class类的几种方法

//测试Class类的创建方式有哪些
public class Test02 {
	public static void main(String[] args) throws ClassNotFoundException{
		Person person = new Student();
		System.out.println("这个人是:"+person.name);
		//方式一、通过对象获得
		Class class1 = person.getClass();
		System.out.println(class1.hashCode());
		//方式二、forname获得
		Class class2 = Class.forName("Reflection.Student");
		System.out.println(class2.hashCode());
		//方式三、通过类名.class获得
		Class class3 = Student.class;
		System.out.println(class3.hashCode());
		
		//方式四、基本内置类型的包装类都有一个Type属性
		Class<Integer> class4 = Integer.TYPE;
		System.out.println(class4);
		System.out.println(class4.hashCode());
		
		//获得父类类型
		Class superclass = class1.getSuperclass();
		System.out.println(superclass);
	}
}

哪些类型可以有class对象?

public static void main(String[] args) {
		// TODO Auto-generated method stub
		Class class1 = Object.class;// 类
		Class class2 = Comparable.class;// 接口也有class
		Class class3 = String[].class;// 一维数组
		Class class4 = int[][].class;// 二维数组
		Class class5 = Override.class;// 注解有class
		Class class6 = ElementType.class;// 枚举也有class
		Class class7 = Integer.class;// 基本属性类型
		Class class8 = void.class;// 空类型也有class对象
		Class class9 = Class.class;// Class本身
		
		System.out.println(class1);
		System.out.println(class2);
		System.out.println(class3);
		System.out.println(class4);
		System.out.println(class5);
		System.out.println(class6);
		System.out.println(class7);
		System.out.println(class8);
		System.out.println(class9);
		
		int[] a = new int[10];
		int[] b = new int[100];
		//两个长度不同的数组,其class对象也是相同的,故只要元素类型与维度一样,就是同一个Class。
		System.out.println(a.getClass().hashCode() == b.getClass().hashCode());
	}

类的加载的理解

加载

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象

链接

将Java类的二进制代码合并到JVM的运行状态之中的过程

验证

确保加载的信息符合JVM规范, 没有安全问题

准备

正式为类变量(static)分配内存并设置初始值,这些内存都将在方法区中进行分配。

解析

虚拟机常量池内的符号引用(常量名)替换为直接饮用(地址)

初始化

执行类构造器()方法的过程,此方法是由编译器自动收集类中所有变量的赋值动作和静态代码块中的语句合并产生。

虚拟机会保证此方法正确的同步和加锁

总结

当你new一个自己写的对象的时候,共有三步。首先加载此类,此时创建了此类的class对象

然后进行链接,此时给变量赋初值0,并使其拥有地址。

最后创建对象,创建对象时通过class对象知道自己应该拥有哪些方法和变量,创建结束后执行clinit方法,其将所有静态代码块和所有赋值语句进行合并,然后执行。

至此,new这句话就结束了。

什么时候会发生类的初始化

类的主动引用,一定会发生类的初始化

·当虚拟机启动,初始化main方法所在的类

·new一个类对象

· 调用类的静态成员和静态方法会发生初始化,但是final常量并不会发生类的初始化

·使用反射调用

·初始化一个类,会首先初始化其父类【这一点应该和C++非常不同,因为C++存在虚基类的概念】

类的被动调用,不会发生类的初始化

·当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量时,不会导致子类初始化>因为父类的静态变量在链接阶段时已经被加载,而子类调用父类的静态变量,则可以直接使用这个引用,而不用初始化自己。

System.out.println(Son.b);
//其中,Son继承自Father类,b是Father类的静态变量

·通过数组定义类引用,不会触发此类的初始化,因为没有真正的new,只是起了个名字

·引用常量不会触发此类的初始化,因为常量在链接阶段就已经存入调用类的常量池中

获取类的运行时结构

package Reflection;

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

//获得类的信息
public class Test05 {
	public static void main(String[] args)
			throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException {
		Class class1 = Class.forName("Reflection.User");

		// 获得类的名字
		System.out.println(class1.getName()); // 包名+类名
		System.out.println(class1.getSimpleName()); // 类名
		System.out.println("==========");
		// 获得类的属性
		Field[] fields = class1.getFields(); // 只能找到public属性
		for (Field field : fields) {
			System.out.println(field);
		}

		System.out.println("==========");

		fields = class1.getDeclaredFields(); // 能找到全部属性
		for (Field field : fields) {
			System.out.println(field);
		}

		// 获得指定属性的值
		System.out.println("==========");
		Field name = class1.getDeclaredField("name");
		System.out.println(name);

		// 获得类的方法
		System.out.println("==========");
		Method[] methods = class1.getMethods();// 获得本类及其父类的全部public方法
		for (Method method : methods) {
			System.out.println("正常的" + method);
		}
		Method[] declaredMethods = class1.getDeclaredMethods();// 获得本类的所有方法,包括private的方法
		for (Method method : declaredMethods) {
			System.out.println("getDeclaredMethods的" + method);
		}

		// 获得指定的方法
		Method getName = class1.getMethod("getName", null);
		Method setName = class1.getMethod("setName", String.class);// 第二个参数为要找的方法的参数类型,因为有重载的存在
		System.out.println(getName);
		System.out.println(setName);

		// 获得所有的构造器
		System.out.println("==========");
		Constructor[] constructors = class1.getConstructors();//public
		for (Constructor constructor : constructors) {
			System.out.println(constructor);
		}
		System.out.println("==========");
		constructors = class1.getDeclaredConstructors();//全部
		for (Constructor constructor : constructors) {
			System.out.println(constructor);
		}
		// 获得指定的构造器
		System.out.println("==========");
		Constructor constructor = class1.getConstructor(String.class,Integer.TYPE,int.class);
		System.out.println(constructor);
	}
}

有了Class对象,实际操作中可以做什么?

//动态的创建对象,通过反射
public class Test06 {

	public static void main(String[] args)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException,
			SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
		Class c1 = Class.forName("Reflection.User");
		// 构造一个对象
		User user = (User) c1.newInstance(); // 本质上调用了无参构造器,所以必须有一个无参构造器才行
		System.out.println(user);

		// 通过构造器创建对象
		Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, Integer.TYPE);
		Object user2 = declaredConstructor.newInstance("琴江", 001, 12);
		System.out.println(user2);

		// 通过反射调用普通方法
		// 首先通过反射获取一个方法
		User user3 = (User) c1.newInstance(); // 本质上调用了无参构造器,所以必须有一个无参构造器才行
		Method declaredMethod = c1.getDeclaredMethod("setName", String.class);
		// invoke :激活的意思
		// (对象,“方法的值”)
		declaredMethod.invoke(user3, "沈冉");
		System.out.println(user3.getName());

		// 通过反射操作属性【不能直接操作私有属性】
		System.out.println("===========");
		User user4 = (User) c1.newInstance();
		Field declaredField = c1.getDeclaredField("name");
		//因为declaredField是User类里的name属性,它是私有的,所以应该设置为可访问(关掉程序安全检测)
		declaredField.setAccessible(true);
		declaredField.set(user4, "沈小冉");
		System.out.println(user4.getName());
	}

}

性能问题

对于频繁要调用的项,setAccessible设置为true可以提高效率

对于性能而言:

普通直接调用 >> 反射关掉检测的效率 > 不关检测的反射

通过反射获取泛型

//通过反射获取泛型
public class Test07 {
	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 = Test07.class.getMethod("test01", Map.class,List.class);
		//通过方法获得泛型参数类型或者泛型返回值类型
		Type[] genericParameterTypes = method.getGenericParameterTypes();
		for(Type genericParameterType:genericParameterTypes){
			System.out.println("#"+genericParameterType);
			//通过泛型判断是不是参数化类型,如果是参数化类型,则获得它的真实类型
			if(genericParameterType instanceof ParameterizedType){
				Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
				for(Type actualTypeArgument:actualTypeArguments){
					System.out.println(actualTypeArgument);
				}
			}
			System.out.println("=========");
		}
		
		Method method2 = Test07.class.getMethod("test02", null);
		Type genericReturnType = method2.getGenericReturnType();
		if(genericReturnType instanceof ParameterizedType){
			System.out.println(genericReturnType);
			Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
			for(Type acType : actualTypeArguments){
				System.out.println(acType);
			}
		}

	}
	
}

反射操作注解,获取注解信息

ORM是对象关系映射,利用注解和反射完成类和表结构的映射关系。可以通过注解知道表名、列名等,这样在实际操作中就可以拼成一个sql语句了。

首先自定义注解

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
	String value();
}
////属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface tableField{
	String column();
	String type();
	int length();
}

然后定义表类

//练习反射操作注解
public class Orm {
//学生表
@Table("db_student")
class Student2 {
	@tableField(column="db_id",type="int",length=10)
	private int id;
	@tableField(column="db_age",type="int",length=10)
	private int age;
	@tableField(column="db_name",type="varchar",length=3)
	private String 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 + "]";
	}	
}

实践

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
		Class class1 = Class.forName("Reflection.Student2");
		//通过反射获得注解
		Annotation[] annotations = class1.getAnnotations();
		for(Annotation annotation : annotations){
			System.out.println(annotation);
		}
		//获得注解的value的值
		//Annotation annotation = class1.getAnnotation(Table.class);
		Table tableAnnotation = (Table)class1.getAnnotation(Table.class);
		String value = tableAnnotation.value();
		System.out.println(value);
		
		
		//获得类指定的注解
		Field f = class1.getDeclaredField("name");
		tableField annotation = f.getAnnotation(tableField.class);
		System.out.println(annotation);
		System.out.println(annotation.column());
		System.out.println(annotation.type());
		System.out.println(annotation.length());
	}

}

你可能感兴趣的:(java,java)