概要
本文主要是总结 Java 注解与反射的相关知识, 加深自己对 Java 类动态语言的理解, 同时为日后学习 Spring 打下基础.
注解:
什么是注解
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={
- "deprecation","unchecked"
- })
- ......
元注解
元注解的作用就是负责注解其他注解, Java 定义了 4 个标准的 meta-annotation 类型, 他们被用来提供对其他 annotaion 类型作说明.
这些类型和他们所支持的类在 java.lang.annotation 包可以找到:
@Target: 用于描述注解的使用范围(即被描述的注解可以用在什么地方)
@Retention: 表示需要在什么级别保存该注释信息, 用于描述注解的生命周期(SOURCE<CLASS<RUNTIME).
@Document: 说明该注解将被包含在 javadoc 中.
@Inherited: 说明子类可以继承父类中的该注解.
自定义注解
使用 @interface 自定义注解, 格式: public @interface 注解名{定义内容}.
自定义注解内部的参数格式: 参数类型 + 参数名(); 若只有一个参数成员, 参数名默认为 value.
注解的元素必须要有值, 我们定义注解元素时, 一般默认使用空字符串, 0 作为默认值.
- // 注解可以显式赋值, 如果没有默认值, 则必须赋值
- @myAnnotation(id = 3)
- public class testAnnotation {
- @myAnnotation(id = 5,school = {"THU","CMU"})
- public void test(){
- }
- }
- @Target(value = {ElementType.METHOD,ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @interface myAnnotation{
- // 注解的参数: 参数类型 + 参数名()
- String name() default "";
- int id();
- int age() default 0;
- String[] school() default {"HDU"};
- }
反射:
什么是反射
Reflection 是 Java 被视为动态语言的关键, 反射机制允许程序在执行期间借助于 Reflection API 取得任何类的内部信息, 并能直接操作任意对象的内部属性及方法.
Java 在加载完类之后, 在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象), 这个对象就包含了完整的类的结构信息. 我们通过这个对象可以看到类的结构, 这种过程称为反射.
正常方式: 引入需要的 "包类" 名称通过 new 实例化取得实例化对象
反射方式: 实例化对象getClass()方法取得完整的 "包类" 名称
反射相关的 API
java.lang.Class: 代表一个类
java.lang.reflect.Method: 代表类的方法
java.lang.reflect.Field: 代表类的成员变量
java.lang.reflect.Constructor: 代表类的构造器
- public class test{
- public static void main(String[] args) throws ClassNotFoundException {
- // 通过反射获得类的 Class 对象
- Class<?> c1 = Class.forName("JavaDoc.User");
- Class<?> c2 = Class.forName("JavaDoc.User");
- Class<?> c3 = Class.forName("JavaDoc.User");
- // 一个类在内存中只有一个 Class 对象
- // 一个类被加载后, 类的整个结构都会被封装在 Class 对象中
- System.out.println(c1.hashCode());
- System.out.println(c2.hashCode());
- System.out.println(c3.hashCode());
- }
- }
- // 实体类 pojo ,Entity
- class User{...}
- // 输出结果: hashCode 相同
- 1846274136
- 1846274136
- 1846274136
获得 Class 类的方式
若已知具体的类, 通过类的 class 属性获取.
若已知某个类的实例化对象, 调用该实例的 getClass()方法获取 Class 对象.
若已知一个类的全类名, 且该类在类路径下, 可以通过 Class 类的静态方法 forName()方法获取.
- public class test02{
- public static void main(String[] args) throws ClassNotFoundException {
- Student student = new Student();
- // 通过 forName 获得
- Class c1 = Class.forName("JavaDoc.Student");
- // 通过对象获得
- Class c2 = student.getClass();
- // 通过类名. class 获得
- Class c3 = Student.class;
- System.out.println(c1.hashCode());
- System.out.println(c2.hashCode());
- System.out.println(c3.hashCode());
- // 获得父类类型
- System.out.println(c1.getSuperclass().hashCode());
- }
- }
- // 实体类 pojo ,Entity
- class Person{...}
- class Student extends Person{...}
Java 内存分析
Java 内存区域 | 功能 |
---|---|
堆 | 存放 new 的对象和数组,可以被所有的线程共享,不会存放别的对象引用 |
栈 | 存放基本类型变量(包含具体数值)和引用对象的变量(包含具体地址) |
方法区 | 可以被所有线程共享,包含了所有 class 和 static 变量 |
这里只是浅显列出, 详细分析需要参考 Java 的 JVM.
Class 类初始化
类的主动引用一定会发生类的初始化
当虚拟机启动时, 先初始化 main 方法所在的类.
调用类的静态成员 (除了 final 常量) 和静态方法会发生初始化.
使用 java.lang.reflect 包反射调用会发生初始化.
类的被动引用不会发生类的初始化
当访问一个静态域时, 只有真正声明这个域的类才会被初始化. 如当通过子类引用父类的静态变量, 不会导致子类初始化.
通过数组定义类的引用, 不会触发此类的初始化.
引用常量不会触发此类的初始化.
- public class test03{
- static {
- System.out.println("Main 类被加载");
- }
- public static void main(String[] args){
- // 主动引用
- Son son = new Son();
- //final 不会初始化父类和子类
- System.out.println(Son.M);
- }
- }
- class Father{
- static int b = 3;
- static {
- System.out.println("父类被加载");
- }
- }
- class Son extends Father{
- static {
- System.out.println("子类被加载");
- m = 200;
- }
- static int m = 100;
- static final int M = 1;
- }
- // 输出结果
Main 类被加载
父类被加载
子类被加载
100
类加载器
类加载器作用是用来把类装载进内存, JVM 规范定义了如下类型的类加载器:
系统类加载器:
负责 java -classpath 或 - D, 导入 java.class.path 所指定的目录下的类与 jar 包装入工作, 是最常用的加载器.
扩展类加载器:
负责 jre/lib/ext 目录下的 jar 包装入工作库.
引导类加载器:
采取 C++ 编写, 是 JVM 自带的类加载器, 负责 Java 平台的核心库, 用来装载核心类库, 无法直接获取.
类加载器采取双亲委派机制, 自底向上检查类是否已经装载, 自顶向下尝试加载类.
自定义类加载器System ClassloaderExtension ClassloaderBootstrap Classloader
- public class test04{
- public static void main(String[] args){
- // 获取系统类加载器
- ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
- System.out.println(systemClassLoader);
- // 获取系统类加载器的父类加载器 --> 扩展类加载器
- ClassLoader parent = systemClassLoader.getParent();
- System.out.println(parent);
- // 获取扩展类加载器的父类加载器 --> 根加载器(c/c++)
- ClassLoader parent1 = parent.getParent();
- System.out.println(parent1);
- // 获取可加载的系统类加载器
- System.out.println(System.getProperty("java.class.path"));
- }
- }
类的运行时结构
- getName()
- getFields()
- getMethods()
- getConstructors()
- ......
- public class test05{
- public static void main(String[] args) throws ClassNotFoundException {
- Class<?> c1 = Class.forName("JavaDoc.User");
- //getFields 只能获取 public 类型信息
- System.out.println("===getFields====");
- Field[] field = c1.getFields();
- for (Field field1 : field) {
- System.out.println(field1);
- }
- //getDeclaredFields 可获取所有信息
- System.out.println("===getDeclaredFields====");
- field = c1.getDeclaredFields();
- for (Field field1 : field) {
- System.out.println(field1);
- }
- }
- }
- class User{
- public double score;
- private String name;
- private int id;
- private int age;
- }
- // 输出结果
- ===getFields====
- public double JavaDoc.User.score
- ===getDeclaredFields====
- public double JavaDoc.User.score
- private java.lang.String JavaDoc.User.name
- private int JavaDoc.User.id
- private int JavaDoc.User.age
动态创建对象执行方法
创建类的对象:
调用 Class 对象的 newInstance()方法
类必须有一个无参构造器.
类的构造器访问权限需要足够.
创建步骤:
通过 Class 类中的 getDeclaredConstructor()取得本类的指定形参类型的构造器.
向构造器的形参中传递一个对象数组进去, 里面包含了构造器中所需的各个参数.
通过 Constructor 实例化对象.
- public class test06{
- public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
- Class<?> c1 = Class.forName("JavaDoc.User");
- //newInstance 默认调用无参构造
- User user = (User) c1.newInstance();
- System.out.println(user);
- // 通过构造器声明含参构造重新调用 newInstance
- Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
- User user2 = (User) constructor.newInstance("zhangsan", 001, 20);
- System.out.println(user2);
- // 通过反射机制调用 User 类中的方法
- Method setName = c1.getDeclaredMethod("setId", int.class);
- setName.invoke(user,001);
- System.out.println(user.getId());
- // 通过关闭程序权限检测, 操作程序的私有属性
- User user4 = (User) c1.newInstance();
- Field name = c1.getDeclaredField("name");
- name.setAccessible(true);
- name.set(user4,"zhangsan2");
- System.out.println(user4.getName());
- }
- }
- class User{
- ...
- public User() {
- }
- public User(String name, int id, int age) {
- this.name = name;
- this.id = id;
- this.age = age;
- }
- ...
- }
反射操作注解
- public class test07{
- public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
- Class<?> c1 = Class.forName("JavaDoc.User");
- // 通过反射获得注解
- Annotation[] annotations = c1.getAnnotations();
- for (Annotation annotation : annotations) {
- System.out.println(annotation);
- }
- // 获得注解内部 value 值
- Table table = c1.getAnnotation(Table.class);
- System.out.println(table.value());
- // 获取内部指定字段的注解信息
- Field f1 = c1.getDeclaredField("name");
- fieldStudent field = f1.getAnnotation(fieldStudent.class);
- System.out.println(field.columnName());
- System.out.println(field.type());
- System.out.println(field.length());
- }
- }
- @Table(value = "db_User")
- class User{
- @fieldStudent(columnName = "db_name",type = "varchar",length = 3)
- private String name;
- @fieldStudent(columnName = "db_id",type = "int",length = 10)
- private int id;
- @fieldStudent(columnName = "db_age",type = "int",length = 10)
- private int age;
- }
- // 类名的注解
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- @interface Table{
- String value();
- }
- // 属性的注解
- @Target(ElementType.FIELD)
- @Retention(RetentionPolicy.RUNTIME)
- @interface fieldStudent{
- String columnName();
- String type();
- int length();
- }
- // 程序输出
- @JavaDoc.Table(value=db_User)
- db_User
- db_name
- varchar
- 3
来源: https://www.cnblogs.com/paragon/p/13406644.html