反射:框架设计的灵魂
概念
将类的各个组成部分封装为其他对象。
优点
1.可以在程序运行过程中,操作Class对象
2.可以解耦,提高程序的可扩展性
获取Class对象的方式
/*
获取Class对象的三种方式
1.Class.forName("全类名");
2.类名.class;
3.对象名.getClass();
*/
//1.Class.forName()
Class cls1 = Class.forName("包名.类名");
//2.类名.class
Class cls2 = Person.class;
//3.对象名.getClass()
Person p = new Person();
Class cls3 = p.getClass();
tips:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪种方式获取的对象都是同一个。
Class对象功能
1.获取成员变量
Field:成员变量
操作:
1.设置值
void set(Object obj , Object value)
2.获取值
get(Object obj)
3.忽略访问权限修饰符的安全检查(暴力反射)
setAccessible(true)
public static void main(String[] args) throws Exception {
//获取public修饰的成员变量
getField();
System.out.println("-----------------------------------------");
//获取所有成员变量
getDeclaredField();
}
/**
* 1.1 获取所有public修饰的成员变量
* 对象名.getFields();
* 对象名.getField(变量名);
*/
public static void getField() throws Exception {
Person p = new Person();
//获取类对象
Class personClass = Person.class;
// (1)--getFields()
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
// (2)--getField
Field a = personClass.getField("a");
//获取public修饰的成员变量的值
Object value = a.get(p);
System.out.println(value);//null
a.set(p,"迪丽热巴");
System.out.println(p);//Person{name='null', age=0, a='迪丽热巴', b='null', c='null'}
}
/**
* 1.2 获取所有成员变量
* 对象名.getDeclaredFields();
* 对象名.getDeclaredField(变量名);
*/
public static void getDeclaredField() throws Exception {
Person p = new Person();
//获取类对象
Class personClass = Person.class;
// (1)--getDeclaredFields()
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
// (2)--getDeclaredField()
Field name = personClass.getDeclaredField("name");
//忽略访问权限修饰的安全检查(暴力反射)
name.setAccessible(true);
//然后给非public修的变量设值
name.set(p,"佟丽娅");
Object value = name.get(p);
System.out.println(value);
}
Person类
public class Person {
private String name;
private int age;
public String a;
protected String b;
String c;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
'}';
}
public void play(String game) {
System.out.println("我想打" + game);
}
public void play() {
System.out.pringln("play...");
}
}
2.获取构造方法
Constructor:构造方法
创建对象
T newInstance(Object... initargs)
public static void getConstructor() throws Exception {
//获取类对象
Class personClass = Person.class;
//获取无参构造方法
Constructor constructor1 = personClass.getConstructor();
System.out.println(constructor1);
//获取有参构造方法
Constructor constructor2 = personClass.getConstructor(String.class, int.class);
System.out.println(constructor2);
//创建对象
//通过构造方法
Object person1 = constructor1.newInstance();
System.out.println(person1);
}
3.获取成员方法
Method:方法对象
执行方法:
Object invoke(Object obj , Object...args)
获取方法名称:
String getName();
/*
获取public修饰的成员方法
若想获取全部成员方法,使用getDeclaredMethod();(同1.获取成员变量)
*/
public static void getMethod() throws Exception {
Person p =new Person();
//获取类对象
Class personClass = Person.class;
//1.获取指定成员方法
Method methodPlay = personClass.getMethod("play", String.class);
//执行方法并传参
methodPlay.invoke(p,"cod16");
//2.获取全部public修饰的成员方法(包含父类中方法)
Method[] methods = personClass.getMethods();
for (Method method : methods) {
//获取成员方法名称
String name = method.getName();
System.out.println(name);
System.out.println(method);
}
}
4.获取类名
public static void getClassName() {
//获取类对象
Class personClass = Person.class;
//获取类名
String className = personClass.getName;
System.out.println(className);
}
案例
需求:写一个反射类,在不改变该类的任何代码的前提下,可以帮我们创建该类的对象并执行其中的任意方法
步骤:
1.将需要创建对象的全类名和需要执行的方法定义在配置文件中
2.在程序中加载读取配置文件
3.使用反射来加载类文件进内存
4.创建对象
5.执行方法
public static void main(String[] args) throws Exception {
//1.加载配置文件
//1.1创建Properties对象(把硬盘中存储的文件读取到集合中使用)
Properties prop = new Properties();//Properties是HashTable的子类
//1.2获取配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();//通过类对象获取类加载器()
InputStream resource = classLoader.getResourceAsStream("prop.properties");//类加载器找到配置文件返回IO流
//1.3加载配置文件
prop.load(resource);
//2.获取配置文件中定义的数据
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
//3.获取类对象
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.getDeclaredConstructor().newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
配置文件(prop.properties)
tips:路径在src/main/resources下,不然找不到
className=包名.Person
methodName=play
注解
注解:说明程序的,给计算机看的。
注释:用文字描述程序的,给人看的。
定义
注解(Annotation),也叫元数据,一种代码级别的说明。
是JDk1.5引入的新特性。
作用分类
1.编写文档:通过代码里标识的注解生成doc文档
2.代码分析:通过代码里标识的注解对代码进行解析(使用反射)
3.编译检查:通过代码里标识的注解让编译器能够实现基本的代码检查(Override)
JDK中预定义的注解
1.@Override
2.@Deprecated:代表已过时
3.@SuppressWarnings:压制警告
自定义注解
格式与本质
//格式
public @interface MyAnno {}
//注解本质上就是接口
publi interface MyAnno extends java.lang.annotation.Annotation {}
元注解
用于描述注解的注解
@Target:描述注解能够作用的位置
@Retention:描述注解被保留的阶段(source、class、runtime)
@Docemented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承
@Target(ElementType.TYPE)//作用于类上
@Retention(RetentionPolicy.RUNTIME)//被描述的注解,会被保留到class字节码文件中,并被JVM读取到
public @interface MyAnno {
}
@Target(ElementType.METHOD)//作用于方法上
@Retention(RetentionPolicy.CLASS)//作用到字节码阶段
public @interface MyAnno {
}
@Target(ElementType.FIELD)//作用于成员变量上
@Retention(RetentionPolicy.SOURCE)//作用于源码阶段
public @interface MyAnno {
}
案例
使用注解完成上面反射篇中的案例
Prop.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Prop {
String className();
String methodName();
}
主方法
@Prop(className = "cn.annotation.Student",methodName = "sleep")
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.解析注解
//1.1获取注解(接口)的实现类对象
Prop prop = ReflectTest.class.getAnnotation(Prop.class);
//1.2通过实现类对象获取返回的数据
String className = prop.className();
String methodName = prop.methodName();
//同reflect\ReflectTest.java
//2.获取类对象
Class<?> cls = Class.forName(className);
//3.创建对象
Object obj = cls.getDeclaredConstructor().newInstance();
//4.获取方法对象
Method method = cls.getMethod(methodName);
//5.执行方法
method.invoke(obj);
}
}
学生类
public class Student {
public void sleep() {
System.out.println("早睡早起身体倍儿棒~");
}
}