| Annotation 是从JDK5.0开始引入的新技术 |
| Annotation的作用 |
| 不是程序本身, 可以对程序作出解释 |
| 可以被其他程序(比如:编译器等)读取 |
| Annotation的格式 |
| 注解是以"@注释名"在代码中存在的 |
| 还可以添加一些参数值 , 例如:@SuppressWarnings(value="unchecked") |
| Annotation在哪里使用? |
| 可以附加在package , class , method , field 等上面 , 相当于给他们添加了额外的辅助信息 |
| 我们可以通过反射机制实现对这些元数据的访问 |
| @Override |
| 定义在 java.lang.Override 中, 此注释只适用于修辞方法, 表示一个方法声明打算重写超类中的另一个方法声明 |
| @Deprecated |
| 定义在java.lang.Deprecated中, 此注释可以用于修辞方法, 属性, 类 |
| 表示不鼓励程序员使用这样的元素, 通常是因为它很危险或者存在更好的选择 |
| @SuppressWarnings |
| 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息 |
| 与前两个注释有所不同, 你需要添加一个参数才能正确使用 |
| @SuppressWarnings("all") |
| @SuppressWarnings("unchecked") |
| @SuppressWarnings(value={"unchecked","deprecation"}) |
| 元注解的作用就是负责注解其他注解, Java定义了4个标准的meta-annotation类型, 他们被用来提供对其他annotation类型作说明 |
| 这些类型和它们所支持的类在java.lang.annotation包中可以找到 ( @Target, @Retention, @Documented, @Inherited ) |
| @Target : 用于描述注解的使用范围(即:被描述的注解可以用在什么地方) |
| @Retention : 表示需要在什么级别保存该注释信息 , 用于描述注解的生命周期 |
| (SOURCE < CLASS < RUNTIME) 如果标注为RUNTIME,则也可以在CLASS和SOURCE中有效,如果标注为CLASS,则也可以在SOURCE中有效 |
| @Document:说明该注解将被包含在javadoc中 |
| @Inherited:说明子类可以继承父类中的该注解 |
| 使用@interface自定义注解时, 自动继承了java.lang.annotation.Annotation接口 |
| |
| @interface用来声明一个注解, 格式: public @interface 注解名 { 定义内容 } |
| 其中的每一个方法实际上是声明了一个配置参数. |
| 方法的名称就是参数的名称. |
| 返回值类型就是参数的类型 (返回值只能是基本类型, Class, String, enum) |
| 可以通过default来声明参数的默认值 |
| 如果只有一个参数成员, 一般参数名为value |
| 注解元素必须要有值, 我们定义注解元素时, 经常使用空字符串,0作为默认值 |
| # 测试自定义注解 |
| public class Test3 { |
| |
| @MyAnnotation2(age = 18, name = "狗大", id = 001, schools = {"狗二"}) |
| public void test() { |
| } |
| |
| |
| @MyAnnotation3("aaa") |
| public void test2(){ |
| } |
| } |
| |
| # 自定义注解@MyAnnotation2 |
| @Target(value = {ElementType.METHOD}) |
| @Retention(value = RetentionPolicy.RUNTIME) |
| @interface MyAnnotation2{ |
| |
| String name() default ""; |
| int age() default 0; |
| |
| int id() default -1; |
| |
| String[] schools() default {"狗蛋","狗剩"}; |
| } |
| |
| # 自定义注解@MyAnnotation3 |
| @Target(value = {ElementType.METHOD}) |
| @Retention(value = RetentionPolicy.RUNTIME) |
| @interface MyAnnotation3{ |
| |
| String value(); |
| } |
| # 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构 |
| # 主要动态语言:Object-C、C |
| Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法 |
| Class c = Class.forName("java.lang.String") |
| 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射 |
| public class Test2 { |
| public static void main(String[] args) { |
| try { |
| |
| Class<?> c1 = Class.forName("com.reflection.User"); |
| |
| System.out.println(c1); |
| |
| Class<?> c2 = Class.forName("com.reflection.User"); |
| System.out.println(c1.hashCode()); |
| System.out.println(c2.hashCode()); |
| } catch (ClassNotFoundException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| 在运行时判断任意一个对象所属的类 |
| 在运行时构造任意一个类的对象 |
| 在运行时判断任意一个类所具有的成员变量和方法 |
| 在运行时获取泛型信息 |
| 在运行时调用任意一个对象的成员变量和方法 |
| 在运行时处理注解 |
| 生成动态代理 |
| 优点:可以实现动态创建对象和编译,体现出很大的灵活性 ! |
| 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于 直接执行相同的操作 |
| java.lang.Class : 代表一个类 |
| java.lang.reflect.Method : 代表类的方法 |
| java.lang.reflect.Field : 代表类的成员变量 |
| java.lang.reflect.Constructor : 代表类的构造器 |
| 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息 |
| |
| Class 本身也是一个类 |
| Class 对象只能由系统建立对象 |
| 一个加载的类在 JVM 中只会有一个Class实例 |
| 一个Class对象对应的是一个加载到JVM中的一个.class文件 |
| 每个类的实例都会记得自己是由哪个 Class 实例所生成 |
| 通过Class可以完整地得到一个类中的所有被加载的结构 |
| Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象 |
| public class Test3 { |
| public static void main(String[] args) throws ClassNotFoundException { |
| |
| Person person = new Student(); |
| System.out.println("这个人是:"+person.name); |
| |
| |
| Class clazz1 = person.getClass(); |
| |
| |
| Class clazz2 = Class.forName("com.reflection.Student"); |
| |
| |
| Class clazz3 = Person.class; |
| |
| |
| Class clazz4 = Integer.TYPE; |
| |
| |
| Class clazz5 = clazz2.getSuperclass(); |
| } |
| } |
方法名 |
功能说明 |
public static Class<?> forName(String className) |
传入完整的“包.类”名称,返回指定类名name的Class对象 |
Object newInstance() |
调用缺省构造函数,返回Class对象的一个实例 |
getName() |
返回此Class对象所表示的实体(类,接口,数组类或void)的名称,即得到一个类完整的“包.类”名称 |
Class getSuperClass() |
返回当前Class对象的父类的Class对象 |
Class[] getinterfaces() |
获取当前Class对象的接口 |
ClassLoader getClassLoader() |
返回该类的类加载器 |
Constructor[] getConstructors() |
返回一个包含某些Constructor对象的数组,即得到一个类的全部的构造方法 |
public Method[] getMethods() |
得到一个类的全部方法 |
Method getMothed(String name,Class.. T) |
返回一个Method对象,此对象的形参类型为paramType |
Field[] getDeclaredFields() |
返回Field对象的一个数组,即得到本类中单独定义的全部属性 |
public Field[] getFields() |
得到本类继承而来的全部属性 |
public Package getPackage() |
得到一个类的包 |
public Class<?> getComponentType() |
返回表示数组类型的Class |
public boolean isArray() |
判断此class是否是一个数组 |
| class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。 |
| interface:接口 |
| []:数组 |
| enum:枚举 |
| annotation:注解@interface |
| primitive type:基本数据类型 |
| void |
| |
| # 代码案例 |
| public class Test4 { |
| public static void main(String[] args) { |
| Class c1 = Object.class; |
| Class c2 = Comparable.class; |
| Class c3 = String[].class; |
| Class c4 = int[][].class; |
| Class c5 = ElementType.class; |
| Class c6 = Override.class; |
| Class c7 = Integer.class; |
| Class c8 = void.class; |
| Class c9 = Class.class; |
| int[] a = new int[10]; |
| int[] b = new int[100]; |
| Class c10 = a.getClass(); |
| Class c11 = b.getClass(); |
| } |
| } |
-
Java内存分析

-
类加载过程

-
ClassLoader
| |
| 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象. |
| |
| 1、验证:确保加载的类信息符合JVM规范,没有安全方面的问题 |
| 2、准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配 |
| 3、解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程 |
| |
| 1、执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器) |
| 2、当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化 |
| 3、虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步 |
| public class Test5 { |
| 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类无参构造器初始化"); |
| } |
| } |
| |
| # 控制台 |
| A类静态代码块初始化 |
| A类无参构造器初始化 |
| 100 |
| # new1个A对象时,执行clinit方法,该方法会自动收集类中所有类变量的赋值动作和静态代码块中的语句合并 |
| <clinit>(){ |
| System.out.println("A类静态代码块初始化"); |
| m = 300; |
| m = 100 |
| } |
| |
| # 最后输出m=100 |
| 类的主动引用(一定会发生类的初始化) |
| 当虚拟机启动,先初始化main方法所在的类 |
| new一个类的对象 |
| 调用类的静态成员(除了final常量)和静态方法 |
| 使用java.lang.reflect包的方法对类进行反射调用 |
| 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类 |
| 类的被动引用(不会发生类的初始化) |
| 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静 |
| 态变量,不会导致子类初始化 |
| 通过数组定义类引用,不会触发此类的初始化 |
| 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了) |
| public class Test6 { |
| |
| static { |
| System.out.println("Main类被加载"); |
| } |
| |
| public static void main(String[] args) { |
| Son son = new Son(); |
| } |
| |
| } |
| |
| class Father{ |
| static int b = 2; |
| |
| static { |
| System.out.println("父类被加载"); |
| } |
| } |
| |
| class Son extends Father{ |
| static int m = 100; |
| static final int M = 1; |
| |
| static { |
| System.out.println("子类被加载"); |
| m = 300; |
| } |
| } |
| |
| # 控制台:当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类 |
| Main类被加载 |
| 父类被加载 |
| 子类被加载 |
| public class Test6 { |
| |
| static { |
| System.out.println("Main类被加载"); |
| } |
| |
| public static void main(String[] args) throws ClassNotFoundException { |
| Class.forName("com.ychen.demo03.Son"); |
| } |
| |
| } |
| |
| class Father{ |
| static int b = 2; |
| |
| static { |
| System.out.println("父类被加载"); |
| } |
| } |
| |
| class Son extends Father{ |
| static int m = 100; |
| static final int M = 1; |
| |
| static { |
| System.out.println("子类被加载"); |
| m = 300; |
| } |
| } |
| |
| # 控制台:使用java.lang.reflect包的方法对类进行反射调用时,会主动引用 |
| Main类被加载 |
| 父类被加载 |
| 子类被加载 |
| public class Test6 { |
| |
| static { |
| System.out.println("Main类被加载"); |
| } |
| |
| public static void main(String[] args) throws ClassNotFoundException { |
| System.out.println(Son.b); |
| } |
| |
| } |
| |
| class Father{ |
| static int b = 2; |
| |
| static { |
| System.out.println("父类被加载"); |
| } |
| } |
| |
| class Son extends Father{ |
| static int m = 100; |
| static final int M = 1; |
| |
| static { |
| System.out.println("子类被加载"); |
| m = 300; |
| } |
| } |
| |
| # 控制台:当通过子类引用父类的静态变量,不会导致子类初始化 |
| Main类被加载 |
| 父类被加载 |
| 2 |
| public class Test6 { |
| |
| static { |
| System.out.println("Main类被加载"); |
| } |
| |
| public static void main(String[] args) throws ClassNotFoundException { |
| Son[] arr = new Son[2]; |
| } |
| |
| } |
| |
| class Father{ |
| static int b = 2; |
| |
| static { |
| System.out.println("父类被加载"); |
| } |
| } |
| |
| class Son extends Father{ |
| static int m = 100; |
| static final int M = 1; |
| |
| static { |
| System.out.println("子类被加载"); |
| m = 300; |
| } |
| } |
| |
| # 控制台:通过数组定义类引用,不会触发此类的初始化 |
| Main类被加载 |
| public class Test6 { |
| |
| static { |
| System.out.println("Main类被加载"); |
| } |
| |
| public static void main(String[] args) throws ClassNotFoundException { |
| System.out.println(Son.M); |
| } |
| |
| } |
| |
| class Father{ |
| static int b = 2; |
| |
| static { |
| System.out.println("父类被加载"); |
| } |
| } |
| |
| class Son extends Father{ |
| static int m = 100; |
| static final int M = 1; |
| |
| static { |
| System.out.println("子类被加载"); |
| m = 300; |
| } |
| } |
| |
| # 控制台:引用常量不会触发此类的初始化 |
| Main类被加载 |
| 1 |
| 1、类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口 |
| 2、类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象 |
| 3、类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器 |


| public class Test7 { |
| 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.ychen.demo03.Test7").getClassLoader(); |
| System.out.println(classLoader); |
| |
| |
| classLoader = Class.forName("java.lang.Object").getClassLoader(); |
| System.out.println(classLoader); |
| |
| |
| System.out.println(System.getProperty("java.class.path")); |
| } |
| } |
| |
| # 控制台 |
| jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc |
| jdk.internal.loader.ClassLoaders$PlatformClassLoader@6e8dacdf |
| null |
| jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc |
| null |
| C:\work\git\spring_learn_repo\javase\demo03\target\classes |
| Field、Method、Constructor、Superclass、Interface、Annotation |
| |
| public class User { |
| public int id; |
| public String name; |
| public String age; |
| |
| public int getId() { |
| return id; |
| } |
| |
| public void setId(int id) { |
| this.id = id; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public String getAge() { |
| return age; |
| } |
| |
| public void setAge(String age) { |
| this.age = age; |
| } |
| |
| public User() { |
| } |
| |
| public User(int id, String name, String age) { |
| this.id = id; |
| this.name = name; |
| this.age = age; |
| } |
| |
| } |
| |
| # 测试 |
| public class Test8 { |
| public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { |
| Class c1 = Class.forName("com.ychen.demo03.User"); |
| |
| |
| System.out.println(c1.getName()); |
| System.out.println(c1.getSimpleName()); |
| |
| |
| Field[] fields = c1.getFields(); |
| |
| |
| fields = c1.getDeclaredFields(); |
| System.out.println(fields); |
| |
| |
| Field name = c1.getDeclaredField("name"); |
| System.out.println(name); |
| |
| |
| Method[] methods = c1.getMethods(); |
| System.out.println(methods); |
| |
| |
| methods = c1.getDeclaredMethods(); |
| System.out.println(methods); |
| |
| |
| Method getName = c1.getMethod("getName", null); |
| Method setName = c1.getMethod("setName", String.class); |
| |
| |
| Constructor[] constructors = c1.getConstructors(); |
| System.out.println(constructors); |
| |
| |
| constructors = c1.getDeclaredConstructors(); |
| System.out.println(constructors); |
| |
| |
| Constructor constructor = c1.getDeclaredConstructor(int.class, String.class, String.class); |
| System.out.println(constructor); |
| |
| } |
| } |
| |
| # 控制台 |
| com.ychen.demo03.User |
| User |
| [Ljava.lang.reflect.Field;@3f3afe78 |
| public java.lang.String com.ychen.demo03.User.name |
| [Ljava.lang.reflect.Method;@2d6e8792 |
| [Ljava.lang.reflect.Method;@2812cbfa |
| [Ljava.lang.reflect.Constructor;@2acf57e3 |
| [Ljava.lang.reflect.Constructor;@506e6d5e |
| public com.ychen.demo03.User(int,java.lang.String,java.lang.String) |
| |
| # 利用反射创建对象,实体类中必须有无参构造器 |
| public class Test9 { |
| public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { |
| Class c1 = Class.forName("com.ychen.demo03.User"); |
| User user = (User) c1.newInstance(); |
| System.out.println(user); |
| } |
| } |
| # 控制台 |
| User{id=0, name='null', age='null'} |
| |
| # 通过构造器创建对象 |
| public class Test9 { |
| public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { |
| Class c1 = Class.forName("com.ychen.demo03.User"); |
| Constructor constructor = c1.getDeclaredConstructor(int.class, String.class, String.class); |
| User user = (User) constructor.newInstance(1, "狗蛋", "18"); |
| System.out.println(user); |
| } |
| } |
| # 控制台 |
| User{id=1, name='狗蛋', age='18'} |
| |
| # 通过反射调用普通方法 |
| public class Test9 { |
| public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { |
| Class c1 = Class.forName("com.ychen.demo03.User"); |
| User user = (User) c1.newInstance(); |
| Method setName = c1.getDeclaredMethod("setName", String.class); |
| setName.invoke(user, "goudan"); |
| System.out.println(user.getName()); |
| } |
| } |
| # 控制台 |
| goudan |
| |
| # 通过反射操作属性 |
| public class Test9 { |
| public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { |
| Class c1 = Class.forName("com.ychen.demo03.User"); |
| User user = (User) c1.newInstance(); |
| Field name = c1.getDeclaredField("name"); |
| |
| name.setAccessible(true); |
| name.set(user, "goudan"); |
| System.out.println(user.getName()); |
| } |
| } |
| # 控制台 |
| goudan |
| Method和Field、Constructor对象都有setAccessible()方法。 |
| setAccessible作用是启动和禁用访问安全检查的开关。 |
| 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。 |
| 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。 |
| 使得原本无法访问的私有成员也可以访问 |
| 参数值为false则指示反射的对象应该实施Java语言访问检查 |
| |
| public class Test11 { |
| |
| 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 = Test11.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 ac : actualTypeArguments){ |
| System.out.println(ac); |
| } |
| } |
| } |
| } |
| |
| } |
| # 控制台 |
| java.util.Map<java.lang.String, com.ychen.demo03.User> |
| class java.lang.String |
| class com.ychen.demo03.User |
| java.util.List<com.ychen.demo03.User> |
| class com.ychen.demo03.User |
| |
| # 测试2 |
| public class Test11 { |
| |
| 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 = Test11.class.getMethod("test02", null); |
| |
| Type genericParameterType = method.getGenericReturnType(); |
| if (genericParameterType instanceof ParameterizedType){ |
| |
| Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); |
| for (Type ac : actualTypeArguments){ |
| System.out.println(ac); |
| } |
| } |
| } |
| |
| } |
| # 控制台 |
| class java.lang.String |
| class com.ychen.demo03.User |
| |
| |
| @Target(value = {ElementType.TYPE}) |
| @Retention(value = RetentionPolicy.RUNTIME) |
| @interface TableKuang{ |
| String value(); |
| } |
| |
| @Target(value = {ElementType.FIELD}) |
| @Retention(value = RetentionPolicy.RUNTIME) |
| @interface FieldKuang{ |
| String columnName(); |
| String type(); |
| int length(); |
| } |
| |
| @TableKuang("db_student") |
| class Student{ |
| @FieldKuang(columnName = "db_id",type="int",length = 10) |
| private int id; |
| @FieldKuang(columnName = "db_name",type="varchar",length = 10) |
| private String name; |
| @FieldKuang(columnName = "db_age",type="int",length = 3) |
| private int age; |
| |
| public Student() { |
| } |
| public Student(int id, String name, int age) { |
| this.id = id; |
| this.name = name; |
| this.age = age; |
| } |
| public int getId() { |
| return id; |
| } |
| public void setId(int id) { |
| this.id = id; |
| } |
| 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; |
| } |
| @Override |
| public String toString() { |
| return "Student{" + |
| "id=" + id + |
| ", name='" + name + '\'' + |
| ", age=" + age + |
| '}'; |
| } |
| } |
| |
| public class Test4 { |
| public static void main(String[] args) { |
| try { |
| |
| Class clazz = Class.forName("com.annotation.Student"); |
| |
| Annotation[] annotations = clazz.getAnnotations(); |
| for (Annotation annotation:annotations){ |
| System.out.println(annotation); |
| } |
| |
| TableKuang table = (TableKuang)clazz.getAnnotation(TableKuang.class); |
| System.out.println(table.value()); |
| |
| Field name = clazz.getDeclaredField("name"); |
| FieldKuang fieldKuang = name.getAnnotation(FieldKuang.class); |
| System.out.println(fieldKuang.columnName()+"-->"+fieldKuang.type()+"-->"+fieldKuang.length()); |
| |
| } catch (ClassNotFoundException e) { |
| e.printStackTrace(); |
| } catch (NoSuchFieldException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2021-05-08 Hexo搭建个人静态博客网站