网络线程多线程
线程与进程
进程:
(1)进程是指一个内存中运行的应用程序,每个进程就有一个独立的内存空间,一个应用程序可以同时运行多个进程。
(2)系统运行一个程序也就是一个进程从创建到消亡的过程。进程是程序的一次执行过程,是系统运行的基本单位。。
线程:
(1)进程的一个执行单元,负责当前进程中程序的执行,一个化进程中至少有一个线程。
(2)一个进程中可以有多个线程,因此称为多线程。
区别 | 进程 | 线程 |
---|---|---|
内存 | 独立内存空间,堆栈空间独立 | 共享堆空间,但栈空间是独立的,线程消耗资源比进程要小 |
一个进程中的多个线程是并发运行的,那么从微观角度也有先后顺序。哪个线程执行完全取决于cpu的调度,程序员干涉不可干涉,因此称之为多线程的随机性。
Java程序的进程里面至少有俩个线程,主线程也就是main()方法线程,另外一个是垃圾回收站线程。每当java执行一个类的时候,就会启动一个Jvm,每一个Jvm实际上也就是操作系统启动了一个线程。java本身具备了垃圾的收集机制,所以java运行时至少会启动俩个线程。
由于创建一个线程的开销比创建一个进程要小的多,那么我们在多任务运行的时候,通常考虑多线程。java是多线程。
线程的创建——继承方式
继承Thread类方式
所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上是执行一段程序流(一段顺序执行的代码)。
- 定义Thread类的子类,并且重写该类的run方法,该run()方法的是该线程的线程执行体。
- 创建Thread子类的实例,也就是创建线程对象。
- 调用线程对象的start()方法来启动线程。
public class mythread extends Thread{
@Override
public void run(){
for(int i=0;i<20;i++)
System.out.println("好的");
}
}
public static void main(String[] args)
{
mythread mt=new mythread();
mt.start();
//在主方法中执行for循环
for (int i = 0; i < 200; i++) { System.out.println("main线程!"+i);
}
}
线程内存图
多线程执行的时候,在占内存,每一个执行的线程都有一块自己专属的栈内存空间,进行方法的弹栈和压栈。
当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。
run()和start()方法
run()方法:线程执行的任务方法,每个线程都会调用run()方法,我们将线程要执行的任务代码都写在run()方法中可以被线程调用执行。
start()方法:开启线程,线程调用run()方法,start()方法源代码会调用本地方法start()来启动线程。每次开启一个线程都会与操作系统进行交互。
线程名字的设置和获取
线程是有默认名字的,如果我们不设置线程的名字,JVM会自动给他默认名。
String getName();//获取线程名字
setName(String name);//设置线程名字
//通过Thread类的构造方法来设置线程的名字
public class MyThread extends Thread{
public void run(){
System.out.println("线程名字:"+super.getName());
}
}
public class Demo {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread();
//设置线程名字
mt.setName("旺财");
//开启新线程
mt.start();
}
}
获取运行main方法线程的名字
由于测试类不是Thread类的子类,不可以用getName获取main方法线程的名字,但是Thread定义了静态方法static Thread currentthread()
获取当前执行的线程对象。main方法同样被调用,也是具有线程名字的。
public static void main(String[] args)
{
Thread t=Thread.currentThread();
System.out.println(t.getName());
}
线程的创建-实现方式
实现Runnable接口方式
定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
调用线程对象的start()方法来启动线程。
public class MyRunnable implements Runnable{
//重写run方法,让当前线程执行什么具体工作的方法
public void run(){
for(int i=0;i<20;i++)
{
System.out.println(i);
}
}
}
public class Demo {
public static void main(String[] args)
{
//创建任务类对象
MyRunnable myRunnable=new MyRunnable();
//创建自定义线程对象
Thread mt=new Thread(new myRunnable);
//启动线程
mt.start();
mt.setName("热巴");
//主方法中的线程执行
for(int i=0;i<100;i++)
{
System.out.println("好的”);
}
}
}
- 通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
- 在启动的多线程的时候,需要先通过Thread类的构造方法
Thread(Runnable target)
构造出对象,然后调用Thread对象的start()方法来运行多线程代码。 - 实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
Thread和Runnable的区别
实现Runnable接口比继承Thread类所既具有的优势:
- 适合多个相同程序代码的线程去共享一个资源
- 可以避免java中单继承的局限性
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
匿名内部类方式创建进程
使用线程的匿名内部类方式,可以实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重写Runnable中的run()方法
public class Demo {
public static void main(String[] args)
{
//给匿名内部类起个名字
Runnable runn=new Runnable(){
public void run()
{
for(int i=0;i<20;i++)
{
System.out.println("i");
}
};
//创建自定义线程对象
Thread mt=new Thread(new runn);
//启动线程
mt.start(); //调用mt线程中的run方法的执行
mt.setName("热巴");
//主方法中的线程执行
for(int i=0;i<100;i++)
{
System.out.println("好的”);
}
}
}
Callable FutureTask接口
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。可以在线程执行完毕后去获取线程执行的结果。
缺点:编码复杂。
网络编程入门
网络编程,是在一定的协议之下,通过直接或者间接的方式,实现俩台计算机通信和数据传递的程序。问题:精确定位网络上主机(IP地址)+找到主机后如何进行信息传递(TCP层)
软件结构
C/S结构 | 客户端和服务器结构(QQ,迅雷)通过安装客户端程序来连接服务器 |
---|---|
B/S结构 | 浏览器和服务器结构 |
C/S结构:全称Client/Server,是指客户端和服务端之间的结构。(客户端向服务端发送请求,服务器对该请求做出反应)
B/S结构:Brower和Server结构,是指浏览器和服务端的结构。(只需要客户机上面有浏览器即可,不需要安装专门客户端软件,客户机的浏览器程序通过http访问服务器的资源)
服务器一般作为守护线程始终运行,监听网络端口。一旦有客户请求,就会启动一个服务进程来相应客户,同时自己监听服务端口,使后来的客户能够及时得到回应。
网络通信协议
网络通信协议是计算机必须遵守的规则,协议中对这些数据的传输格式,传输效率,传输步骤等做了统一的规定,最终完成数据交换。(好比道路上的汽车要遵守交通规则一样)
TCP/IP协议是Internet最基本广泛的协议,定义了计算机如何连入因特网以及数据在他们之间传输的标准。它的内部包含一系列数据通信的协议,包含四层。每一层都呼叫他的下一层所提供的协议完成自己的需求。
java.net
包中提供了对俩种常见的网络协议的支持。
TCP:传输控制协议。TCP协议是面向连接的通信协议,也就是传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据。它提供了俩台计算机之间毫无差错的数据传输。
三次握手:TCP协议中,在*发射数据的准备期,客户端和服务器之间的三次交互,以保持连接的可靠。
完成三次握手建立连接之后,客户端和用户端就可以数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用很广。
UDP:用户数据报协议。UDP协议是一个面向无连接的协议。(类似直播)传播数据的时候,不需要建立连接,不管对方服务端是否启动,直接把数据和数据源等封装在数据包中,直接发送。但是不稳定。
网络编程三要素
利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。
协议
计算机网络通信必须遵循的规则。
IP地址
(1)IP地址是互联网协议地址,用来给一个网络中的计算机设备做唯一的编号。假设把个人电脑比作一台电话的话,IP地址就相当于电话号码。
(2)IP地址分为IPv4和IPv6.
IPv4:是一个32位的二进制数,通常被分为4个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。有资料显示,全球IPv4地址在2011年2月分配完毕。为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
(3)查看本机的IP地址:控制台输入ipconfig
(4)检查网络是否通:
ping 空格 IP地址
ping 220.181.57.216
ping www.baidu.com
(5)本机的IP地址:127.0.0.1或者localhost
端口号
网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如果说IP地址可以唯一标识网络中的设备,那么端口号就可以**唯一标识设备中的进程(应用程序)**了。
端口号:用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
TCP通信协议
TCP通信可以实现俩台计算机之间的数据交互,通信的俩端要区分客户端和服务端。
俩端通信的步骤:
(1)服务端系统:需要事先启动,等待客户端连接
(2)客户端主动连接服务器端,连接成功后才能够通信,服务端不能够主动连接客户端
客户端Socket
该类实现服务器套接字,套接字指的是俩台设备之间通讯的端点。
Socket client=new Socket("127.0.0.1",6666);
//创建套接字对象将其连接到指定手机的指定端口号,如果指定端的host是null,则相当于指定位置为回送地址。
结构如下:
协议(面向连接的TCP和无连接的UDP)+本机地址+本机端口+远程地址+远机接口
套接字主要分为俩类,一类是面向
服务端ServerSocket
实现服务器的套接字,请对象等待网络的请求。
ServerSocket server=new ServerSocket(6666);//6666是端口号
简单的通信案例
java实现网络编程,一种是通过URL类和URLconnection类访问WWW网络资源。另一种是借助Socket套接字实现基于TCP协议或者UDP协议的网络编程。
基于URL的网络编程
URL是一种完整描述Internet网页和其他资源地址的方法。浏览器解析给定的URL地址,可以访问特定网站的资源。
协议://主机名[:端口号]/路径/#引用
url的对象是不可以修改的,但是可以读取。
public class ParseUrl {
public static void main(String[] args) {
URL u1=null;
try{
ul=new URL("https://blog.csdn.net/qq_32073131/article/details/119510176");//创建url对象
}catch(MalformedURLException e){
e.printStackTrace();
}
sout("协议"+u1.getProtocol());
sout("主机号"+u1.getHost()):
}
}
通过URLConnection连接www
URL u1=new URL();
URLConnection uc=u1.openConnection();
//URLConnection可以实现应用程序和URL之间的通信连接,用来读写网URL所指的资源
使用URL读取WWW数据资源
可以使用URL对象特定的openStream()方法来获得所需的特定WWW资源
openStream==openConnection.getInputStream();
基于连接的套接字方式
多个TCP连接或者多个应用程序进程可能需要通过同一个TCP协议端口进行传递数据。但是为区分不同的应用程序进程和连接,提供了套接字的接口。套接字是实现网络通信的应用程序接口,还可以实现客户机和服务器之间的通信。
客户端程序向服务器发送数据–>客户端程序
public class ClientTCP {
public static void main(String[] args) throws Exception {
System.out.println("客户端 发送数据");
// 1.创建 Socket ( ip , port ) , 确定连接到哪里.
Socket client = new Socket("localhost", 6666);
// 2.获取流对象 . 输出流
OutputStream os=client.getOutputStream();
//3.写出数据
os.write("你好吗?".getBytes());
//4.关闭资源
os.close();
client.close();
}
}
客户端程序向服务器发送数据–>服务器端程序
public class ServerTcp{
puclic static void main(String[] args)throws Exception{
// 1.创建 ServerSocket对象,绑定端口,开始等待连接
ServerSocket ss = new ServerSocket(6666);
// 2.接收连接 accept 方法, 返回 socket 对象.
Socket server = ss.accept();
// 3.通过socket 获取输入流
InputStream is = server.getInputStream();
// 4.一次性读取数据
// 4.1 创建字节数组
byte[] b = new byte[1024];
// 4.2 据读取到字节数组中.
int len = is.read(b);
// 4.3 解析数组,打印字符串信息
String msg = new String(b, 0, len);
System.out.println(msg);
//5.关闭资源. is.close(); server.close();
}
}
服务器向客户端回写数据——服务端程序
/**
* 客户端程序, 获取服务器端 返回的数据
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1.创建一个客户端Socket对象, 指定要连接的服务器地址
Socket socket = new Socket("127.0.0.1", 6666);
//2.获取Socket对象的输出流, 发送数据给服务器
OutputStream out = socket.getOutputStream();
out.write("你好, 服务器".getBytes());
//------------接收服务器端返回的数据-------------------------
InputStream in = socket.getInputStream();
byte[] buff = new byte[1024];
//读取客户端发来的数据
int len = in.read(buff);
System.out.println(new String(buff, 0, len));
//3.关闭客户端的Socket对象
in.close();
out.close();
socket.close();
}
}
服务器向客户端回写数据——客户端程序
/**
* 服务器程序 端口 6666, 获取服务器端 返回的数据
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建一个服务器端ServerSocket对象, 指定服务器端口号
ServerSocket serverSocket = new ServerSocket(6666);
//开启服务器, 等待客户端连接, 当有客户端连接时, 或得到连接的客户端Socket对象
System.out.println("服务器已启动:");
Socket socket = serverSocket.accept();
//通过客户端Socket对象,获取客户端发来的数据
InputStream in = socket.getInputStream();
byte[] buff = new byte[1024];
//读取客户端发来的数据
int len = in.read(buff);
//打印客户端发来的数据
System.out.println(new String(buff, 0, len));
//------------返回数据给客户端-------------------------
OutputStream out = socket.getOutputStream();
out.write("好的, 你已经成功连接上服务器".getBytes());
//关闭Socket对象 (资源释放)
in.close();
out.close();
socket.close();
}
}
反射注解
类的加载器
使用时机
1.创建类的实例。
2. 类的静态变量,或者为静态变量赋值。
3. 类的静态方法。
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
5. 初始化某个类的子类。
6. 直接使用java.exe命令来运行某个主类。
以上6个情况,只要有1个出现,那么类的加载器就会将这个类的class文件加载到内存中,我们就可以使用这个类了。
具体介绍
引导类加载器Bootstrap:
ClassLoader loader = String.class.getClassLoader();
扩展类加载器ExtClassLoader
ClassLoader loader = DNSNameService.class.getClassLoader();
System.out.println(loader);
应用类加载器AppClassLoader
ClassLoader loader = Test.class.getClassLoader();
System.out.println(loader);
当一个类的class文件被类加载器加载到内存后,类的加载器会创建出此class文件的对象。class文件的对象是Class类的对象,是反射技术的基石。
反射
反射:对于任何一个class类,在运行的时候直接得到这个类的成分。运行时动态获取类信息以及动态调用类中成分的能力叫反射。
- 在运行的时候,直接得到这个类的构造器对象Constructor
- 在运行的时候,直接得到这个类的成员变量对象Field
- 在运行的时候,直接得到这个类的成员方法对象Method
核心思想:得到编译以后的class文件对象
好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
反射获取类对象
获取class类的对象
//class类中一个静态方法 forName(包名+类名)
Class c=Class.forName("");
//类名.class
Class c1=Student.class;
//对象.getClass()获取对象对应类的class对象
Student s=new Student();
Class c=s.getClass();
//学生类
public class Student {
private String name;
private int age;
public Student(){
}
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public void study(){
System.out.println("学生在学习");
}
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//获取Student类的class文件对象
public static void main(String[] args)throws Exception{
Student student = new Student();
//通过对象的getClass()获取
Class c1 = student.getClass();
System.out.println(c1);
//类名.class
Class c2 = Student.class;
System.out.println(c2);
//通过指定字符串路径获取
Class c3 = Class.forName("com.kkb.reflect.Student");
System.out.println(c3);
}
反射获取构造方法
Constructor[] getConstructors()
//获取所有的public修饰的构造方法
2. Constructor getConstructor(Class... parameterTypes)
//根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
//如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。
//可以写参数,给定的参数必须是Class对象.不写参数,使用无参
比如:
参数 String name,int age
调用此方法: String.class,int.class
T newInstance(Object... initargs)
根据指定参数创建对象。
T newInstance()
空参构造方法创建对象。
获取无参构造:
public static void main(String[] args)throws Exception{
//获取Studentclass
Class cla = Class.forName("com.kkb.reflect.Student");
//获取无参数构造方法
Constructor constructor = cla.getConstructor();
//运行构造方法
Object object = constructor.newInstance();
System.out.println(object);
}
获取有参构造:
public static void main(String[] args)throws Throwable{
Class cla = Class.forName("com.kkb.reflect.Student");
//获取有参数构造方法
Constructor constructor = cla.getConstructor(String.class, int.class);
//运行构造方法,传递实际参数
Object object = constructor.newInstance("张三",20);
System.out.println(object);
public static void main(String[] args)throws Throwable{
//Class类中定义方法T newInstance()可以直接运行获取到的构造方法
//被反射的类中必须有public权限的无参数构造方法
Class cla = Class.forName("com.kkb.reflect.Student");
Object object = cla.newInstance();
System.out.println(object);
}
反射成员变量
反射成员方法
Method[] getMethods()
//获取所有的public修饰的成员方法,包括父类中
Method getMethod("方法名",方法的参数类型...类型)
//根据方法名和参数类型获得一个方法对象,只能是获取public修饰的
Method类中常用的方法:
Object invoke(Object obj,Object...args)
//返回值Object,表示调用方法后,该方法的返回值
//根据参数args调用对象obj的该成员方法
//如果obj=null,表示该方法是静态方法
public static void main(String[] args)throws Throwable{
Class cla = Class.forName("com.kkb.reflect.Student");
Object object = cla.newInstance();
//获取study方法
Method method = cla.getMethod("study");
//执行方法,传递对象
method.invoke(object);
}
反射作用
(1)可以在运行时得到一个类的全部成分然后操作。
(2)可以破坏封装性。(很突出)
(3)也可以破坏泛型的约束性。(很突出)
(4)更重要的用途是适合:做Java高级框架
(5)基本上主流框架都会基于反射设计一些通用技术功能。
绕过编译阶段为集合添加对象
Class c=list.getClass();
Method add=c.getDeclareMethod("add",Object.class);
boolean rs=(boolean)add.invoke(list,"黑马");
//代替方法
Arraylist list2=list;
通用框架的底层原理
给你任意一个对象,在不清楚对象字段的情况可以,可以把对象的字段名称和对应值存储到文件中去。
单元测试
(1)黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
(2)白盒测试:需要写代码的。关注程序具体的执行流程。
(3)单元测试:针对最小的功能单元编写测试代码,java中最小的功能单元是方法。因此单元测试就是针对java方法的测试,进而检查方法的正确性。
(4)目前测试存在的问题:
只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响。
无法得到测试的结果报告,需要程序员自己去观察测试是否成功。
无法实现自动化测试。
(4)Junit是一个Java语言的单元测试框架,属于白盒测试,简单理解为可以用于取代java的main方法。Junit属于第三方工具,需要导入jar包后使用。
优点:
JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
Junit可以生成全部方法的测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
单元测试中的某个方法测试失败了,不会影响其他测试方法的测试
Junit的使用
NOTE:
(1)该方法的返回值类型,必须写为void.
(2)该方法必须没有参数列表.
运行:
(1)方法上右键运行,运行的是含有@Test注解的方法
(2)类上右键运行,运行的是类当中含有@Test注解的所有方法
注解
基础知识
(1)注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。
(2)注解可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
(3)作用
对Java中类、方法、成员变量做标记,然后进行特殊处理
编写文档:通过代码里标识的注解生成文档【例如,生成文档doc文档】
代码分析:通过代码里标识的注解对代码进行分析【例如,注解的反射】
编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【例如,Override】
(4)常见注解
@author:用来标识作者名
@version:用于标识对象的版本号,适用范围:文件、类、方法。
@Override :用来修饰方法声明,告诉编译器该方法是重写父类中的方法,如果父类不存在该方法,则编译失败。
注解本质上就是一个接口,该接口默认继承Annotation接口。任何一个注解,都默认的继承Annotation接口。
注解属性
作用:可以让用户在使用注解时传递参数,让注解的功能更加强大。
格式:
格式1:数据类型 属性名();
格式2:数据类型 属性名() default 默认值;
适用的数据类型:
int,float,boolean,byte,double,char,long,short,String类型,Class类型,枚举类型,注解类型,以上所有类型的一维数组
特殊属性:
value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写。但是如果有多个属性, 且多个属性没有默认值,那么value名称是不能省略的。
// 定义注解Book
public @interface Book {
// 书名
String value();
// 价格
double price() default 100;
// 多位作者
String[] authors();
}
// 使用Book注解:正确方式
@Book(value="红楼梦",authors = "曹雪芹")
public class BookShelf {
// 使用Book注解:正确方式
@Book(value="西游记",authors = {"吴承恩","白求恩"})
public void showBook(){
}
}
// 使用Book注解:错误方式
public class BookShelf {
@Book("西游记",authors = {"吴承恩","白求恩"})
public void showBook(){
}
}
// 此时value属性名不能省略了。
元注解
含义:注解注解的注解
分类:
@Target: 约束自定义注解只能在哪些地方使用,
@Retention:申明注解的生命周期
@Target中可使用的值定义在ElementType枚举类中,常用值如下:
TYPE,类,接口
FIELD, 成员变量
METHOD, 成员方法
PARAMETER, 方法参数
CONSTRUCTOR, 构造器
LOCAL_VARIABLE, 局部变量
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下
SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在
CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值.
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
解析注解
解析注解的技巧
(1)注解在哪个成分上,我们就先拿哪个成分对象。
(2) 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解
(3) 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解
(4)比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解