字符流 = 字节流+ 编码表
对象流来进行读写操作 类必须实现这个接口 Serializable 重写toString()方法
io 输出流 OutputStream 输入流 InputStream
记忆:出入 输出对应写入 入取 输入对应读取
输出是out 写入是write 输入时int 读取是read
记图片 intput 输入流 是磁盘数据给内存 output 输出流 内存数据传给磁盘
字节流
FileOutputStream fos = new FileOutputStream(“1.txt”); fos.write()输出写入
FileInputStream fis = new FileInputStream(“1.txt”); fis.read()输入读取
字节缓冲高效流 所有的高效流都是 加一个Buffered前缀词
序列化: 将数据结构或对象转换成二进制字节流的过程
反序列化:将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程
使用对象流就行读取,本质还是字节流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“18.txt”));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“18.txt”));
用集合存储对象,直接用集合进行序列化
Properties 就是hashtable(线程安全)的一个子类 可以把Properties 当做是一个map对象来看待
程序:一个固定逻辑与数据的集合 就称为程序
进程:运行在内存中的程序 就是进程
线程:线程是进程中执行运算的最小单位,也称为轻量级进程,单线程 执行一条通向cpu 的执行的路径 就是单线程
三种创建方式
1.自定义类继承Thread,重写run方法
public class MyThread extends Thread
MyThread th = new MyThread();
2.自定义类连接Runnable接口,
public class MyRunnable implements Runnable
MyRunnable runnable = new MyRunnable();
Thread th =new Thread(runnable);
3.自定义类连接Callable接口,有返回值
public class MyCallable implements Callable
//实例化MyCallable对象
MyCallable call = new MyCallable();
//实例化任务对象
FutureTask ft = new FutureTask<>(call);
//实例化Thread对象
new Thread(ft).start();
//调用任务对象的get方法 获取其返回值
Integer count = ft.get();
start和run 区别:start 新建一个栈,run 不会
线程的基本状态,大学计算机原理课程角度
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
1)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
记忆:一个三角形 加外延两点 即 可运行 正在运行 阻塞 新建和死亡
线程的基本状态,Java角度
新建(new Therad()),运行(抢到cpu),阻塞(未抢到cpu),死亡(run方法结束,stop方法),计时等待(wait(时间)),无限等待(wait())
线程中断与阻塞方法区别
中断和阻塞是两种不同的状态
中断
interrupt();改变线程的中断状态,中断线程
如果线程是一般逻辑,没有使用锁synchronized,lock,只能短暂停止,还是会运行完线程,
如果线程调用了wait,join,sleep方法,在执行interrupt(),就会抛出InterruptedException异常,执行wait,join,sleep内部自带的方法中的catch方法,这个异常也是wait,join,sleep抛出并捕获的,你可以修改catch中的语句,可以让线程活过来,转回正常的逻辑,或自定义逻辑,或return结束方法停止线程
阻塞
Thread.sleep(1000); 静态方法,类名调用,正在执行的线程休眠(暂停执行) 进入休眠等待状态,不释放锁资源
Thread.yield();yield() 是静态方法 类名调用 暂停当前正在运行的线程 执行其他线程在执行该线程 进入阻塞状态
join() 表示等待该线程执行完毕之后 其它线程才能执行(方法调用必须是在开启线程之后调用才有效果) 比设置优先级更稳定的效果
wait(); Object方法,所以线程也能用,无线等待(需要被唤醒)加时间参数等同于sleep 进入休眠等待状态 释放锁资源
notify() 唤醒一个在此对象监视器上等待的一个线程
notifyAll()唤醒所有在此对象监视器上等待的一个线程
stop(); 停止线程 对象名调用 方法已过时了因为不安全 但还是有效果 进入死亡状态 一般不使用,建议用wait();
因为线程是抢占式调度,为了避免发生购买同样的票,或者购买到不存在的票问题,需要线程同步进行,所以用锁
synchronized (锁的对象){
出现问题的代码
}
(1)锁的对象可以是任意的对象,但要保证多线程数据安全,应该锁多线程共享的数据
(2)所有的线程都必须使用同一个锁的对象
synchronized也可以加在方法上,在方法返回值类型前面
第二种锁方式
lock,unlock
public class MyMovieRunnable implements Runnable {
private int count =100;
Lock lock =new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try {
Thread.sleep(500);
if(count>0){
System.out.println(Thread.currentThread().getName()+"买了第"+count+"张票");
//需要递减
count--;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
共享资源有一双鞋(左鞋和右鞋)且只有拿到一双鞋才能出门(运行结束),A拿到左鞋,B拿到右鞋,A B 都霸占锁资源都不释放 所以出现死锁 都不能出门
如果单一线程先抢到两只鞋就能运行完,a占了b锁资源,b占了a锁资源才会运行不完
造成死锁的原因及对应锁的属性
1.⼀个资源每次只能被⼀个线程使⽤ -> 多个线程不能同时使用同一个资源
2.⼀个线程在阻塞等待某个资源时,不释放已占有资源 -> 不释放已占有资源
3.⼀个线程已经获得的资源,在未使⽤完之前,不能被强⾏剥夺 -> 资源未使⽤完之前,不能被强⾏剥夺
4.若⼲线程形成头尾相接的循环等待资源关系
避免死锁方法(前3个条件对应锁的属性无法改变,只能打破第4个条件,不出现循环等待锁的关系):
1.要注意加锁顺序,保证每个线程按同样的顺序进⾏加锁
加锁的顺序要一致,不要出现锁1嵌套锁2,锁2又嵌套锁1的情况
2.要注意加锁时限,可以针对所设置⼀个超时时间(常用)
Lock锁里面有一个tryLock的方法,可以设置一个超时时间,可以利用这个方法做一个获取锁时间上的限制
多个类(称为线程一类,线程二类) 需要一个共有的变量,即将这个变量封装为一个类(称为属性类)的属性,注意属性类的属性修饰符为public公共的,在测试类中new 这个属性类实例化为对象,然后将此对象作为参数传入需要的多个类(线程一,线程二)中,就可以实现共享对象的属性即变量,且多个类(线程一,线程二) 需要定义此对象(属性类)为私有变量,并提供有参构造
共享资源类
public class BaoZi {
public String pi;
public String xian;
//定义一个标记表示是否存在包子 false 没有包子 ture 有包子
boolean flag = false;
//boolean 默认值为false
}
生产者类
public class ProducterThread extends Thread{
private BaoZi baoZi;
public ProducterThread(BaoZi baoZi){
this.baoZi = baoZi;
}
@Override
public void run() {
//一直生产包子
while (true){
//同步代码块 //互斥锁
synchronized(baoZi){
//已存在包子 等待消费 不需要在生产
if(baoZi.flag){
try {
baoZi.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//不存在包子 开始生产包
System.out.println("我生产了一个包子");
//将标记值设置为ture
baoZi.flag = true;
//通知消费者去消费
baoZi.notify();
}
}
}
}
测试类
BaoZi baoZi =new BaoZi();
//开启生产者的线程
new ProducterThread(baoZi).start();
实例化静态内部类对象的模板是: 外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()
实例化非静态内部类对象的模板是:外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()
BreadShop.Consumer consumer =bs.new Consumer(1);
线程池:用于存储大量线程的的容器 就称为叫做线程池
线程池的优势:
线程池会初始化许多线程存放在线程池中这个容量可以自行设置,
每次使用完线程之后都会将线程归还线程池线程池将其设置为初始状态,避免反复创建与销毁线程
线程都由线程池来进行统一的管理,方便获取线程,便于维护
如果遇到破坏性比较大的任务,线程停止运行,并归还给线程池,线程池会分配新的线程来执行这个任务
一般开发过程都是封装好了的线程池工具类,很少使用自定义线程池,只要从线程池工具类获取一个线程就可以了,也很少直接使用线程开发,都是间接使用,懂得原理就好
ThreadPoolExecutor是JDK中的线程池实现,其他创建线程池的方法最终都会导向这个类,
此类构造方法共有7个参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。
一、corePoolSize 核心线程数,即线程池最小线程数量,低于就直接新建一个线程执行,核心线程创建后不会被回收(注意:设置allowCoreThreadTimeout=true后,空闲的核心线程超过存活时间也会被回收)。
二、maximumPoolSize 最大线程数,核心线程数满,工作队列满,小于最大线程数就直接新建线程执行,大于就执行决绝策略
三、keepAliveTime 空闲线程存活时间
当一个可被回收的线程的空闲时间大于keepAliveTime,就会被回收。
可被回收的线程:1.设置allowCoreThreadTimeout=true的核心线程。2.大于核心线程数的线程(非核心线程)。
四、unit 空闲线程存活时间单位
keepAliveTime的计量单位 如:TimeUnit.SECONDS
五、workQueue 工作队列
当提交的任务数超过核心线程数大小后,再提交的任务就存放在工作队列,任务调度时再从队列中取出任务。
JDK中提供了5种工作队列:
线程池执行流程
线程池原文地址
枚举的选项实例 BLUE,YELLOW,RED都可以理解是Color的子类对象(实现类) 且每个枚举实例都是static final类型的,也就表明只能被实例化一次,且每次访问都是在实例化,因为final修饰地址值不变,对象是相等的
枚举类有且默认是私有的构造方法,可以有抽象方法,但是每一个枚举项都必须实现其抽象方法
public enum Color {
//枚举的选项 BLUE,YELLOW,RED都可以理解是Color的子类对象
BLUE{
@Override
public String getColor() {
return "蓝色";
}
},YELLOW{
@Override
public String getColor() {
return "黄色";
}
},RED{
@Override
public String getColor() {
return "红色";
}
};
private String name;
private Color(){
}
public abstract String getColor();
}
定义:有且仅实例化一个对象 保证对象的唯一 即实例化两次对象,第一次对象 == 第二次对象 为true
懒汉双重锁式
理解: 在多个线程中如果是单个锁,在线程1进入getInstance()方法通过if(utils2 ==null)是被其他线程抢占,就会多次执行utils2=new DateUtils2(); 语句,导致utils2地址冲突
而双重锁 在线程1进入getInstance()方法通过if(utils2 ==null)还要获取锁对象,如果获取了synchronized (DateUtils2.class)锁对象,此时被其他线程抢占,其执行到synchronized (DateUtils2.class)时,因为锁对象被线程1占取,进入阻塞状态,只有等线程1执行完所有语句(创建了utils2对象),在释放锁对象其他线程才能进入运行状态,当其他线程进行运行状态,此时线程1已创建了utils2对象,if(utils2 ==null)结果为false
从阻塞状态进入运行状态,其代码接着未运行的代码继续运行(每个线程都有单独的栈运行方法,阻塞只是栈没获取到cpu,放在阻塞区,转为就绪状态时无需新建栈重新运行),所以如果变量发生改变就可能报错,通过锁来保证安全,
记得属性加volatile 关键字
为什么要使用 volatile 关键字呢
不是原子性操作
1.分配内存空间,2.执行构造方法,3.把对象指向这个空间
指令重排可能会发生 加上volatile关闭指令重排
public class Singleton {
//私有静态成员变量,加上了volatile关键字确保可见性 变量发生改变之后 所有线程获取到改变后的值
private static volatile Singleton instance;
//私有构造方法
private Singleton() { }
//公有静态访问方法
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
//利用到volatile关键字的可见性,再次判断空,不是原子操作,加上volatile关闭指令重排
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
//测试类获取
Singleton instance = Singleton.getInstance();
枚举式
public enum EnumSingle {
INSTANCE;
public static EnumSingle getInstance() {
return INSTANCE;
}
}
//测试类获取
EnumSingle instance1 = EnumSingle.INSTANCE;
静态内部类(IoDH)式
public class Singleton {
private Singleton() {
}
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
}
//测试类获取
Singleton instance= Singleton.getInstance();
IP地址:是在网络连接中 每一台电脑的唯一标识
ip地址的分类 IP4 IP6
IP4 是由四个字节的二进制组成 由于不好记忆 IP4 改成以十进制 来进行表示 每一个字节是以.来进行分割
192.168.1.33 子网 10.1
IP6 ip6是由16进制来组成的
查看IP地址
第一步:windons+r
第二步:cmd
第三步 指定 ipconfig
查看服务器是否可以访问 ping 服务器的IP地址
ping + ip地址
端口号:每一个进程的唯一的标识 每一个执行的程序的唯一的标识 这个标识可以自行设置 也可以
由系统进行分配
端口号的范围0-65535 必须设置在这个范围之内 0-1024 这个范围之内的端口号 不进行使用 一
般是系统使用
常见的端口号 tomcat:8080 mysql 3306 orcale 1207
协议: 数据再进行交互的时候遵循的规范 必须按照这个规范来进行传输 客户端与服务才能进行有效
的交互
常见的协议
TCP 面向连接的协议 三次握手 之后 表示连接成功 特点: 传输数据稳定安全 效率低一点
UDP 面向无连接的协议 特点: 传输数据安全性低 效率高 丢失数据 丢包
HTTP 一次性的协议 表示客户端与服务器连接成功 交互完成之后 就断开的了 一般用于web端
明文进行传输 数据没有进行任何加密 上线的项目都不能直接使用http
协议进行数据交互
HTTPS= http+ssl证书 ssl 证书 保证对传输的数据进行加密 保证数据的安全性
TCP连接三次握手
必须是函数式接口 也就是接口中只能有一个抽象方法 默认方法与其它方法都是可以存在的
作用:简化匿名内部类(匿名接口)的写法 匿名接口 即相当于实例化接口的实现类,需重写接口的方法
语法:
(参数) ->{方法体}
()==>接口中方法的参数
-> 是Lambda 表示特有的语法 指向方法体
{} ==> 就是方法体 执行的操作
Lambda表达式简化方法
第一种情况 如果括号中只有一个参数 可以省略括号 和数据类型
第二种情况 方法体只有一句话 大括号和分号可以省略
第三种情况 方法体只有一句话 就是retrun 可以省略大括号 分号 retrun
public class Test07 {
public static void main(String[] args) {
show(5,(int num)->{
System.out.println(num);
});
show(5,num -> {
System.out.println(num);
});
show(5,num -> System.out.println(num));
//Arrays.sort(students,(Student o1, Student o2)-> o1.getAge()-o2.getAge());
}
public static void show(int num,Inner02 inner){
inner.showInfo(num);
}
}
Stream流 主要用于对集合数据进行过滤
list.stream() 将集合转为Stream流 在调用filter()方法结合Lambda表达式进行过滤筛选
Stream筛选只要三个用limit(3) 不要前三个要后面所有的skip(3) 转类型 map((原来的数据类型 形参名)->{ retrun 将原来数据类型转为其他类型的代码语句}) 例 map((String n)->{return new Person(n); 极简省略 map(n->new Person(n));
list 类型筛选用subList(i,j) 包头不包尾 同String 的subString用法
list.stream() 将集合转为Stream流的结果是Stream 流,转为list 需要使用.collect(Collectors.toList());方法
filter() 方法是筛选出符合条件的结果,如果要去除掉掉这些结果,需要转为list2,再用原先list1.removeAll(list2);
removeAll 方法是返回布尔变量,用了之后list1会删除list2 相同的结果,输出list1 就可以了
public class Work {
public static void main(String[] args) {
List<String> list1 =new ArrayList<>();
List<String> list2 =new ArrayList<>();
list1.add("宫本武藏");
list1.add("苏一二");
list1.add("宋一明");
list1.add("石头人");
list1.add("时一二");
list1.add("李耳");
list1.add("庄子");
list1.add("洪七公");
list2.add("帕瓦一二");
list2.add("张三丰");
list2.add("赵薇薇");
list2.add("张一二");
list2.add("波尔至济南铁一二");
list2.add("张一二");
list2.add("张翠花");
Stream<String> stre1 = list1.stream().filter((String s) -> s.length() == 3).limit(3);
/*.forEach(s -> System.out.println(s));*/
Stream<String> stre2 = list2.stream().filter((String s) -> s.contains("张")).skip(2);
/*.forEach(s -> System.out.println(s));*/
/*Stream.concat(stre1,stre2).map((String s)->{return new Person(s);})
.forEach((Person p)-> System.out.println(p));*/
List<Person> personList = new ArrayList<>();
Stream.concat(stre1,stre2).map((String n)->{
return new Person(n);
}).forEach((Person p)->{
personList.add(p);
});
System.out.println(personList);
//至臻极简版
list1.stream().filter(s-> s.length() == 3).limit(3)
.forEach(s -> System.out.println(s));
list2.stream().filter(s-> s.contains("张")).skip(2)
.forEach(s -> System.out.println(s));
list1.stream().filter(s-> s.length() == 3).limit(3).map(s -> new Person(s))
.forEach(s -> System.out.println(s));
}
}
Stream 转为list
List<User> users =getUsers().stream().filter(p -> p.getAge()>20).collect(Collectors.toList());
getUsers().removeAll(users);
将类的各个组成部分 分装成其他的对象(Class对象)就是反射机制,反射是在编译后操作.class文件
泛型是在编译期间起作用的,在编译后的.class文件中是没有泛型的。所有泛型,本质都是通过Object处理的。反射是在编译后操作.class文件,所以可以越过泛型
理解:泛型通过object在javac编译时自动编译为对应类型,编译完后生成.class文件,这时反射在操作.class文件,所以可以忽略泛型
反射小总结
1.用反射class创建对象,实质是用无参构造,所以该类需要有无参构造才能用此方式 Object p = cla.newInstance();
2.得到类对象就能得到 构造方法getConstructor 成员方法getMethod 成员变量getFiled
3.加s 即得到多个即所有 加Declared 即能得到私有 加方法参数即得到指定方法
4.单数返回一个对象类型,复数返回一个对象类型数组
5.Modifier.toString(类或方法或属性.getModifiers()) 得到相应的修饰符 public private
6.构造方法或成员方法.getParameterTypes(); 得到方法的参数类型数组
7.m.getReturnType().getSimpleName() 方法返回值类型
8.f.getType().getSimpleName()属性类型
9. m.getName() 方法名 f.getName() 属性名 cla.getSimpleName() 类名
10.setAccessible(true); 暴力解除私有 之后不安全 其他类也能访问私有属性
11.属性.set(实例化的对象名,赋值的属性值) get(实例化的对象名) 赋值取值
12.方法.invoke(实例化的对象名,该方法需传入的实参1,实参2) 得到方法的返回值,void类型返回null
13.构造方法.newInstance();无参构造不传参数,有参构造传需要的参数
14.method.getParameters() parameters[i].getName() 得到默认的形参命名 arg0
package day25.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo01 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class<?> cla = Class.forName("day25.demo.Person");
//获取包名加类名
System.out.println(cla.getName());
//获取类名
System.out.println(cla.getSimpleName());
//用反射class创建对象,实质是用无参构造,所以该类需要有无参构造才能用此方式
Object p = cla.newInstance();
//获取所有的方法
Constructor<?>[] declaredConstructors = cla.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
//declaredConstructor.setAccessible(true);
System.out.println(declaredConstructor);
}
//获取单独指定的构造方法 无参构造并实例化
Constructor<?> con = cla.getConstructor();
Object o = con.newInstance();
Person p1 = (Person)o;
//获取有参构造并实例化
Constructor<?> con1 = cla.getConstructor(String.class, int.class);
Person p2 = (Person) con1.newInstance("张三", 18);
//获取所有的方法
Method[] declaredMethods = cla.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
//获取指定的方法 第一个参数 方法的名称 第二个参数 方法参数的类型的.class
// 调用 调用方法invoke (Object obj,Object ...args)
// 第一个时调用方法的对象 第二个参数时多需要的具体的值 返回值就是参数的返回值
Method setName = cla.getMethod("setName", String.class);
setName.invoke(p2,"换成李四啦");
System.out.println(p2);
//其值是该方法的返回值,为void 返回null 有具体的返回值就是该返回值
System.out.println(setName.invoke(p2,"换成李四啦"));
//获取所有的属性
Field[] declaredFields = cla.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
}
//获取指定的属性
Field age = cla.getDeclaredField("age");
//因为是私有的 需要先破解 打破封装
age.setAccessible(true);
//获取属性
System.out.println(age.get(p2));
//设置属性
age.set(p2,20);
System.out.println(age.get(p2));
}
}
1.注释:用于对代码的进行解释 主要是给程序员看的
2.注解:用于对代码进行说明 主要是用于JVM(计算机)看
3.定义:
注解(Annotation),也叫元数据,一种代码级别的说明,本质是一个接口。它是jdk1.5及以后版本引入的一个特性,与类、接口、枚举 是同一个层次。
它可以声名在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释
1.元注解格式
public @interface 注解的名称 {}
2.注解本质:其实是一个接口
3.属性:表示接口中所有的成员 静态常量(不关注) 抽象方法(关注)注解的属性就是接口的抽象方法,只是在注解里被称为属性
属性的返回值类型
1.基本数据类型 四类八种
2.字符串类型
3.枚举类型
4.注解类型
5.以上类型的数组
代码
public @interface MyAnno{}
等同于public interface MyAnno extends java.lang.annotation.Annotation{}
所以注解本质是接口
属性赋值
1.使用注解的时候 必须给所有的属性进行赋值
2.如果使用注解的时候 不想给其赋值 必须设置默认值 default 来设置默认值
3.如果是给注解的中属性的数组进行赋值 多个值可以使用大括号 一个值可以省略大括号
4.如果属性值只有一个且属性名为value 可以不写属性名 直接进行赋值 例SuppressWarnings(value=“all”) 简写为SuppressWarnings(“all”)
1.用于来描述注解的注解就称为是元注解
2.常用的元注解四个
@Target 主要用于来表示注解作用的位置
ElementType.TYPE 表示注解可以在类上使用
ElementType.FIELD 表示注解在属性上使用
ElementType.METHOD 表示注解可以在方法使用
@Retention 主要用于注解是否可以被识别
RetentionPolicy.RUNTIME ==>在运行期间 生成对应的class文件 并且jvm 可以识别这个注解 开发的大部分场景都是使用这个
例子
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
String name();
int age();
}
反射获取注解
@ProAnno(className = "day26.Student",methodName = "show")
public class Test02 {
public static void main(String[] args) {
//获取当前类的对象
Class<Test02> cla = Test02.class;
//获取注解的对象
//不能省略强转
ProAnno proAnno = (ProAnno) cla.getAnnotation(ProAnno.class);
//获取包名的名称
String className = proAnno.className();
//获取方法的名称
String methodName =proAnno.methodName();
System.out.println(className);
System.out.println(methodName);
//获取这个类Class对象
try {
Class<?> c = Class.forName(className);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method m = c.getMethod(methodName);
m.invoke(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}