注解和反射
注解(java.Annotation)
内置注解
@Override:重写注解
@Deprecated:废弃注解
@SuppressWarnings():镇压警告
元注解
元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明。
@Target:描述注解可以用在什么地方
@Retention:表示在什么级别保存该注解信息(描述注解的生命周期 SOURCE<CLASS<RUNTIME)
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//自定义注解
public class TestAnno {
//如果没有默认值 必须给注解赋值
@MyAnnotation(name = "1122")
public void test(){
}
@MyAnnotation1("222")
public void test2(){
}
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
//注解的参数(不是方法):参数类型+参数名()
String name();
int age() default 0;
int id() default -1;
String[] school() default {"清华大学"};
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
//如果只有一个参数 建议使用value 这样就可以在使用注解时不用指定参数名
String value();
}
反射(java.Reflection)
静态语言与动态语言
动态语言
一类在运行时可以改变其结构的语言,例如新的函数、对象、已有的函数被删除或是其他结构上的变化。主要动态语言:Object-C、C#、JavaScript、PHP、Python等
静态语言
运行时结构不可变,如Java、C、C++。Java不是动态语言,但Java可以成为“准动态语言”。即Java有一定的动态性,我们可以利用反射来获得类似动态语言的特性。Java的动态性让编程更加灵活。
反射概述
-
反射被认为是Java动态语言的关键,反射机制允许程序在执行期借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性或方法(包括private修饰的字段)
Class c = Class.forName("java.lang.String")
-
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过对象看到类的结构。这个对象就像一面镜子,通过这个镜子看到类的结构。所以称之为反射。
反射机制提供的功能
- 运行时判断任意一个对象所属的类
- 运行时构造任意一个类的对象
- 运行时判断任意一个类所具有的成员变量和方法
- 运行时获取泛型信息
- 运行时调用任意一个对象的成员变量或方法
- 运行时处理注解
- 生成动态代理
优缺点
优点:动态创建对象和编译,灵活性高
缺点:对性能有影响。反射是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
反射相关API
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Construtor:代表类的构造器
Class
概述
反射可以得到的信息:类的属性、方法、构造器、类实现了哪些接口。对于每个类而言,JRE都会为其保留一个不变的Class类型的对象。
- Class本身也是一个类
- Class对象只能有系统创建(文明只能通过反射去获得)
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成的
- 通过Class可以完整的得到一个勒种的所有被加载的结构
- Class类是Reflection的根源,任何想要动态加载、运行的类,唯有先获得相应的Class对象
获取Class类的三种方式
-
前提:知道具体的类 该方法最安全可靠、程序性能高
Class clazz = Person.class
-
前提:已知类的实例
Class clazz = person.getClass() 这是Object类中定义的方法
-
前提:已知类的全类名,且该类在类路径下
Class c = Class.forName(“java.lang.String”)
-
内置对象的包装类都有一个Type属性
Class c4 = Integer.TYPE;
Class类的常用方法
static ClassforName(String name) 返回指定类名的Class对象
Object newInstance() 通过反射 创建一个实例
getName() 返回此Class对象所表示的实体(类、接口、数组类或void)的名称 Class getSuperClass() 返回当前Class对象父类的Class对象
Class[] getinterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Method getMethod(String name, Class .. T) 返回一个Method的对象
Field[] getDeclaredFields() 返回Field对象的一个数组
哪些类型可以有Class对象?
class(外部类、局部内部类、匿名内部类等)、interface、数组、enum(枚举)、注解、基本数据类型、void
Java内存分析
获取类的运行时结构
Class cl1 = Class.forName("com.ys.reflect.User");
System.out.println(cl1.getName()); //获得包名+类名
System.out.println(cl1.getSimpleName()); //获得类名
Field[] fields = cl1.getFields(); //只能找到public属性
fields = cl1.getDeclaredFields(); //找到全部属性
Field field = cl1.getDeclaredField("name"); //获得指定属性的值
Method[] methods = cl1.getMethods(); //获得本类及父类的所有public方法
methods = cl1.getDeclaredMethods(); //获得本类的所有方法
//获得指定的方法
Method method = cl1.getMethod("setName", String.class);
method = cl1.getMethod("getName", null);
Constructor[] constructors = cl1.getConstructors(); //获得public的构造器
constructors = cl1.getDeclaredConstructors(); //获得所有的构造器
Constructor constructor = cl1.getConstructor(String.class, int.class, int.class); //获取指定的构造器
通过反射创建对象
Class c1 = Class.forName("com.ys.reflect.User");
//通过反射创建实例
User user = (User)c1.newInstance();//本质是调用类的无参构造
//没有无参构造的情况下 通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
user = (User)constructor.newInstance("zs", 16,1);
//通过反射调用普通方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user, "ls"); //将user的name改为ls invoke是激活的意思
//通过反射操作属性
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//不能直接操作私有属性 需要通过这个方法关闭安全检测
name.set(user, "zs");