反射
反射:将类的每一个部分都拆分成对象使用
反射中需要先获取类的字节码文件Class对象,然后调用Class类中的方法来获取类的各个部分的对象
获取Class对象的方式
1.利用类的class属性
2.通过实例对象getClass()方法 (Object中的方法)
3.通过Class类中静态方法 forName(String className) 参数为某个类的全路径的字符串 完整包名的路径
Class类中的常用方法
getConstructors() 获取公共构造函数对象数组
getDeclaredConstructors() 获取全部构造函数对象数组
getConstructor() 获取指定公共构造函数对象 (参数为想要获得的构造函数参数的类型Calss对象,必须一一对应)
getDeclaredConstructor() 参数同上 获取全部构造函数对象中指定一个
getField() 获取指定公共成员字段 Field类型
getDeclaredField() 字段操作与构造方法类似,参考即可
getMethod(String name, Class<?>... parameterTypes) 获取公共成员方法(包括继承来的) 参数为方法名和方法参数的Class类型对象
getDeclaredMethod() 参数同上 其它与构造方法类似 (不包括继承来的)
Constructor 构造函数类型
newInstance() 生成实例对象
setAccessible() 如果得到的是私有构造器对象,需要先把参数设置为true 才能进行实例化(私有的外部无法使用)
Field 字段类型(成员变量) set(Object obj, Object value) 为对象指定字段设置值 (跳过检查同上)
Method 方法类型 invoke(Object obj, Object... args) 调用方法 参数为调用方法的对象和参数 (跳过检查同上)
** 代码示例
Person类
public class Person {
String name;
private Person() {
}
private void show(){
System.out.println("show方法被调用了");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
测试类
public class MyTestTwo {
public static void main(String[] args) {
Class<Person> personClass = Person.class;
try {
Constructor<Person> p_con = personClass.getDeclaredConstructor();
Field name = personClass.getDeclaredField("name");
Method show = personClass.getDeclaredMethod("show");
p_con.setAccessible(true);
show.setAccessible(true);
Person person = p_con.newInstance();
name.set(person,"张三");
System.out.println(person);
Object invoke = show.invoke(person);
System.out.println(invoke);
} catch (NoSuchMethodException | NoSuchFieldException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
运行结果
Person{name='张三'}
show方法被调用了
null
注解
定义:
也叫元数据,代码级别的说明,与类、接口、枚举在一个层次,可以声明在包、类、字段、方法、局部变量、
方法参数前,对这些元素进行说明,注释
作用:
1.编写文档:通过代码里标识的注解来生成文档(JDK的api这种)
2.代码分析:通过代码里标识的注解对代码进行分析(通过反射获取注解的内容)
3.编译检查:通过代码里标识的注解让编译器能够实现基本编译检查(看是否符合特定的写法,如实现接口方法)
内置注解(JDK预先定义的,可直接使用)
@Override:检测被标注的方法是否是继承自父类/接口
@SuppressWarnings("all"):压制警告,参数一般为all
@Deprecated:说明标注的地方已过时
自定义注解(框架常用的技术)
通过下面注解定义代码格式可以看到,可以在注解内定义属性(相当于接口的抽象方法,注解实质是一个接口,
可以通过生成的Class字节码文件看到,下面会展示)
属性的种类有
基本数据类型、字符串类型、枚举类型、注解类型(An2是一个注解)、前面几种类型的数组类型
属性在定义的时候可以在后面加 default 属性 的方式来设置默认值
** 注解定义示例
元注解
public @interface An {
int age();
String name();
MyEnum myEnum();
An2 an();
int[] ints();
}
** 属性的赋值(也是为啥叫属性的原因)
如果注解只有一个属性value,可以省略属性名,数组类型的赋值是用{ }
@An(age=1,name="张三",myEnum = MyEnum.p1,ints={1,2},an=@An2(2))
public class Num { }
元注解:用于描述注解的注解
@Target:描述注解能够作用的位置、
** 只有一个枚举类型属性ElementType:
** 取值TYPE 可以作用在类上 METHOD 可以作用在方法上 FIELD 可以作用在成员变量上
@Retention:描述注解被保留到什么阶段
** 属性 枚举类型RetentionPolicy
** 取值 SOURCE 源代码阶段 CLASS 类对象阶段 RUNTIME 运行阶段
@Documented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承(定义注解的类在它的子类中是否还存在该注解)
看一下注解被编译的Class文件(可以了解到注解实质是一个接口,属性实质是抽象方法)
public interface anno.An extends java.lang.annotation.Annotation {
public abstract int age();
public abstract java.lang.String name();
public abstract int[] ints();
}
案例演示(通过注解来调用任何类的任何方法,运行结果即为Teacher类的tea方法执行结果)
Teacher类
public class Teacher {
public void tea(){
System.out.println("teacher中的方法执行了");
}
}
注解定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface pro {
String className();
String methodName();
}
测试类
@pro(className = "anno.Teacher",methodName = "tea")
public class MyTest {
public static void main(String[] args) {
Class<MyTest> myTestClass = MyTest.class;
pro annotation = myTestClass.getAnnotation(pro.class);
String className = annotation.className();
String methodName = annotation.methodName();
try {
Class<?> aClass = Class.forName(className);
Method method = aClass.getMethod(methodName);
Object o = aClass.newInstance();
method.invoke(o);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
}