当前位置:网站首页>反射注解基础
反射注解基础
2022-08-03 05:09:00 【*super】
一、反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
1.字节码文件对象的创建
Class类:
获取class对象的三种方式
方式一:通过Object类中的getOobject()方法
public static void main(String[] args) { Person p =new Person(); Class c = p.getClass();//获取到Person对应的Class类的对象 System.out.println(c); }
方式二:通过类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
public static void main(String[] args) { Person p =new Person(); Class c1 = Person.class; System.out.println(c1); }
方式三:通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
public static void main(String[] args) throws ClassNotFoundException { Person p =new Person(); Class c3= Class.forName("fan.Person"); System.out.println(c3); }
2.通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过class类中提供的方法获取构造方法:
package fan;
import java.lang.reflect.Constructor;
/**
* @Author:张金贺
* @Date:2022/7/7 5:26
* @Version 1.0
*/
//获取Persor的构造方法
public class demo02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.获取Person的字节码文件对象
Class c= Class.forName("fan.Person");
// Constructor[] con= c.getConstructors();//获取到Person类的所有public构造方法
Constructor[] con1=c.getDeclaredConstructors();//获取到Person类的所有构造方法
for (Constructor constructor : con1) {
System.out.println(constructor);
}
Constructor con2=c.getConstructor(String.class);//获取某一个public构造方法
System.out.println(con2);
Constructor con3=c.getDeclaredConstructor(String.class,Integer.class);//获取某一个private构造方法
System.out.println(con3);
}
}
3.通过构造方法创建对象
package fan;
import java.lang.reflect.Constructor;
/**
* @Author:张金贺
* @Date:2022/7/7 5:38
* @Version 1.0
*/
public class demo03 {
public static void main(String[] args) throws Exception {
Class c= Class.forName("fan.Person");
//获取到Person的空参构造方法
Constructor con1= c.getConstructor(null);
//执行构造方法创建对象
Object obj= con1.newInstance(null);
System.out.println(obj);
//获取到Person的私有构造方法
Constructor con2=c.getDeclaredConstructor(String.class,Integer.class);
//取消访问控制
con2.setAccessible(true);
Object obj1=con2.newInstance("张三",22);
System.out.println(obj1);
}
}
4.获取成员变量
public static void main(String[] args) throws ClassNotFoundException { Class c= Class.forName("fan.Person"); //获取所有public成员变量 Field[] fs= c.getFields(); for (Field f : fs) { System.out.println(f); } //获取所有成员变量 Field[] fs1 = c.getDeclaredFields(); for (Field field : fs1) { System.out.println(field); } }
获取和设詈成员变量
public static void main(String[] args) throws Exception { //1.获取Person的字节码对象 Class c= Class.forName("fan.Person"); //2.获取构造方法 Constructor con = c.getDeclaredConstructor(String.class, Integer.class); //3.取消访问限制 con.setAccessible(true); //4.执行构造方法创建对象 Object obj = con.newInstance("张三", 22); //5.获取公共成员变量 Field f1 = c.getField("address"); //6.给address赋值 f1.set(obj,"河北"); //7.取出address的值 System.out.println(f1.get(obj)); //8.获取name和age的值 Field f2 = c.getDeclaredField("name"); f2.setAccessible(true); f2.get(obj); System.out.println(f2.get(obj)); }
5.通过反射获取成员方法并使用
public static void main(String[] args) throws Exception { Class c= Class.forName("fan.Person"); // Method[] methods =c.getMethods();//获取person类里所有public方法 Method[] methods =c.getDeclaredMethods();//获取person类里所有方法 for (Method method : methods) { System.out.println(method); } Method m1=c.getMethod("method1",null); System.out.println(m1); Method m2=c.getMethod("method2",String.class); System.out.println(m2); }
执行方法:
public static void main(String[] args) throws Exception { Class c= Class.forName("fan.Person"); Constructor con = c.getConstructor(null);//获取空参的构造方法 Object obj =con. newInstance(null);//创建对象 Method m1 = c.getMethod( "method1",null); m1. invoke(obj,null);//执行无参数的方法 Method m2 = c.getMethod( "method2",String.class) ; m2. invoke(obj,"hello"); Method m3 = c.getDeclaredMethod( "method3",null);//获取私有的方法 m3.setAccessible(true);//取消访问限制 m3.invoke(obj,null); }
6.泛型擦除
思考,将已存在的ArrayList<integer>集合中添加一个字符串数据,如何实现呢?
其实程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素。
public static void main(String[] args)throws Exception { ArrayList<Integer> arr = new ArrayList<Integer>(); arr.add(3); arr.add(5); // arr.add("hello""); Class c = Class.forName("java.util.ArrayList"); Method m = c.getMethod("add", Object.class); m.invoke(arr,"he11o"); System.out.println(arr); }
7.反射配置文件
(1)people.txt
className=fan.Person methodName=method1
(2)psvm
public static void main(String[] args) throws Exception { Properties properties =new Properties(); properties.load(new FileInputStream("people.txt")); String className = properties.getProperty("className"); String methodName=properties.getProperty("methodName"); Class c= Class.forName("className"); Constructor con =c.getConstructor(null); Object obj=con.newInstance(null); Method m=c.getMethod(methodName,null); m.invoke(obj,null); }
二、注解
注解(Annotation)是一种标记性的接口,注解本质上是一个接口,它维承了Annotation接口,注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
1.预置注解@Deprecated
public class Hero { @Deprecated public void say(){ System.out.println("say"); } public void run(){ System.out.println("run"); } @Override public String toString() { return "Hero{}"; } }
public static void main(String[] args) { Hero h=new Hero(); h.say();//已经过时 h.run(); }
2.元注解
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
public @interface TestAnnoation { }
@Retention(RetentionPolicy.SOURCE)
Retention 的英文意为保留期的意思。当@Retention应用到一个注解上的时候,它解释说明了这个注解的的存活时间。
它的取值如下:
RetentionPolicy.SOURCE注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到JVM中。 RetentionPolicy.RUNTIME注解可以保留到程序运行的时候,它会被加载进入到JVM中,所以在程序运行时可以获取到它。
@Documented;
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到Javadoc 中去。
@Target
Target是目标的意思,@Target指定了注解运用的地方。
当一个注解被@Target注解时,这个注解就被限定了运用的场景。类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为@Target 的存在,它张贴的地方就非常具体了,比如只台张贴到方法上、类上、方法参数上等等。
@Target有下面的取值:
ElementType.ANNOTATION_TYPE可以给一个注解进行注解。
ElementType.CONSTRUCTOR 可以给构造方法进行注解。
ElementType.FIELD可以给属性进行注解。
ElementType.LOCAL_VARIABLE可以给局部变量进行注解。
ElementType.METHOD可以给方法进行注解。
ElementType.PACKAGE可以给一个包进行注解.
ElementType.PARAMETER 可以给一个方法内的参数进行注解。
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举。
@lnherited
Inherited是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被@Inherited注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
3.注解的属性
注解既然本质上是接口,那么注解里面自然就是抽象方法,也被叫做属性。
public @interface TestAnnoation { int id() default -1; String message(); }
@TestAnnoation(id=1,message = "hello")//一个属性可以直接写值
4.类上注解的提取
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Inherited public @interface TestAnnoation { int id() default -1; String message() default "hello"; }
@TestAnnoation public class Test { public static void main(String[] args) { boolean isHas = Test.class.isAnnotationPresent(TestAnnoation.class);//判断Test类上是否有@TestAnnoation注解 if (isHas){ TestAnnoation ann = Test.class.getAnnotation(TestAnnoation.class);//获取到TestAnnoationi注解 System.out.println(ann.id()); System.out.println(ann.message()); } } }
5.属性上注解的提取
@Retention(RetentionPolicy.RUNTIME) public @interface Check { String value(); }
public class Test2 { @Check(value="hi") int a; public static void main(String[] args) throws Exception { Field f = Test2.class.getDeclaredField("a");//获取到属性a f.setAccessible(true); Check c = f.getAnnotation(Check.class); System.out.println(c.value()); } }
6.方法上注解的提取
@Retention(RetentionPolicy.RUNTIME) public @interface PerForm { }
public class Test2 { @Check(value="hi") int a; @PerForm public void test1(){ System.out.println("test1"); } public static void main(String[] args) throws Exception { Method m = Test2.class.getDeclaredMethod("test1"); m.setAccessible(true); Annotation[] anns = m.getAnnotations(); for (int i=0;i<anns.length;i++) { System.out.println(anns[i].annotationType().getSimpleName()); } } }
7.注解案例
我要写一个测试框架,测试程序员的代码有无异常。
程序员A:我写了一个类,它的名字叫做NoBug,因为它所有的方法都没有错误。
我:自信是好事,不过为了防止意外,让我测试一下如何?
程序员A:怎么测试?
我:把你写的代码的方法都加上 @Check这个注解就好了。
程序员A:好的。
package zhu.anli;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @Author:张金贺
* @Date:2022/7/7 7:27
* @Version 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
package zhu.anli;
import zhu.Check;
/**
* @Author:张金贺
* @Date:2022/7/7 7:26
* @Version 1.0
*/
public class Calculater {
@Check
public void add(){
System.out.println("add");
}
@Check
public void sub(){
int i= 1/0;
System.out.println("sub");
}
@Check
public void times(){
System.out.println("times");
}
}
package zhu.anli;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Author:张金贺
* @Date:2022/7/7 7:30
* @Version 1.0
*/
public class Test {
public static void main(String[] args) {
Calculater cal =new Calculater();
Class c = cal.getClass();//获取到Calculater的字节码对象
Method[] m = c.getMethods();//获取Calculater中的所有方法
int errNum=0;//记录出错次数
StringBuilder builder=new StringBuilder();//记录出错的信息
for (Method method : m) {
if (method.isAnnotationPresent(Check.class)){
try {
method.invoke(cal,null);
} catch (Exception e) {
errNum++;
builder.append("出错的方法是:"+method.getName()+"错误是:"+e.getCause().getMessage());
}
}
}
System.out.println("出错了"+errNum+"次");
System.out.println(builder.toString());
}
}
8.注解应用junit
Junit测试框架
测试分类:
黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
白盒测试:需要写代码的,关注程序具体的执行流程。
package zhu.jisuan;
/**
* @Author:张金贺
* @Date:2022/7/7 7:47
* @Version 1.0
*/
public class ji {
public int add(int num1,int num2){
return num1+num2;
}
public int sub(int num1,int num2){
return num1-num2;
}
public int mul(int num1,int num2){
return num1*num2;
}
}
package zhu.jisuan;
/**
* @Author:张金贺
* @Date:2022/7/7 7:49
* @Version 1.0
*/
public class Testji {
@Test//需要引入junit的jar包
public void testadd(){
ji ji=new ji();
System.out.println(ji.add(1,1));
Assert.assertEquals(2,c.add(1,1));//断点值比较 期望值与实际值
}
@Before//先执行
public void init(){
System.out.println("初始化");
}
@After//最后执行
public void finish(){
System.out.println("结束");
}
}
边栏推荐
- Detailed explanation of MOSN reverse channel
- Alienware上线首个数字时装AR试穿体验
- Unity2D horizontal board game tutorial 6 - enemy AI and attack animation
- 1. 两数之和
- Bubble sort in c language structure
- typescript45-接口之间的兼容性
- shell脚本循环语句
- Interface Test Framework Practice (4) | Get Schema Assertion
- Build your own web page on raspberry pie (1)
- 【生物素叠氮化物|cas:908007-17-0】价格_厂家
猜你喜欢
2. 两数相加
【Harmony OS】【ARK UI】ets使用startAbility或startAbilityForResult方式调起Ability
MCM box model modeling method and source analysis of atmospheric O3
UV 裂解的生物素-PEG2-叠氮|CAS:1192802-98-4生物素接头
How to use the interface management tool YApi?Beautiful, easy to manage, super easy to use
typescript47-函数之间的类型兼容性
【Harmony OS】【FAQ】Hongmeng Questions Collection 1
在树莓派上搭建属于自己的网页(1)
Build your own web page on raspberry pie (1)
Alienware上线首个数字时装AR试穿体验
随机推荐
接口和抽象
Alienware上线首个数字时装AR试穿体验
How to use the interface management tool YApi?Beautiful, easy to manage, super easy to use
Odps temporary query can write SQL, turned out to a named?
Harmony OS ets ArkUI 】 【 】 the development basic page layout and data connection
【Harmony OS】【ArkUI】ets开发 基础页面布局与数据连接
用户密码验证
VR全景展打造专属元宇宙观展空间
1. 两数之和
DFS对剪枝的补充
13.< tag-动态规划和回文字串>lt.647. 回文子串 + lt.516.最长回文子序列
探索性测试的概念及方法
Flink状态
在线密码生成工具推荐
js的垃圾回收机制
接口测试如何准备测试数据
接口测试框架实战(二)| 接口请求断言
【Harmony OS】【FAQ】鸿蒙问题合集1
IO进程线程->线程->day5
C#异步和多线程