教程
狂神说java官网
B站视频链接
注解Annotation
注解可以被其他程序(比如编译器)读取。 注解以”@注释名“在代码中存在,还可以添加一些参数值,例如@SuppressWarnings(value = "unchecked")
。注解可以附加在package、class、method、field等上面。相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问。
内置注解
@Override
:只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。
@Deprecated
:可以用来修辞方法、属性、类,表示不鼓励开发者使用这样的元素,因其很危险或者存在更好的选择。
- @
SuppressWarnings
:用来抑制编译时的警告信息,需要添加一个参数才能正常使用,这些参数都是已经定义好的,选择性使用即可。
@SuppressWarnings("all")
@SuppressWarnings("unchecked")
@SuppressWarnings(value={"unchecked","deprecated"})
元注解
元注解负责注解其他注解。Java定义了4个标准的meta-annotation类型,提供对其他annotation类型作说明。
@Target
:用于描述注解的使用范围
@Retention
:表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
@Documented
:说明该注解将被包含在javadoc中
@Inherited
:说明子类可以继承父类中的该注解
@MyAnnotation public class Test01 { public void test() { } }
@Target(value = {ElementType.METHOD, ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) @Documented @Inherited @interface MyAnnotation { }
|
自定义注解
使用@interface
自定义注解时,自动继承了java.lang.annotation.Annotation
接口。
格式public @interface name{}
。其中每一个方法实际上是声明了一个配置参数,方法名称就是参数名称,返回值类型就是参数类型(只能是基本类型Class、String、enum)。可以通过default声明参数默认值。如果只有一个参数,一般名为value。注解元素必须要有值,定义注解元素时经常使用空字符串或0作为默认值。
public class Test02 { @MyAnnotation2(name = "Lfalive", place = {"重庆", "武汉"}) public void test2() { }
@MyAnnotation3("HUST") public void test3() { } }
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2 { String name() default "张三";
int age() default 0;
String[] place(); }
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation3 { String value(); }
|
反射Reflection
动态语言:在运行时,代码可以根据某些条件改变自身结构。主要有Object-C、C#、JS、PHP、Python等。
静态语言:运行时结构不可变的语言,如Java、C、C++。Java可以称之为“准动态语言”,即有一定动态性,可以利用反射机制获得类似动态语言的特性,让编程更加灵活。
反射机制允许程序在执行期间取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类后,堆内存的方法区内就有了一个Class类型的对象,包含了类的结构信息。可以通过这个对象看到类的结构。
优点:可以实现动态创建对象和编译,灵活。
缺点:对性能有影响。使用反射基本上是一种解释操作,我们告诉JVM我们希望做什么并且它满足我们的要求,这类操作总是慢于直接执行相同的操作。
一个类在内存中只有一个Class对象,一个类被加载后,类的整个结构都会被封装在Class对象中。
Class类
可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构的有关信息。
- Class本身也是一个类
- Class对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 每个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是Reflection的根源
常用方法:
获取Class类实例
若已知具体类,通过类的class属性获取。
Class<Teacher> c3 = Teacher.class;
|
已知某个类的实例,调用该实例的getClass()方法获取Class对象。
Class<? extends Person> c1 = person.getClass();
|
已知一个类的全类名,可通过Class类的静态方法forName()获取,可能抛出异常。
Class<?> c2 = Class.forName("Student");
|
内置基本数据类型可以直接用类名.Type
。还可以利用ClassLoader
。
public class TestReflection01 { public static void main(String[] args) throws ClassNotFoundException { Person person = new Student(); System.out.println(person); Class<? extends Person> c1 = person.getClass(); Class<?> c2 = Class.forName("Student"); Class<Teacher> c3 = Teacher.class; Class<Integer> c4 = Integer.TYPE; Class<?> c5 = c1.getSuperclass();
System.out.println(c1); System.out.println(c2); System.out.println(c3); System.out.println(c4); System.out.println(c5); } }
class Person { public String name;
public Person() { }
@Override public String toString() { return "Person{name='" + name + "'}"; } }
class Student extends Person { public Student() { this.name = "学生"; } }
class Teacher extends Person { public Teacher() { this.name = "老师"; } }
|
哪些类型可以有Class对象?
- class:外部类,成员(成员内部类、静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
public class TestReflection02 { public static void main(String[] args) { Class<Object> c1 = Object.class; Class<Runnable> c2 = Runnable.class; Class<String[]> c3 = String[].class; Class<int[][]> c4 = int[][].class; Class<Override> c5 = Override.class; Class<ElementType> c6 = ElementType.class; Class<Integer> c7 = Integer.class; Class<Void> c8 = void.class; Class<Class> c9 = Class.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); } }
|
内存分析
什么时候会发生类的初始化?
主动引用(会发生类的初始化)
- 虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(除了final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 初始化一个类,如果其父类没有被初始化,先初始化其父类
被动引用(不会发生类的初始化)
- 访问一个静态域时,只有真正声明这个域的类才会初始化。当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类的引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
public class TestReflection03 { static { System.out.println("Main类被加载"); }
public static void main(String[] args) throws ClassNotFoundException { Son son = new Son(); Class.forName("Son"); System.out.println(Son.b); Son[] array = new Son[5]; System.out.println(Son.M); } }
class Father { static int b = 2;
static { System.out.println("父类被加载"); } }
class Son extends Father { static { System.out.println("子类被加载"); }
static final int M = 1; }
|
类加载器
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
public class TestReflection04 { public static void main(String[] args) throws ClassNotFoundException { ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader);
ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent);
ClassLoader parent1 = parent.getParent(); System.out.println(parent1);
System.out.println(Class.forName("Person").getClassLoader()); System.out.println(Class.forName("java.lang.Object").getClassLoader()); System.out.println(System.getProperty("java.class.path")); } }
|
获取运行时类的完整结构
class Person { public String name;
public Person() { }
@Override public String toString() { return "Person{name='" + name + "'}"; } }
class Student extends Person { private int age;
public Student() { this.name = "学生"; }
public Student(int age) { this.age = age; }
public void run() { System.out.println("run"); } }
|
public class TestReflection05 { public static void main(String[] args) throws ReflectiveOperationException { Class<?> aClass = Class.forName("Student");
System.out.println(aClass.getName()); System.out.println(aClass.getSimpleName()); System.out.println("==========================");
Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("=========================="); fields = aClass.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } System.out.println("=========================="); System.out.println(aClass.getField("name")); System.out.println("==========================");
Method[] methods = aClass.getMethods(); for (Method method : methods) { System.out.println(method); } System.out.println("=========================="); Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("declared " + method); } System.out.println("=========================="); System.out.println(aClass.getMethod("toString")); System.out.println("==========================");
Constructor[] constructors = aClass.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("=========================="); Constructor[] declaredConstructors = aClass.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } System.out.println("=========================="); System.out.println(aClass.getDeclaredConstructor(int.class)); System.out.println("=========================="); } }
|
Class对象用来做什么
可以创建类的对象,可以通过反射调用类中的方法或者获取类中的属性。
调用方法时,Object invoke(Object obj, Object[] args)
,Object
对应原方法的返回值,若无返回值,则是null
。若原方法为静态方法,形参Object
可为null
。若原方法形参列表为空,则Object[] args
为null
。若原方法声明为private
,则需要在调用此invoke()
方法前,显式调用方法对象的setAccessible()
方法。
Method和Field、Constructor对象都有setAccessible()
方法,是启动和禁用访问安全检查的开关。参数为true则在使用时取消java语言访问检查。
class Person { public String name;
public Person() { }
@Override public String toString() { return "Person{name='" + name + "'}"; } }
class Student extends Person { private int age;
public Student() { this.name = "学生"; }
public Student(int age) { this.age = age; }
public void run(int para) { System.out.println("name=" + name + ",age=" + age + ",para=" + para); } }
|
public class TestReflection06 { public static void main(String[] args) throws ReflectiveOperationException { Class<?> c1 = Class.forName("Student");
Student student1 = (Student) c1.getDeclaredConstructor().newInstance(); Student student2 = (Student) c1.getDeclaredConstructor(int.class).newInstance(22);
Method run = c1.getDeclaredMethod("run", int.class); run.invoke(student1, 111); run.invoke(student2, 222);
Field age = c1.getDeclaredField("age"); age.setAccessible(true); age.set(student1, 30); run.invoke(student1, 666); } }
|
性能分析
public class TestReflection07 { public static void test01() { Student student = new Student();
long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { student.getAge(); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); }
public static void test02() throws ReflectiveOperationException { Student student = new Student(); Class<? extends Student> aClass = student.getClass();
Method getAge = aClass.getMethod("getAge");
long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getAge.invoke(student); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); }
public static void test03() throws ReflectiveOperationException { Student student = new Student(); Class<? extends Student> aClass = student.getClass();
Method getAge = aClass.getMethod("getAge"); getAge.setAccessible(true);
long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { getAge.invoke(student); } long endTime = System.currentTimeMillis(); System.out.println(endTime - startTime); }
public static void main(String[] args) throws ReflectiveOperationException { test01(); test02(); test03(); } }
|
普通方式最快。
反射操作泛型
ParameterizedType
:表示一种参数化类型,比如Collection<String>
。
GenericArrayType
:表示一种元素类型是参数化类型或者类型变量的数组类型。
TypeVariable
:是各种类型变量的公共父接口。
WildcardType
:代表一种通配符类型表达式。
public class TestReflection08 { public void test01(Map<String, Student> map, List<Student> list) { System.out.println("test01"); }
public Map<String, Student> test02() { System.out.println("test02"); return null; }
public static void main(String[] args) throws NoSuchMethodException { Method method = TestReflection08.class.getMethod("test01", Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println(genericParameterType); if (genericParameterType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println("- " + actualTypeArgument); } } } System.out.println("===================================="); method = TestReflection08.class.getMethod("test02"); Type genericReturnType = method.getGenericReturnType(); System.out.println(genericReturnType); if (genericReturnType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println("- " + actualTypeArgument); } } } }
|
获取注解信息
getAnnotations
和getAnnotation
。
ORM:Object Relationship Mapping,对象关系映射。
类和表结构对应,属性和字段对应,对象和记录对应。
public class TestReflection09 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class<?> c1 = Class.forName("Student2"); Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } Tablelf tablelf = c1.getAnnotation(Tablelf.class); System.out.println(tablelf.value());
Field name = c1.getDeclaredField("name"); Fieldlf fieldlf = name.getAnnotation(Fieldlf.class); System.out.println(fieldlf.columnName()); System.out.println(fieldlf.type()); System.out.println(fieldlf.length()); } }
@Tablelf("db_student") class Student2 { @Fieldlf(columnName = "db_id", type = "int", length = 10) private int id; @Fieldlf(columnName = "db_age", type = "int", length = 10) private int age; @Fieldlf(columnName = "db_name", type = "varcher", length = 3) private String name;
public Student2(int id, int age, String name) { this.id = id; this.age = age; this.name = name; }
public Student2() { }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override public String toString() { return "Student2{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}'; } }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Tablelf { String value(); }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface Fieldlf { String columnName();
String type();
int length(); }
|