笔记基于B站的教学视频:https://www.bilibili.com/video/BV1p4411P7V3?p=16&share_source=copy_web
1 注解
1.1 什么是注解?
-
Annotation的作用:
- 不是程序本身但可以对程序做出解释
- 可以被其他程序(例如编译器)读取
-
Annotation的格式:
- 注解以"@注解名"在代码证存在,并且可以添加一些参数
- 示例:@SuppressWarnings(value=“bunchecked”)。
-
Annotation在哪使用:
- 可以附加在package,class,method或者field等上面,根据其定义来确定。可以通过反射机制来访问这些元数据
1.2 JAVA中常见的几个内置注解
-
@Override
定义在java.lang.Override中,只用于修饰方法表示一个方法将重写超类中的另一个方法
-
@Deprecated
定义在java.lang.Deprecated中,用于修饰方法、属性或者类,表示不推荐使用某元素
-
@SuppressWarnings
定义在java.lang.SuppressWarnings中,用于抑制编译时的警告信息。并且此注解需要添加一个参数,可以选择使用"all"、"unchecked"等
1.3 元注解
元注解用于注解其他的注解,JAVA中定义了四个元注解(这些类型和它们所支持的类可以在java.lang.annotation包中找到):
-
@Target
用于描述注解可以用在哪些地方(TYPE、FIELD、METHOD等)
示例:@Target(value=ElementType.METHOD)
-
@Retention
表示需要在什么级别保存该注释信息(SOURCE、CLASS和RUNTIME)
示例:@Retention(RetentionPolicy.RUNTIME)
-
@Document
用于说明该注解将被包含在javadoc中
-
@Inherited
用于说明子类可以继承父类中的该注解
1.4 自定义注解
使用@interface来自定义注解将自动继承java.lang.annotation.Annotation接口
- 自定义注解格式:public @interface 注解名{定义内容} (public可不加)
- 声明参数的格式:参数类型 参数名();参数类型只能为Class,String和enum,且可以使用default来声明参数默认值。
- 示例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{
String value() default "";
}
2 反射
2.1 反射概述
-
类在加载完成后,便在堆内存的方法区内产生一个Class类型的对象(每个类只有一个Class对象),此对象包含完整的类结构信息。
-
反射机制允许程序在运行阶段借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
-
反射的优点:可以实现动态创建对象和编译有很强的灵活性;反射的缺点:会对性能产生影响。
2.2 获得反射对象
对每个类JRE都为其保留了一个不变的Class类型对象,此对象包含了类的所有被加载的结构:类的属性、方法等,Class对象只能有系统来建立,每个类的实例也都知道自己由哪一个Class对象生产。Class类也是反射的根源,对任何想动态加载的类都需要先获得对应的Class对象。
获取Class类示例的方法:
- 已知具体类可以通过类的class属性获取
Class c1=Person.class;
- 已知类的示例通过getClass方法获取
Class c2=person.getClass();
- 已知一个类的全类名且该类在类路径下,通过Class类静态方法forName获取
Class c3=Class.forName("xxxxx");
- 内置的基本数据类型可以直接使用类名.Type获取
Class c4=Integer.TYPE;
2.3 获取类的运行时结构
运用2.2中的方法获得对应对象后,调用相应方法便可获得想得到类的信息,如下列代码所示:
package Study;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Myjava {
public static void main(String[] args) throws Exception{
//获取Class对象
Class c=Class.forName("Study.User");
//获取包名+类名
System.out.println(c.getName());
//获取类名
System.out.println(c.getSimpleName());
//获取类属性
Field[] fields1 = c.getFields();//只能找到public属性的
Field[] fields2 = c.getDeclaredFields();//获取全部属性
Field name = c.getDeclaredField("name");//根据参数获取指定的属性
//获取类的方法
Method[] methods1 = c.getMethods();//获取本类及父类的全部public方法
Method[] methods2 = c.getDeclaredMethods();//获取本类的全部方法
Method setId = c.getMethod("setId", int.class);//根据参数获得指定方法
//获取类构造器,下面三行代码与获取类的方法的代码类似只是不会获取父类构造器
Constructor[] constructors1 = c.getConstructors();
Constructor[] constructors2 = c.getDeclaredConstructors();
Constructor constructor = c.getDeclaredConstructor(int.class,String.class);
}
}
class User{
private int id;
private String name;
public User() {}
private void a(){}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
}
2.3 动态创建对象并执行方法
创建类的对象方法:
- 调用Class对象的newInstance()方法,这要求类必须有一个无参构造器并且构造器的访问权限足够
- 通过getDeclaredConstructors方法获取类中指定参数的构造器,利用此构造器实例化对象
代码示例如下(继续使用上述的User类):
//获取Class对象
Class c=Class.forName("Study.User");
User u1=(User)c.newInstance();//本质是调用无参构造器
//通过构造器创建对象
Constructor constructor = c.getDeclaredConstructor(int.class, String.class);
User u2 = (User)constructor.newInstance(1, "JAVA");
通过反射调用普通方法:
//通过反射调用普通方法
Method setId = c.getDeclaredMethod("setId", int.class);
setId.invoke(u1,1);
通过反射操作属性:
//通过反射操作属性
Field id = c.getDeclaredField("id");
id.setAccessible(true);//不能直接操作私有属性需要关闭安全检查
id.set(u1,2);
2.4 获取泛型与注解信息
JAVA采用泛型擦除机制来引入泛型,为通过反射来操作泛型,JAVA新增了四种类型:
- ParameterizedType:表示参数化类型比如Collection
- GenericArrayType:表示元素类型是参数化类型或类型变量的数组类型
- TypeVariable:是各种类型变量的公共父接口
- WildcardType:代表一种通配符类型表达式
通过反射获取泛型信息代码如下:
public Map<ArrayList<String>,Integer> test01(Map<String,Integer> a,ArrayList<Integer> b){
System.out.println("test01");
return null;
}
public static void main(String[] args) throws Exception{
Method m=Myjava.class.getDeclaredMethod("test01",Map.class,ArrayList.class);
Type[] types1 = m.getGenericParameterTypes();//获得参数泛型信息
Type types2=m.getGenericReturnType();//获得返回值泛型信息
//输出泛型信息
for(Type type:types1){
if(type instanceof ParameterizedType){
Type[] types3=((ParameterizedType)type).getActualTypeArguments();
for(Type type2:types3)
System.out.println(type2);
}
}
}
通过反射获取注解信息代码如下:
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
int i() default 0;
String value() default "";
}
@MyAnnotation(value = "Myjava")
public class Myjava {
@MyAnnotation(i=1)
private int id;
public static void main(String[] args) throws Exception{
Class c=Class.forName("Study.Myjava");
MyAnnotation annotation1 = (MyAnnotation)c.getAnnotation(MyAnnotation.class);//获得类注解
String s=annotation1.value();//获得注解的值
System.out.println(s);
Field f=c.getDeclaredField("id");
MyAnnotation annotation2=(MyAnnotation)f.getAnnotation(MyAnnotation.class);//获得属性的注解
int anid=annotation2.i();
System.out.println(anid);
}
}