javaSE笔记5

多线程
1、什么是进程?什么是线程?
    进程是一个应用程序。(1个进程是一个软件)
    线程是一个进程中的执行场景/执行单元。
    一个进程可以启动多个线程。
2、对于java来说,当再DOS命令窗口中输入:
    java HelloWorld 回车之后
    会先启动JVM,而JVM就是一个进程。
    JVM再启动一个主线程调用main方法。
    同时在启动一个垃圾回收线程负责看护,回收垃圾。
    最起码,现在的java程序中至少有两个线程并发。
3、两个进程内存不共享。
4、在java语言中:线程A和线程B,堆内存和方法区内存共享。但是栈内存独立。
5、假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,互不干扰,各自执行各自的,这就是多线程并发。
6、main方法结束只是主线程结束了,主栈空了,其他的栈可能还在压栈弹栈。
7、单核CPU并不能做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。
    对于单核的CPU来说,在某一个事件点上实际上只能处理一件事情,但是对于CPU的速度是及快的,多个线程之间频繁切换执行,给人的感觉是:多个事情同时在做。

实现多线程的两种方式:
1、编写一个类直接继承java.lang.Thread类,重写run()方法,调用线程对象的start方法启动线程。【示例代码如下】
    注意:直接调用Thread对象的run方法,不会启动线程,不会启动新的分支栈。
    
    public class Test01 {

        public static void main(String[] args) {
            //这里是main方法,这里的代码属于主线程,在主栈中运行
            //ThreadTest01继承了Thread类,重写了run方法
            //创建一个分支线程对象
            ThreadTest01 t1 = new ThreadTest01();
            //启动线程
            //start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,开辟之后,这段代码瞬间结束。
            //这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start方法就结束了。线程就启动成功了。
            //启动成功的线程会自动调用run方法,并且run方法在分支栈的底部(压栈)。
            //run方法在分支栈的底部,main方法在主栈的栈底部。run和main是平级的。
            t1.start();//线程对象的run方法会自动调用
            //这里的代码还是在主线程中运行
            for (int i = 0; i < 1000; i++) {
                System.out.println("main===>" + i);
            }
        }

    }
    class ThreadTest01 extends Thread {
        
        @Override
        public void run() {
            //这段代码是在分支栈中运行
            for (int i = 0; i < 1000; i++) {
                System.out.println("次线程===>" + i);
            }
        }
    }
    
2、编写一个类,实现java.lang.Runnable接口,实现run方法。【示例代码如下】

    public class Test02 {
        public static void main(String[] args) {
            //实例化一个可运行的类
            MyRunnable mr = new MyRunnable();
            //将可运行的类封装成一个线程对象
            Thread t = new Thread(mr);
            //启动线程
            t.run();
        }
    }
    //这并不是一个线程类,是一个可运行的类【因为它实现了可运行的接口(Runnable接口)】。他还不是一个线程
    class MyRunnable implements Runnable {

        @Override
        public void run() {
            System.out.println("分支线程执行");
        }
    }
 
3、//采用匿名内部类的方式【属于第二种方式】
    Thread t = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("采用匿名内部类形式");
        }
    });
    t.start();

线程的生命周期【新建、就绪、运行、阻塞、死亡】
1、刚new出来线程对象叫做“新建状态”。
2、调用了start方法,进入“就绪状态”。
    就绪状态的线程又叫做“可运行状态”,表示当前线程具有抢夺CPU时间片的权力。【CPU时间片就是执行权】
3、当一个线程抢到CPU时间片(执行权)之后,就进入了“运行状态”【开始执行run方法】。
    run方法的开始执行标志着这个线程进入运行状态,当之前抢占的CPU时间片用完之后
    会重新回到“就绪状态”,继续抢夺CPU时间片,当抢到CPU时间片之后,会重新进入run方法接着上一次的代码继续往下执行。
4、在“就绪状态”和“运行状态”之间来回切换叫做“JVM调度”。
5、run方法执行结束,代表线程进入“死亡状态”。
6、线程在“运行状态”的线程遇到阻塞事件【例如:接收用户键盘输入或者sleep方法等】此时线程会进入“阻塞状态”,阻塞状态的线程会放弃之前占有的CPU时间片。
    当线程“阻塞接触”之后,因为之前的时间片没了,需要再次回到“就绪状态”抢夺时间片。
7、在“运行状态”的线程遇到synchronized关键字,并且与synchronized关键字后面的小括号中的对象是同一个对象。
   那么在运行状态的线程会进入“锁池(lockpool)”。
   进入锁池的线程会在锁池中找共享对象的对象锁,并释放之前占有的CPU时间片,有可能找到了,有可能没找到。
   没找到则在锁池中等待,如果找到了会进入“就绪状态”继续抢夺CPU时间片。

线程类的常用方法
1.void setName(String name)  将此线程更名为name
2.String getName()  返回此线程的名字【主线程的名字就是mian】,当线程没有设置名字的时候,默认的名字有什么规律?
    Thread-0\Thread-1\Thread-2\....
3.static Thread currentThread()  返回对当前正在执行的线程对象的引用。 
4.static void sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停【作用是让当前线程进入休眠,进入“阻塞状态”,放弃占有的CPU时间片,让给其他线程使用】
    可以做到这种效果,间隔特定的时间,去执行特定的代码,每隔多久执行一次。
    【面试题】
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println(i);
                }
            }
        });
        t.start();
        try {
            t.sleep(1000 * 5); // 会使t线程进入休眠状态吗?[答案:不会,sleep是静态方法,和对象引用无关,编译的时候会自动将t换成类名Thread,所以会使main线程休眠5秒]
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
5.怎样唤醒正在睡眠的线程?【这个不是中断线程的执行,而是终止线程的睡眠】
    void interrupt()  干扰,这种中断睡眠的方式依靠了java的异常处理机制
        interrupt 方法一但执行sleep方法就会抛出InterruptedException异常
6.怎样启动线程和终止线程:
    void start()  启动线程
    void stop()  终止线程,这种方法本质上是不安全的。
        这种方式存在很大的缺点:容易丢失数据,因为这种方式直接将线程杀死了,线程没有保存的数据将会丢失,不建议使用。

线程的调度
1、常见的线程调度模型:
    1.抢占式调度模型:
        哪个线程优先级比较高,强盗的CPU时间片的概率就高一些/多一些.
        java采用抢占式线程调度模型.
    2.均分式调度模型:
        平均分配CPU时间片.每个线程占有的CPU时间片长度一样.[平均分配].
2、常用方法:
    1.void setPriority(int newPriority)  更改此线程优先级
    2.void getPriority()  获取此线程优先级
        最低优先级是:1
        默认优先级是:5
        最高优先级是:10
    3.static void yield()  线程让位的方法,暂停当前正在执行的线程,并执行其他线程.
        yield方法会让当前线程从运行状态回到就绪状态
    4.void join()  等待这个线程死亡[合并线程]
        class MyThread1 extends Thread {
            public void doSome() {
                Thread t = new Thread(new Runnable () {
                    @override
                    public void run () {
                        //分支线程
                    }
                });
                t.join();//当前线程进入阻塞状态,t线程执行结束之后,当前线程才能解除阻塞状态进入就绪状态.
                //线程合并,t线程合并到当前线程中【只是两个栈之间发生了等待关系,并不是栈合并了】
            }
        }
        
线程安全【重点】
1、线程不安全的条件。【满足一下三个条件有可能存在线程安全问题】
    1.多线程并发。
    2.多个线程之间有共享数据。
    3.共享数据有修改行为。
2、怎样解决线程安全问题?
    使用“线程同步机制”。
    线程同步机制就是:线程排队执行(不能并发)。
    用线程排队执行解决线程安全问题。
    线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全是第一位。    
3、两种编程模型:
    异步编程模型:
        线程t1和t2各自执行各自的,t1不管t2,t2也不管t1,谁也不需要等谁,这种变成模型叫做:异步编程模型。其实就是多线程并发(效率较高。)
    同步编程模型:
        线程t1和线程t2,在线程t1执行的时候,必须等待线程t2执行结束。
        两个线程直线发生了等待关系,这就是同步编程模型。效率较低。线程排队执行。
    异步就是并发,同步就是排队。
4、线程同步机制的语法是:
    synchronized (数据) {
        //线程同步代码块
    }
    synchronized后面小括号中传的这个“数据”是相当关键的。
    这个数据必须是多线程共享的数据。才能达到多线程排队。
    小括号中写什么?
        必须是多线程共享的那个对象,否则无法达到多线程排队执行的效果。
    执行原理:当多线程执行过程中遇到同步代码块的时候,就判断有没有线程【synchronized后面小括号中传的这个“数据”】和当前线程的【synchronized后面小括号中传的这个“数据”】相同
              如果相等,代表多个线程操作的是同一个对象(多个线程之间有共享数据)。
              所以多个线程之间就会发生等待关系。
              【简单说:两个线程synchronized后面小括号中传的这个“数据”相同,则后开始执行的线程必须等待先执行的那个线程执行完毕之后才能执行】
    synchronized (this) {} //大多数这样写
    Object obj = null;
    synchronized (obj) {} //出现空指针异常
    synchronized ("abc") {} //可以,因为"abc"在字符串常量池中只有一份,只不过这样写所有的线程都会同步【没有共享数据也会同步】。
5、 synchronized (数据) {
        //synchronized代码块
    }
    进入synchronization代码块的时候,就需要先敲门看看有没有线程正在携带小括号中的“数据”正在执行。如果有就等等,排队执行。
    【synchronized代码块可以不在同一个方法,也可以不是同类中的,只需要看小括号中的这个“数据”。】
6、java中的三大变量:
    实例变量:在堆中。
    静态变量:在方法区中。
    局部变量:在栈中。【永远都不会存在线程安全问题。因为多个线程不共享局部变量,一个线程一个栈。】
    实例变量在堆中,静态变量在方法区内存中,堆和方法区内存只有一个
    堆和方法区都是多个线程共享的,所以可能存在线程安全问题。
7、同步代码块越小(括的代码越少),效率越高。
8、synchronization出现在实例方法上:
    相当于synchronized后面小括号中传的这个“数据”是“this”。
    表示整个方法体都需要同步,可能胡无辜扩大同步的范围,导致程序的执行效率较低。
9、在静态方法上使用synchronized:目的是为了保证静态变量的安全。
    表示找类锁。
    类锁永远只有一把。
    就算创建了100个对象,那么类锁也只有一把。
10、为了减少“死锁”的发生,在开发中synchronized代码块一般不要嵌套使用。
11、解决线程安全问题的几种方法?
    1.尽量使用局部变量代替“实例变量和静态变量”。
    2.如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了,就没有数据安全问题了。【一个线程一个对象,线程之间对象不共享了,就没有数据安全问题了】
    3.如果1和2都不能用,就是能使用synchronized了。线程同步机制。【最后考虑使用这种方式,synchronization会让程序的执行效率较低,用户体验差,系统吞吐量降低,在不得已的情况下在选择线程同步机制。】

守护线程
1、java中所有的线程分为两大类,一种是用户线程(例如:主线程),一种是守护线程(例如:垃圾回收线程)
2、守护线程又可以叫做“后台线程”。
3、一般守护线程是一个死循环,一直在做某一件事情。所有的用户线程只要结束,守护线程自动结束。
4、Thread类的方法:
    void setDaemon(boolean on)  通过本方法可以将线程设置为守护线程。【设置成守护线程之后所有用户线程执行结束,守护线程自动执行结束】

定时器
1、定时器的作用:间隔特定的时间去执行特定的程序。
2、定时器在java中可以采用多种方式实现:
    1.可以使用sleep方法,设置睡眠。【这种方式比较low需要自己写】
    2.在java类库中已经写好了一个定时器:java.util.Timer,可以直接用。不过这种方式在现在的开发中也很少用,因为现在有很多高级框架都是支持定时任务的。
    3.在实际开发中目前使用较多的是spring框架提供的springTask框架。
3、Timer类
    1.常用构造方法
        Timer() 定时器可以看作是一个普通线程
        Timer(boolean isDaemon) 将定时器设置成线程守护的方式,isDaemon是否守护线程
        Timer(String name) 创建一个新的定时器,其相关线程具有指定的名称。 
        Timer(String name, boolean isDaemon) 
    2.常用方法
        void schedule(TimerTask task, Date firstTime, long period)   
            task:是执行的任务,间隔特定的时间间隔去执行的代码
            firstTime:是第一次开始执行的时间
            period:是每间隔多少次执行一次。
        
实现线程的第三种方式
1、步骤:
    1.创建一个Callable接口的实现类对象。
        call方式和run方法类似。
    2.创建一个“未来任务类对象”【FutureTask(Callable c)】。
    3.因为FutureTask是Runnable类的一个实现类。
    4.实例化一个Thread对象,将FutureTask对象传进去,这样就构建成了一个线程对象。
2、这种方式可以获取到某个线程执行的返回值。
3、FutureTask常用方法:
    Object get() 得到call方法的返回值【get方法的执行会导致当前线程阻塞】
        get()方法可能需要执行很久。因为get()方法是为了拿到另一个线程的执行结果。需要等待另一个线程执行结束(call方法执行结束)之后才get方法才可以执行结束。

Object类的wait方法和notify方法(生产者和消费者模式)
1、wait方法和notify方法不是线程对象特有的方法,是任何一个java对象都有的方法。
2、wait方法的作用:
    Onject o = new Object();
    synchronized (o) {  //必须在synchronized代码块中才可以用wait方法
        o.wait();
    }
    表示:让正在o对象上活动的线程进入无期限等待状态,并且会释放掉t线程之前占有的o对象的锁。
    o.wait();方法的调用,会让“当前线程(正在o对象上活动的线程)”进入无期限等待状态。 
    当线程再次醒来的时候,从o.wait();的下一行代码开始执行。
3、notify方法的作用:
    o.notify();方法的调用,会让“当前线程(正在o对象上等待的线程)”从无期限等待状态唤醒过来。
   还有一个notifyAll()方法:
    这个方法用于唤醒o对象上处于等待的所有线程。
4、生产者和消费者模式
    生产线程负责生产,消费线程负责消费。
    生产和消费要达到均衡。
5、notify方法唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,
    并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。    
6、调用wait()、notify()方法时,当前线程必须要成功获得锁(必须写在同步代码块锁中),否则将抛出异常。

反射机制
1、通过java中的反射机制可以操作字节码文件。
    可以读取和修改字节码文件。通过反射机制可以操作代码片段。
2、反射机制的相关类在那个包下?
    java.lang.reflect.*;
3、反射机制相关类有哪些?
    java.lang.Class  代表字节码文件
    java.lang.reflect.Method 代表字节码中的方法字节码
    java.lang.reflect.Constructor 代表字节码中的构造方法字节码
    java.lang.reflect.Field 代表字节码中的属性字节码
4、要操作一个类的字节码,首先需要获取这个类的字节码,怎样获取java.lang.Class实例。
    三种方式:
        1.static Class<?> forName(String className)  返回以className相关联的类对象。“className”必须是完整的类名
        2.java中任何一个对象都有一个方法:Class<?> getClass()  【通过某个类对象去调用getClass方法获取的是某个类的字节码文件】
                例如:String s = "abc"; Class c = s.getClass(); //c就是String类型的字节码文件
                      String s = "def"; Class c2 = s.getClass(); //c2就是String类型的字节码文件
                因为字节码文件装载到JVM的时候,只装载一份。所以“c==c2”返回true      
        3.java语言中任何一种类型,包括基本数据类型,他都有class属性【引用数据类型包括:类、接口、枚举类型、注解】。
            【语法结构:“类型名.class”。例如:Class c1 = int.class;Class c2 = String.class;】
            不可以是“对象.class”。
            注意:"int.class == Integer.class"返回false,说明int的class和Integer的class不是同一个class文件。 
    返回Class对象可以调用newInstance()方法创建一个某类型的对象,底层调用无参构造方法。
5、Class.forName("完整类名");//这行代码执行的时候,发生了类加载,类加载的时候静态代码块执行
6、 1.Thread.currentThread().getContextClassLoader().getResource("类的根路径为起点的资源名称").getPath();
      getContextClassLoader() 获取当前线程的类加载器对象
      getResource("文件名")  获取资源
      以上方式可以动态获取资源的绝对路径,这种方式是通用的【windows和linux】,我们获取到了文件的绝对路径,然后new一个流对象,把绝对路径传进去。
    2.Thread.currentThread().getContextClassLoader().getResourceAsStream("类的根路径为起点的资源名称");
      这种方式直接以Stream流的形式返回。
7、这种方式只适合于读取属性文件:
    java.util包下提供了一个资源绑定器,便于获取属性配置文件的内容。
    使用本方法读取属性配置文件的时候,属性配置文件xxx.properties必须放到类路径下。【扩展名必须是properties,getBundle("路径名");路径名是以类的根路径作为起点,并且文件名不能带有文件扩展名】
    ResourceBundle bundle = ResourceBundle.getBundle("db");
    String s = bundle.getString("name");
    System.out.println(s);
8、关于JDK自带的类加载器:
    1.类加载器是专门负责加载类的命令/工具。ClassLoader
    2.JDK中自带了3个类加载器:
        启动类加载器
        扩展类加载器
        应用类加载器
    3.例如: String s = "abc";
      代码在执行之前,会将所需要类全部加载到JVM当中。
      通过类加载器加载,看到以上代码类加载器会找String.class字节码文件。找到了就加载,那么是怎么加载的呐?
      
      3.1加载过程:
        首先通过“启动类加载器”加载。
            注意:启动类加载器专门加载:D:\Program Files\Java\jdk1.8.0_221\jre\lib\rt.jar
            rt.jar[rt是runtime]中都是JDK中最核心的类库。
            
            如果通过“启动类加载器”加载不到的时候,
            会通过扩展类加载器加载,
            注意:扩展类加载器专门负责加载:D:\Program Files\Java\jdk1.8.0_221\jre\lib\ext
            
            如果扩展类加载器没有加载到,那么会通过应用类加载器加载
            注意:应用类加载器专门负责加载:classpath中的类。
9、java中为了保证类加载的安全,使用了双亲委派机制。
    优先从启动类加载器中加载,这个称为“父”,“父”无法加载到,再从扩展类加载器中加载,这个称为“母”。

反射属性
1、属性相关的方法:
    【Class类的】
    1.Field[] getFields() 获取Class字节码中所有的public修饰的Field
    2.Field[] getDeclaredFields()  获取Class字节码中所有的的Field
    4.String getName()  获取完整类名、属性名、方法名、
    5.String getSimpleName()  获取简单类名、
    6.int getModifiers()  获取类或者字段的修饰符列表
    【Field类的】
    7.Class<?> getType()  返回Filed对象的类型
    【Modifier类的】
    8.static String toString(int mod)  通过 getModifiers 方法的返回值可以获取某个类或Field类对象的修饰符列表
2、怎样通过反射机制访问一个java对象的属性?
    【Class类的】
    1.Field getField(String name)  根据属性名返回一个Field对象,只能获取公开的(public)修饰的属性对象
    2.Field getDeclaredField(String name)  根据属性名返回一个Field对象,可以获取所有属性对象
    【Field类的】
    3.void set(Object obj, Object value)  设置obj对象的当前属性值为value,【如果当前Field对象是私有的,不能设置】
    4.Object get(Object obj)  获取obj对象的当前属性的值,【如果当前的Field对象是私有的不能访问】
    5.void setAccessible(boolean flag); 设置为true表示打破封装。打破封装以后就可以在外面访问到私有属性了

可变长参数
1、public static void m(int... args){};
    其中的"int... args"就是可变长参数
    语法结构是: 类型... 形参名 【注意:一定是三个点】
2、可变长参数要求参数个数是“0~N”个。
3、可变长参数在参数列表中必须在最够一个位置上,并且可变长度参数只能有一个。
4、可变长参数可以当作是一个数组来看待。“args.length”args有length属性。
5、可变长参数可以接收一个数组,要是接收一个数组,就不能再接受其他的数据了,就只能接收一个数组。

反射普通方法
1、关于“反射方法”常用的方法:
    【Class类的】
    1.Method[] getDeclaredMethods()  返回方法对象数组,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 
    2.Method[] getMethods()  只能获取public修饰的方法,包括继承的方法。
    3.Method getMethod(String name, Class<?>... parameterTypes)  通过方法名“name”和参数列表“parameterTypes”获取某个特定的方法(只能获取public修饰方法)
    4.Method getDeclaredMethod(String name,Class<?>... parameterTypes)  通过方法名“name”和参数列表“parameterTypes”获取某个特定的方法
    【Method类的】
    3.Class<?> getReturnType()  获取方法的返回值类型
    4.String getName()  返回方法的名字
    5.int getModifiers()  返回修饰符列表,通常与Modifier类的静态方法toString联合使用
2、通过反射机制怎样调用一个对象的方法?
    使用Method类的invoke方法:
        Object invoke(Object obj, Object... args) //代表调用obj对象的当前方法,参数是可变长参数。
    invoke方法不能调用私有的方法:
    但是可以通过Method类的setAccessible方法打破封装:
        语法结构:“Method类对象引用.setAccessible(true)”

反射构造方法
1、获取构造方法什么的等操作根普通方法一样。【只是不需要方法名了,因为构造方法的方法名都是一样的】
2、调用的时候采用Constructor类的“newInstance(形式参数列表)”方法调用。

获取某个类的父类和父接口
1、【Class类的方法】
    1.Class<? super T> getSuperclass()  获取本类的父类
    2.Class<?>[] getInterfaces()  获取本类的实现的所有的接口

注解
1、注解,或者叫做注释,引文单词是:Annotation
2、注解Annotation是一种引用数据类型。编译之后是生成xxx.class文件。
3、自定义注解的语法格式:
    [修饰符列表] @interface 注解类型名 {
        
    }
   自定义注解属性:
    语法结构:[访问控制修饰符] 数据类型 属性名() default 默认值;【访问控制修饰符写与省略他都是public,不能是其他的】
      注解当中的数据类型可以是:byte、short、int、long、float、double、boolean、char、String、Class、枚举类型。【以及每一种类型的数组形式。】
      默认值必须是“常量”。【使用注解的时候,给属性赋值的时候也必须是常量】
        例如:String name();  //默认值可以省略,这个属性没有默认值,那么使用时必须给属性赋值。
        例如:String name() default "薛英豪"; //指定了默认值,使用注解的时候,如果没有给该属性赋值,那么该属性就是默认值。
    另一种形式:数据类型 变量名 = 变量值;【默认是public static final修饰的,注解中定义的变量都是常量,且使用时不能重新赋值】
   给属性赋值:如果一个注解中有属性,并且这个属性没有默认值,那么使用时必须给属性赋值。
     语法结构:@注解名(属性名1=属性值1,属性名2=属性值2,...);//赋值的时候没有先后顺序,没有必要必须按照定义时的顺序依次赋值。
     一个注解的属性的名字是value的话,在使用的时候,该属性名可以省略。
        例如: String value(); 使用时--> @注解名("属性值")。【仅限于注解中有且仅有一个属性,并且属性名为value的时候,有多个属性的时候,value不能省略】
        例如:String[] tel(); 使用时--> @注解名(tel={"值1","值2","值3"...})。数组中有多个元素的时候,只能由一个大括号“{}”包着,不能“new String[]{"值1"...}”因为这里的数组必须是常量
        例如:String[] tel(); 使用时--> @注解名(tel="值1")。数组中只有一个元素的时候可以省略大括号“{}”。
4、注解使用时的语法格式是:@注解类型名    
   注解用在什么地方:类上、属性上、方法上、接口上、枚举类型上、局部变量前(包括形式参数前)、注解上...等。
5、JDK内置了那些注解?
    Override:
        如果“@Override”出现在方法前,该方法将覆盖或实现在超类型中声明的方法。
        java中的参考的,和运行阶段没有关系。凡是java中的方法带有“@Override”注解,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译报错。
        因为@Override注解被@Target(ElementType.METHOD)修饰,所以@Override注解只能注解方法。
        “@Target(ElementType.METHOD)”叫做“元注解”。
    Deprecated:
        “@Deprecated”修饰的元素表示已过时的。这个注解主要用于向其他程序员告知一个信息,告知已过时,有更好的解决方案。
        @Deprecated标注的元素再被使用的时候,会出现一个“删除线”。【但是再本类被调用的时候不会出现】
6、元注解:是标注注解的注解。    
    常见的元注解有哪些?
        Target:用来标注被标注的注解可以出现在哪些位置上。
            @Target(ElementType.METHOD):表示被标注的注解只能出现在方法上。
        Retention:注解用来标注“被标注的注解”最终保存在哪里。
            @Retention(RetentionPolicy.SOURCE):表示该注解只被保留再java源文件当中。
            @Retention(RetentionPolicy.CLASS):表示该注解被保留在class文件中
            @Retention(RetentionPolicy.RUNTIME):表示该注解被保留在class文件中,并且可以被反射机制读取。
7、反射机制获取注解:
    常用方法:
    【Class类的常用方法】
    1.boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)  //判断当前类型是否被annotationClass标注
    2.A <A extends Annotation> getAnnotation(Class<A> annotationClass)  可以获取标注当前类型的注解annotationClass,如果当前类型没有被annotationClass标注则返回null
    3.通过反射机制可以获取注解对象:
        直接通过注解对象获取注解的属性值,语法结构:
            注解对象引用.属性名(); 
7、注解的作用是什么?
    注解能起到标记作用,以后开发中经常是判断有这个注解怎么怎么样,没有这个注解怎么怎么样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

薛英豪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值