文章目录
反射
反射机制概述
反射被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API 取得任何类的内部信息,并能直接操作任何对象的内部属性和方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个对象),这个对象就包含了完整类的结构信息。
Java反射机制研究及应用:
在运行是判断任何一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解()
生成动态代理
反射相关的主要API
java.lang.Class:代表一个方法
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
如何理解Class类
在Object类中定义了以下的方法,此方法将被所有子类继承:
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
获取实例的四种方法:
//先定义一个类
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属性获取,该方法最安全可靠,程序性能最高。
//方法一:
Class class1 = Person.class;
System.out.println(class1);
方法二:
已知某个类的实例。
//方法二:
Person person = new Person();
Class class2 = person.getClass();
System.out.println(class2);
方法三:
已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能会抛出异常ClassNotFoundException(该类为找到)。
方法三更能体现动态性,因为在运行才会判断类是否存在等。
//方法三:
Class class3 = Class.forName("com.jiang.Person");
System.out.println(class3);
方法四:
通过getClassLoader()得到:
//方法四:
ClassLoader classLoader = this.getClass().getClassLoader();
Class class4 = classLoader.loadClass("com.jiang.Person");
System.out.println(class4);
创建运行时类的对象
创建完Class对象后,我们可以创建类的对象。
我们调用Class对象的newInstance(),newInstance()方法在jdk1.9之后过时了。
1、 类必须有一个无参数的构造器
2、 类的构造器的访问权限需要足够
Class class3 = Class.forName("com.jiang.Person");
//得到运行时类对象
Person person = (Person) class3.newInstance();
System.out.println(person);
我们也可以使用带参数的构造器:
//调用指定参数结构的构造器,生成Constructor的实例
Constructor con = class1.getConstructor(String.class, String.class);
//通过生成Constructor的实例创建对应类的对象,并初始化类属性
Person person2 = (Person)con.newInstance("jiang", "男");
System.out.println(person2);
Dame:
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,才能通过反射得到注解。
@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";
}
创建一个父类:
@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("生物吃东西");
}
}
创建一个接口:
public interface MyInterface {
void info();
}
创建一个类:
@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的属性
//拿到运行时类
Class class1 = Class.forName("com.jiang.Person");
//getFields()方法拿到当前运行时类以及父类权限为public的属性
Field[] fields = class1.getFields();
for (Field field : fields) {
System.out.println(field);
}
得到当前运行时类的所有权限的属性,但不包括父类的属性
//getDeclaredFields()方法得到当前运行时类的所有权限的属性,但不包括父类的属性
Field[] declaredFields = class1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
获取属性的具体结构
权限修饰符 getModifiers()
数据类型 getType()
变量名 getName()
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();
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);
}
获取方法的具体结构
Method[] declaredMethods = class1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
//1、获取注解
Annotation[] annotations = declaredMethod.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//2、获取权限修饰符
int modifiers = declaredMethod.getModifiers();
System.out.print(Modifier.toString(modifiers) + "\t");
//3、返回值类型
System.out.print(declaredMethod.getReturnType().getName() + "\t");
//4、方法名
System.out.print(declaredMethod.getName());
//5、形参列表
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(")");
// 6、抛出异常
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();
}
获取当前运行时类的构造器
//获取当前权限修饰符为public的构造器
Constructor[] constructor = class1.getConstructors();
for (Constructor constructor1 : constructor) {
System.out.println(constructor1);
}
//获取全部权限的构造器
Constructor[] declaredConstructor = class1.getDeclaredConstructors();
for (Constructor constructor1 : declaredConstructor) {
System.out.println(constructor1);
}
获取父类的泛型
//获取带泛型的父类
Type genericSuperclass = class1.getGenericSuperclass();
System.out.println(genericSuperclass);
//获取带泛型的父类的泛型
ParameterizedType genericSuperclass1 = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = genericSuperclass1.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
获取运行时类的注解、所在的包、接口
//获取运行时类的接口
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);
}
调用运行时类指定的结构
调用属性
//拿到运行时类
Class class1 = Class.forName("com.jiang.Person");
//创建运行时类对象
Person person = (Person)class1.newInstance();
//1、获取指定属性 要求权限为public 一般不用
Field id = class1.getField("id");
id.set(person,4);
int ids = (int)id.get(person);
System.out.println(ids);
//2、任何权限都可以拿到
Field name = class1.getDeclaredField("name");
//保证当前属性是可访问的
name.setAccessible(true);
name.set(person,"jiang");
String name1 = (String)name.get(person);
System.out.println(name1);
调用方法
//1、获取指定方法
//参数1:方法名,参数2:形参列表
Method getNation = class1.getDeclaredMethod("getNation", String.class);
getNation.setAccessible(true);
//invoke的返回值就是当前方法的返回值
getNation.invoke(person, "CHINA");
//2、任何调用静态方法
Method show = class1.getDeclaredMethod("show");
show.setAccessible(true);
//因为是一个静态方法,可以传递一个空对象
show.invoke(null);
调用构造器
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
表示当前的方法定义将覆盖超类中的方法。如果不小心拼写错误,或者方法签名对不上被覆盖的方法,编译器就会发出错误提示。
@Target(ElementType.METHOD) //只能用在方法上
@Retention(RetentionPolicy.SOURCE) //在编译器处理完之后,注解失效
public @interface Override {
}
@Deprecated
所修饰的元素已经过时,但为了项目的维护,不可以删除。如果程序员使用了注解为他的元素,那么编译器会发出警告信息。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
/**
* Returns the version in which the annotated element became deprecated.
* The version string is in the same format and namespace as the value of
* the {@code @since} javadoc tag. The default value is the empty
* string.
*
* @return the version string
* @since 9
*/
String since() default "";
/**
* Indicates whether the annotated element is subject to removal in a
* future version. The default value is {@code false}.
*
* @return whether the element is subject to removal
* @since 9
*/
boolean forRemoval() default false;
}
@SuppressWarnings
**关闭不当的编译器警告信息,**当代码出现一些警告时,比如变量为使用,泛型等。使用注解可以将警告关闭。注解在使用需要添加参数。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/**
* The set of warnings that are to be suppressed by the compiler in the
* annotated element. Duplicate names are permitted. The second and
* successive occurrences of a name are ignored. The presence of
* unrecognized warning names is <i>not</i> an error: Compilers must
* ignore any warning names they do not recognize. They are, however,
* free to emit a warning if an annotation contains an unrecognized
* warning name.
*
* <p> The string {@code "unchecked"} is used to suppress
* unchecked warnings. Compiler vendors should document the
* additional warning names they support in conjunction with this
* annotation type. They are encouraged to cooperate to ensure
* that the same names work across multiple compilers.
* @return the set of warnings to be suppressed
*/
String[] value();
}
元注解
**元注解专门负责新注解的创建。**注解其他的注解。
@Target
表示注解可以用在什么地方,可能的ElementType参数包括:
一个注解有多个参数。
参数 | 注解使用具体元素 |
---|---|
CONSTRUCTOR | 构造器的声明 |
FIELD | 域声明(包括enum实例) |
LOCAL_VARIABLE | 局部变量声明 |
METHOD | 方法声明 |
PACKAGE | 包声明 |
PARAMETER | 参数声明 |
TYPE | 类、接口(包括注解类型)或enum声明 |
ANNOTATION_TYPE | 注解声明 |
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
@Retention
表示需要在什么级别保存该注解信息,一个注解中有唯一的@Retention,默认参数是CLASS 。
参数有:
SOURCE: 注解将被编译器丢弃
CLASS : 注解在class文件中可用,但会被JVM丢弃。
RUNTIME: JVM将在运行 期也保留注解,因此可以通过反射机制读取注解信息。
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
@Documented
它的作用是能够将注解中的元素包含到 Javadoc 中去。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Inherited
允许子类继承父类的注解。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
定义注解
类,接口,注解是同一级别的。
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo {
public int id();
public String decsription() default "no decsription";
}
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();
}
}