这是一个学习的过程,学一点就少一点,懂的越多,不懂的也越多。
注解
什么是注解
Annotation是从JDK5.0开始引入的新技术。
- 作用:
- 不是程序本身,可以对程序作出解释。可以被其它程序读取,例如:编译器;
- 对代码进行规范和约束。
- 格式:“@注解名”在代码中存在,还可以添加写参数值,例如:@SuppressWarnings(value=“unchecked”)。
- 使用位置:可以附加在class、method、field等上面,可以通过反射机制编程实现对这些元数据的访问。
内置注解
- @Override:定义在java.lang.Override中,此注释只适用于修辞方法,表示一个方法声明打算
重写超类中的另一个方法声明; - @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修辞方法、属性、类,被标识方法说明弃用,已有更好的代替;(新版本已弃用)
- @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息。与前两个注释有所不同,你需要添加一个参数才能正确使用。这些参数都是已经定义好了的,我们选择性的使用就好了。例如:
- @SuppressWarnings(“all”);
- @SuppressWarnings(“unchecked”);
- @SuppressWarnings(value={“unchecked”,“deprecation”})
名称 | 作用 |
---|---|
deprecation | 过时的类或方法警告 |
unchecked | 执行了未检查的转换时警告 |
allthrough | 当Switch程序块直接通往下一种情况而没有Break时的警告。 |
path | 在类路径、源文件路径等中有不存在的路径时的警告。 |
serial | 当在可序列化的类上缺少serialVersionUID定义时的警告。 |
finally | 任何finally子句不能完成时的警告。 |
all | 关于以上所有情况的警告 |
元注解
作用:就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,它们被用来提供对其他annotation类型作说明。这些类型和它们所支持的类在java.lang annotation包中可以找到。
名称 | 作用 |
---|---|
@Target(开发常用) | 用于描述注解的使用范围(即:被描述的注解可以用在什么地方) |
@Retention(开发常用) | 描述注解被保留的阶段(注解的生命周期) (SOURCE < CLASS < RUNTIME(开发常用)) |
@Document | 说明该注解将被包含在javadoc中 |
@Inherited | 说明子类可以继承父类中的该注解 |
@Target
注解的常用值说明:
名称 | 作用 |
---|---|
ElementType.TYPE | 作用接口、类、枚举、注解 |
ElementType.FIELD | 作用属性字段、枚举的常量 |
ElementType.METHOD | 作用方法 |
ElementType.PARAMETER | 作用方法参数 |
ElementType.CONSTRUCTOR | 作用构造函数 |
ElementType.LOCAL_VARIABLE | 作用局部变量 |
ElementType.ANNOTATION_TYPE | 作用于注解(@Retention注解中就使用该属性) |
ElementType.PACKAGE | 作用于包 |
ElementType.TYPE_PARAMETER | 作用于类型泛型,即泛型方法、泛型类、泛型接口 (jdk1.8加入) |
@Retention
注解的常用值说明:
名称 | 作用 |
---|---|
RetentionPolicy.SOURCE | 仅存在于源码中 |
RetentionPolicy.CLASS | 默认的策略,在class字节码文件中存在,但运行时无法获得 |
RetentionPolicy.RUNTIME | 在运行时可以通过反射获取到,也是最常用的。 |
自定义注解
@interface自定义注解,使用自定义注解时,自动继承了java.lang .annotation.Annotation接口。
作用:@ interface
用来声明一个注解。
- 注意:
- 其中的每一个方法实际上是声明了一个配置参数;
- 方法的名称就是参数的名称;
- 返回值类型就是参数的类型(返回值只能是基本类型、
Class
、String
、enum
); - 可以通过default来声明参数的默认值;
- 如果只有一个参数成员,一般参数名为value;
- 注解元素必须要有值,我们定义注解元素时, 经常使用空字符串,0作为默认值。
public class CustomAnnotation {
// 注解可以显示赋值,如果没有默认值就必须给注解赋值
@MyAnnotation(age = 20)
public void test1(){}
@MyAnnotation2("value")
public void test2(){}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
// 注解的参数格式: 参数类型 + 参数名()
String name() default "";
int age();
int id() default -1; // 如果默认值为-1,代表不存在
String[] schools() default {"清华大学", "北京大学"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
// 只有当参数名称是value时,才可省略不写
String value();
}
反射
反射机制概述
反射(Reflect):是Java被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API
取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。
优点:可以实现动态创建对象和编译,体现出很大的灵活性。
缺点:对性能有影响。(使用反射基本上是一种解释操作,相当于告诉JVM希望它做什么)。
理解Class类并获取Class实例
聊聊Class
在Object类中定义了 getClass()
方法:
public class Object {
// ...
/*
* 此方法返回值类型是一个Class类,此类是Java反射源头,
*/
public final native Class<?> getClass();
// ...
- Class本身也是一个类;
- Class对象只能由系统建立对象;
- 一个加载的类在JVM中只会有一个Class对象;
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class 实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
Class中的常用方法
方法名 | 功能说明 |
---|---|
static ClassforName(String name) | 返回指定类名name的Class对象 |
Object newInstance() | 调用缺省构造函数,返回Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类,接口,数组类或void)的名称。 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class[] getinterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMothed(String name,Class… T) | 返回一个Method对象,此对象的形参类型为paramType |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
获取Class类的实例
方式一 :
若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
Class clazz = Person.class;
方式二 :
已知某个类的实例, 调用该实例的getClass()方法获取Class对象。
Class clazz = person.getClass();
方式三 :
已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出 ClassNotFoundException
。
Class clazz = Class.forName("demo01 .Student");
方式四 :
内置基本数据类型可以直接用类名.Type
Class clazz = Class.forName("demo01 .Student");
方式五 :
还可以利用ClassLoader我们之 后讲解
代码示例:
public class Test03 {
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.forName获取
Class c2 = Class.forName("cn.zhuzicc.reflect.Student");
System.out.println(c2.hashCode());
// 方式三 类名.class
Class c3 = Student.class;
System.out.println(c3.hashCode());
// 方式四 基本类型包装类.TYPE
Class c4 = Integer.TYPE;
System.out.println(c4);
// 方式五 获取父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person {
public String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person {
public Student() {
this.name = "学生";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "老师";
}
}
运行结果:
身份:学生
356573597
356573597
356573597
int
class cn.zhuzicc.reflect.Person
哪些类型可以有Class对象?
- class:外部类、成员(成员内部类,静态内部类)、局部内部类、匿名内部类;
- interface;
- 数组;
- 枚举;
- annotation;
- 基本数据类型;
- void;
Class<Object> c1 = Object.class; // 类
Class<Comparable> c2 = Comparable.class; // 接口
Class<String[]> c3 = String[].class; // 一维数组
Class<int[][]> c4 = int[][].class; // 二维数组
Class<Override> c5 = Override.class; // 注解
Class<ElementType> c6 = ElementType.class; // 枚举
Class<Integer> c7 = Integer.class; // 基本数据类型包装类
Class<Void> c8 = void.class; // void
Class<Class> c9 = Class.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);
System.out.println("\n数组长度不相同,是否是同一个class对象?");
// 数组长度不相同,是否是同一个class对象
int[] arr1 = new int[10];
int[] arr2 = new int[100];
System.out.println(arr1.getClass().hashCode());
System.out.println(arr2.getClass().hashCode());
运行结果:
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对象?
356573597
356573597
类的加载与ClassLoader
Java内存分析
- 类的加载过程:
- 将类的class文件读入内存中,并创建一个java.lang.Class对象。此过程由类加载器完成;
- 将类的二进制数据合并到JRE中;
- JVM负责类的初始化。
类的加载与ClassLoader的理解
- 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的
java.lang.Class
对象。 - 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static) 分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
- 初始化:
- 执行类构造器< clinit> ()方法的过程。类构造器< clinit> ()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虛拟机会保证一 个类的 < clinit> ()方法在多线程环境中被正确加锁和同步。
什么时候会发生类初始化
- 类的主动引用(一定会发生类的初始化)
- 当虚拟机启动,先初始化main方法所在的类;
- new一个类的对象;
- 调用类的静态成员(除了final常量)和静态方法;
- 使用java.lang.reflect包的方法对类进行反射调用;
- 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。
- 类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化;
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)。
类加载器的作用(classload)
作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
JVM规范定义了几种类加载器类型
- 引导类加载器:用C++编写的, 是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取;
- 扩展类加载器:负责jre/lib/ext目录下的jar包或- D java.ext.dirs指定目录下的jar包装入工作库;
- 系统类加载器:负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器;
获取类加载器代码示例:
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统的类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.print("获取系统的类加载器:");
System.out.println(systemClassLoader);
// 获取系统的类加载器的父类加载器 -> 扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.print("获取系统的类加载器的父类加载器 -> 扩展类加载器:");
System.out.println(parent);
// 获取扩展类加载器的父类加载器 -> 根加载器(由C/C++提供)
ClassLoader parent1 = parent.getParent();
System.out.print("获取扩展类加载器的父类加载器 -> 根加载器(由C/C++提供):");
System.out.println(parent1);
// 测试当前类是哪个类加载器加载的
ClassLoader classLoader = Class.forName("cn.zhuzicc.reflect.Test07").getClassLoader();
System.out.print("测试当前类是哪个类加载器加载的:");
System.out.println(classLoader);
// 测试JDK内置类是哪个类加载器加载的
ClassLoader innerClass = Class.forName("java.lang.Object").getClassLoader();
System.out.print("测试当前类是哪个类加载器加载的:");
System.out.println(innerClass);
// 如何获取系统类加载器可以加载的路径
String[] split = System.getProperty("java.class.path").split(";");
System.out.println(split.length);
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}
}
}
运行结果:
获取系统的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
获取系统的类加载器的父类加载器 -> 扩展类加载器:sun.misc.Launcher$ExtClassLoader@1540e19d
获取扩展类加载器的父类加载器 -> 根加载器(由C/C++提供):null
测试当前类是哪个类加载器加载的:sun.misc.Launcher$AppClassLoader@18b4aac2
测试当前类是哪个类加载器加载的:null
25
E:\develop\jdk\jdk1.8\jre\lib\charsets.jar
E:\develop\jdk\jdk1.8\jre\lib\deploy.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\access-bridge-64.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\cldrdata.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\dnsns.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\jaccess.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\jfxrt.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\localedata.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\nashorn.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunec.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunjce_provider.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunmscapi.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\sunpkcs11.jar
E:\develop\jdk\jdk1.8\jre\lib\ext\zipfs.jar
E:\develop\jdk\jdk1.8\jre\lib\javaws.jar
E:\develop\jdk\jdk1.8\jre\lib\jce.jar
E:\develop\jdk\jdk1.8\jre\lib\jfr.jar
E:\develop\jdk\jdk1.8\jre\lib\jfxswt.jar
E:\develop\jdk\jdk1.8\jre\lib\jsse.jar
E:\develop\jdk\jdk1.8\jre\lib\management-agent.jar
E:\develop\jdk\jdk1.8\jre\lib\plugin.jar
E:\develop\jdk\jdk1.8\jre\lib\resources.jar
E:\develop\jdk\jdk1.8\jre\lib\rt.jar
D:\ideaprojects\java\annotation&reflect\classes\production\java
E:\develop\JetBrains\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar
创建运行时类的对象
创建类的对象:调用 Class
对象的 newInstance()
方法。
- 注意:目标类必须有一个无参构造器;(不然会报错:
java.lang.NoSuchMethodException
)
思考:如果没有无参构造器就不能创建对象了吗?
没有无参构造器也可以创建,只要在操作的时候明确调用类中的哪个构造器,并将参数传递进去以后,也可进行实例化。
- 操作步骤:
- 通过
Class
类的getDeclaredConstructor(Class<?>... parameterTypes)
方法获取到指定形参类型的构造器; - 向构造器的形参中传递一个对象数组进去,包含该构造器中所需的所有参数;
- 通过
Constructor
实例化对象。
- 通过
代码示例:
public class Test09 {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("cn.zhuzicc.reflect.User");
// 1. 通过反射构造一个对象
System.out.println("1. 通过反射构造一个对象");
// 1.1 本质是调用了类的无参构造器
User user = (User) c1.newInstance();
System.out.println("1.1 本质是调用了类的无参构造器:\n" + user);
// 1.2 通过类的有参构造器创建对象
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, Integer.class, LocalDate.class);
user = (User) declaredConstructor.newInstance("张三", 21, LocalDate.of(2000, 3, 8));
System.out.println("1.2 通过类的有参构造器创建对象:\n" + user);
System.out.println("\n");
// 2. 通过反射操作方法
System.out.println("2. 通过反射操作方法");
// 2.1 通过反射调用普通方法
Method setName = c1.getDeclaredMethod("setName", String.class);
user = (User) c1.newInstance();
setName.invoke(user, "李四");
System.out.println("2.1 通过反射调用普通方法:\n" + user.getName());
// 2.1 通过反射调用私有方法
System.out.println("2.1 通过反射调用私有方法:");
Method thisSecret = c1.getDeclaredMethod("thisSecret", Boolean.class);
thisSecret.setAccessible(true);
thisSecret.invoke(user, true);
System.out.println("\n");
// 3. 通过反射操作属性
System.out.println("3. 通过反射操作属性");
Field name = c1.getDeclaredField("name");
user = (User) c1.newInstance();
name.setAccessible(true);
name.set(user, "王五");
System.out.println(user.getName());
}
运行结果:
1. 通过反射构造一个对象
1.1 本质是调用了类的无参构造器:
User{name='null', age=null, birthday=null}
1.2 通过类的有参构造器创建对象:
User{name='张三', age=21, birthday=2000-03-08}
2. 通过反射操作方法
2.1 通过反射调用普通方法:
李四
2.1 通过反射调用私有方法:
你是我的朋友,你可以知道这个秘密
3. 通过反射操作属性
王五
setAccessible
-
Method和Field、Constructor对象都有setAccessible()方法。setAccessible作用是启动和禁用访问安全检查的开关。
-
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
-
提高反射的效率。
- 如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true;
- 使得原本无法访问的私有成员也可以访问。
-
参数值为false则指示反射的对象应该实施Java语言访问检查。
setAccessible
效率测试:
public class Test10 {
public static void main(String[] args) throws Exception {
test01();
test02();
test03();
}
/**
* 普通方式执行
*/
public static void test01(){
User user = new User();
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("执行10亿个数,普通方式执行:" + (endTime - beginTime) + "ms");
}
/**
* 反射方式执行, 关闭安全检测
*/
public static void test02() throws Exception {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getMethod("getName", null);
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("执行10亿个数,反射方式执行, 开启安全检测:" + (endTime - beginTime) + "ms");
}
/**
* 反射方式执行, 开启安全检测
*/
public static void test03() throws Exception {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getMethod("getName", null);
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.setAccessible(true);
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("执行10亿个数,反射方式执行, 关闭安全检测:" + (endTime - beginTime) + "ms");
}
}
运行结果:
执行10亿个数,普通方式执行:8ms
执行10亿个数,反射方式执行, 开启安全检测:6448ms
执行10亿个数,反射方式执行, 关闭安全检测:1877ms
反射获取泛型
Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
为了通过反射操作这些类型,Java新增了 ParameterizedType
, GenericArrayType
, TypeVariable
和 WildcardType
几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
ParameterizedType
:表示一种参数化类型,比如Collection< String>;GenericArrayType
:表示一种元素类型是参数化类型或者类型变量的数组类型;TypeVariable
:是各种类型变量的公共父接口;WildcardType
:代表一种通配符类型表达式。
代码示例:
public class Test11 {
public static void main(String[] args) throws Exception {
// 1. 获取泛型信息
Class<Test11> c1 = Test11.class;
Method method1 = c1.getMethod("test01", Map.class, List.class);
// 1.1 获取方法参数泛型
Type[] genericParameterTypes = method1.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("1.1 获取方法参数泛型:\n" + genericParameterType);
// 1.1.1 获取真实参数类型
if (genericParameterType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("1.1.1 获取真实参数类型:\n" + actualTypeArgument);
}
}
}
System.out.println("\n");
Method method2 = c1.getMethod("test02");
// 1.2 获取方法返回值泛型
Type genericReturnType = method2.getGenericReturnType();
System.out.println("1.2 获取方法返回值泛型:\n" + genericReturnType);
if (genericReturnType instanceof ParameterizedType) {
// 1.2.1 获取真实参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("1.2.1 获取真实参数类型:\n" + actualTypeArgument);
}
}
}
}
public void test01(Map<String, User> map, List<User> list) throws Exception{
System.out.println("test01");
}
public Map<String, User> test02() {
System.out.println("test02");
return null;
}
运行结果:
1.1 获取方法参数泛型:
java.util.Map<java.lang.String, cn.zhuzicc.reflect.User>
1.1.1 获取真实参数类型:
class java.lang.String
1.1.1 获取真实参数类型:
class cn.zhuzicc.reflect.User
1.1 获取方法参数泛型:
java.util.List<cn.zhuzicc.reflect.User>
1.1.1 获取真实参数类型:
class cn.zhuzicc.reflect.User
1.2 获取方法返回值泛型:
java.util.Map<java.lang.String, cn.zhuzicc.reflect.User>
1.2.1 获取真实参数类型:
class java.lang.String
1.2.1 获取真实参数类型:
class cn.zhuzicc.reflect.User
获取运行时类的完整结构
通过反射获取运行时类的完整结构包括:Field、Method、Constructor、SuperClass、Interface、Annotation。
代码示例:
public class Test08 {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("cn.zhuzicc.reflect.User");
// 1. 获取类名
// 1.1 获取类的全限定类名
System.out.println(c1.getName());
// 1.2 获取类的名称
System.out.println(c1.getSimpleName()+"\n");
// 2.获取类的属性
// 2.1 获取类public修饰的属性
Field[] fields = c1.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("\n");
// 2.2 获取类的所有属性
Field[] declaredFields = c1.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
System.out.println("\n");
// 2.3 获取类的指定属性
Field name = c1.getField("name");
System.out.println("public修饰:" + name);
Field age = c1.getDeclaredField("age");
System.out.println("private修饰:" + age);
System.out.println("\n");
// 3. 获取类的方法
System.out.println("3. 获取类的方法");
// 3.1 获取本类及其父类的所有public方法
System.out.println("3.1 获取本类及其父类的所有public方法:");
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
// 3.2 获取本类的所有方法
System.out.println("3.2 获取本类的所有方法:");
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method);
}
// 3.3 获取本类指定名称的方法
System.out.println("3.3 获取本类指定名称的方法:");
Method getName = c1.getMethod("getName");
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
// 4. 获取构造器
System.out.println("4. 获取构造器");
// 4.1 获取public修饰的构造器
System.out.println("4.1 获取所有构造器:");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
// 4.2 获取所有构造器
System.out.println("4.2 获取所有构造器:");
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
// 4.3 获取指定构造器
System.out.println("4.3 获取指定构造器:");
Constructor constructor = c1.getConstructor(String.class, Integer.class);
System.out.println(constructor);
}
}
class User{
public String name;
private Integer age;
private LocalDate birthday;
public User() {
}
public User(String name) {
this.name = name;
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public User(String name, Integer age, LocalDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
private void thisSecret(Boolean flag){
if (flag) {
System.out.println("你是我的朋友,你可以知道这个秘密");
} else {
System.out.println("这是一个不为人知的秘密");
}
}
// 省略 getter/setter...
// 省略 toString...
}
运行结果展示:
cn.zhuzicc.reflect.User
User
public java.lang.String cn.zhuzicc.reflect.User.name
public java.lang.String cn.zhuzicc.reflect.User.name
private java.lang.Integer cn.zhuzicc.reflect.User.age
private java.time.LocalDate cn.zhuzicc.reflect.User.birthday
public修饰:public java.lang.String cn.zhuzicc.reflect.User.name
private修饰:private java.lang.Integer cn.zhuzicc.reflect.User.age
3. 获取类的方法
3.1 获取本类及其父类的所有public方法:
public java.lang.String cn.zhuzicc.reflect.User.toString()
public java.lang.String cn.zhuzicc.reflect.User.getName()
public void cn.zhuzicc.reflect.User.setName(java.lang.String)
public java.lang.Integer cn.zhuzicc.reflect.User.getAge()
public void cn.zhuzicc.reflect.User.setAge(java.lang.Integer)
public void cn.zhuzicc.reflect.User.setBirthday(java.time.LocalDate)
public java.time.LocalDate cn.zhuzicc.reflect.User.getBirthday()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
3.2 获取本类的所有方法:
public java.lang.String cn.zhuzicc.reflect.User.toString()
public java.lang.String cn.zhuzicc.reflect.User.getName()
public void cn.zhuzicc.reflect.User.setName(java.lang.String)
public java.lang.Integer cn.zhuzicc.reflect.User.getAge()
public void cn.zhuzicc.reflect.User.setAge(java.lang.Integer)
public void cn.zhuzicc.reflect.User.setBirthday(java.time.LocalDate)
public java.time.LocalDate cn.zhuzicc.reflect.User.getBirthday()
private void cn.zhuzicc.reflect.User.thisSecret(java.lang.Boolean)
3.3 获取本类指定名称的方法:
public java.lang.String cn.zhuzicc.reflect.User.getName()
public void cn.zhuzicc.reflect.User.setName(java.lang.String)
4. 获取构造器
4.1 获取所有构造器:
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer,java.time.LocalDate)
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer)
public cn.zhuzicc.reflect.User(java.lang.String)
public cn.zhuzicc.reflect.User()
4.2 获取所有构造器:
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer,java.time.LocalDate)
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer)
public cn.zhuzicc.reflect.User(java.lang.String)
public cn.zhuzicc.reflect.User()
4.3 获取指定构造器:
public cn.zhuzicc.reflect.User(java.lang.String,java.lang.Integer)
反射操作注解
代码示例:
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("cn.zhuzicc.reflect.Person1");
// 1. 通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("1. 通过反射获取注解:\n" + annotation);
}
// 2. 获取注解中的属性值
TableName annotation = (TableName) c1.getAnnotation(TableName.class);
String value = annotation.value();
System.out.println("2. 获取注解中的属性值:\n" + value);
// 3. 获取字段注解
Field[] declaredFields = c1.getDeclaredFields();
System.out.println("3. 获取字段注解:");
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
ColunmName colunmName = declaredField.getAnnotation(ColunmName.class);
System.out.print(colunmName.name() + ",");
System.out.print(colunmName.length() + ",");
System.out.print(colunmName.type());
System.out.println("\n");
}
}
}
@TableName("person_info")
class Person1 {
@ColunmName(name = "db_name", type = "varchar", length = 5)
private String name;
@ColunmName(name = "db_age", type = "int", length = 3)
private int age;
@ColunmName(name = "db_stature", type = "int", length = 3)
private int stature;
@ColunmName(name = "db_weight", type = "int", length = 3)
private int weight;
public Person1() {
}
public Person1(String name, int age, int stature, int weight) {
this.name = name;
this.age = age;
this.stature = stature;
this.weight = weight;
}
// 省略 getter/setter...
// 省略 toString...
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableName {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface ColunmName {
String name();
String type();
int length();
}
运行结果:
1. 通过反射获取注解:
@cn.zhuzicc.reflect.TableName(value=person_info)
2. 获取注解中的属性值:
person_info
3. 获取字段注解:
db_name,5,varchar
db_age,3,int
db_stature,3,int
db_weight,3,int