Java多线程和注解

前言

介绍Java实现多线程的机制,以及Java的反射机制。同时还包括Java的注解。


目录

前言

一、Java线程概述

二、Java创建线程的方式

1、Java创建线程的三种方式

2、对线程的一些操作 

三、线程调度

1、Java线程调度的机制和常用方法

2、多线程并发

3、守护线程和定时器

四、Java反射机制

1、反射机制主要的四大类:

2、获取类的三种方式: 

3、反射机制获取字段

4、反射机制获取方法 

6、反射机制获取构造方法和继承的基类及接口

五、Java注解

1、Java注解简介

2、注解的属性

3、反射注解


一、Java线程概述

        启动JVM时就是创建了一个进程,而一个进程又可以创建多个线程。运行Java程序时JVM至少有两个线程,一个是主线程去执行main( )方法,另一个进行垃圾回收。

        Java中,进程之间的内存不共享,线程之间栈区的内存不共享,堆区和方法区的内存共享。  


二、Java创建线程的方式

1、Java创建线程的三种方式

        (1)、编写类继承java.lang.Thread,并重写run( )方法,然后创建对象,调用对象的start( )方法

//创建线程的第一种方式:
//创建一个类继承Thread,重写run()方法,然后创建该对象,调用对象的start()方法启动线程
//注意Thread是java.lang包下的,所以不需要导包

class ThreadTest1 extends Thread{
    @Override
    public void run() {
        System.out.println("新的线程创建了");
    }
}


public class Test01 {
    public static void main(String[] args) {
        //创建线程对象,调用线程的start()方法
        ThreadTest1 t = new ThreadTest1();
        t.start();
        System.out.println("这是主线程");
    }
}

运行一次的输出结果是:

        这是主线程
        新的线程创建了 

因为是不同的线程,所以这两句话的输出的先后顺序不一定。不过由于线程的创建和调用方法需要时间,因此这个例子中main( )方法中的输出会先进行。可以在输出之前加个循环,循环几十万次,然后再输出。

 调用start( )方法启动线程时,会创建一个新的栈空间。不过start( )方法仍然是在当前栈也就是主栈完成的,start( )方法完成,创建好栈空间后,会自动将run( )方法压栈,调用该方法。

      

        (2)、实现java.lang.Runnable接口,也是重写run( )方法,然后创建对象,传入Thread的有参构造方法中,再调用创建的Thread对象的start( )方法

//创建对象的第二种方式:
//实现Runnable接口,重写run()方法。然后创建对象传入Thread的有参构造中,调用Thread对象的start()方法

class ThreadTest2 implements Runnable{

    @Override
    public void run() {
        System.out.println("利用第二种方式创建线程");
    }
}
public class Test02 {
    public static void main(String[] args) {
        ThreadTest2 rt = new ThreadTest2();
        Thread t = new Thread(rt);
        //也是调用start()方法创建新的栈然后调用run()方法
        t.start();
        System.out.println("主线程");
    }

}

        (3)、实现Callable接口,重写call()方法。创建对象传入FutureTask的有参构造中,然后将FutureTask再传入Thread的有参构造,调用start()方法

//创建线程的第三种方式:
//实现Callable接口,重写call()方法。创建对象传入FutureTask的有参构造中,然后将FutureTask再传入Thread的有参构造,调用start()方法
//该接口在java.util包下
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class ThreadTest3 implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("要重写call()方法");
        return "返回值";
    }
}
public class Test03 {
    public static void main(String[] args) {
        ThreadTest3 ct = new ThreadTest3();
        //需要传入到未来任务类FutureTask的有参构造中
        FutureTask ft = new FutureTask(ct);
        //然后将FutureTask再传入Thread的有参构造,调用start()方法
        Thread t = new Thread(ft);
        t.start();
        Object o = null;
        //这种方式创建的线程可以拿到返回值
        //注意,是用传入Thread中的未来任务类FutureTask的get()方法拿到的Callable中call()方法的返回值
        try {
            o = ft.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
        System.out.println(o);//返回值
    }
}

 注意,该类创建线程的方式要导入java.util包。

这种方式创建线程的特点在于可以得到线程的返回值,使用未来任务类FutureTask的get( )方法可以的到Callable中call( )方法的返回值。但是这个方法会造成当前线程阻塞。

2、对线程的一些操作 

        (1)、设置和获取线程的名字

                使用setName( )和getName( )方法修改和得到线程的名字

        (2)、获取当前线程对象

                使用Thread.currentThread( )方法返回一个线程对象

        (3)、让线程睡眠的方法

                用Thread.sleep( )方法让当前线程睡眠 ,用实例方法interraput( )方法终止睡眠。注意,终止睡眠只能是终止别的线程,不能终止当前线程。

        (4)、终止线程的方法

                在线程类中添加一个布尔类型的标记属性


class ThreadTest4 extends Thread{
    //为线程终止设置一个标记
     private boolean run = true;
    @Override
    public void run() {
        for(int i = 1;i<100;i++){
            if(run){
                //让当前线程睡眠的方法
                try {
                    Thread.sleep(100);//传入的是毫秒数,该方法有异常
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("支线程第"+i+"次执行");
            }
            else{
                return;
            }
        }

    }

    public boolean isRun() {
        return run;
    }

    public void setRun(boolean run) {
        this.run = run;
    }
}
public class  Test04{
    public static void main(String[] args) {
        ThreadTest4 t1 = new ThreadTest4();
        t1.start();
        //设置和得到线程名字
        System.out.println(t1.getName());//Thread-0
        t1.setName("线程2");
        System.out.println(t1.getName());//线程2

        //获取当前线程
        Thread t2 = Thread.currentThread();
        System.out.println(t2.getName());//main

        //让当前线程睡眠的方法
        try {
            Thread.sleep(5000);//传入的是毫秒数,该方法有异常
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //终止线程睡眠,注意不能终止当前线程的睡眠
        t1.interrupt();

        //用标记终止线程
        t1.setRun(false);//只执行到45次就停止了

    }

}


三、线程调度

1、Java线程调度的机制和常用方法

         Java的线程调度采用的是抢占式的方式,各线程具有优先级可用线程对象的getPriority( )方法和setPriority( )方法来获取和设置线程执行的优先级。数字越大,代表优先级越高。最高优先级为10,最低为1,默认优先级为5。

静态方法Thread.yield( )方法会暂停当前对象,将当前对象状态由执行转换为就绪,而不是阻塞。并开始其它就绪线程。 

实例方法join( )会将调用该方法的线程对象合并到当前线程,会使原来的线程阻塞。

public class Test05 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 1;i<100;i++){
                    System.out.println("支线程第"+i+"次执行");
                }
            }
        });
        t.start();
        //默认优先级是5
        System.out.println(t.getPriority());//5

        //将t线程合并到当前线程,该方法有异常
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for(int i = 1;i<10;i++){
            System.out.println("主线程第"+i+"次执行");
        }
    }
}

2、多线程并发

 线程并发时并共享数据时,就一定会涉及到数据安全的问题。解决方法就是使用线程同步机制,将涉及到共享数据的内容排队依次执行。

Java中的线程同步机制使用synchronized关键字,它有三种语法格式:

        (1)、线程同步代码块:

                格式:synchronized( 共享的对象,共享对象的属性也可以){

                                 对共享数据的处理

                        }

括号里放共享的数据,共享数据的某个属性值也可以。只要能造成等待,因为每个对象就只有一把锁,找到属性锁也是锁了该对象。

当遇到synchronized同步代码块,就会放弃抢到的时间片去锁池找这把锁,没找到就进入锁池等待,找到了就重新进入就绪状态。

注意,局部变量和常量没有数据安全的问题,只有实例变量和静态变量才有。 

        (2)、synchronized修饰实例方法

                放在返回值类型前面,是对象锁,锁某个对象。但是synchronized修饰方法会扩大锁住的范围,导致效率低,因此不常用。

public synchronized void fun1(){
        
    }

        (3)、synchronized修饰静态方法

                表示寻找类锁,类锁每个类只有一把, 保护静态变量的安全。

public static synchronized void fun2(){
        
    }

Java中的PV操作: 

        简单来说,PV操作就是同步过程中,对共享资源和数据状态进行访问和修改的两个通用手段。Java中的PV操作就是每个对象都有的wait( )和notify( )方法,也就是等待和唤醒。

下面用一个例子来说明如何用线程同步机制和wait( )和notify( )方法,来实现对资源的互斥访问:

import java.util.ArrayList;
import java.util.List;

/**
 * 模拟实现简单的生产者和消费者模式
 * 规定只有一个生产者和一个消费者
 * 有一个共享的缓冲区存放资源,最多放一个
 */

/**
 * 生产线程
 */
class Producer implements Runnable{
    private List sour;

    public List getSour() {
        return sour;
    }

    public void setSour(List sour) {
        this.sour = sour;
    }

    public Producer(List sour) {
        this.sour = sour;
    }

    public Producer() {
    }

    @Override
    public void run() {
       while(true){
           synchronized(sour){
               if(sour.size()>0){
                   try {
                       sour.wait();//wait()方法会进入等待并释放锁,会结束线程同步代码块
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }
               }
               //进入到这说明缓冲区是空的
               sour.add(new Object());
               System.out.println("生产者生产");
               sour.notify();//唤醒其它共享该缓冲区的线程
           }
       }
    }
}

class Customer implements Runnable{
    private List sour;

    public List getSour() {
        return sour;
    }

    public void setSour(List sour) {
        this.sour = sour;
    }

    public Customer() {
    }

    public Customer(List sour) {
        this.sour = sour;
    }

    @Override
    public void run() {
        while(true){
            synchronized(sour){
                if(sour.size()==0){
                    try {
                        sour.wait();//wait()方法会进入等待并释放锁,会结束线程同步代码块
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                //进入到这说明缓冲区是空的
                sour.remove(0);
                System.out.println("消费者消费");
                sour.notify();//唤醒其它共享该缓冲区的线程
            }
        }
    }
}

public class Test06 {
    public static void main(String[] args) {
        List sour = new ArrayList<>();
        Thread pt = new Thread(new Producer(sour));
        Thread ct = new Thread(new Customer(sour));

        pt.start();
        ct.start();
    }
}

 Java中的死锁:

                非常容易出现死锁的原因是,synchronized线程同步代码块的嵌套,例如:

synchronized(o1){
    synchronized(o2){
        
    }
}

当有一个线程先拿到o1的锁,另一个线程先拿到o2的锁时,就会出现死锁。 

3、守护线程和定时器

         Java中的线程分两种:用户线程和守护线程

        我们通常自定义的线程就是用户线程,守护线程是指在后台执行的线程,主要是为用户线程服务。比如垃圾回收器GC。守护线程一般都是死循环,当用户线程结束时,守护线程就结束。自定义守护线程的方法是启动线程前,将线程对象的setDaemon( )方法传true.就可以变成守护线程。


class ProtectedThread implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("守护线程守护中");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}
public class Test07 {
    public static void main(String[] args) {
        Thread t = new Thread(new ProtectedThread());
        //线程启动前调用setDaemon()方法,传入true设置为守护线程
        t.setDaemon(true);
        t.start();
        for(int i = 1;i<31;i++){
            System.out.println("这是主线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //用户线程结束,守护线程结束

    }
}

        定时器: 

                定时器,顾名思义就是特定的时间执行特定的任务。

sleep( )方法是最原始的定时器,Java中的定时器是java.util包下的Timer类,该类有个方法:public void schedule(TimerTask task, Date firstTime, long period),TimerTask task是个接口,也是要将执行的任务写进run( )方法中, Date firstTime是定时器启动的时间, long period是执行任务间隔的时间,单位是毫秒。

 计时器无需用statrt( )开启线程。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class Test08 {
    public static void main(String[] args) {
       Timer timer = new Timer();
       //匿名类实现接口
       TimerTask task = new TimerTask() {
           private int count = 0;
           @Override
           public void run() {
               count++;
               System.out.println("特定的时间,执行特定的程序,程序第"+count+"次执行");
           }
       };
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Date firstTime = null;
        try {
            firstTime = format.parse("2024-04-28 01:51");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        timer.schedule(task,firstTime,3000);
    }
}


四、Java反射机制

1、反射机制主要的四大类:

java.lang.Class:代表整个类的字节码

java.lang.reflect.Field:代表属性的字节码

java.lang.reflect.Constructor:代表构造方法的字节码

java.lang.reflect.Method:代表除构造方法外的方法的字节码

2、获取类的三种方式: 

         要想的到一个类的属性或者方法,必须首先得到这个类。Java中获取类有三种方式:

        (1)、Class.forName(String className )

                 该静态方法会导致类加载,需要传入带包名的完整类名。该方法有ClassNotFoundException异常。

        (2)、任何一个对象都有getClass( )方法,可以得到该对象的类

        (3)、任何一种类型都有class属性,也可以得到该类型

/**
 * 获取类的三种方式
 */
public class Test9 {
    public static void main(String[] args) {
        //1、调用Class.forName()方法,传入完整类名获取,有异常
        Class c1 = null;

        {
            try {
                c1 = Class.forName("java.lang.Thread");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        System.out.println(c1);//class java.lang.Thread

        //2、调用对象的getClass()方法
        String s = "abc";
        Class c2 = s.getClass();
        System.out.println(c2);//class java.lang.String

        //3、使用类型的class属性
        Class c3 = Integer.class;
        System.out.println(c3);//class java.lang.Integer
    }
}

通过反射机制创建对象:

//默认创建的是Object类型对象
String s2 =(String) c2.newInstance();

Class对象有两个方法可用来获取类名:

public String getName( ):获取带包名的完整类名

public String getSimpleName( ):获取简单类名 

获取类路径文件下的绝对路径:

        IDEA下的类路径相当与src下,但是不可以获取Java文件的绝对路径

//获取类路径下的文件的绝对路径
        String path = Thread.currentThread().getContextClassLoader().getResource("datatest.txt").getPath();
        System.out.println(path);///D:/Javatest/out/production/blogtest2/datatest.txt
        //直接得到一个输入流
        InputStream ips = Thread.currentThread().getContextClassLoader().getResourceAsStream("datatest.txt");

资源绑定器: 

        Java的properties文件的读取有两种主要方式:

                (1)、用集合Properties的实例方法load( )传入路径

                (2)、用资源绑定器ResourceBundle

import java.io.*;
import java.util.*;
/**
 * 访问资源配置文件properties的两种主要方式
 */
public class Test10 {
    public static void main(String[] args) {
        //1、集合Propertoes的load()方法
        InputStream fis = null;

        {
            try {
                fis = new FileInputStream("D:\\Javatest\\blogtest\\blogtest2\\test.properties");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        Properties p = new Properties();
        try {
            p.load(fis);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println(p.getProperty("name"));//wangwu

        //2、资源绑定器:ResourceBundle
        //注意不要加后缀properties
        //放在类路径下可只写名称
        ResourceBundle bundle = ResourceBundle.getBundle("test");
        System.out.println(bundle.getString("age"));//20
    }

}

3、反射机制获取字段

        在通过反射机制获取Class对象后,对字段操作的主要的方法有:

1、public Field[ ] getFields( ):通过得到的Class对象获取该类所有的公开的属性

2、public Field[ ] getDeclaredFields( ):通过得到的Class对象获取该类所有的属性,包括私有

3、public Field getDeclaredField(String name ):通过指定的属性名,返回指定属性对象,该方法有异常

4、public Class getType( ):通过得到的Field字段类得到该属性类型对应的Class类

5、public int getModifiers( ):通过得到的Field字段类得到修饰符列表的修饰符代号,然后通过调用工具类Modifier.toString( )方法,将代号传进去,就可以得到修饰符列表对应的字符串

6、public void set( Object obj,Object value):某个Field调用该方法给该属性赋予某值,第一个参数是Class对应的类创建的对象,第二个参数就是该属性要赋予的值,该方法有异常

7、public Object get(Object obj):某个Field调用该方法,返回Class对应的类创建的对象上的该属性的值

8、public String getName( ):Field调用该方法获取该属性名字

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import com.classtest.SimpleClass;

/**
 * 由于包的问题,因此将SimpleClass复制在这,下面就是SimpleClass的复制版
 */
class SimpleClassCopy{
    public String name;
    private int age;

    public SimpleClassCopy() {
    }

    public SimpleClassCopy(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge(){
        return age;
    }

    private void fun(){
        System.out.println("私有方法");
    }
}

public class Test01 {
    public static void main(String[] args) {

        try {
            //先通过反射机制获取类对应的Class对象
            Class sc = Class.forName("com.classtest.SimpleClass");

            //得到该类对应的属性
            //getFields()方法得到所有的公开属性
            Field[] fs1 = sc.getFields();
            for(int i = 0;i<fs1.length;i++){
                System.out.println(fs1[i].getName());//Field的getName()方法可获取该属性名
            }
            //输出结果是一个name

            //调用Class的getDeclaredName()方法得到所有的属性,包括私有属性
            Field[] fs2 = sc.getDeclaredFields();
            for(int i = 0;i<fs2.length;i++){
                System.out.println(fs2[i].getName());//Field的getName()方法可获取该属性名
            }
            //输出结果是name和age

            //Field的getType()方法得到该属性的类型对应的Class类
            Class type = fs2[1].getType();
            System.out.println(type.getSimpleName());//调用Class的getSimpleName()方法获取属性的简单类名
            //结果是int

            //Field的getModifiers()方法得到该属性的修饰符列表代号
            //再调用Modifier.toString()方法得到该属性的修饰符列表
            int k = fs2[1].getModifiers();
            String modifier = Modifier.toString(k);
            System.out.println(modifier);//private

            //调用Class的getDeclaredField()方法得到指定的Field对象
            Field f = sc.getDeclaredField("age");

            //用Field给对应类对象的属性赋值
            //Field的set()方法给某个对象的属性赋值,get()方法获取该属性对应的值
            //但是注意,只能是对公有属性,如果要操作私有属性,要先打破封装
            f.setAccessible(true);//setAccessible(true)打破封装
            SimpleClass c =new SimpleClass();;
            f.set(c,20);
            System.out.println(f.get(c));//20


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

注意: 用Field的set()方法给某个对象的属性赋值,get()方法获取该属性对应的值。但是注意,只能是对公有属性,如果要操作私有属性,要先打破封装。用Field的setAccessible(true)打破封装。

4、反射机制获取方法 

        在通过反射机制获取Class对象后,可以通过它获取该类对应的方法(不包括构造方法)

1、 public Method[ ] getDeclaredMethods( ):通过Class对象获取对应的所有方法,getMethods( )返回所有公开方法

2、public String getName( ):Method的方法,返回这个Method的名称

3、public Class getReturnType( ):Method的方法,返回这个Method的返回值类型

4、public int getModifiers( ):Method的方法,返回这个Method的修饰符列表对应的代号,之后传入Modifier.toString( )中可得到该修饰符列表的字符串

5、public Class[ ] getParameterTypes( ):Method的方法,返回这个Method的参数类型

6、利用反射机制调用方法:

        首先得到方法对象Method:使用Class的getDeclaredMethod(方法名,参数类型Class1,参数类型Class2...),

        然后调用Method的invoke( 该Class类创建的对象,实参1,实参2...),私有方法也要先打破封装

(上面可传入不定个数的参数,是因为使用了可变长参数,格式是:类型... 参数名,如int... args,相当于一个数组,且有length属性。

import java.lang.reflect.*;
import com.classtest.SimpleClass;

/**
 * 由于包的问题,因此将SimpleClass复制在这,下面就是SimpleClass的复制版
 */
class SimpleClassCopy{
    public String name;
    private int age;

    public SimpleClassCopy() {
    }

    public SimpleClassCopy(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge(){
        return age;
    }

    private void fun(int i){
        System.out.println("私有方法");
    }
}

public class Test01 {
    public static void main(String[] args) {

        try {
            //先通过反射机制获取类对应的Class对象
            Class sc = Class.forName("com.classtest.SimpleClass");

            //得到该类对应的方法
            //得到所有的公开方法
            Method[] ms1 = sc.getMethods();
            for(int i = 0;i<ms1.length;i++){
                System.out.println(ms1[i].getName());//获取该方法名
            }
            //输出结果没有fun

            //得到所有的方法,包括私有方法
            Method[] ms2 = sc.getDeclaredMethods();
            for(int i = 0;i<ms2.length;i++){
                System.out.println(ms2[i].getName());
            }
            //输出结果有fun

            //Method的getReturnType()方法得到返回值类型
            Class type = ms2[1].getReturnType();
            System.out.println(type.getSimpleName());//调用Class的getSimpleName()方法获取属性的简单类名
            //结果是void

            //Method的getModifiers()方法得到该属性的修饰符列表代号
            //再调用Modifier.toString()方法得到该属性的修饰符列表
            int k = ms2[1].getModifiers();
            String modifier = Modifier.toString(k);
            System.out.println(modifier);//private


            //利用反射机制调用方法
            Method m = sc.getDeclaredMethod("fun",int.class);
            //私有方法也要先打破封装
            m.setAccessible(true);//setAccessible(true)打破封装
            SimpleClass c =new SimpleClass();
            m.invoke(c,1);


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }  catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }
}

6、反射机制获取构造方法和继承的基类及接口

1、public Constructor[ ] getDeclaredCounstructors( ):利用Class获取对应的所有构造方法

2、public Class[ ] getParameterTypes( ):Constructor的方法,获取该构造方法所有的参数类型

3、public String getName( ):Constructor的方法,获取该构造方法的名称

4、利用反射机制创建对象:

        首先利用getDeclaredConstroctor( )获得指定的构造方法Constroctor,传入要指定构造方法的参数类型。

        然后调用Class的newInstance( )方法,传入该Comstructor,就创建了该Class对应的对象

5、public Class getSupeclass( ):用Class的该方法获取该类的基类

6、public Class[ ] getInterfaces( ):用Class的该方法获取所有的所有实现的接口    

import java.lang.reflect.*;

//由于包的问题,这里把类和接口都复制一份
interface ACopy{

}
class SimpleClass2Copy implements ACopy {
    private String name;
    private int id;

    public SimpleClass2Copy() {
    }

    public SimpleClass2Copy(String name, int id) {
        this.name = name;
        this.id = id;
    }
}

public class Test02 {
    public static void main(String[] args) {
        try {
            //首先获取对应的Classfo
            Class sc = Class.forName("com.classtest.SimpleClass2");

            //获取所有的构造方法
            Constructor[] cs = sc.getDeclaredConstructors();
            for(int i=0;i<cs.length;i++){
                System.out.println(cs[i].getName());//Constructor的getName()方法获取构造方法名
                Class[] c = cs[i].getParameterTypes();
                for(int j=0;j<c.length;j++){
                    System.out.println(c[i].getName());
                }
            }

            //调用指定构造方法创建对象
            Constructor test = sc.getConstructor(String.class,int.class);
            Object obj = sc.newInstance();//Class没有泛型没有指定时,返回对象类型默认Object

            //获取该类的基类和实现接口
            Class f = sc.getSuperclass();
            System.out.println(f.getName());
            Class[] is = sc.getInterfaces();
            for(int i=0;i<is.length;i++){
                System.out.println(is[i].getName());
            }


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}



五、Java注解

1、Java注解简介

        注解(Annatation),就是写在类或者方法,或者变量之前的,给编译器参考的一类像注释一样的东西,如@Override。在Java中,注解也是一种类,编译后也生成字节码文件。

        自定义注解格式:

                [修饰符列表]  @interface  注解名{

                ...

                }

        Java常见注解:

                Deprecated:表示不鼓励这样的元素使用,因为可能有点危险,或已过时

                Override:最常见的一种,表示重写了该方法

                SuppressWarnings:取消指定编译器警告

        Java元注解:

                主要有两个:

                Target:用来指定该注解用在哪儿,可以是类上,可以是方法,可以是属性,也可以是局部变量。

                 Retention: 用来标志该注解最终保存在哪

                        @Retention(RetentionPolitcy.SOURCE):表示保存在Java源文件中

                        @Retention(RetentionPolicy.CLASS):表示保存在class文件中,但不可以被反射机制获取

                        @Retention(RetentionPolicy.RUNTIME):表示保存在class文件中,且可以被反射机制获取

import java.lang.annotation.*;

//元注解Targe用来指定该注解修饰的地点
//ElementType.TYPE表示可以修饰类
//ElementType.FIELD表示可以修饰属性
//ElementType.METHOD表示可以用来修饰方法
//元注解Retention用来指定该注解最终保存在哪
//RetentionPolicy.SOURCE表示保存在源代码中
//RetentionPolicy.CLASS表示保存在class文件中,但是不可以被反射机制获取
//RetentionPolicy.RUNTIME表示保存在class文件中,可以被反射机制获取
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {

}

2、注解的属性

        注解当中可以添加属性,格式是:

                类型 属性名( );

        注意,定义注解的属性后,使用时必须给属性赋值。

        用法:@注解名(属性名=属性值)

        但是,如果在定义注解时,使用default给属性赋默认值,就可以不用再给该属性赋值

        当属性名称是value,可以直接在括号中赋值,不用再写属性名;给数组赋值时,如果只有一个元素可以不用加{ }

        属性的类型可以是基本数据类型、String、枚举和Class以及它们对应的数组。

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
     int id() default 1;
     int value();

     String[] name();
}


//如果在定义注解时,使用default给属性赋默认值,就可以不用再给该属性赋值
//当属性名称是value,可以直接在括号中赋值,不用再写属性名;给数组赋值时,如果只有一个元素可以不用加{ }
@MyAnnotation(value=6,name="ww")
public class Test03 {
    public static void main(String[] args) {

    }
}

3、反射注解

(1)、查看某个类是否有注解

public boolean isAnnotationPresent(注解的Class):使用某个类的Class查看是否有某个注解

(2)、获得注解类对象

public Annotation getAnnotation(注解的Class):获取某个类的某个注解

public Annotation[ ]  getAnnotations( ):获取某个类的所有注解

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
     int id() default 1;
     int value();

     String[] name();
}
import com.classtest.*;

import java.lang.annotation.Annotation;

public class Test03 {
    public static void main(String[] args) {
        try {
            //首先获取SimpleClass的Class对象
            Class sc = Class.forName("com.classtest.SimpleClass");

            //首先判断是否有某个注解
            if(sc.isAnnotationPresent(MyAnnotation.class)){
                //获取某个特定的注解
                MyAnnotation m =(MyAnnotation) sc.getAnnotation(MyAnnotation.class);
                //获取注解的的属性值的格式是:
                //注解对象.属性名()
                //注意只有注解的Retention指定RUNTIME时,才可以获取到
                int id = m.id();
                System.out.println(id);//1
            }

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

 注意:获取属性值时一定要带上括号,但不是使用方法而是属性。


如有错误,希望能批评指正,谢谢。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值