Java进阶 —— 注解与反射

news/2024/11/17 0:48:50/

前言

        在了解完Java中的集合和IO流之后,荔枝继续梳理有关Java反射的知识,这个基础知识点也是在面试中可能会考到滴哦,荔枝希望自己对于整个Java知识的理论体系有一个完整的认知,所以在接下来的几篇文章中依旧梳理是比较枯燥的理论知识,希望荔枝顶得住吧哈哈哈哈哈。加油!啃完就去做项目嘿嘿嘿~~~


文章目录

前言

 一、Java反射

1.1 基本概念

1.2 Class类

1.3 通过反射得到父类、接口和构造方法

1.4 反射创建对象的几种方法

1.5 反射获取类的方法、属性和包 

1.6 反射机制调用指定方法和属性

二、Java动态代理

三、注解Annocation

总结


 一、Java反射

1.1 基本概念

        Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。简单理解JVM中的反射机制和类加载机制其实是一个相反的概念,Java反射是在JVM加载过该类对象的前提下根据类名得到类的具体信息。

Java反射机制功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

Java反射机制主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法

1.2 Class类

        在Object类中定义了以下的方法,此方法将被所有子类继承:public final Class getClass(),该方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

创建class对象的方法

C1ass c0 = Person.c1ass;/通过类名.c1a3s创建指定类的c1ass实例Class c1 = p.getC1ass()://通过一个类的实例对象的getC1ass()方法,获取对应实例对象的类的c1ass实例
try{//通过c1ass的静态方法forNam.e(String className)来获取一个类的c1ass实例//forName(String className)方法中的参数是全路径(包名.类名)Class c2 = C1ass.forName("day1l4.Person")://常用方式
}catch(ClassNotFoundException e){e.printStackTrace();
}

        反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息,Class本身也是一个类,Class对象只能由系统建立对象并且一个类在JVM中也只会有一个Class实例,一个Class对象对应的是一个加载到JVM中的一个.class文件。也就是说,通过class可以完整地得到一个类的完整结构

1.3 通过反射得到父类、接口和构造方法

首先定义一个子类继承父类和两个接口

public class Student extends Person implements Move,Study{//有参、无参、私有构造方法public void Student(){}public void Student(String school){this.school = school;}private Student(String name,int age){this.name = name;this.age = age;}String school;public void showInfo(){System.out.println("学校是:"+this.school);}@Overridepublic void studyInfo(){System.out,println("学习知识");}@Overridepublic void moveType(){System.out.println("跑步");}//方法private void test(String name){}public String getschool(){return this.school;}
}

构造方法示例:

import java.lang.reflect.Constructor;
public class Test{public static void main(String[] args){try{Class clazz = Class.forName("paggage1.Student"); //通过包名.类名的字符串,调用c1ass.forName方法获取指定类C1ass superClazz=cL1azz.getSuperclass(); //获取父类System.out.println("父类:"+superClazz.getName():Class[] interfaces = clazz.getInterfaces();//获取当前类的所有接口for(Class c : interfaces)(System.out.println("接口全路径:"+c.getName());//获取类的公有构造方法Constructor[] cons1 = clazz.getConstructors();for(Constructor c : cons1){System.out,print1n("构造方法名称:"+c,getName()://取得方法名称System.out.println("构造方法:"+c.getName()+"的修饰符是:"+c.getModifiers()); //getModifiers取得方法的修饰符Class[] paramClazz = c.getParameterTypes();for(Class pc : paramClazz)(System.out.println("构造方法:"+c.getName()+"的参数类型是:"+pc.getName());}}//获取类的所有构造方法Constructor[] cons2 = clazz.getDeclaredConstructors();for(Constructor c : cons2){System.out,print1n("构造方法名称:"+c,getName():/取得方法名称//getModifiers取得方法的修饰符,1表示public;2表示privateSystem.out.println("构造方法:"+c.getName()+"的修饰符是:"+c.getModifiers()); }}catch(ClassNotFoundException e){e.printstackTrace();}}
}

1.4 反射创建对象的几种方法

Class clazz = Class.forName("paggage1.Student");try{//调用Student类的无参构造方法Object obj = clazz.newInstance();Student stu = (Student)obj;//调用Student类的有参构造方法Constructor c = clazz.getConstructor(String.class); //指定获取有一个参数并且为String类型的公有构造方法Student stul = (Student)c.newInstance("第一中学");  //newInstance实例化对象,相当于调用public Student//通过反射机制强制调用私有构造方法Constructor c = clazz.getDeclaredConstructor(String.class,int.class);c.getAccessible(true); //解除私有的封装,下面就可以对这个私有方法进行强制调用Student stu = (Student)c.newInstance("zhang",12);}catch (Exception e)(e.printStackTrace();
}

1.5 反射获取类的方法、属性和包 

获取类的方法 

Method[] ms=clazz.getMethods();/获取到类的公有的方法
for(Method m : ms){System.out.println("方法名:"+m.getName());System.out,println("返回值类型:"+m,getReturnType();System..out.println("修饰符:"+m.getModifiers();Class[] pcs = m.getParameterTypes(); //获取方法的参数类型,是一个数组,方法有几个参数,数据就有几个if(pcs != null && pcs.length > 0)(for(Class pc : pcs)(System.out.printin("参数类型:"+pc.getName();}}
}Method[] ms1 = clazz.getDeclaredMethods();/获取到类的所有的方法

获取类的属性和包

//获取公有属性
Field[] f = clazz.getFields();
//获取所有属性
Field[] fd = clazz.getDeclaredFields();f.getName() ---属性的名称
f.getType() ---属性的类型

 获取类所在的包

Package p = clazz.getPackage();

1.6 反射机制调用指定方法和属性

 调用公有方法

//获取无参构造
Class clazz = Class.forName("paggage1.Student");
//使用无参构造创建对象
Constructor con = clazz.getConstructor();Object obj = con.newInstance ()//得到名称为setInfo,参数是String、String的方法
Method m = clazz.getMethod("setInfo",String.class,String.class);
//调用方法
m.invoke(obj,"荔枝","当大佬"); //参数1是需要实例化的对象,后面跟着的是调用当前方法的实际参数

调用私有方法

//如果想要调用一个私有方法
Method ml = clazz.getDeclaredMethod("test",String.class);//获取方法名为test,参数为一个String类型的方法
ml.setAccessible(true);   //解除私有发封装,下面可以强制调用私有
ml.invoke(obj,"lzddl");

调用有返回值的方法

String school = (String)m3.invoke(obj);

调用属性

//反射创建一个对象
Constructor con = clazz.getConstructor();
Student stu = (Student)con.newInstance();Fie1d f=c1azz.getFie1d("schoo.1");      //获取名称为schoo1的属性
f.set(stu,"第三中学");                   //对双对象的schoo1属性设置值"第三中学"
String schoo1 = (String)f.get(stu);     //获取su对象的schoo1.属性的值

二、Java动态代理

        Java动态代理其实就是在Java中使用动态代理的方式对已有的类的方法进行修改,可能是一次性为所有类方法加上某段程序。Java中提供了一个专门完成代理的操作类Proxy,是所有动态代理类的父类,通过此类为一个或多个接口动态地生成实现类。

 首先先对接口与实现类进行声明 

//接口
public interface ITestDemo{void test1();void test2();
}
//实现类
public class TestDemoImpl implement ITestDemo{@overwidepublic void test1(){...}@overwidepublic void test2(){...}
}

接着创建一个动态代理类 

//动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class ProxyDemo implements InvocationHandler{Object obj; //被代理的对象public ProxyDemo(Object obj){this.obj = obj;}@Overridepublic Object invoke (Object proxy,Method method,object[]args) throws Throwable{System..out.println(method.getName()+"方法开始执行");Object result = method.invoke(this.obj,args);//执行的是指定代理对象的指定的方法System.out.println(method.getName()+"方法执行完毕");return result;}    
}

 动态代理过程

public class Test2{public static void main(String[] args){//注意:如果一个对象想要通过Proxy.newProxyInstance方法被代理.那么这个对象的类一定要有相应的接口,就像本类中的ITestDemo接口和实现类TestDemoImplITestDemo test = new TestDemoImpl();//test.testl();//test.test2();//这是因为proxy实现的是InvocationHandler这个接口InvocationHandler handler = new ProxyDemo(test);//三个参数:1.代理对象的类加载器;2.被代理对象的接口;3.代理对象//成功被代理后就返回一个Object对象并需要根据实际需求转换对象ITestDemo t = (ITestDemo)Proxy.newProxyInstance(handler.getclass().getclassLoader(),test.getClass().getInterfaces(),handler);t.test1();t.test2();}
}

需要注意的是:我们在操作实现类的时候使用的是该实现类的接口类型来引用并实例化对象,并将该对象使用proxy实现的接口类型来引用才能使用Proxy.newProxyInstance,并在最后强制转换成ITestDemo对象并操作相应的方法。


三、注解Annocation

        Annotation其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息.Annotation可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的“name=value”对中。使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用.用于修饰它支持的程序元素。

三个基本的Annotation

  • @Override:限定重写父类方法,该注释只能用于方法
  • @Deprecated:用于表示某个程序元素(类,方法等)已过时
  • @SuppressWarnings:抑制编译器警告。

自定义注解Annotation

        定义新的Annotation类型使用@interface关键字。Annotation的成员变量在Annotation定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字,没有成员定义的Annotation称为标记;包含成员变量的Annotation称为元数据Annotation。

下面自定义一个注解TestAnn:

@Target(ElementType.FIELD)/这个注解类是给其他类的属性做注解
@Retention(RetentionPolicy.RUNTIME)//定义注解的声明周期
@Documented
@interface TestAnn{public int id() default 0;public String desc() default "";
}

总结

        在这篇文章中,荔枝主要梳理了有关Java反射、动态代理类的实现和注解的相关知识。其中Java反射中通过反射来获取指定方法、属性、父类和接口等内容需要掌握,还有一个重点就是如何实现动态代理类。学到这里其实Java中的重点内容基本就结束辽,接下来荔枝将会在下一篇文章中去梳理有关Java多线程的知识,希望能够帮助到有需要的小伙伴~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~


http://www.ppmy.cn/news/977674.html

相关文章

Docker Compose 容器编排 + Docker--harbor私有仓库部署与管理

目录 一、Docker Compose简介 1、Docker Compose 的YAML 文件格式及编写注意事项 2、Docker compose 使用的三个步骤 3、 Docker Compose配置常用字段 4、 Docker Compose 常用命令 5、 Docker Compose 文件结构 二: Docker Compose 安装 1、Docker Compose…

mybatis_使用

第一步&#xff1a; 编写接口 第二步&#xff1a; 编写对应的mapper中的sql语句 第三步&#xff1a; 测试 CRUD <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http…

用 Node.js 手写 WebSocket 协议

目录 引言 从 http 到 websocekt 的切换 Sec-WebSocket-Key 与 Sec-WebSocket-Accept 全新的二进制协议 自己实现一个 websocket 服务器 按照协议格式解析收到的Buffer 取出opcode 取出MASK与payload长度 根据mask key读取数据 根据类型处理数据 frame 帧 数据的发…

浏览器 html通知权限已经开了,但是还不提醒

如果您已经在Chrome浏览器中开启了HTML5通知&#xff0c;但是仍然不收到提醒&#xff0c;可能有几种可能的原因。下面是一些建议的解决方法&#xff1a; 检查浏览器设置: 确保HTML5通知在Chrome浏览器中正确启用。您可以按照以下步骤检查设置&#xff1a; 在Chrome中输入 chrom…

获取Token价格行情的多种方式

文章目录 前言一、获取类型二、获取类型的区别 优势方法总结 前言 在开发Dapp项目,很多时候都想要获取行情数据,提供可视化的价格给用户观看,这个时候如何去获取价格呢,下来会有好几种获取的方式供大家学习 一、获取类型 中心化: 使用交易所API 使用聚合数据提供商API 去中…

labview 多线程同步

所谓通讯的同步是指多个线程同时进行或严格按照顺序执行&#xff0c;数据的严格性是指发送多少数据接收多少数据&#xff0c;不能出现数据丢失或重复接收的现象。 labview的同步机制有事件发生、集合点、通知器、信号量。 可以这么来记忆&#xff1a;事急&#xff08;集&…

工程安全监测无线振弦采集仪在建筑物中的应用

工程安全监测无线振弦采集仪在建筑物中的应用 工程安全监测无线振弦采集仪是一种用于建筑物结构安全监测的设备&#xff0c;它采用了无线传输技术&#xff0c;具有实时性强、数据精度高等优点&#xff0c;被广泛应用于建筑物结构的实时监测和预警。下面将从设备的特点、应用场…

Git 命令行教程:如何在 GitLab 中恢复已删除的分支

在软件开发过程中&#xff0c;版本控制是一个至关重要的环节。Git 是最流行的分布式版本控制系统之一&#xff0c;它能够帮助团队高效地管理代码。然而&#xff0c;有时候会发生意外&#xff0c;例如代码误合、错误的删除等情况&#xff0c;导致重要的开发分支本地和远程不慎被…