05反射与注解
1. 反射
1.1获取Class对象
/*
* java反射机制是在运行状态中,对于任意一个类,都能知道这个类的所以属性和方法;
* 对于任意一个对象,都能调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法称为java语言的反射机制
* 想要解剖一个类,必须先要获取该类的class对象,而剖析一个类或用反射解决具体问题就是使用相关的API
* (1)java.lang.Class
* (2)java.lang.Reflect
* 所以,class对象是反射的根源*/
public static void main(String[] args) throws ClassNotFoundException {
/*哪些类型可以获取class对象*/
// 1.基本数据类型和void
Class<Integer> integerClass = int.class;
Class<Void> voidClass = void.class;
// 2.类和接口类型
Class<Object> objectClass = Object.class;
Class<Runnable> runnableClass = Runnable.class;
// 3.枚举
Class<ElementType> elementTypeClass = ElementType.class;
// 4.注解
Class<Override> overrideClass = Override.class;
// 5.数组
Class<Array> arrayClass = Array.class;
/*获取class对象的四种方式
* 1.类型名.class
* 2.对象.getClass()
* 3.Class.forName(类型全名称),通常需要配置文件配合使用,可以获取编译期间未知的类型
* 4.ClassLoader的类加载器对象,loadClass(类型全名称)*/
// 第一种类型名.class
Class clazz = Reflect1.class;
// 第二种 对象.getClass()
Reflect1 r1 = new Reflect1();
Class aClass = r1.getClass();
// 第三种 Class.forName(类型全名称)
Class clazz1 = Class.forName("Reflect.User");
/*
* 反射的应用场景
* 各个框架的设计(主要场景)
* 各大框架的内部实现了大量的反射机制,想要深入了解框架,则必须要了解反射机制*/
}
1.2获取类型的详细信息
public class Reflcet2 {
/*
* 反射的应用
* 通过反射获取类型的详细信息
* 可以获取:包,修饰符,类型名,父类(包括泛型父类),父接口(包括泛型父接口),成员(属性,构造器,方法)
* 注解(类上的,方法上的,属性上的)*/
public static void main(String[] args) throws ClassNotFoundException {
Class clazz = Class.forName("Reflect.User");
// 1.获取类名
String name = clazz.getName();
System.out.println(name);
// 2.获取该类实现的所有接口
Class[] interfaces = clazz.getInterfaces();
System.out.println(interfaces);
// 3.获取该类的所有属性
Field[] declaredFields = clazz.getDeclaredFields();
System.out.println(declaredFields);
// 4.获取父类的字节码对象
Class aClass = clazz.getSuperclass();
System.out.println(aClass);
// 5.获取该类的所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
System.out.println(declaredMethods);
// 6.获取该类的所有构造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
System.out.println(declaredConstructors);
}
}
1.3通过反射创建任意类型对象
public class Reflcet3 {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
NoSuchMethodException, InvocationTargetException {
/*创建任意引用类型的对象
* 1.直接通过class对象来实例化(要求必须有无参构造)
* 2.通过获取构造器对象来进行实例化*/
// 1.直接通过class对象来实例化
Class<?> aClass = Class.forName("Reflect.User");
// 如果类没有无参构造,就没有无参实例化方法
User o = (User) aClass.newInstance();
o.fn();
// 2.通过获取构造器对象来进行实例化
// 1.获取该类型的Class对象2.获取构造器对象3.创建对象
// 已知的话,都倒是可以这样操作
// 1.Class<User> u = User.class;
Class<?> clazz = Class.forName("Reflect.User");
// 2.获取位置类的构造函数
Constructor constructor = clazz.getDeclaredConstructor(int.class, String.class);
// 3.创建对象
User o1 = (User) constructor.newInstance(18, "老王");
o1.fn();
}
}
1.4通过反射获取任意类型的属性
public class Reflcet4 {
public static void main(String[] args)
throws ClassNotFoundException, NoSuchFieldException, InstantiationException,
IllegalAccessException {
// 操作任意类型的属性
Class clazz = Class.forName("Reflect.User");
Object obj = clazz.newInstance();
// 获取所有的属性名
/*Field[] declaredFields = clazz.getDeclaredFields();
for (Field item : declaredFields) {
System.out.println(item);
}*/
// 获取已知的属性名
// Field gender = clazz.getDeclaredField("gender");
Field gender = clazz.getDeclaredField("age");
// 如果属性值为私有的,则需要设置属性可以访问
gender.setAccessible(true);
// 设置属性值
gender.set(obj, 18);
// 获取属性值
Object value = gender.get(obj);
System.out.println(value);
}
}
1.5通过反射调用任意类型的方法
public class Reflcet5 {
// 反射调用任意类型的方法
public static void main(String[] args)
throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
// 1.获取该类型的class对象
Class clazz = Class.forName("Reflect.User");
Object obj = clazz.newInstance();
// 2.获取对象方法
// 全部方法
/*Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method item : declaredMethods) {
System.out.println(item);
}*/
// 单个方法
Method fn = clazz.getDeclaredMethod("fn", String.class);
// 3.调用方法 =>如果需要突破权限依旧是调用setAccessible(true)
fn.invoke(obj, "你好啊");
}
}
测试用的代码
public class User {
private int age;
private String name;
public int gender;
public User() {
this.age = 0;
this.name = "无";
}
public User(int age, String name) {
this.age = age;
this.name = name;
}
public void fn() {
System.out.println(name + "今年" + age + "了");
}
public void fn(String str) {
System.out.println(str);
}
}
2. 注解
public class Annotation1 {
/*什么是注解
* 注解是一种代码级别的说明和类,接口平级.相当于一种标记,在程序中加入注解就等于为程序打上某种标记
* javac编译器,开发工具和其他程序可以通过反射来了解你的类及元素上有无标记,看你的程序有什么标记,就去干相应的是去
* 标记可以加在包,类,属性,方法,方法的参数以及局部变量上定义*/
/*
* 注解的作用
* 执行编译期的检查 例如@Override 代表着重写父类的方法
* 分析代码(主要用途:替代配置文件); 用在框架里面,注解开发*/
/*
* JDK提供的3种基本注解
* 1.@Override:描述方法的重写
* 2.@SupperssWarnings:压制警告
* 3.@Deprecated:标记过时*/
public static void main(String[] args)
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
IllegalAccessException, InstantiationException {
demo1 d = new demo1();
// d.fn(); // 表现出来了
/*自定义标注*/
// 1.获取Demo1字节码对象
Class clazz = demo1.class;
// 2.获取Demo1的注解对象
Annottation2 an = (Annottation2) clazz.getAnnotation(Annottation2.class);
System.out.println(an);
// 3.反射获得方法对象
Method fn2 = clazz.getDeclaredMethod("fn2");
// 想使用就得使用构造函数,给它一个对象
Object o = clazz.newInstance();
fn2.invoke(o);
// System.out.println(fn2);
}
}
/*元注解
* 元注解是使用在自定义的注解,为自定义的注解提供支持的
* 常用的元注解
* @Target:定义该注解作用在什么上面(位置),默认注解可以在任何位置,值为:ElementType的枚举值
* METHOD:方法
* TYPE:类型
* FIELD:字段
* CONSTRUCTOR:构造方法声明
* @Retention:定义该注解保留到那个代码阶段,值为:RetentionPolicy类型,默认只在源码阶段保留
* SOURCE:只在源码上保留
* CLASS:在源码和字节码上保留
* RUNTIME:在所有阶段都保留 */
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.SOURCE)
public @interface Annottation2 {}
@Annottation2
public class demo1 {
@Deprecated
public void fn() {
System.out.println("标记已过时");
}
@Annottation2 // 使用注解时给属性赋值
public void fn2() {
System.out.println("你好");
}
}