众所周知,注解和反射是框架的底层知识,本人就是因为在 Spring动态代理时学习时觉得很懵逼,特意回来重新学习了一下反射和注解,并整理了相关笔记,希望对你们会有所帮助!
动态语言
静态语言
Reflection(反射)是 Java 被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName("java.lang.String")
加载完类之后,在堆内存的方法区就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射!
正常方式:引入需要的包类名称 --> 通过 new 实例化 --> 取得实例化对象
反射方式:实例化对象 --> getClass()方法 --> 得到完整的包类名称
Java 反射机制提供的功能
Java 反射优点和缺点
优点:
缺点:
Class类:
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留了一个不变的 Class 类型对象。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void)的有关信息。
Class c1 = Person.class;
Class c1 = Person.getClass();
Class c1 = Class.forName("demo01.Student");
测试代码如下:
package cn.edu.zhku.reflection;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是" + person.name);
// 方式一:通过 getClass 的方式获得 Class 对象
Class c1 = person.getClass();
// 方式二:通过 类名.class 的方式获得
Class c2 = Student.class;
// 方式三:通过 forName 的方式获得
Class c3 = Class.forName("cn.edu.zhku.reflection.Student");
// 包装类型的获取
Class<Integer> c4 = Integer.TYPE;
// 判断获得的 class 对象是不是同一个对象
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4);
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name = "学生";
}
}
输出结果:
这个人是学生
1229416514
1229416514
1229416514
int
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
类的加载和 ClassLoader 的理解
()
方法的过程。类构造器 ()
是由编译期间自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造该类的信息,不是构造该类对象的构造器) ()
在多线程环境中被正确加锁和同步。测试代码
public class Test02 {
public static void main(String[] args) {
A a = new A();
System.out.println(a.m);
}
}
class A{
static {
System.out.println("这是静态代码块");
m = 300;
}
/*
m = 300
m = 100
进行覆盖了
*/
static int m = 100;
public A() {
System.out.println("这是构造方法");
}
}
输出结果:
这是静态代码块
这是构造方法
100
分析执行步骤:
加载到内存,会产生一个类对应 Class 的对象
通过链接初始化变量,m = 0
初始化clinit()
clinit(){
System.out.println("这是静态代码块");
// 然后
m = 300;
}
System.out.println("这是构造方法");
什么时候会发生类初始化
测试代码如下:
public class Test03 {
static {
System.out.println("Main被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//Son son = new Son();
/**
* 输出结果:
* Main被加载
* 父类被加载
* 子类被加载
*/
// 通过反射进行类初始化
//Class.forName("cn.edu.zhku.reflection.Son");
/**
* 输出结果:
* Main被加载
* 父类被加载
* 子类被加载
*/
//System.out.println(Son.father);
/**
* 输出结果:
* Main被加载
* 父类被加载
* 1
*/
System.out.println(Son.SON);
/**
* 输出结果:
* Main被加载
* 50
*/
}
}
class Father{
static {
System.out.println("父类被加载");
}
static int father = 1;
}
class Son extends Father{
static {
System.out.println("子类被加载");
son = 300;
}
static int son = 100;
static final int SON = 50;
}
类加载器作用是用来把 class 装载进内存的。JVM 规范定义了如下类型的类的加载器。
Java核心库在 rt.jar 中
测试代码:
// 类加载器
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统类的加载器
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);
// 测试当前类是哪个类加载加载的
ClassLoader classLoader = Class.forName("cn.edu.zhku.reflection.Test04").getClassLoader();
System.out.println(classLoader);
// 测试jdk 内部类是哪个类加载加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
}
}
输出结果如下:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@49476842
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
执行步骤:用户加载器(AppClassLoader) --> 扩展加载器(ExtClassLoader) --> 根加载器(RootClassLoader)
通过反射获取运行时类的完整结构包括以下这些:
Field、Method、Constructor、SuperClass、Interface、Annotation
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
// 获得类的信息
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1= Class.forName("cn.edu.zhku.reflection.User");
// 获得类的名字 包名 + 类名
System.out.println(c1.getName());
// 获得类的简单名字 类名
System.out.println(c1.getSimpleName());
// 获得类的属性
/**
* getFields();只能获取 public 属性
* getDeclaredFields();可以获取全部的属性
*/
System.out.println("===================");
Field[] fields = c1.getFields();
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();//获得本类及其父类的全部 public 方法
for (Method method : methods) {
System.out.println("正常的:" + method);
}
methods = c1.getDeclaredMethods(); // 获得本类的所有方法
for (Method method : methods) {
System.out.println("getDeclaredMethods" + method);
}
// 获得指定方法
Method getName = c1.getDeclaredMethod("getName", null);
System.out.println(getName);
Method setName = c1.getDeclaredMethod("setName", String.class);
System.out.println(setName);
// 获得指定的构造器
System.out.println("===================");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("----" + constructor);
}
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定的构造器" + declaredConstructor);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class User{
private String name;
private int id;
private int age;
小结:
有了 Class 对象,能做什么?
newInstance
()方法
测试代码:
User.java
@Data
@AllArgsConstructor
@NoArgsConstructor
class User{
private String name;
private int id;
private int age;
}
Test06.java
// 动态的创建对象,通过反射
public class Test06 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class c1 = Class.forName("cn.edu.zhku.reflection.User");
// 构造一个对象
User user = (User) c1.newInstance(); // 本质上是调用了类的无参构造器
System.out.println(user);
}
}
输出结果:
User(name=null, id=0, age=0)
思考?
难道没有无参的构造器就不能创建对象了吗?把生成无参构造器的这个注解@NoArgsConstructor
去掉后,测试发现抛出了异常信息java.lang.InstantiationException
怎么解决呢?
其实只要在操作的时候明确的调用类中的构造器并将参赛传递进去之后,才可以实例化操作。
步骤如下:
测试代码如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
// 动态的创建对象,通过反射
public class Test06 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class c1 = Class.forName("cn.edu.zhku.reflection.User");
// 通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user = (User) constructor.newInstance("白衣",001,18);
System.out.println(user);
}
}
输出结果:
User(name=白衣, id=1, age=18)
通过反射操作类对应的属性
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 动态的创建对象,通过反射
public class Test06 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1 = Class.forName("cn.edu.zhku.reflection.User");
// 通过反射调用普通方法
User user = (User) c1.newInstance();
// 通过反射获得一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
// invoke:激活,调用的意思 (对象,"方法的值")
setName.invoke(user, "白衣");
System.out.println(user.getName());
// 通过反射操作属性
System.out.println("=============");
User user2 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
// 不能直接操作私有属性,我们需要关闭程序的安全监测,需要设置 setAccessible(true) 才可以
name.setAccessible(true);
name.set(user2,"白衣2");
System.out.println(user2);
}
}
小结:
setAccessible()
方法。根据普通方式、反射方式、反射方式+关闭检测进行性能分析:
测试代码:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 分析性能问题
public class Test07 {
// 普通方式调用
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 NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
测试结果:
普通方式执行10亿次:4ms
反射方式执行10亿次:2664ms
关闭资源方式执行10亿次:1666ms
Java 采用泛型擦除的机制来引入泛型,Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
为了通过反射操作这些类型,Java 新增了 ParameterizedType,GenericArrayType,TypeVariable 和 WildcardType 几种类型来代表不能被归一到Class 类中的类型但是又和原始类型齐名的类型。
ParameterizedType:表示一种参数化类型,比如 Collection
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公关父接口
WildcardType:代表一种通配符类型表达式
测试代码:
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 Test08 {
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 = Test08.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 = Test08.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);
}
}
}
}
import java.lang.annotation.*;
import java.lang.reflect.Field;
// 练习反射操作注解
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("cn.edu.zhku.reflection.Student2");
// 通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation); // @cn.edu.zhku.reflection.TableTest(value=db_student)
}
// 获得注解的 value 值
TableTest tableTest = (TableTest) c1.getAnnotation(TableTest.class);
System.out.println(tableTest.value()); //db_student
// 获得类指定注解
Field name = c1.getDeclaredField("name");
FieldTest annotation = name.getAnnotation(FieldTest.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
/**
* 输出结果
* db_name
* String
* 3
*/
}
}
@TableTest("db_student")
class Student2{
@FieldTest(columnName = "db_id",type = "int",length = 10)
private int id;
@FieldTest(columnName = "db_age",type = "int",length = 10)
private int age;
@FieldTest(columnName = "db_name",type = "String",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;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
// 类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableTest{
String value();
}
// 属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest{
String columnName();
String type();
int length();
}
小结:
Class.getDeclaredField(属性名).getAnnotation(字段对应的注解名)
。如果对您有帮助,请别忘了给我点个赞!