反射和注解
反射
“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”,如Python,Ruby是动态语言;显然C++,Java,C#不是动态语言,但是JAVA有着一个非常突出的动态相关机制:Reflection。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制,很多优秀的开源框架都是通过反射完成的。
功能
java反射机制可以实现以下功能:
①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法;
⑤生成动态代理
了解反射首先需要获取源头Class
所有的类其实都是Class的实例。这个Class实例可以理解为类的模子,通过其来创建各种类。
获取Class对象三种方式
第一种方式:类.class
// 1、通过类.class获取Class对象
Class c = String.class;
System.out.println(c);
//输出
class java.lang.String
第二种方式:对象.getClass()
// 2、通过对象.getClass()方法获取Class对象
String s = "";
System.out.println(s.getClass());
//输出
class java.lang.String
第三种方式:Class.forName
// 3、通过class.forName("包名.类名")-->推荐使用
Class c2 = Class.forName("java.lang.String");
System.out.println(c2);
//输出
class java.lang.String
这里介绍一个方法,通过获取Class对象获取父类的class对象
// 4、通过Class对象获取父类的Class对象
Class<?> superclass = "".getClass().getSuperclass();
System.out.println(superclass);
//输出
class java.lang.Object
修饰符
通过getModifiers()
方法获取修饰符, Modifier
类提供了 static
方法和常量来解码类和成员访问修饰符
下面是对应的修饰符的常量表示表格:
get.Modifiers()返回的是修复符的常量和
public --> return 1
public final --> return 16+1
-
Modifier and Type Constant Field Value public static final int
ABSTRACT
1024
public static final int
FINAL
16
public static final int
INTERFACE
512
public static final int
NATIVE
256
public static final int
PRIVATE
2
public static final int
PROTECTED
4
public static final int
PUBLIC
1
public static final int
STATIC
8
public static final int
STRICT
2048
public static final int
SYNCHRONIZED
32
public static final int
TRANSIENT
128
public static final int
VOLATILE
64
反射创建对象
根据Class对象, 我们可以获取某个类的构造器,进而创建类的对象。
代码演示:
// 这里的User是一个User类,使用的是泛型,传入的是User.class对象
public static void newClass(Class<User> cls) throws Exception {
// 1、newInstance()自动调用无参构造器创建对象
User u1 = cls.newInstance();
System.out.println(u1);
// 2、Constructor() 通过构造器获取对象
// 1)getConstructor(Class<?> ... paramType)可变参数选择不同参数的构造器获取
// 获取User类的无参构造器
Constructor<User> con1 = cls.getConstructor();
System.out.println(con1);
// 获取无参构造器之后通过newInstance()创建对象
System.out.println(con1.newInstance());
// 获取User的 private User(Integer id)一个参数的构造器
Constructor<User> con2 = cls.getDeclaredConstructor(Integer.class);
System.out.println(con2);
// 调用私有权限构造器方法需要调用setAccessible()方法,忽略权限,true为开启,false为关闭
con2.setAccessible(true);
System.out.println(con2.newInstance(1000));
con2.setAccessible(false);
// 获取User的双参构造器
Constructor<User> con3 = cls.getConstructor(String.class, String.class);
System.out.println(con3);
System.out.println(con3.newInstance("张三","123"));
}
反射操作方法
具体 的方法查看API文档,
Package java.lang.reflect
提供用于获取关于类和对象的反射信息的类和接口。
演示一些常用方法:
public static void getMethodTest(Class<User> cls) throws Exception{
// 所有的方法
// getMethods()返回所有方法,包含父类继承
// getDeclaredMethods()返回所有方法,只有自己内部的方法
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m);
}
// 获取指定的公共的方法,使用Method类接收
Method methodGet = cls.getMethod("getPwd");
// 创建对象调用方法
User user = cls.getConstructor(Integer.class,String.class,String.class).newInstance(1200,"李四","123123");
// 获取方法
Method methodSet = cls.getDeclaredMethod("setPwd", String.class);
// 调用setPwd方法
methodSet.invoke(user, "123456");
// 调用getPwd方法,使用获取到的Method对象调用 Invoke(调用对象,对应的方法参数)方法
String s = (String) methodGet.invoke(user);
System.out.println(s);
// 私有方法
// 方法忽略权限问题
methods[4].setAccessible(true);
methods[4].invoke(user);
// 静态方法
Method hello = cls.getDeclaredMethod("hello");
System.out.println(hello);
hello.invoke(user);
}
反射操作属性
在这里演示一些简单方法操作属性,具体的请阅读相关API文档。
public static void testField(Class<User> cls) throws Exception {
// 创建对象调用方法
User user = cls.getConstructor(Integer.class,String.class,String.class).newInstance(1200,"李四","123123");
// 获取属性,getField()、getDeclaredField()都可以
Field name = cls.getDeclaredField("name");
// 开启权限忽略
name.setAccessible(true);
// 修改属性,set(调用对象,方法参数)
name.set(user, "王五");
// 获取属性值
System.out.println(name.get(user));
// 获取name属性的修饰符对应的常量值
System.out.println(name.getModifiers());
// 获取修饰符
System.out.println(Modifier.toString(name.getModifiers()));
}
注解
注解是Java 1.5 引入的,目前已被广泛应用于各种Java框架,如Hibernate,Jersey、Spring。注解相当于是一种嵌入在程序中的 元数据 ,可以使用注解解析工具或编译器对其进行解析,也可以指定注解在编译期或运行期有效。在注解诞生之前,程序的元数据存在的形式仅限于java注释或javadoc,但注解可以提供更多功能,它不仅包含元数据,还能作用于运行期,注解解析器能够使用注解决定处理流程。
Annotation
(注解)就是Java提供了一种元程序中的元素关联任何信息和任何元数据 (metadata
)的途径和方法。Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来获取注解里面的元数据。注解API非常强大,被广泛应用于各种Java框架。
元数据:描述数据的数据
注解的分类
根据注解参数的个数分类
1)、标记注解:一个没有成员定义的Annotation类型被称为标记注解。
2)、单值注解:只有一个值
3)、完整注解:拥有多个值
根据注解使用方法和用途分类
1)、JDK内置系统注解
2)、元注解
3)、自定义注解
内置注解
@Override
限定重写父类方法,若想要重写父类的一个方法时,需要使用该注解告知编译器我们正在重写一个方法。如此一来,当父类的方法被删除或修改了,编译器会提示错误信息;或者该方法不是重写也会提示错误。
public class User{
public void eat(){
System.out.println("吃饭");
}
}
// 重写在一定程度上体现了多态现象
class Student extends User{
@Override
public void eat(){
System.out.println("学生正在吃饭");
}
}
@Deprecated
标记已过时,当我们想要让编译器知道一个方法已经被弃用(deprecate)时,应该使用这个注解。Java推荐在javadoc中提供信息,告知用户为什么这个方法被弃用了,以及替代方法是什么
/**
* Deprecated -->该方法过时(有更好的解决方案)
*/
public class TestDeprecated {
@Deprecated
public int test(){
System.out.println("TestDeprecated.test()");
return 0;
}
public void test(int a){
System.out.println("TestDeprecated.test(int)"
);
}
}
@SuppressWarnings
抑制编译器警告,该注解仅仅告知编译器,忽略它们产生了特殊警告。如:在java泛型中使用原始类型。其保持性策略(retention policy)是SOURCE,在编译器中将被丢弃。
/**
* SuppressWarnings 压制警告,更多的请去官网查询
* @author Administrator
*/
public class TestSuppressWarnings {
public static void main(String[] args) {
@SuppressWarnings("unused")// 没有使用的代码
List<String> list =new
ArrayList<String>();
}
@SuppressWarnings("rawtypes") //没有定义范型
public static List test(){
return new ArrayList();
}
}
自定义注解
// @Target标记自定义注解使用的范围,方法、属性、类等等
@Target(ElementType.METHOD)
// @Retention标记自定义注解的生命周期,源码SOURCE、编译CLASS、运行RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface Identify {
// 一个属性的时候建议使用value()
int value();
}