1.反射
1.1注解
1.1.1定义
Java注解:又称Java标注
- Java 标注可以通过反射获取标注内容。
- 在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容
1.1.2分类
- 内置注解
- 自定义注解
内置注解:
注解 | 说明 |
---|---|
@Override | 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。 |
@Deprecated | 标记过时方法。如果使用该方法,会报编译警告。 |
@SuppressWarnings | 指示编译器去忽略注解中声明的警告。 |
@Retention | 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。 |
@Documented | 标记这些注解是否包含在用户文档中。 |
@Target | 标记这个注解应该是哪种 Java 成员。 |
@Inherited | 标明注解是能被继承的,例如B继承了A,A添加了注解,那么B也会继承同样的注解 |
@SafeVarargs | Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。 |
@FunctionalInterface | Java 8 开始支持,标识一个匿名函数或函数式接口。 |
@Repeatable | Java 8 开始支持,标识某注解可以在同一个声明上使用多次。 |
@Retention,@Documented,@Target,@Inherited,@Repeatable:被称为元注解
Annotation 架构:
1.1.3应用:掌握
①注解可以存在于三个位置
1)保留在JAVA文件当中
2)保留在CLASS文件当中
3)在JVM执行过程当中依旧能够获取
②自定义注解通常需要使用反射来获取内容
③使用注解是必须掌握的技能,而开发注解不需要大家在日常工作当中,时常接触。
1.1.4开发:了解
①ElementType(枚举):指定注解的使用范围
类型 | 说明 |
---|---|
TYPE | 类、接口(包括注释类型)或枚举声明 |
FIELD | 字段声明(包括枚举常量) |
METHOD | 方法声明 |
PARAMETER | 参数声明 |
CONSTRUCTOR | 构造方法声明 |
LOCAL_VARIABLE | 局部变量声明 |
ANNOTATION_TYPE | 注释类型声明 |
PACKAGE | 包声明 |
②RetentionPolicy(枚举):指定注解存活的位置
类型 | 说明 |
---|---|
SOURCE | Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 |
CLASS | 编译器将Annotation存储于类对应的.class文件中。默认行为 |
RUNTIME | 编译器将Annotation存储于class文件中,并且可由JVM读入 |
③声明自定义注解:在接口关键字interface之前添加@符号
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {//声明一个自定义注解
public String value1() default "";
public int value2();
public Sex value3();
}
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {//声明一个自定义注解
MyAnnotation3[] value();
}
@Repeatable(MyAnnotation2.class)
public @interface MyAnnotation3 {//声明一个自定义注解
}
@MyAnnotation1(value2=1,value3=Sex.MAN)
public class Person<T> {
@MyAnnotation3
@MyAnnotation3
@MyAnnotation3
private Integer id;
@Deprecated
public Person(Integer id) {
this.id = id;
int i1 = 1;
int i2 = 2;
}
// @SafeVarargs
@SuppressWarnings("unchecked")
public void test1(T ... t){
}
// @SuppressWarnings("unchecked")
@SafeVarargs
public final void test2(T ... t){
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "Person [id=" + id + "]";
}
}
④默认情况下,注解是只存在于class当中的。如果要让该注解在实际开发中,被反射获取,那么我们需要使用元注解
1)什么是元注解:注解注解的注解,使用在注解上的一种注解
2)@Target:用于指定注解的使用范围,通常需要给定参数ElementType
3)@Retention:用于指定注解的存活区域,通常需要给定参数RetentionPolicy
1.1.5可以给注解当中添加属性:
a.声明抽象方法,方法名为属性名,方法返回值为属性类型
b.可以在抽象方法之后,添加default给定默认值,有默认值的属性可以不必一定给值
1.1.6常规的内置注解
a.@Deprecated:过时内容,所标注内容,不再被建议使用。
b.@Override:只能标注方法,表示该方法覆盖父类中的方法,用于检测该方法是否是重写的方法
c.@SuppressWarnings:所标注内容产生的警告,编译器会对这些警告保持静默,取消编译器的警告
1.1.7新特性注解
@FunctionalInterface:标识一个匿名函数或函数式接口。
@FunctionalInterface
public interface TestInterface {
static void test1(){
System.out.println("test1");
}
default void test2(){
System.out.println("test2");
}
void test3();
}
public class Test {
public static void main(String[] args) {
TestInterface.test1();
TestInterface i = new TestInterface() {
@Override
public void test3() {
System.out.println("test3");
}
};
i.test2();
i.test3();
}
}
@SafeVarargs:忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@Repeatable:该注解为元注解,标识某注解可以在同一个声明上使用多次。
1.2反射
1.2.1什么是反射
动态加载类及其成员
① 常规情况下,一个类的执行过程,是从源文件开始,经过编译,然后加入类加载器当中,然后再执行改程序。属于是预先加载好,然后再执行。
② 反射是,程序已经在执行了,在执行过程当中,获取类,类的方法属性等等,然后再组织到当前的类加载器当中,动态执行。
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
获取Annotation类:
方法 | 说明 |
---|---|
getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
1.2.2使用场景:框架开发,框架升级
1.2.3 开源框架的问题:安全性得不到保障
1.2.4Object:用于描述对象的类
1.2.5Class:用于描述类的类
方法 | 说明 |
---|---|
forName(String className) | 局部变量声明 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
注意:setAccessible(true)设置是否允许通过反射访问
① 通过类名获取Class类,会有一个异常,类名不能定位到类,一般是在配置文件当中获取的字符串
② 通过对象来获取Class类,一般适用在传参的时候,或者参数数据类型不定的时候使用
③ 通过类来获取Class类,一般用于明确类型的参数传递
④ 使用newInstance的方法创建的实例,默认情况下实际上是调用的目标类的空参构造
1.2.6访问不可见成员时,需要指定访问权限
1.2.7Field:用于描述属性
获取Field类:
方法 | 说明 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
Field类的重要方法:
方法 | 说明 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
1.2.8Method:用于描述方法
获取Method类:
方法 | 说明 |
---|---|
getMethod(String name, Class…<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class…<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
Method类重要方法:
方法 | 说明 |
---|---|
invoke(Object obj, Object… args) | 传递object对象及参数调用该对象对应的方法 |
1.2.9Constructor:用于描述构造
获取Constructor类:
方法 | 说明 |
---|---|
getConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
Constructor类重要方法:
方法 | 说明 |
---|---|
newInstance(Object… initargs) | 根据传递的参数创建类的对象 |
1.2.10其他重要的方法
方法 | 说明 |
---|---|
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
1.2.11实例
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {//声明一个自定义注解
public String value() default "";
}
@MyAnnotation1(value="shisan is good")
public class Person implements A{
private Integer id;
private String name;
public Person() {
System.out.println("空参");
}
public Person(Integer id) {
this.id = id;
}
public Person(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
interface A{}
public class Test1 {
public static void main(String[] args) throws Exception {
//1.通过类名获取Class
Class<?> c1 = Class.forName("com.m.demo02.Person");
//2.通过对象获取Class
// Object o = new Person();
// Class<?> c2 = o.getClass();
//3.通过类来获取Class
// Class<?> c3 = Person.class;
//创建实例
Object obj1 = c1.newInstance();
System.out.println(c1.getPackage());
System.out.println(c1.getSimpleName());
System.out.println(c1.getSuperclass());
Class<?>[] is = c1.getInterfaces();
System.out.println(Arrays.toString(is));
}
}
public class Test2 {
public static void main(String[] args) throws Exception {
Class<?> c1 = Class.forName("com.m.demo02.Person");
Object obj1 = c1.newInstance();
// Field id = c1.getField("id");//访问可见的成员
Field id = c1.getDeclaredField("id");//访问所有成员
//设定访问权限:运行通过反射来获取,不操作不报错
id.setAccessible(true);
id.set(obj1, 1);//设值
System.out.println(id.get(obj1));//取值
// c1.getFields();
for(Field f : c1.getDeclaredFields()){
f.setAccessible(true);
System.out.println(f.getName());
System.out.println(f.getType().getSimpleName());
}
}
}
public class Test3 {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
Class<?> c1 = Class.forName("com.m.demo02.Person");
Object obj1 = c1.newInstance();
// Method setId = c1.getMethod("setId", Integer.class);
// setId.invoke(obj1, 11);//执行方法
// Method getId = c1.getMethod("getId");
// Object id = getId.invoke(obj1);
// System.out.println(id);
//模拟从XML当中读出来的数据,假设这里有什么数据,我们都不知道
Map<String,String> xml = new HashMap<>();
xml.put("id", "1");
xml.put("name", "shisan");
List<String> tnames = Arrays.asList(new String[]{//包装类
Integer.class.getTypeName(),
Double.class.getTypeName()
});
//思考题:如果是基本数据类型,怎么处理
for(Field f : c1.getDeclaredFields()){
String fname = f.getName();//属性名
Object value = xml.get(fname);//属性值
Class<?> ftype = f.getType();//属性类型
String setMethodName = "set" + String.valueOf(fname.charAt(0)).toUpperCase() + fname.substring(1);
Method setMethod = c1.getMethod(setMethodName, ftype);
if(tnames.contains(ftype.getTypeName())){//包装类
Constructor<?> c = ftype.getConstructor(String.class);//获取包装类构造new Integer("1")
value = c.newInstance(value);//创建包装类对象
// setMethod.invoke(obj1, ftype.getConstructor(String.class).newInstance(value));//new Integer("1")
setMethod.invoke(obj1, value);//new Integer("1")
}else{
setMethod.invoke(obj1, value);
}
}
System.out.println(obj1);
}
}
public class Test4 {
public static void main(String[] args) throws Exception {
Class<?> c1 = Class.forName("com.m.demo02.Person");
Constructor<?> c = c1.getConstructor(Integer.class,String.class);//获取构造
Object obj1 = c.newInstance(1,"shisan");//通过构造来创建对象
System.out.println(obj1);
}
}
public class Test5 {
public static void main(String[] args) throws Exception {
Class<?> c1 = Class.forName("com.m.demo02.Person");
//获取注解
MyAnnotation1 ma1 = c1.getAnnotation(MyAnnotation1.class);
String v = ma1.value();
System.out.println(v);
}
}