第二十八天:注解与反射
那些用手做就很快了的事情,就不要用计算机去做了。
[【狂神说Java】注解和反射
学习内容
注解Annotation
什么是注解
注解是从JDK5.0开始引入的技术
Annotation的作用
不是程序本身,可以对程序做出解释(这一点和注释Comment没什么区别)
可以被其他程序,如编译器等 读取
Annotation的格式
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如,@SuppressWarning(value=“locked”)
Annotation在哪里使用
内置注解
@Override
定义在 java.lang.Override 中。
此注释只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明
@Deprecated
定义在 java.lang.Deprecated 中。
此注释可以用于修辞方法,属性,类。表示不鼓励程序员使用这样的元素。通常是因为它很危险或者存在更好的选择
@SuppressWarnings
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 package com.joker_yue.javalearn.Annotation_Comment;import java.util.ArrayList;import java.util.List;public class Test01 extends Object { @Override public String toString () { return "Test01{}" ; } @Deprecated public static void test () { System.out.println("Deprecated" ); } @SuppressWarnings("all") public void test02 () { List list = new ArrayList (); } public static void main (String[] args) { test(); } }
元注解
元注解的作用就是==负责注解其他注解 ==,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
这些类型和它们所支持的类在java.lang.annotaion包中可以找到(@Target,@Retention,@Document,@Inherited )
@Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
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 package com.joker_yue.javalearn.Annotation_Comment;import java.lang.annotation.*;@MyAnnotation public class Test02 { @MyAnnotation public void test () { } } @Target(value = {ElementType.METHOD, ElementType.TYPE}) @Retention(value = RetentionPolicy.RUNTIME) @Documented @Inherited @interface MyAnnotation { }
自定义注解
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 package com.joker_yue.javalearn.Annotation_Comment;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;public class Test03 { @MyAnnotation2(name = "Joker", schools = {"湖南大学"}) public void test () { } @MyAnnotation3("Yue") public void test2 () { } } @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2 { String name () default "" ; int age () default 0 ; int id () default -1 ; String[] schools() default {"北京大学" , "清华大学" }; } @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation3 { String value () ; }
反射Reflection
静态VS动态语言
动态语言
是一类在运行时可以改变其结构的语言。 例如新的函数 、 对象 、 甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。 通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言 : Object-C 、 C# 、 JavaScript 、 PHP 、 python 等。
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。 如 Java 、 c 、 C++
Java 不是动态语言,但 Java 可以称之为 " 准动态语言 "。 即 Java 有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。 Java 的动态性让编程的时候更加灵活 !
Java反射
Reflection ( 反射 ) 是 Java 被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类 的内部信息,并能直接操作 任意对象的内部属性及方法
Class c = Class.forName(“java.lang.String”)
加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象 ( 一个类只有一个 Class 对象 ),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。 这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为 : 反射
Java 反身几制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
…
反射的优缺点
优点 :
可以实现动态创建对象和编译 , 体现出很大的灵活性
缺点 :
对性能有影响 。 使用反射基本上是一种解释操作 , 我们可以告诉 JVM , 我们希望做什么并且它满足我们的要求 。 这类操作总是慢于直接执行相同的操作 。
反射的主要API
java.lang.Class 代表一个类
java.lang.reflect. Method 代表类的方法
java.lang.reflect.Field 代表类的成员变量
java.lang.reflect .Constructor 代表类的构造器
…
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 62 63 64 65 66 67 68 69 70 package com.joker_yue.javalearn.Reflection;public class Test01 extends Object { public static void main (String[] args) throws ClassNotFoundException { Class C1 = Class.forName("com.joker_yue.javalearn.Reflection.User" ); System.out.println(C1); Class C2 = Class.forName("com.joker_yue.javalearn.Reflection.User" ); Class C3 = Class.forName("com.joker_yue.javalearn.Reflection.User" ); Class C4 = Class.forName("com.joker_yue.javalearn.Reflection.User" ); System.out.println(C2.hashCode()); System.out.println(C3.hashCode()); System.out.println(C4.hashCode()); } } class User { private String name; private int id; private int age; public User () { } public User (String name, int id, int age) { this .name = name; this .id = id; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } 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; } @Override public String toString () { return "User{" + "name='" + name + '\'' + ", id=" + id + ", age=" + age + '}' ; } }
Class类
得到Class类的几种方式
Class类的常用方法
获取Class类的实例
若已知具体的类 , 通过类的 class 属性获取 , 该方法最为安全可靠 , 程序性能最高 。
Class clazz = Person.class;
已知某个类的实例 , 调用该实例的 getClass() 方法获取 Class 对象
CIass clazz = person.getClass();
已知一个类的全类名 , 且该类在类路径下 , 可通过 Class 类的静态方法 forName() 获取 ,可能抛出 CIassNotFoundException
Class clazz = CIass.forName(“dem001 .Student”);
内置基本数据类型可以直接用类名.Type
还可以利用 ClassLoader
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 package com.joker_yue.javalearn.Reflection;public class Test02 { public static void main (String[] args) throws ClassNotFoundException { Person person = new Student (); System.out.println("这个人是:" + person.name); Class c1 = person.getClass(); System.out.println(c1.hashCode()); Class c2 = Class.forName("com.joker_yue.javalearn.Reflection.Student" ); System.out.println(c2.hashCode()); Class c3 = Student.class; System.out.println(c3.hashCode()); Class c4 = Integer.TYPE; System.out.println(c4); Class c5 = c1.getSuperclass(); System.out.println(c5); } } class Person { String name; public Person (String name) { this .name = 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对象?
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 package com.joker_yue.javalearn.Reflection;import java.lang.annotation.ElementType;public class Test03 { public static void main (String[] args) { Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int [][].class; Class c5 = Override.class; Class c6 = ElementType.class; Class c7 = Integer.class; Class c8 = void .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); } }
上述结果的输出结果为
1 2 3 4 5 6 7 8 9 class java.lang.Object interface java.lang.Comparable class [Ljava.lang.String; class [[I interface java.lang.Override class java.lang.annotation.ElementType class java.lang.Integer void class java.lang.Class
只要元素类型与维度一样,就是同一个Class
1 2 3 int [] a = new int [10 ];int [] b = new int [100 ];
Java内存分析
类的加载过程:
类的加载与ClassLoader的理解:
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 package com.joker_yue.javalearn.Reflection;public class Test04 { public static void main (String[] args) { A a = new A (); System.out.println(A.m); } } class A { static { System.out.println("A类的静态代码块初始化" ); m = 300 ; } static int m = 100 ; public A () { System.out.println("A类的无参构造初始化" ); } }
首先是准备阶段,静态代码块并没有执行,m给予了默认值0,然后初始化的时候从上到下执行。m被赋予=300,然后又=100。
这里static代码块和其他代码的执行顺序与代码文本顺序相同,先写static代码块就先执行m=300,把m=100放在前面就先执行m=100。
什么时候会发生类初始化
类的主动引用 ( 一定会发生类的初始化 )
当虚拟机启动 , 先初始化 main 方法所在的类
new 一个类的对象
调用类的静态成员 ( 除了 final 常量 ) 和静态方法
使用 java.lang.reflect包的方法对类进行反射调用
当初始化一个类 , 如果其父类没有被初始化 , 则先会初始化它的父类
类的被动引用 ( 不会发生类的初始化 )
当访问一个静态域时 , 只有真正声明这个域的类才会被初始化 。 如 : 当通过子类引用父类的静态变量 , 不会导致子类初始化
通过数组定义类引用 , 不会触发此类的初始化
引用常量不会触发此类的初始化 ( 常量在链接阶段就存入调用类的常量池中了 )
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 package com.joker_yue.javalearn.Reflection;public class Test05 { static { System.out.println("Main类被加载" ); } public static void main (String[] args) throws ClassNotFoundException { Son son = new Son (); Class.forName("com.joker_yue.javalearn.Reflection.Test05.Son" ); System.out.println(Son.b); Son[] sonArr = 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("子类被加载" ); m=300 ; } static int m = 100 ; static final int M = 1 ; }
当初始化子类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
当创建一个类的实例时(例如: new关键词,通过反射,克隆,反序列化)
当调用类是静态方法时(即当使用了字节码invokestatic指令)
当使用类,接口的静态字段时(final修饰特殊考虑)(例如: getstatic或putstatic指令)
如果一个接口定义了default方法,那么直接实现或间接实现该接口的类的初始化,该接口要在其之前初始化
当初始调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类()
ConstantValue属性的作用是通知虚拟机自动为静态变量赋值, 只有被static修饰的变量才可以使用这项属性。
常量在连接阶段已经初始化了,所以后面给常量赋值,没有引用类
说白了就是需要哪个类的数据就把这个类拿来初始化
类加载器的作用
类加载的作用 : 将 class 文件字节码内容加载到内存中 , 并将这些静态数据转换成方法区的运行时数据结构 , 然后在堆中生成一个代表这个类的 java.lang.Class 对象 , 作为方法区中类数据的访问入口 。
类缓存 : 标准的 JavaSE 类加载器可以按要求查找类 , 但一旦某个类被加载到类加载器中 , 它将维持加载( 缓存 )一段时间 。 不过JVM 垃圾回收机制可以回收这些 Class 对象
类加载器作用是用来把类( class )装载进内存的 。 JVM 规范定义了如下类型的类的加载器 。
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 62 63 64 65 66 67 68 69 70 package com.joker_yue.javalearn.Reflection;public class Test06 { 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); ClassLoader classLoader = Class.forName("com.joker_yue.javalearn.Reflection.Test06" ).getClassLoader(); System.out.println(classLoader); classLoader = Class.forName("java.lang.Object" ).getClassLoader(); System.out.println(classLoader); System.out.println(System.getProperty("java.class.path" )); } }
创建运行时类的对象
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 62 63 64 65 66 package com.joker_yue.javalearn.Reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class Test07 { public static void main (String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class c1 = Class.forName("com.joker_yue.javalearn.Reflection.User" ); System.out.println(c1.getName()); System.out.println(c1.getSimpleName()); System.out.println("=====================" ); Field[] fields = c1.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } Field name = c1.getDeclaredField("name" ); System.out.println(name); System.out.println("=====================" ); Method[] methods = c1.getMethods(); for (Method method : methods) { System.out.println("getMethods():" +method); } methods = c1.getDeclaredMethods(); for (Method method : methods) { System.out.println("getDeclaredMethods():" +method); } Method getName = c1.getMethod("getName" , null ); Method setName = c1.getMethod("setName" , String.class); System.out.println(getName); System.out.println(setName); System.out.println("=====================" ); Constructor[] constructors = c1.getConstructors(); for (Constructor constructor:constructors){ System.out.println("getConstructors():" +constructor); } constructors = c1.getDeclaredConstructors(); for (Constructor constructor:constructors){ System.out.println("getDeclaredConstructors():" +constructor); } Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int .class, int .class); System.out.println("指定构造器:" +declaredConstructor); } }
有了Class对象,能做什么?
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 package com.joker_yue.javalearn.Reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Test08 { public static void main (String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class c1 = Class.forName("com.joker_yue.javalearn.Reflection.User" ); User user = (User) c1.newInstance(); System.out.println(user); Constructor constructor = c1.getConstructor(String.class, int .class, int .class); User user2 = (User) constructor.newInstance("Joker" , 001 , 19 ); System.out.println(user2); User user3 = (User) c1.newInstance(); Method setName = c1.getDeclaredMethod("setName" , String.class); setName.invoke(user3,"joker" ); System.out.println(user3.getName()); User user4 = (User)c1.newInstance(); Field name = c1.getDeclaredField("name" ); name.setAccessible(true ); name.set(user4,"Yue" ); System.out.println(user4.getName()); } }
调用指定的方法
通过反射 , 调用类中的方法 , 通过 Method 类完成 。
通过 Class 类的 getMeth0d(String name,Class…parameterTypes) 方法取得一个 Method 对象 , 并设置此方法操作时所需要的参数类型。
之后使用 Object invoke(Object obj, Object[] args) 进行调用 , 并向方法中传递要设置的 obj 对象的参数信息 。
Object invoke(Object Obj , Object …args)
Object 对应原方法的返回值 , 若原方法无返回此时返回null
若原方法若为静态方法 , 此时形参 Object obj 可为null
若原方法形参列表为空 , 则 ObJect[] args 为 null
若原方法声明为private, 则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true) 方法 , 将可访问 private 的方法 。
SetAccessible
Method 和 FieId 、 Constructor 对象都有 setAccessibIe() 方法 。
setAccessible 作用是启动和禁用访问安全检查的开关 。
参数值为 true 则指示反射的对象在使用时应该取消Java 语言访问检查 。
提高反射的效率 。 如果代码中必须用反射 , 而该句代码需要频繁的被调用 , 那么请设置为 true 。
使得原本无法访问的私有成员也可以访问
参数值为 false则指示反射的对象应该实施 Java 语言访问检查
性能分析
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 package com.joker_yue.javalearn.Reflection;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Test09 { public static void test01 () { User user = new User (); long startTime = System.currentTimeMillis(); for (int i = 0 ; i < 1000000000 ; i++) { user.getName(); } long endTime = System.currentTimeMillis(); System.out.println("普通方式执行10亿次需要的时间:" +(endTime-startTime) + "ms" ); } public static void test02 () throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User (); Class c1 = user.getClass(); Method getName = c1.getDeclaredMethod("getName" , null ); long startTime = System.currentTimeMillis(); for (int i = 0 ; i < 1000000000 ; i++) { getName.invoke(user,null ); } long endTime = System.currentTimeMillis(); System.out.println("反射方式执行10亿次需要的时间:" +(endTime-startTime) + "ms" ); } public static void test03 () throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User (); Class c1 = user.getClass(); Method getName = c1.getDeclaredMethod("getName" , null ); getName.setAccessible(true ); long startTime = System.currentTimeMillis(); for (int i = 0 ; i < 1000000000 ; i++) { getName.invoke(user,null ); } long endTime = System.currentTimeMillis(); System.out.println("关闭检测执行10亿次需要的时间:" +(endTime-startTime) + "ms" ); } public static void main (String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { test01(); test02(); test03(); } }
上述代码的输出结果为
1 2 3 普通方式执行10亿次需要的时间:7ms 反射方式执行10亿次需要的时间:7506ms 关闭检测执行10亿次需要的时间:5606ms
反射操作泛型
Java 采用泛型擦除的机制来引入泛型 , Java 中的泛型仅仅是给编译器 javac 使用的 , 确保数据的安全性和免去强制类型转换问题但是一旦编译完成所有和氵乏型有关的类型全部擦除
为了通过反射操作这些类型 , Java 新增了 ParameterizedType , GenericArrayType,TypeVariable 和 WildcardType 几种类型来代表不能被归一到 CIass 类中的类型但是又和原始类型齐名的类型
ParameterizedType:表示一种参数化类型 , 比如 Collection<String>
GenericArrayType:表示一种元素举型早参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式
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 package com.joker_yue.javalearn.Reflection;import java.lang.reflect.AnnotatedType;import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.List;import java.util.Map;public class Test10 { public void test01 (Map<String, User> map, List<User> list) { System.out.println("test01" ); } public Map<String, User> test02 () { System.out.println("test02" ); return null ; } public static void main (String[] args) throws NoSuchMethodException { Method method = Test10.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); } } } method = Test10.class.getMethod("test02" ,null ); Type genericReturnType = method.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }
ORM
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 package com.joker_yue.javalearn.Reflection;import java.lang.annotation.*;import java.lang.reflect.Field;public class Test11 { public static void main (String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c1 = Class.forName("com.joker_yue.javalearn.Reflection.Student2" ); Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } TableJoker tableJoker = (TableJoker)c1.getAnnotation(TableJoker.class); String value = tableJoker.value(); System.out.println(value); Field f = c1.getDeclaredField("name" ); FieldJoker annotation = f.getAnnotation(FieldJoker.class); System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); } } @TableJoker("db_student") class Student2 { @FieldJoker(columnName = "db_id", type = "int", length = 10) private int id; @FieldJoker(columnName = "db_age", type = "int", length = 10) private int age; @FieldJoker(columnName = "db_name", type = "varchar", length = 3) private String name; public Student2 () { } public Student2 (int id, int age, String name) { this .id = id; this .age = age; this .name = name; } 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; } } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableJoker { String value () ; } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldJoker { String columnName () ; String type () ; int length () ; }