java基础第21天-反射和注解

  1. 反射:
    Class 类的实例表示正在运行的 Java 应用程序中的类和接口;
    枚举是一种类,注解(指的是注解Annotation)是一种接口;
    每个数组都是 Class字节码类中的一个具体 对象
    基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象;
    注意 :
    1、 Class类 和它的实例的产生: Class的对象是已经存在的类型,
    所以不能够直接new一个Class对象出来,是通过Class类中的一个方法获取到的。
    例如:通过全限定路径类名

     2、同一种类型不管通过什么方式得到Class的实例都是相等的;一个类型的字节码对象只有一份,在元空间。
     3、Class的实例就看成是Java中我们学过的所有的数据类型在JVM中存在的一种状态(字节码对象)
     String.class   int.class  List.class  int[].class
     
    
     1.概念:通过一个全限定类名,获取字节码文件
    
     2.作用:
     	1. 提高开发灵活度,提高程序的扩展性
     	2. 框架(提高开发效率的别人封装好的代码)底层都是使用反射技术。例如:Tomcat、Spring...
     	3. 缺点:破坏封装性,性能低下(以后,能不用反射技术就不用)
     
     3. 使用:(重点)
     	1. 获取字节码文件
     		1. 
     			1.1 Class clazz  = Class.forName(全限定路径名) (最多使用)默认就是调用下面的方法
     			1.2 static 类<?> forName(String name, boolean initialize, ClassLoader loader)  
     					name:全限定路径名
     					initialize:表示是否初始化,默认是false
     					loader:可以指定一个类加载器加载字节码文件
     		
     		2. 全限定类名.class
     		3. 对象名.getClass()
     		
     		Class类中方法newInstance():创建当前字节码对象(只能调用无参且是public修饰的构造方法)
     		
     		
     	2. 根据字节码文件获取构造方法、普通方法、字段等
     		构造方法
     		Constructor[] constructors = clazz.getConstructors() 获取public修饰的构造方法数组
     		Constructor[] constructors = clazz.getDeclaredConstructors() 获取任意权限的所有造方法数组
     		Constructor constructor = clazz.getConstructor(Class 参数字节码)根据参数类型获取public修饰的指定的的构造方法
     		Constructor constructor = clazz.getDeclearConstructor(Class  参数字节码) 获取任意访问权限指定的构造方法
     
     		//通过构造方法对象去用构造方法创建对象 => 相当于new 一个对象
     		Object instance  = constructor.newInstance(Object 实参);//可以创建任意访问权限的有参或者无参构造
     
     		普通方法
     		Method[] methods = clazz.getMethods() 获取public修饰的构造方法数组,有父类中的方法
     		Method[] methods = clazz.getDeclaredMethods() 获取任意访问权限所有造方法数组,并且都是自己的方法
     		Method method = clazz.getMethod(String methodName,Class... 参数字节码)根据方法名和参数类型获取指定的的方法
     			methodName:方法名
     			Class:形参类型。如果方法没有形参,则Class可变参数不用写
     		
     		Method method = clazz.getDeclaredMethod(String methodName,Class... 参数字节码)根据方法名和参数类型获取指定的的方法
     			methodName:方法名
     			Class:形参类型。如果方法没有形参,则Class可变参数不用写
     		
     		//通过普通方法对象调用执行方法
     		method.invoke(Object obj,Object... args);
     			obj:对象。如果是对象的方法,就传入一个当前字节码创建的对象,如果是static方法,则写null
     			args:就是具体实参
     		
     		字段
     			Field[] fields = clazz.getFields() 获取public修饰的字段
     			Field[] fields = clazz.getDeclaredFields() 获取任意权限所有字段
     			Field field = clazz.getDeclaredField(String fieldName) 根据字段名获取任意访问权限的指定字段
     			Field field = clazz.Field(String fieldName)根据字段名获取public的指定字段
     		
     		//通过当前的字段对象,给某一个字段赋值取值
     		  	field.get(Object obj);//如果是属于非static,就传入一个对象,如果是静态的,就传入null
     		  		obj:对象
     			field.set(Object obj, Object value);//如果是属于非static,就传入一个对象,如果是静态的,就传入null
     		  		obj:对象
     				value:值
     		
     		
     	String getName()  获取全限定类名(全限定,包含包名)	Class类中方法
     	String getSimpleName()   获取类名简称			Class类中方法
     	Package getPackage()   获取包名				Class类中方法
     	T newInstance()  根据当前字节码创建对应的对象		Class类中方法
    
     注意:
     	1. Class类中方法newInstance():创建当前字节码对象(只能调用无参且是public修饰的构造方法)
     	2. Constructor类中方法newInstance(Object 实参);//可以创建任意访问权限的有参或者无参构造
     	3. 私有化方法、字段、构造方法都必须破坏封装才能使用:
     		public void setAccessible(boolean flag) true表示破坏封装,false是不破坏
     		
     		是哪个private修饰的方法、字段、构造方法需要执行,就需要用这个对象破坏哪一个的封装
     		例如:
     			//获取cn.itsource.reflect.User字节码文件
     			Class<?> clazz = Class.forName("cn.itsource.reflect.User");
     			//获取User的有String参构造
     			Constructor<?> constructor = clazz.getConstructor(String.class);
     			//调用有参String构造,创建一个User对象
     			Object newInstance = constructor.newInstance("某文");
     			//获取private修饰的方法:testPrivate
     			Method method2 = clazz.getDeclaredMethod("testPrivate");
     			method2.setAccessible(true);//破坏普通方法Method封装
     
     			//破坏封装后就可以执行了
     			Object invoke2 = method2.invoke(newInstance);//没有形参就不用写
     			System.out.println(invoke2);
     			
     	4. 调用static方法、字段:
     		例如:
     			Field field = clazz.getDeclaredField("country");//获取任意访问权限静态变量country
     			field.set(null, "中国");//因为字段country是static修饰,所以不用对象调用,就传入null。字段country赋值:中文
     			Object object = field.get(null);//字段country取值
     			System.out.println(object);	
    
  2. 注解:
    1.概念: 就是一个标签,有标签后,就具有某一些标签的特性。
    本质就是跟类、接口、枚举平级的新结构
    2.作用:
    1. 帮助程序员校验代码
    2. 可以提高开发效率和程序的扩展性
    @Test
    @Before
    @After

     	3. 可以生成文档说明 api
     		操作步骤:
     			1.选中项目/代码,右键选中Export
     			2.输入Javadoc
     			3.第一个next(可以设置生成文档注释的目录),第二个next,设置字符集
     				如果是UTF-8编码,且有中文,请输入-encoding UTF-8 -charset UTF-8   
     			4. 勾选一个生成完毕后,直接通过浏览器打开的选项
     			5. finish
     3.使用:(重点)
     	1. 使用jdk或者别人定义好的标签
     		@ + 注解的名称  -- 比如@Override ->注解
    
     	2. 使用自定义的标签
     			1.JDK的元注解:就是专门用来声明其他注解的注解
     				作用:通过元注解了解其他注解的使用特点,还可以自定义注解
     			2.元注解: 
     				1.
     				@Target
     				@Target 作用
     					用来限制被修饰注解的使用范围,即注解可以在类的哪些成员上使用
     				@Target 取值使用ElementType.()
     					1.	CONSTRUCTOR:可以在构造器上使用注解
     					2.	FIELD:可以在字段上使用注解
     					3.	LOCAL_VARIABLE:可以在局部变量上使用注解
     					4.	METHOD:可以在普通方法上使用注解
     					5.	PACKAGE:可以在包上使用注解
     					6.	PARAMETER:可以在参数列表上使用注解
     					7.	TYPE:可以在类、接口(包括注解类型) 或enum上使用注解
     					
     					例如:@Target(ElementType.METHOD)//意味着@Override只能在普通方法上使用
     						public @interface Override {
     						}
     				2.
     				@Retention
     				@Retention 作用
     					限制注解的作用时间
     					注解的生命周期和三个阶段有关:源代码阶段、CLASS文件中有效、运行时有效,
     					故其取值也就三个值,分别代表着三个阶段
     				@Retention RetentionPolicy.(枚举)
     					1.	SOURCE:在源文件中有效(即源文件保留),这个一般用来校验代码
     					2.	CLASS:在class文件中有效(在源文件和字节码文件时有作用)
     					3.	RUNTIME:在运行时有效(在源文件、字节码文件、运行的时候起作用)
     						一般是通过反射技术使用,通过程序的扩展性
     					
     					例如:@Retention(RetentionPolicy.SOURCE)//编译期有效,主要用来校验代码
     						public @interface Override {
     						}
     					
     				3.
     				@Documented
     				@Documented 作用:文档注解,可以使用javadoc生成文档注释
     					Documented是一个标记注解,没有成员。
     				4.
     				@Inherited
     				@Inherited 作用:从该类的子类上可以继承父类的注解
     					假设一个注解在定义时,使用了@Inherited,然后该注解在一个类上使用,
     					如果这个类有子类,那么通过反射我们可以从类的子类上获取到同样的注解
     				
     		自定义注解声明步骤:
     			1. 创建一个注解类@Annotation:
     			2. 在类上用元注解修饰(注意,根据情况,决定写几个)
     				@Target({ElementType.TYPE,ElementType.METHOD})
     				@Retention(RetentionPolicy.RUNTIME)
     				@Documented 
     				@Inherited 
     			3. 描述注解的取值(声明一些属性)
     			
     			语法:
     				@4大元注解
     				public @interface 注解名{
     					类型 属性名()  default 默认值; //default表示一个默认取值,可以不写
     				}
     				数据类型支持:数组、String、8个基本类型、枚举
     				属性名:见名知意,小驼峰,如果只有一个属性,建议声明为:value
     				因为使用的时候,value可以省略。
     				
     		使用注解
     			@注解名(属性名=值)//类型要一致
     			@注解名(属性名={值1,值2})  //注解类型为数组,如果属性名是value可以省略
     			
     		例如:
     				声明自定义注解:
     				//@Target(value=ElementType.TYPE)//value可以省略
     				//只有一个属性名的时候且名字是value的时候,value可以省略。因为Target注解,取值的数据类型是数组,所以,多个值用{}括起来
     				@Target({ElementType.TYPE,ElementType.METHOD})//表示,只能在类和普通方法上使用
     				@Retention(RetentionPolicy.RUNTIME)//在运行期一直发生作用
     				@Documented //表示使用Vip注解后,可以生成文档说明
     				@Inherited //表示如果一个类A使用了注解@Vip,则A类的子类,会继承到该注解@Vip
     				public @interface Vip {
     					/
     					  	类型 属性名()  default 默认值; //default表示一个默认取值,可以不写
     					  	数据类型只能有:数组、String、8个基本类型、枚举
     					  	属性名:见名知意,小驼峰,如果只有一个属性,建议声明为:value
     					  	因为使用的时候,value可以省略。
     					  
     					  枚举类型比较符合Vip取值
     					 /
     				//	Level value();//可以不写default,写就表示有默认值
     				//	String test() default "test";//这里可以写多个 属性
     				//	int test2();//这里可以写多个 属性
     					Level value() default Level.ONE;//表示vip默认级别是ONE
     				}	
     				
     				使用自定义注解:
     					//@Vip(value=Level.FIVE) //表示每一个User对象都有一个Vip注解,可以不用默认值
     					@Vip //表示每一个User对象都有一个Vip注解,且默认值都是 Level.ONE
     					public class User {}
     				
     4. 四大内置注解(了解,掌握@Override)
     	@Override 注解用于标识方法重写,帮助我们校验代码
     	@Deprecated 注解用于标识方法已过时,只要是在版本迭代中使用。
     	
     	以下作为了解
     	@SuppressWarnings 注解用于抑制编译器警告,直白的讲就是消除Eclipse上那条黄色警告线,
     	但编译器里关于这条警告的信息还在。没什么用
     	代码示例
     	List list=null;
     	
     	@SafeVarargs 抑制堆污染警告(单独警告),直白的讲就是使用泛型和可变参数搭配使用将会产生的
     	编译器警告,可以使用@SuppressWarnings消除警告,后来在Java7引入了新的注解,
     	就是@SafeVarargs,如果开发人员确信某个使用了可变参数的方法,
     	在与泛型类一起使用时不会出现类型安全问题,就可以用这个注解进行声明,这样编译器就不会报警告。		
    

    扩展:
    用反射优化简单工厂模式

发布了30 篇原创文章 · 获赞 1 · 访问量 862

猜你喜欢

转载自blog.csdn.net/weixin_45061669/article/details/104906648