反射 反射机制概述 反射被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API 取得任何类的内部信息,并能直接操作任何对象的内部属性和方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个对象),这个对象就包含了完整类的结构信息。
Java反射机制研究及应用:
在运行是判断任何一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解()
生成动态代理
反射相关的主要API
1 2 3 4 java.lang.Class:代表一个方法 java.lang.reflect.Method:代表类的方法 java.lang.reflect.Field:代表类的成员变量 java.lang.reflect.Constructor:代表类的构造器
如何理解Class类 在Object类中定义了以下的方法,此方法将被所有子类继承:
1 public final Class getClass ()
以上的方法返回值的类型是一个Class类型,此类是Java反射的源头。
对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。
一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
Class本身也是一个类
Class 对象只能由系统建立对象
一个加载的类在 JVM 中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个 Class 实例所生成
通过Class可以完整地得到一个类中的所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
获取Class类的实例 那些类型可以有Class对象?
class、interface、数组、enum、annotation、primitive type(基本数据类型)、void
获取实例的四种方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Person { public String name; public String sex; public Person () { } public Person (String name, String sex) { this .name = name; this .sex = sex; } @Override public String toString () { return "Person{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + '}' ; } }
方法一:
已知具体的类,通过类的class属性获取,该方法最安全可靠,程序性能最高。
1 2 3 Class class1 = Person.class; System.out.println(class1);
方法二:
已知某个类的实例。
1 2 3 4 Person person = new Person ();Class class2 = person.getClass(); System.out.println(class2);
方法三:
已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能会抛出异常ClassNotFoundException(该类为找到)。
方法三更能体现动态性,因为在运行才会判断类是否存在等。
1 2 3 Class class3 = Class.forName("com.jiang.Person" ); System.out.println(class3);
方法四:
通过getClassLoader()得到:
1 2 3 4 ClassLoader classLoader = this .getClass().getClassLoader();Class class4 = classLoader.loadClass("com.jiang.Person" ); System.out.println(class4);
创建运行时类的对象 创建完Class对象后,我们可以创建类的对象。
我们调用Class对象的newInstance(),newInstance()方法在jdk1.9之后过时了。
1、 类必须有一个无参数的构造器
2、 类的构造器的访问权限需要足够
1 2 3 4 Class class3 = Class.forName("com.jiang.Person" );Person person = (Person) class3.newInstance(); System.out.println(person);
我们也可以使用带参数的构造器:
1 2 3 4 5 Constructor con = class1.getConstructor(String.class, String.class);Person person2 = (Person)con.newInstance("jiang" , "男" ); System.out.println(person2);
Dame:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static Object getInstance (String classPath) throws Exception { Class class1 = Class.forName(classPath); return class1.newInstance(); } public static void main (String[] args) throws Exception { int num = new Random ().nextInt(2 ); String classPath = null ; switch (num) { case 0 : classPath = "com.jzw.Person" ; break ; case 1 : classPath = "java.util.Date" ; } Object instance = getInstance(classPath); System.out.println(instance); }
获取运行时类的完整结构 先定义一个复杂的类。
创建一个注解:只能RetentionPolicy.RUNTIME,才能通过反射得到注解。
1 2 3 4 5 6 7 @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.LOCAL_VARIABLE,ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationDemo { public int id () ; public String decsription () default "no decsription" ; }
创建一个父类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @AnnotationDemo(id = 1) public class Creature <T> implements Serializable { private char gender; public double weight; private void breath () { System.out.println("生物呼吸" ); } public void eat () { System.out.println("生物吃东西" ); } }
创建一个接口:
1 2 3 4 public interface MyInterface { void info () ; }
创建一个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @AnnotationDemo(id = 0) public class Person extends Creature <String> implements Comparable <String>,MyInterface { private String name; int age; public int id; public Person () { } @AnnotationDemo(id = 2) public Person (String name, int age) { this .name = name; this .age = age; } public Person (String name, int age, int id) { this .name = name; this .age = age; this .id = id; } @AnnotationDemo(id = 2) private String getNation (String nation) { System.out.println("我的国籍是:" + nation); return nation; } @Override public void eat () { super .eat(); } @Override public void info () { System.out.println("我是一个人" ); } @AnnotationDemo(id = 0) @Override public int compareTo (String o) { return 0 ; } @Override public String toString () { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + ", weight=" + weight + '}' ; } private static void show () { System.out.println("这是一个静态方法" ); } }
获取属性结构 获取当前运行时类及其父类中声明为public的属性
1 2 3 4 5 6 7 8 Class class1 = Class.forName("com.jiang.Person" ); Field[] fields = class1.getFields();for (Field field : fields) { System.out.println(field); }
得到当前运行时类的所有权限的属性,但不包括父类的属性
1 2 3 4 5 Field[] declaredFields = class1.getDeclaredFields();for (Field declaredField : declaredFields) { System.out.println(declaredField); }
获取属性的具体结构
权限修饰符 getModifiers()
数据类型 getType()
变量名 getName()
1 2 3 4 5 6 7 8 9 10 11 12 13 Field[] declaredFields1 = class1.getDeclaredFields();for (Field declaredField : declaredFields1) { int modifiers = declaredField.getModifiers(); System.out.print(Modifier.toString(modifiers) + "\t" ); Class type = declaredField.getType(); System.out.print(type.getName() + "\t" ); String name = declaredField.getName(); System.out.println(name); }
获取方法结构 获取当前运行时类及其父类中声明为public的方法getMethods();
得到当前运行时类的所有权限的方法,但不包括父类的方法getDeclaredMethods();
1 2 3 4 5 6 7 8 9 10 11 Method[] methods = class1.getMethods();for (Method method : methods) { System.out.println(method); } System.out.println(); Method[] declaredMethods = class1.getDeclaredMethods();for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); }
获取方法的具体结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 Method[] declaredMethods = class1.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { Annotation[] annotations = declaredMethod.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } int modifiers = declaredMethod.getModifiers(); System.out.print(Modifier.toString(modifiers) + "\t" ); System.out.print(declaredMethod.getReturnType().getName() + "\t" ); System.out.print(declaredMethod.getName()); System.out.print("(" ); Class[] parameterTypes = declaredMethod.getParameterTypes(); if (parameterTypes.length != 0 ) { for (int i = 0 ; i < parameterTypes.length; i++) { if (i == parameterTypes.length - 1 ) { System.out.print(parameterTypes[i].getName() + "_args" + i); break ; } System.out.print(parameterTypes[i].getName() + "_args" + i + "," ); } } System.out.print(")" ); Class[] exceptionTypes = declaredMethod.getExceptionTypes(); if (exceptionTypes.length != 0 ) { System.out.print("throws" ); for (int i = 0 ; i < exceptionTypes.length; i++) { if (i == exceptionTypes.length - 1 ) { System.out.print(exceptionTypes[i].getName()); break ; } System.out.print(exceptionTypes[i].getName() + "," ); } } System.out.println(); System.out.println(); }
获取当前运行时类的构造器 1 2 3 4 5 6 7 8 9 10 Constructor[] constructor = class1.getConstructors();for (Constructor constructor1 : constructor) { System.out.println(constructor1); } Constructor[] declaredConstructor = class1.getDeclaredConstructors();for (Constructor constructor1 : declaredConstructor) { System.out.println(constructor1); }
获取父类的泛型 1 2 3 4 5 6 7 8 9 10 11 Type genericSuperclass = class1.getGenericSuperclass(); System.out.println(genericSuperclass);ParameterizedType genericSuperclass1 = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = genericSuperclass1.getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); }
获取运行时类的注解、所在的包、接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Class[] interfaces = class1.getInterfaces();for (Class anInterface : interfaces) { System.out.println(anInterface); } System.out.println(); Class[] interfaces1 = class1.getSuperclass().getInterfaces();for (Class aClass : interfaces1) { System.out.println(aClass); } System.out.println();Package aPackage = class1.getPackage(); System.out.println(aPackage); System.out.println(); Annotation[] annotations = class1.getAnnotations();for (Annotation annotation : annotations) { System.out.println(annotation); }
调用运行时类指定的结构 调用属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Class class1 = Class.forName("com.jiang.Person" );Person person = (Person)class1.newInstance();Field id = class1.getField("id" ); id.set(person,4 );int ids = (int )id.get(person); System.out.println(ids);Field name = class1.getDeclaredField("name" ); name.setAccessible(true ); name.set(person,"jiang" );String name1 = (String)name.get(person); System.out.println(name1);
调用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 Method getNation = class1.getDeclaredMethod("getNation" , String.class); getNation.setAccessible(true ); getNation.invoke(person, "CHINA" );Method show = class1.getDeclaredMethod("show" ); show.setAccessible(true ); show.invoke(null );
调用构造器 1 2 3 4 5 6 7 Constructor declaredConstructor = class1.getDeclaredConstructor(String.class, int .class); declaredConstructor.setAccessible(true );Person o = (Person)declaredConstructor.newInstance("jiang" , 1 ); System.out.println(o);
面试题 描述一下JVM加载class文件的原理机制? 答:JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。
注解 注解定义
注解(也称元数据) 为我们在代码中添加信息提供了一种形式化的方法 ,使我们可以在之后的某个时刻非常方便地这些数据。
这些定义太过抽象,简单来说就是注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”。注解就是代码的标签,注解起到标识做用。
三种标准注解 @override
表示当前的方法定义将覆盖超类中的方法 。如果不小心拼写错误,或者方法签名对不上被覆盖的方法,编译器就会发出错误提示。
1 2 3 4 5 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
@Deprecated
所修饰的元素已经过时 ,但为了项目的维护,不可以删除。如果程序员使用了注解为他的元素,那么编译器会发出警告信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE}) public @interface Deprecated { String since () default "" ; boolean forRemoval () default false ; }
@SuppressWarnings
关闭不当的编译器警告信息, 当代码出现一些警告时,比如变量为使用,泛型等。使用注解可以将警告关闭。注解在使用需要添加参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
元注解 元注解专门负责新注解的创建。 注解其他的注解。
@Target
表示注解可以用在什么地方 ,可能的ElementType参数包括:
一个注解有多个参数。
参数
注解使用具体元素
CONSTRUCTOR
构造器的声明
FIELD
域声明(包括enum实例)
LOCAL_VARIABLE
局部变量声明
METHOD
方法声明
PACKAGE
包声明
PARAMETER
参数声明
TYPE
类、接口(包括注解类型)或enum声明
ANNOTATION_TYPE
注解声明
1 2 3 4 5 6 7 8 9 10 11 12 13 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
@Retention
表示需要在什么级别保存该注解信息,一个注解中有唯一的@Retention,默认参数是CLASS 。
参数有:
SOURCE: 注解将被编译器丢弃
CLASS : 注解在class文件中可用,但会被JVM丢弃。
RUNTIME: JVM将在运行 期也保留注解,因此可以通过反射机制读取注解信息。
1 2 3 4 5 6 7 8 public @interface Retention { RetentionPolicy value () ; }
@Documented
它的作用是能够将注解中的元素包含到 Javadoc 中去。
1 2 3 4 5 6 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
@Inherited
允许子类继承父类的注解。
1 2 3 4 5 6 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
定义注解 类,接口,注解是同一级别的。
1 2 3 4 5 6 @Target({ElementType.METHOD,ElementType.TYPE,ElementType.LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationDemo { public int id () ; public String decsription () default "no decsription" ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class AnnotationTest { @AnnotationDemo(id = 47,decsription = "Password must contain at least one numeric") public boolean validatePassword (String password) { return (password.matches("\\w*\\d\\w*" )); } @AnnotationDemo(id = 48) public String encryptPassword (String password) { return new StringBuilder (password).reverse().toString(); } }