Java学习笔记Day10:反射

1.反射

1.1注解

1.1.1定义

Java注解:又称Java标注

  • Java 标注可以通过反射获取标注内容。
  • 在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容
1.1.2分类
  • 内置注解
  • 自定义注解

内置注解:

注解说明
@Override检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings指示编译器去忽略注解中声明的警告。
@Retention标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented标记这些注解是否包含在用户文档中。
@Target标记这个注解应该是哪种 Java 成员。
@Inherited标明注解是能被继承的,例如B继承了A,A添加了注解,那么B也会继承同样的注解
@SafeVarargsJava 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterfaceJava 8 开始支持,标识一个匿名函数或函数式接口。
@RepeatableJava 8 开始支持,标识某注解可以在同一个声明上使用多次。

@Retention,@Documented,@Target,@Inherited,@Repeatable:被称为元注解

Annotation 架构:

在这里插入图片描述

1.1.3应用:掌握

①注解可以存在于三个位置
1)保留在JAVA文件当中
2)保留在CLASS文件当中
3)在JVM执行过程当中依旧能够获取
在这里插入图片描述

②自定义注解通常需要使用反射来获取内容
③使用注解是必须掌握的技能,而开发注解不需要大家在日常工作当中,时常接触。

1.1.4开发:了解

①ElementType(枚举):指定注解的使用范围

类型说明
TYPE类、接口(包括注释类型)或枚举声明
FIELD字段声明(包括枚举常量)
METHOD方法声明
PARAMETER参数声明
CONSTRUCTOR构造方法声明
LOCAL_VARIABLE局部变量声明
ANNOTATION_TYPE注释类型声明
PACKAGE包声明

②RetentionPolicy(枚举):指定注解存活的位置

类型说明
SOURCEAnnotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了
CLASS编译器将Annotation存储于类对应的.class文件中。默认行为
RUNTIME编译器将Annotation存储于class文件中,并且可由JVM读入

③声明自定义注解:在接口关键字interface之前添加@符号

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {//声明一个自定义注解
	public String value1() default "";
	public int value2();	
	public Sex value3();
}

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {//声明一个自定义注解
	MyAnnotation3[] value();
}

@Repeatable(MyAnnotation2.class)
public @interface MyAnnotation3 {//声明一个自定义注解
	
}

@MyAnnotation1(value2=1,value3=Sex.MAN)
public class Person<T> {
	
	@MyAnnotation3
	@MyAnnotation3
	@MyAnnotation3
	private Integer id;
	
	@Deprecated
	public Person(Integer id) {
		this.id = id;
		int i1 = 1;
		int i2 = 2;
	}
	
//	@SafeVarargs
	@SuppressWarnings("unchecked")
	public void test1(T ... t){
		
	}
	
//	@SuppressWarnings("unchecked")
	@SafeVarargs
	public final void test2(T ... t){
		
	}
	
	public Integer getId() {
		return id;
	}
	
	public void setId(Integer id) {
		this.id = id;
	}

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

④默认情况下,注解是只存在于class当中的。如果要让该注解在实际开发中,被反射获取,那么我们需要使用元注解
1)什么是元注解:注解注解的注解,使用在注解上的一种注解
2)@Target:用于指定注解的使用范围,通常需要给定参数ElementType
3)@Retention:用于指定注解的存活区域,通常需要给定参数RetentionPolicy

1.1.5可以给注解当中添加属性:

a.声明抽象方法,方法名为属性名,方法返回值为属性类型
b.可以在抽象方法之后,添加default给定默认值,有默认值的属性可以不必一定给值

1.1.6常规的内置注解

a.@Deprecated:过时内容,所标注内容,不再被建议使用。
b.@Override:只能标注方法,表示该方法覆盖父类中的方法,用于检测该方法是否是重写的方法
c.@SuppressWarnings:所标注内容产生的警告,编译器会对这些警告保持静默,取消编译器的警告

1.1.7新特性注解

@FunctionalInterface:标识一个匿名函数或函数式接口。

@FunctionalInterface
public interface TestInterface {
	static void test1(){
		System.out.println("test1");
	}
	default void test2(){
		System.out.println("test2");
	}
	void test3();
}

public class Test {
	public static void main(String[] args) {
		TestInterface.test1();
		

		TestInterface i = new TestInterface() {
			@Override
			public void test3() {
				System.out.println("test3");
			}
		};
		
		i.test2();
		i.test3();
	}

}

@SafeVarargs:忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

在这里插入图片描述

@Repeatable:该注解为元注解,标识某注解可以在同一个声明上使用多次。

在这里插入图片描述

1.2反射

1.2.1什么是反射

动态加载类及其成员

① 常规情况下,一个类的执行过程,是从源文件开始,经过编译,然后加入类加载器当中,然后再执行改程序。属于是预先加载好,然后再执行。

② 反射是,程序已经在执行了,在执行过程当中,获取类,类的方法属性等等,然后再组织到当前的类加载器当中,动态执行。

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量(成员变量也称为类的属性)
Method类代表类的方法
Constructor类代表类的构造方法

在这里插入图片描述

获取Annotation类:

方法说明
getAnnotation(Class annotationClass)返回该类中与参数类型匹配的公有注解对象
getAnnotations()返回该类所有的公有注解对象
getDeclaredAnnotation(Class annotationClass)返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()返回该类所有的注解对象
1.2.2使用场景:框架开发,框架升级
1.2.3 开源框架的问题:安全性得不到保障
1.2.4Object:用于描述对象的类
1.2.5Class:用于描述类的类
方法说明
forName(String className)局部变量声明
newInstance()创建类的实例
getPackage()获得类的包
getSimpleName()获得类的名字
getSuperclass()获得当前类继承的父类的名字
getInterfaces()获得当前类实现的类或是接口

注意:setAccessible(true)设置是否允许通过反射访问

① 通过类名获取Class类,会有一个异常,类名不能定位到类,一般是在配置文件当中获取的字符串

② 通过对象来获取Class类,一般适用在传参的时候,或者参数数据类型不定的时候使用

③ 通过类来获取Class类,一般用于明确类型的参数传递

④ 使用newInstance的方法创建的实例,默认情况下实际上是调用的目标类的空参构造

1.2.6访问不可见成员时,需要指定访问权限
1.2.7Field:用于描述属性

获取Field类:

方法说明
getField(String name)获得某个公有的属性对象
getFields()获得所有公有的属性对象
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象

Field类的重要方法:

方法说明
equals(Object obj)属性与obj相等则返回true
get(Object obj)获得obj中对应的属性值
set(Object obj, Object value)设置obj中对应属性值
1.2.8Method:用于描述方法

获取Method类:

方法说明
getMethod(String name, Class…<?> parameterTypes)获得该类某个公有的方法
getMethods()获得该类所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes)获得该类某个方法
getDeclaredMethods()获得该类所有方法

Method类重要方法:

方法说明
invoke(Object obj, Object… args)传递object对象及参数调用该对象对应的方法
1.2.9Constructor:用于描述构造

获取Constructor类:

方法说明
getConstructor(Class…<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class…<?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法

Constructor类重要方法:

方法说明
newInstance(Object… initargs)根据传递的参数创建类的对象
1.2.10其他重要的方法
方法说明
isAnnotation()如果是注解类型则返回true
isAnnotationPresent(Class<? extends Annotation> annotationClass)如果是指定类型注解类型则返回true
isAnonymousClass()如果是匿名类则返回true
isArray()如果是一个数组类则返回true
isEnum()如果是枚举类则返回true
isInstance(Object obj)如果obj是该类的实例则返回true
isInterface()如果是接口类则返回true
isLocalClass()如果是局部类则返回true
isMemberClass()如果是内部类则返回true
1.2.11实例
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {//声明一个自定义注解
	public String value() default "";
}
@MyAnnotation1(value="shisan is good")
public class Person implements A{
	private Integer id;
	private String name;
	public Person() {
		System.out.println("空参");
	}
	public Person(Integer id) {
		this.id = id;
	}
	public Person(Integer id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public Integer getId() {
		return id;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public void setId(Integer id) {
		this.id = id;
	}
	
	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + "]";
	}
}

interface A{}



public class Test1 {
	public static void main(String[] args) throws Exception {
		//1.通过类名获取Class
		Class<?> c1 = Class.forName("com.m.demo02.Person");
		//2.通过对象获取Class
//		Object o = new Person();
//		Class<?> c2 = o.getClass();
		//3.通过类来获取Class
//		Class<?> c3 = Person.class;
		//创建实例
		Object obj1 = c1.newInstance();
		System.out.println(c1.getPackage());
		System.out.println(c1.getSimpleName());
		System.out.println(c1.getSuperclass());
		Class<?>[] is = c1.getInterfaces();
		System.out.println(Arrays.toString(is));
	}
}

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class<?> c1 = Class.forName("com.m.demo02.Person");
		Object obj1 = c1.newInstance();
//		Field id = c1.getField("id");//访问可见的成员
		Field id = c1.getDeclaredField("id");//访问所有成员
		//设定访问权限:运行通过反射来获取,不操作不报错
		id.setAccessible(true);
		id.set(obj1, 1);//设值
		System.out.println(id.get(obj1));//取值	
//		c1.getFields();
		for(Field f : c1.getDeclaredFields()){
			f.setAccessible(true);
			System.out.println(f.getName());
			System.out.println(f.getType().getSimpleName());
		}
	}
}

public class Test3 {
	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws Exception {
		Class<?> c1 = Class.forName("com.m.demo02.Person");	
		Object obj1 = c1.newInstance();
//		Method setId = c1.getMethod("setId", Integer.class);
//		setId.invoke(obj1, 11);//执行方法
//		Method getId = c1.getMethod("getId");
//		Object id = getId.invoke(obj1);
//		System.out.println(id);
		
		//模拟从XML当中读出来的数据,假设这里有什么数据,我们都不知道
		Map<String,String> xml = new HashMap<>();
		xml.put("id", "1");
		xml.put("name", "shisan");
		
		List<String> tnames = Arrays.asList(new String[]{//包装类
				Integer.class.getTypeName(),
				Double.class.getTypeName()
		});
		
		//思考题:如果是基本数据类型,怎么处理
		for(Field f : c1.getDeclaredFields()){
			String fname = f.getName();//属性名
			Object value = xml.get(fname);//属性值
			Class<?> ftype = f.getType();//属性类型
			String setMethodName = "set" + String.valueOf(fname.charAt(0)).toUpperCase() + fname.substring(1);
			Method setMethod = c1.getMethod(setMethodName, ftype);
			if(tnames.contains(ftype.getTypeName())){//包装类
				Constructor<?> c = ftype.getConstructor(String.class);//获取包装类构造new Integer("1")
				value = c.newInstance(value);//创建包装类对象
//				setMethod.invoke(obj1, ftype.getConstructor(String.class).newInstance(value));//new Integer("1")
				setMethod.invoke(obj1, value);//new Integer("1")
			}else{
				setMethod.invoke(obj1, value);
			}
		}
		System.out.println(obj1);
	}
}


public class Test4 {
	public static void main(String[] args) throws Exception {
		Class<?> c1 = Class.forName("com.m.demo02.Person");	
		Constructor<?> c = c1.getConstructor(Integer.class,String.class);//获取构造	
		Object obj1 = c.newInstance(1,"shisan");//通过构造来创建对象	
		System.out.println(obj1);
	}
}


public class Test5 {
	public static void main(String[] args) throws Exception {
		Class<?> c1 = Class.forName("com.m.demo02.Person");
		//获取注解
		MyAnnotation1 ma1 = c1.getAnnotation(MyAnnotation1.class);
		String v = ma1.value();
		System.out.println(v);		
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值