动态特性 ---反射、注解、动态代理、类加载器
提示:本文属于基础语法和例子,个人复习整理,用于备忘,谢谢!!!!
动态特性简介:
Java中有一些动态特性,用来协助开发者在运行时编写出动态的代码,实现更优质的功能。比如反射、注解、动态代理、类加载器。利用这些特性,可以优雅地实现一些通过的功能,它们经常用于各种框架、库和系统程序中。比如:
(1) Springmvc、Jersey 用于处理web请求,利用反射和注解,能方便地将用户的请求参数和内容转换为Java对象,将Java对象转为响应内容。
(2) Spring、Guice 利用这些特性实现了对象管理容器,方便程序员管理对象的生命周期以及其中复杂的依赖关系。
(3) 应用服务器(如Tomcat)利用类加载器实现不同应用之间的隔离,JSP技术利用类加载器实现修改代码不用重启就能生效。
(4) 面向对象的编程(Aop)将编程中通用的关注点(如日志记录,安全检查等)与业务的主体逻辑相分离,减少冗余代码,提高程序的可维护性,Aop需要依赖上面的这些特性来实现。
反射:
它是运行的时候,动态获取类型的信心,比如接口信息、成员信息、方法信息,构造方法信息等。根据这些动态获取到的信息创建对象,访问/修改出成员,调用方法等。
(1) Class 类:
程序运行时,每个已经加载的类在内存都有一份类信息,每个对象都有指向它所属类信息的引用。
获取类对象的方法:
方法 | 返回值 | 说明 |
---|---|---|
getClass() | Class<?> | Object 方法,通用获取Class对象方法 |
forName | Class<?> | 根据全限定类名直接加载Class |
还有一种类名方式:直接类名.class,在写程序的时候就知道类名,就可以<类名>.class 。基础类型是没有getClass()方法的,只能直接类名的方式。示例:
Class<Integer> intCls = int.class;
Class<Double> doubCls = double.class;
(2) 名称信息:
Class有如下方法,可以获取与名称有关的信息:
方法 | 返回值 | 说明 |
---|---|---|
getName() | String | 获取java内部使用的名称 |
getSimpleName | String | 获取不带包名的名称 |
getCanonicalName | String | 获取更加友好的名称 |
getPackage | Package | 获取包信息 |
示例:
Class<?> stringClass = String.class;
//返回java内部使用的真正名称
String name = stringClass.getName();
System.out.println(name);
//返回名称不带包信息
String simpleName = stringClass.getSimpleName();
System.out.println(simpleName);
//返回的名称更加友好
String canonicaName = stringClass.getCanonicalName();
System.out.println(canonicaName);
//返回包信息
String packageName = stringClass.getPackage().toString();
System.out.println(packageName);
(3) 字段信息:
类中定义的静态和实例变量都被称为字段,用Field表示,位于java.util.reflect下,下面是获取字段信息的方法:
方法 | 返回值 | 说明 |
---|---|---|
getFields() | Field [] | 获取所有的public,包括父类的,如果没有字段,就返回空数组 |
getDeclaredFields() | Field [] | 获取本类所有字段,包括非public,但不包括父类的 |
getField(String name) | Field | 获取本类或父类指定名称的字段,找不到抛出异常NoSuchFieldeException |
getDeclaredField(String name) | Field | 获取本类指定名称的字段,找不到抛出异常 NoSuchFieldeException |
示例:
//直接类名.class,获取class对象
Class<?> refectClass = Refect.class;
//返回所有的public字段,包括其父类的,如果没有字段,就返回空数组
Field [] fields = refectFor.getFields();
//返回本类的申明的所有字段, 包括非public的,但不包括父类的
Field [] declaredFields = refectFor.getDeclaredFields();
//返回本类或者父类中指定名称的 public字段,找不到抛出异常NoSuchFieldException
Field field = refectFor.getField("name");
//返回本类中声明的指定名称的字段,找不到抛出异常NoSuchFieldException
Field declaredField = refectFor.getDeclaredField("schoolName");
Field 也有很多操作方法,用来获取字段的信息,也可以通过Field 访问指定对象中该字段的值,基本方法如下:
方法 | 返回值 | 说明 |
---|---|---|
getName | String | 获取字段名称 |
isAccessible() | boolean | 判断当前程序是否有该字段访问权限 |
setAccessible(boolean flag) | void | 设置字段权限,true表示忽略java访问检查机制 ,运行读写非public的字段 |
get(Object obj) | Object | 获取指定对象obj该字段的值 |
set(Object obj,Object value) | void | 将指定对象obj中该字段的值设为value |
getModifiers() | int | 获取字段的修饰符 |
getType() | Class<?> | 获取字段的类型 |
getAnnotation(Class annotationClass) | T | 获取字段的注解信息 |
示例:
//获取字段的名称
String fieldName = field.getName();
//class 创建对象的方法,默认调用无参构造,如果没有无参构造,则会抛出异常 instantiationException
Refect refect = (Refect) refectFor.newInstance();
refect.idCard = "12354616786";
refect.schoolName = "中兴";
//设置非public的读写权限
declaredField.setAccessible(true);
//获取指定对象该字段的值
String schoolName = (String)declaredField.get(refect);
//将指定对象的该字段值设为xxx
declaredField.set(refect,"中国");
System.out.println(schoolName);
System.out.println(refect.getSchoolName());
(4) 方法信息:
类中定义的静态和实例方法都用类Method表示,类有如下方法:
方法 | 返回值 | 说明 |
---|---|---|
getMehods() | Method [] | 获取所有pubic 方法,包括父类的,没有则返回空数组 |
getDeclaredMethods() | Method [] | 获取本类的所有方法,包括非public 的 |
getMethod(String name,Class<?>… paramterTypes) | Method | 获取本类和父类中指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException |
getDeclaredMethod(String name,Class<?>… paramaterTypes) | Method | 获取本类 指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException |
示例:
//返回所有的public方法,包括其父亲的,如果没有方法,返回空数组
Method [] methods = refectClass.getMethods();
//返回本类的所有方法,包括非public的,但不包括父类的
Method [] declaredMethods = refectClass.getDeclaredMethods();
//返回本类或者父类中指定名称和参数类型的public 方法,找不到抛出异常NoSuchMethodException
Method method = refectClass.getMethod("getAge",null);
//返回本类中声明的指定名称和参数类型的方法,找不到抛出异常NoSuchMethodException
Method declaredMethod = refectClass.getDeclaredMethod("setSchoolName", String.class);
Method类有许多操作方法用来获取方法的信息,基本方法有:
方法 | 返回值 | 说明 |
---|---|---|
getName | String | 获取方法名称 |
setAccessible(boolean flag) | void | 设置方法访问权限,true表示允许调用非public 方法 |
inoke(Object obj,Object – args,) | Object | 在指定对象上调用Method代表的方法 |
示例:
String methodName = method.getName();
//设置非public方法是否允许调用
method.setAccessible(true);
//在指定对象obj上调用Method代表的方法,传递的参数列表为args,
//对于invoke 方法,如果method为静态方,Obj被忽略,可以为null,args可以为 null,也可以为一个空的数组,方法调用
//的返回值被包装为Object 返回,如果实际方法调用抛出异常,异常被包装为 InvacationTargetException重新抛出,
//可以通过getCause()方法得到异常.
declaredMethod.invoke(refect,"三小");
(4) 创建对象和构造方法:
方法 | 返回值 | 说明 |
---|---|---|
newInstance() | T | 调用类无参构造创建对象,若没无参构造则抛出异常InstantiationException |
getConstructors() | Constructor<?> [] | 获取所有public 构造方法,返回值可能时是长度为o空数组 |
getDeclaredConstructors() | Constructor<?> [] | 获取所有的构造方法,包括非public |
getConstructor(Class<?>… paramterTypes) | Constructor | 获取指定参数类型的public 构造方法,没有则抛出异常NoSuchMethodException |
getDeclaredConstructor(Class<?>… paramterTypes) | Constructor | 获取指定参数类型的构造方法,包括非public 的构造方法 |
(5) 类型检查和转换:
方法 | 返回值 | 说明 |
---|---|---|
isInstance(Object obj) | boolean | 用来判断动态类型 |
cast(Object) | T | 用来转换动态类型 |
反射的简单应用示例:
package ref;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* 反射应用实例,实现简单的序列/反序列
* 实现简单的类,即有默认的构造方法,成员类型只有基础类型,包装类或者String
*/
public class ReflectCase {
static class Stuent {
private String name;
private int age;
private double secore;
public Stuent() {
}
public Stuent(String name, int age, double secore) {
this.name = name;
this.age = age;
this.secore = secore;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSecore() {
return secore;
}
public void setSecore(double secore) {
this.secore = secore;
}
}
/**
* 序列化为字符串
*/
public static String toString(Object obj){
StringBuilder sb = new StringBuilder();
try {
Class<?> cls = obj.getClass();
sb.append(cls.getName() + "\n");
Field[] fileds = cls.getDeclaredFields();
for (Field field : fileds) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
sb.append(field.getName() + "=" + field.get(obj).toString() + "\n");
}
}catch (IllegalAccessException e){
e.printStackTrace();
}
return sb.toString();
}
/**
* 反序列化为对象
* @param str
* @return
*/
public static Object fromString(String str){
String [] lines = str.split("\n");
Object obj = null;
if(lines.length < 1){
throw new IllegalArgumentException(str);
}
try {
Class<?> cls = Class.forName(lines[0]);
obj = cls.newInstance();
if(lines.length>1){
for(int i = 1; i<lines.length; i++){
String [] fv = lines[i].split("=");
if(fv.length < 2){
throw new IllegalArgumentException("传入字符串格式不正确!");
}
Field field = cls.getDeclaredField(fv[0]);
if(!field.isAccessible()){
field.setAccessible(true);
}
setFieldValue(field,obj,fv[1]);
}
}
}catch (ClassNotFoundException e){
e.printStackTrace();
}catch (IllegalAccessException | InstantiationException e){
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
public static void setFieldValue(Field field,Object obj,String fieldValue) throws Exception{
Class<?> type = field.getType();
if(type == int.class){
field.setInt(obj,Integer.parseInt(fieldValue));
}else if(type == Byte.class){
field.setByte(obj,Byte.parseByte(fieldValue));
}else if(type == short.class){
field.setShort(obj,Short.parseShort(fieldValue));
}else if(type == long.class){
field.setLong(obj,Long.parseLong(fieldValue));
}else if(type == float.class){
field.setFloat(obj,Float.parseFloat(fieldValue));
}else if(type == double.class){
field.setDouble(obj,Double.parseDouble(fieldValue));
}else if(type == char.class){
field.setChar(obj,fieldValue.charAt(0));
}else if(type == boolean.class){
field.setBoolean(obj,Boolean.parseBoolean(fieldValue));
}else if(type == String.class){
field.set(obj,fieldValue);
}else{
//当是简单对象时
Constructor<?> constructor = type.getConstructor(new Class[]{String.class});
field.set(obj,constructor.newInstance(fieldValue));
}
}
public static void main(String[] args) {
Stuent stuent = new Stuent("张三",19,80.0);
System.out.println(toString(stuent));
System.out.println(stuent);
System.out.println(fromString(toString(stuent)));
}
}
总结:
博主还只是一个小菜鸟,每天都在学习中,这是我自己看书整合出来的资料,每学习一个章节,都会写一篇博文来备忘。同时希望看我博文的小伙伴们能有好的收获,谢谢!!