注解和反射
注解和反射
注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
java内置注解
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
package org.reflect;
import java.util.ArrayList;
import java.util.List;
public class reflect {
@Override//重写的注解,可不写
public String toString() {
return "reflect{}";
}
@Deprecated//废弃的,标记方法,表示此方法已过时,但是可以使用,只是不推荐使用
public static void test1(){
System.out.println("废弃的Deprecated");
}
@SuppressWarnings("all")//@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
//镇压警告,需要参数,使所有警告消失,抑制编译时的警告信息,可以放在类和方法上
public static void test2(){
List<String> list = new ArrayList<>();
}
public static void main(String[] args) {
test1();
}
}
元注解
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
@Target(ElementType.METHOD)
public enum ElementType {
//类,接口,或者枚举类型
TYPE,
/*字段声明(包括枚举常量)*/
FIELD,
/*方法生名*/
METHOD,
/*形式参数声明 */
PARAMETER,
/*Constructor declaration */
CONSTRUCTOR,
/*Local variable declaration*/
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/*类型参数声明*/
TYPE_PARAMETER,
/*类型的使用*/
TYPE_USE
}
@Retention(RetentionPolicy.RUNTIME)
public enum RetentionPolicy {
//源码,编译时有效
SOURCE,
//class,编译后,class有效
CLASS,
//运行时有效
RUNTIME
}
package org.reflect;
import javax.xml.bind.Element;
import java.lang.annotation.*;
public class Demo1 {
@MyAnnotation
public void test(){ }
}
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})//注解作用域,此时可以放在方法上,value可加可不加
@Retention(value = RetentionPolicy.RUNTIME)//注解有效周期,表示我们的注解再什么时候有效
@Documented//表示是否将文档生成再javadoc中
@Inherited//说明子类可以继承父类中的该注解
@interface MyAnnotation{
}
//@interface自定义注解,自动继承java.lang.annotation.Annotation接口
package org.reflect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Demo2 {
@MyAnnotation1(name = "sang",age = 18,id = 2019,school = {"school","13"})
public void test(){
}
}
@Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
//注解参数:参数类型 + 参数名 + () 可以加默认值使用default关键字
String name() default "";//注解参数,可以设置default,后接默认值
int age() default 0;
int id() default -1;//如果默认值为-1,代表不存在,indexof,如果找不到就返回-1
String[] school() default "";
}
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
Annotation 架构
1 个 Annotation 和 1 个 RetentionPolicy 关联。
1 个 Annotation 和 1~n 个 ElementType 关联。
Annotation 有许多实现类,包括:Deprecated, Documented, Inherited, Override 等等
反射
Java反射技术的作用:主要是在是在程序运行的时候动态的改变运行结构
静态语言和动态语言
动态语言的意思就是在运行的时候可以更改编译好的class文件,可以对其方法,属性或者属性类型做改变。但是静态语言在运行的时候不能够更改已经编译好的class文件的内容。java虽然是静态语言但是却可以通过反射机制编程动态语言,也就是说通过反射可以在运行的时候更改java中已经编译好的class文件,对其中的属性,属性类型,方法做一些修改。
package org.reflect;
import java.lang.reflect.Constructor;
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取class对象
Class<?> forName = Class.forName("org.reflect.Demo");
Class<?> forName1 = Class.forName("org.reflect.Demo");
Class<?> forName2 = Class.forName("org.reflect.Demo");
System.out.println(forName);
//一个类再内存中只能有一个Class对象
//一个类,在被加载后,类的整个结构都会被封装再class对象中
System.out.println(forName.hashCode());
System.out.println(forName1.hashCode());
System.out.println(forName2.hashCode());
System.out.println(forName.getName());
System.out.println(forName.getAnnotations());
System.out.println(forName.getClass().getMethods());
}
}
Person student = new Student();
//方式一:通过对象获得
Class aClass1 = student.getClass();
//方式2:通过forname获得
Class<?> aClass2 = Class.forName("org.entity.Student");
//方式3,通过类名获得
Class aClass3 = Student.class;
package org.reflect;
import org.entity.Person;
import org.entity.Student;
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException {
Person student = new Student();
System.out.println(student.name);
//方式一:通过对象获得
Class aClass1 = student.getClass();
System.out.println(aClass1.hashCode());
//方式2:通过forname获得
Class<?> aClass2 = Class.forName("org.entity.Student");
System.out.println(aClass2.hashCode());
//方式3,通过类名获得
Class aClass3 = Student.class;
System.out.println(aClass3.hashCode());
//方式4,基本内置类型的包装类都有一个Type属性
Class type = Integer.TYPE;
//public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
System.out.println(type);
//获得父类类型
Class superclass = aClass1.getSuperclass();
Class<?> superclass1 = aClass2.getSuperclass();
Class superclass2 = aClass3.getSuperclass();
System.out.println(superclass);
System.out.println(superclass.hashCode()
+ "\t" + superclass1.hashCode() +
"\t" + superclass2.hashCode());
}
}
那些类可以用Class对象呢?
package org.reflect;
import java.lang.annotation.ElementType;
public class Test3 {
public static void main(String[] args) {
Class c1 = Object.class;
Class c2 = Runnable.class;
Class c3 = MyAnnotation.class;
Class c4 = String[].class;
Class c5 = int[][].class;
Class c6 = Integer.class;
Class c7 = void.class;
Class c8 = Class.class;
Class c9 = ElementType.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
int[] a = new int[10];
int[] b = new int[200];
System.out.println(a.getClass().hashCode());//一个类型的对象只能有
System.out.println(b.getClass().hashCode());//一个class对象
}
}
/*
class java.lang.Object
interface java.lang.Runnable
interface org.reflect.MyAnnotation
class [Ljava.lang.String;
class [[I
class java.lang.Integer
void
class java.lang.Class
class java.lang.annotation.ElementType
*/
java内存分析
方法区就是一个特殊的堆
类的加载过程
当程序使用某个类时,如果该类还未被加载到内存中,则系统会通过:
- 类的加载(Load):将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成
- 类的链接(Link):将类的二进制数据合并到jre中
- 类的初始化(Initialize):JVM负责对类进行初始化
package org.reflect;
public class Test4 {
public static void main(String[] args) {
System.out.println(A.m);
A a = new A();
}
}
class A{
static int m = 10;
static {
System.out.println("static");
System.out.println(A.m);
m = 100;
}
public A(){
System.out.println("A类的无参构造");
}
}
什么时候会发生类的初始化
package org.reflect;
public class Test5 {
static {
System.out.println("Main类静态代码块被初始化了");
}
public static void main(String[] args) throws ClassNotFoundException {
//虚拟机启动时,main方法所在类先被初始化
//Son son = new Son();//new一个类时父类静态先被初始化之后是子类
//System.out.println(Son.m);//子类调用静态变量时主类,父类,子类静态代码块先加载,之后是静态变量被打印
//System.out.println(Son.a);//子类调用父类静态变量时,子类不会被加载,只有父类被加载
//System.out.println(Son.B);//子类调用父类常量时,都不会被加载
//System.out.println(Son.M);//子类调用自己常量时,都不会被加载
//Class.forName("org.reflect.Son");//反射时都会被加载
//Son[] sons = new Son[20];//定义此类型数组时不会被加载
}
}
class Father{
static int a = 10;
static {
System.out.println("Father静态代码块被初始化了");
}
static final int B = 20;
}
class Son extends Father{
static {
System.out.println("Son静态代码块被初始化了");
m = 100;
}
static int m = 20;
static final int M = 666;
}
类加载器
类加载的作用:将class文件字节码内容加载到内存中,,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过jvm垃圾回收机制可以回收这些Class对象
package org.reflect;
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//AppClassLoader
//获取系统类加载器的父类加载器--》扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);//ExtClassLoader
//获取扩展类加载器的父类加载器--》根(引导类)加载器(C/C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//null,根加载器是由C/C++写的,获取不到
//测试当前类是由那个加载器加载的
Class<?> aClass = Class.forName("org.reflect.Demo3");
ClassLoader classLoader1 = aClass.getClassLoader();
System.out.println(classLoader1);//AppClassLoader,系统类加载器,
//测试JDK内置的类是由谁加载的
ClassLoader classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);//null,根加载器
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
/*可以加载的类路径,只有再这些路径中的类拆可以被加载
C:\Program Files\Java\jdk1.8.0_333\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\access-bridge-64.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\dnsns.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jaccess.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\jfxrt.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\localedata.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\nashorn.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunec.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunjce_provider.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunmscapi.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\sunpkcs11.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\ext\zipfs.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\javaws.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\jfxswt.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\management-agent.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\plugin.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_333\jre\lib\rt.jar;
D:\ideaProject\Test\reflection\target\classes;
E:\idea\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar
双亲委派机制
自己定义的类若要加载,会一级一级向上找,先看系统类加载器里是否由此jar包,
再向上找,看扩展类加载器是否有此jar包,再向上找,看根加载器路径中是否有此类
若根加载器路径中有此jar包,则会用根加载器路径下的jar包,而不会用自己定义的
类,如果自己定义类一个和系统类加载器路径下的jar包,而不会用我们自己定义的jar包
*/
}
}
package org.reflect;
import org.entity.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class DemoTest3 {
public static void main(String[] args) throws Exception {
Class aClass = Class.forName("org.entity.Student");
// Student student = new Student();
// aClass = student.getClass();
System.out.println(aClass.getName());//包名 + 类名
System.out.println(aClass.getSimpleName());//类名
Field[] fields = aClass.getFields();//获得public修饰的属性,连同父类public属性也会被获得
for (Field f : fields) {//public修饰的属性会被打印出来,如果该类父类有public修饰的属性,则也会被打印出来
System.out.println(f);//private修饰的属性不会被打印出来,如果该类的父类属性也为private修饰,则不会被打印出来
}
Field[] fields1 = aClass.getDeclaredFields();//获得类的私有属性
for (Field f1 : fields1) {//不会获得子类中父类的私有属性,只能获得子类的私有属性
System.out.println(f1);//和public属性
}
//获得指定属性的值,无该字段会报错NoSuchFieldException
System.out.println(aClass.getDeclaredField("id"));
//获得类的方法
Method[] methods = aClass.getMethods();//获得本类包括父类的所有public方法
for (Method method : methods) {
System.out.println("正常的:" + method);
}
Method[] declaredMethods = aClass.getDeclaredMethods();//只获取本类的所有方法包括private方法
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
//获得指定方法
Method getName = aClass.getDeclaredMethod("getName", null);
System.out.println(getName);
Method setName = aClass.getDeclaredMethod("setName", String.class);
System.out.println(setName);
//获得构造器
Constructor[] constructors = aClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);//获得本类的public方法
}
constructors = aClass.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);//获得本类的所有构造方法
}//获得指定的构造方法
Constructor constructor = aClass.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(constructor);
}
}
动态创建对象执行方法
package org.reflect;
import org.entity.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Demo4 {
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("org.entity.Student");
// Object o = aClass.newInstance();//调用的无参构造,打印的toString方法
// System.out.println(o);//Student{name='null', age=0',id = 2019121762}
// Student student = (Student) aClass.newInstance();
// System.out.println(student);//使用的是无参构造器的构造方法,打印的toString方法
//System.out.println(student.getId());//可以通过此实例对象使用方法
//通过构造器创建对象
//Constructor<?> constructor = aClass.getDeclaredConstructor(String.class, int.class, String.class);
//Object student = constructor.newInstance("zhang", 12, "2012222");
//System.out.println(student);//使用有参构造,创建对象
Student o = (Student) aClass.newInstance();
//通过反射获取方法
Method setName = aClass.getDeclaredMethod("setName", String.class);
setName.invoke(o,"sang");//激活此方法,传入对象及参数
System.out.println(o.getName());
//反射操作属性
Field id = aClass.getDeclaredField("id");
id.setAccessible(true);//修改权限,关闭程序的安全访问
id.set(o,"20191217");//若字段修饰为private,则会报错,需要用方法修改权限1
System.out.println(o.getId());
}
}
setAccessible
性能对比分析
反射会影响执行效率,反射关闭安全检查会有所提高
package org.reflect;
import org.entity.Student;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo5 {
public static void main(String[] args) {
test1();//7ms
try {
test2();//46ms
test3();//27ms
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}//普通方法调用
public static void test1(){
Student student = new Student();
long l1 = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
student.getId();
}
long l2 = System.currentTimeMillis();
System.out.println(l2 - l1 + "ms");
}//反射方法调用
public static void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> aClass = Class.forName("org.entity.Student");
Student student =(Student) aClass.newInstance();
Method getName = aClass.getDeclaredMethod("getName");
long l1 = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
getName.invoke(student,null);
}
long l2 = System.currentTimeMillis();
System.out.println(l2 - l1 + "ms");
}//反射方法关闭安全检查调用
public static void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> aClass = Class.forName("org.entity.Student");
Student student =(Student) aClass.newInstance();
Method getName = aClass.getDeclaredMethod("getName");
getName.setAccessible(true);
long l1 = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
getName.invoke(student,null);
}
long l2 = System.currentTimeMillis();
System.out.println(l2 - l1 + "ms");
}
}
反射操作泛型
获得方法中参数类型泛型信息
package org.reflect;
import org.entity.Student;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获得泛型信息
public class Demo6 {
public static void main(String[] args) throws NoSuchMethodException {
Class aClass = Demo6.class;//获取Demo6类,通过反射获取方法
//获取里面的test1()方法
Method test1 = aClass.getMethod("test1", Map.class, List.class);
//通过getGenericParameterTypes()方法,获得方法的参数列表,返回参数数组
Type[] parameterTypes = test1.getGenericParameterTypes();
//遍历参数数组,打印参数信息
for (Type parameterType : parameterTypes) {
System.out.println(parameterType + "++++++++++");
//如果,参数信息属于一种参数化类型,将此参数信息强制转换为参数化类型,然后
if (parameterType instanceof ParameterizedType){//通过getActualTypeArguments()方法,获得真实参数
Type[] actualTypeArguments = ((ParameterizedType) parameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);//遍历真实参数列表
}//打印信息,获得参数中的,参数类型信息。
}//获得,方法中的参数类型信息
}
}
public void test1(Map<String, Student> map, List<Student> list){
System.out.println("test1");
}
}
获得返回值类型中的泛型参数
package org.reflect;
import org.entity.Student;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
public class Demo7 {
public static void main(String[] args) {
Class<Demo7> demo7Class = Demo7.class;
try {
Method test2 = demo7Class.getMethod("test2",null);
//获得返回值类型
Type genericReturnType = test2.getGenericReturnType();
//如果返回值类型为参数类型
if (genericReturnType instanceof ParameterizedType){
//获得真实的返回值类型中的参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
///遍历结果,查看真实参数信息
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public Map<String, Student> test2(){
System.out.println("test2");
return null;
}
}
反射获得注解
package org.reflect;
import org.entity.MyField;
import org.entity.Sang;
import org.entity.User;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class Demo8 {
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("org.entity.User");
User user =(User) aClass.newInstance();
Annotation[] annotations = aClass.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}//获得注解的value值
Sang annotation = aClass.getAnnotation(Sang.class);
System.out.println(annotation.value());
//获得指定的注解
Field name = aClass.getDeclaredField("name");
MyField annotation1 = name.getAnnotation(MyField.class);
System.out.println(annotation1);
}
}