网络线程+反射注解

网络线程多线程

线程与进程

进程
(1)进程是指一个内存中运行的应用程序,每个进程就有一个独立的内存空间,一个应用程序可以同时运行多个进程。

(2)系统运行一个程序也就是一个进程从创建到消亡的过程。进程是程序的一次执行过程,是系统运行的基本单位。。

线程
(1)进程的一个执行单元,负责当前进程中程序的执行,一个化进程中至少有一个线程。

(2)一个进程中可以有多个线程,因此称为多线程。
在这里插入图片描述在这里插入图片描述

区别进程线程
内存独立内存空间,堆栈空间独立共享堆空间,但栈空间是独立的,线程消耗资源比进程要小

一个进程中的多个线程是并发运行的,那么从微观角度也有先后顺序。哪个线程执行完全取决于cpu的调度,程序员干涉不可干涉,因此称之为多线程的随机性。

Java程序的进程里面至少有俩个线程,主线程也就是main()方法线程,另外一个是垃圾回收站线程。每当java执行一个类的时候,就会启动一个Jvm,每一个Jvm实际上也就是操作系统启动了一个线程。java本身具备了垃圾的收集机制,所以java运行时至少会启动俩个线程。

由于创建一个线程的开销比创建一个进程要小的多,那么我们在多任务运行的时候,通常考虑多线程。java是多线程。

线程的创建——继承方式

继承Thread类方式

所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上是执行一段程序流(一段顺序执行的代码)。

  1. 定义Thread类的子类,并且重写该类的run方法,该run()方法的是该线程的线程执行体。
  2. 创建Thread子类的实例,也就是创建线程对象。
  3. 调用线程对象的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文件加载到内存中,我们就可以使用这个类了。

具体介绍

V
引导类加载器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文件对象
好处:

  1. 可以在程序运行过程中,操作这些对象。
  2. 可以解耦,提高程序的可扩展性。
    在这里插入图片描述

反射获取类对象

在这里插入图片描述在这里插入图片描述

获取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对象,再来拿上面的注解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太一TT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值