多线程
并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行
实现方式
继承Thread类
//MyThread.java
public class MyThread extends Thread{
}
//test.java
public static void main(String[] args){
//多线程的第一种启动方式:
// 1.自己定义一个类继承Thread
// 2.重写run方法
// 3。创建子类的对象,并启动线程
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
tl.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
实现Runnable接口
//MyRun.java
public class MyRun implements Runnable{
@Override
public void run() {
//书写线程要执行的代码
for (int i = 0; i < 10; i++){
//获取到当前线程的对象
Thread t = Thread.currentThread();
System.out.println(t.getName() + "HelloWorld!");
}
}
}
//test.java
// 多线程的第二种启动方式:
// 1.自己定义一个类实现Runnable接口
// 2.重写里面的run方法
// 3.创建自己的类的对象
// 4.创建一个Thread类的对象,并开启线程
//创建MyRun的对象
//表示多线程要执行的任务
MyRun mr = new MyRun()
//创建线程对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
//给线程设置名字
t1.setName("线程1");
t2.setName("线程2"):
//开启线程
t1.start();
t2.start();
利用Callable接口和Future接口
/*
多线程的第三种实现方式:
特点:可以获取到多线程运行的结果
1.创建一个类MyCallable实现Callable接口
2.重号ca11 (是有返回值的,表示多线程运行的结果)
3.创建MyCallable的对象 (表示多线程要执行的任务)
4.创建FutureTask的对象 (作用管理多线程运行的结果)
5.创建Thread类的对象,并启动(表示线程)
*/
//创建MyCallable的对象 (表示多线程要执行的任务)
MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer> ft = new FutureTask<>(mc);
//创建线程的对象
Thread t1 = new Thread(ft);
//启动线程
tl.start();
//获取多线程运行的结果
Integer result = ft.get();
System.out.println(result);
常见的成员方法
基础方法
/*
String getName() 返回此线程的名称
void setName(String name) 设置线程的名字(构造方法也可以设置)
细节:
1、如果我们没有给线程设置名字,线程也是有默认的名字的格式: Thread-X(X字号,从0开始的)
*/
//MyThread.java
public class MyThread extends Thread{
public MyThread(){
}
//重写
public MyThread(String name){
super(name);
}
@Override
public void run(){
for (int i = 0; i < 1; i++){
System.out .printIn(getName() + "@" + i);
}
}
}
//test.java
MyThread t1 = new MyThread("飞");
MyThread t2 = new MyThread("克");
t1.start();
t2.start();
/*
static Thread currentThread()
细节:
获取当前线程的对象。
当JVM虚拟机启动之后,会自动的启动多条线程。
其中有一条线程就叫做main线程。
他的作用就是去调用main方法,并执行里面的代码。
在以前,我们写的所有的代码,其实都是运行在main线程当中。
*/
、
// static void sleep(long time)
// 让线程休眠指定的时问,单位为毫秒
Thread t = Thread.currentThread();
String name = t.getName();
System.out.println(name);//main
System.out.println("11111111111");
Thread.sleep(5000);
System.out.println("22222222222");
//run()方法中,每隔一秒进行线程
try{
Thread.sLeep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
关于优先级的方法:
// setPriority(int newPriority) 设置线程的优先级,默认值5
// final int getPriority() 获取线程的优先级
MyRunnable mr = new MyRunnable();//创建线程对象
Thread t1 = new Thread(mr,"机");
Thread t2 = new Thread(mr,"克");
t1.setPriority(1);
t2.setPriority(10);//只是抢占的概率高
tl.start();
t2.start();
//守护线程
// final void setDaemon(boolean on) 设置为守护线程
/*细节:
当其他的非守护线程执行完中之后,守护线程会陆续结束
迪俗易懂:
当女神线程结束了,那么备胎也没有存在的必要了
*/
MyThread1 t1 = new MyThread1();MyThread2 t2 = new MyThread2();
tl.setName("女神");
t2.setName("备胎");
//把第二个线程设置为守护线程
t2.setDaemon(true);
tl.start();
t2.start();
//女神结束后,备胎陆陆续续的结束
//应用场景:聊天和发送软件
//出让线程/礼让线程
// public static void yield()
// run(),输出后
//尽可能的均匀
Thread.yield();
//插入线程
// public final void join()
MyThread2 t2 = new MyThread2();
tl.setName("土豆");
t.join();
//插入到当前线程之前,全部执行完毕再执行当前线程
for (int i = 0; i < 10; i++){
System.out.println("main线程" + i);
}
线程的生命周期
线程的安全问题
//表示这个类所有的对象,都共享ticket数据
static int ticket = 0;//0 ~ 99
//同一张或者超出范围
//线程执行代码时可能来不及执行,执行权被其他线程抢走
//有随机性
同步代码块
把操作共享数据的代码锁起来
格式:
synchronized(锁){
操作共享数据的代码
}
特点1: 锁默认打开,有一个线程进去了,锁自动关闭
特点2: 里面的代码全部执行完毕,线程出来,锁自动打开
//细节:
//1. synchronized在循环里面
//2. 锁对象一定唯一
同步方法
就是把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数) {...}
特点1: 同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定
StringBuilder 用于多线程不安全
使用StringBuffer
lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
-
ReentrantLock构造方法
方法名 说明 ReentrantLock() 创建一个ReentrantLock的实例 -
加锁解锁方法
方法名 说明 void lock() 获得锁 void unlock() 释放锁
public class Ticket implements Runnable {
//票的数量
private int ticket = 100;
private Object obj = new Object();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//synchronized (obj){//多个线程必须使用同一把锁.
try {
lock.lock();
if (ticket <= 0) {
//卖完了
break;
} else {
Thread.sleep(100);
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
//一定会执行
}
// }
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
死锁
出现了锁的嵌套
生产者和消费者(等待唤醒机制)
生产者消费者模式是一个十分经典的多线程协作的模式
代码示例:
public class Desk {
//定义一个标记
//true 就表示桌子上有汉堡包的,此时允许吃货执行
//false 就表示桌子上没有汉堡包的,此时允许厨师执行
//public static boolean flag = false;
private boolean flag;
//汉堡包的总数量
//public static int count = 10;
//以后我们在使用这种必须有默认值的变量
// private int count = 10;
private int count;
//锁对象
//public static final Object lock = new Object();
private final Object lock = new Object();
public Desk() {
this(false,10); // 在空参内部调用带参,对成员变量进行赋值,之后就可以直接使用成员变量了
}
public Desk(boolean flag, int count) {
this.flag = flag;
this.count = count;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Object getLock() {
return lock;
}
@Override
public String toString() {
return "Desk{" +
"flag=" + flag +
", count=" + count +
", lock=" + lock +
'}';
}
}
public class Cooker extends Thread {
private Desk desk;
public Cooker(Desk desk) {
this.desk = desk;
}
// 生产者步骤:
// 1,判断桌子上是否有汉堡包
// 如果有就等待,如果没有才生产。
// 2,把汉堡包放在桌子上。
// 3,叫醒等待的消费者开吃。
@Override
public void run() {
while(true){
synchronized (desk.getLock()){
if(desk.getCount() == 0){
break;
}else{
//System.out.println("验证一下是否执行了");
if(!desk.isFlag()){
//生产
System.out.println("厨师正在生产汉堡包");
desk.setFlag(true);
desk.getLock().notifyAll();
}else{
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Foodie extends Thread {
private Desk desk;
public Foodie(Desk desk) {
this.desk = desk;
}
@Override
public void run() {
// 1,判断桌子上是否有汉堡包。
// 2,如果没有就等待。
// 3,如果有就开吃
// 4,吃完之后,桌子上的汉堡包就没有了
// 叫醒等待的生产者继续生产
// 汉堡包的总数量减一
//套路:
//1. while(true)死循环
//2. synchronized 锁,锁对象要唯一
//3. 判断,共享数据是否结束. 结束
//4. 判断,共享数据是否结束. 没有结束
while(true){
synchronized (desk.getLock()){
if(desk.getCount() == 0){
break;
}else{
//System.out.println("验证一下是否执行了");
if(desk.isFlag()){
//有
System.out.println("吃货在吃汉堡包");
desk.setFlag(false);
desk.getLock().notifyAll();
desk.setCount(desk.getCount() - 1);
}else{
//没有就等待
//使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法.
try {
desk.getLock().wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
/*消费者步骤:
1,判断桌子上是否有汉堡包。
2,如果没有就等待。
3,如果有就开吃
4,吃完之后,桌子上的汉堡包就没有了
叫醒等待的生产者继续生产
汉堡包的总数量减一*/
/*生产者步骤:
1,判断桌子上是否有汉堡包
如果有就等待,如果没有才生产。
2,把汉堡包放在桌子上。
3,叫醒等待的消费者开吃。*/
Desk desk = new Desk();
Foodie f = new Foodie(desk);
Cooker c = new Cooker(desk);
f.start();
c.start();
}
}
阻塞队列
public class Cooker extends Thread {
private ArrayBlockingQueue<String> bd;
public Cooker(ArrayBlockingQueue<String> bd) {
this.bd = bd;
}
// 生产者步骤:
// 1,判断桌子上是否有汉堡包
// 如果有就等待,如果没有才生产。
// 2,把汉堡包放在桌子上。
// 3,叫醒等待的消费者开吃。
@Override
public void run() {
while (true) {
try {
bd.put("汉堡包");
System.out.println("厨师放入一个汉堡包");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Foodie extends Thread {
private ArrayBlockingQueue<String> bd;
public Foodie(ArrayBlockingQueue<String> bd) {
this.bd = bd;
}
@Override
public void run() {
// 1,判断桌子上是否有汉堡包。
// 2,如果没有就等待。
// 3,如果有就开吃
// 4,吃完之后,桌子上的汉堡包就没有了
// 叫醒等待的生产者继续生产
// 汉堡包的总数量减一
//套路:
//1. while(true)死循环
//2. synchronized 锁,锁对象要唯一
//3. 判断,共享数据是否结束. 结束
//4. 判断,共享数据是否结束. 没有结束
while (true) {
try {
String take = bd.take();
System.out.println("吃货将" + take + "拿出来吃了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo {
public static void main(String[] args) {
ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);
Foodie f = new Foodie(bd);
Cooker c = new Cooker(bd);
f.start();
c.start();
}
}
线程的状态
七个练习
线程池
原理:
- 创建一个池子,池子中是空的
- 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
- 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
//1.获取线程池对象
ExecutorService pool1 = Executors.newCachedThreadPool();
//2.提交任务
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
pool1.submit(new MyRunnable());
//3.销毁线程池
pool1.shutdown();
ExecutorService pool1 = Executors.newFixedThreadPool(3);
自定义线程池
/**
* 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ;
// 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常
for(int x = 0 ; x < 5 ; x++) {
threadPoolExecutor.submit(() -> {
System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
});
}
线程池合适大小
最大并行数:
//向Java虚拟机返回可用处理器的数目
int count = Runtime.getRuntime().availableProcessors();
额外扩展
资料中有
网络编程
在网络通信协议下,不同计算机上运行的程序,进行的数据传输
C/S: Client/Server
B/S: Browser/Server
BS优缺点:
- 不需要开发客户端,只需要页面 + 服务端
- 用户不需要下载,打开浏览器就能使用
- 如果应用过大,用户体验受到影响
CS优缺点:
- 画面可以做的非常精美,用户体验好
- 需要开发客户端,也需要开发服务端
- 用户需要下载和更新的时候太麻烦
网络编程三要素
IP
IPV4:
全称: Internet Protocol version 4,互联网通信协议第四版
32位
点分十进制
IPV6:
128位
冒分十六进制、0位压缩表示法
特殊IP地址
127.0.0.1,也可以是localhost: 是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机。
常用CMD命令:
ipconfig:查看本机IP地址
ping:检查网络是否连通
InetAddress
//获取InetAddress的对象
//IP的对象 一台电脑的对象
InetAddress address = InetAddress.getByName("DESKTOP-50JJSAM");
System.out.println(address);
String name = address.getHostName();
System.out.printIn(name);//DESKTOP-50JJSAM
String ip = address.getHostAddress();
System.out.println(ip);//192.168.1.100
端口号
应用程序在设备中唯一的标识。
端口号:由两个字节表示的整数,取值范围:0~65535
其中0~1023之间的端口号用于一些知名的网络服务或者应用
我们自己使用1024以上的端口号就可以了
协议
UDP
UDP发送数据
-
构造方法
方法名 说明 DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任何可用端口 DatagramPacket(byte[] buf,int len,InetAddress add,int port) 创建数据包,发送长度为len的数据包到指定主机的指定端口 -
相关方法
方法名 说明 void send(DatagramPacket p) 发送数据报包 void close() 关闭数据报套接字 void receive(DatagramPacket p) 从此套接字接受数据报包 public class SendDemo { public static void main(String[] args) throws IOException { //创建发送端的Socket对象(DatagramSocket) // DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口 DatagramSocket ds = new DatagramSocket(); //创建数据,并把数据打包 //DatagramPacket(byte[] buf, int length, InetAddress address, int port) //构造一个数据包,发送长度为 length的数据包到指定主机上的指定端口号。 byte[] bys = "hello,udp,我来了".getBytes(); DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("127.0.0.1"),10086); //调用DatagramSocket对象的方法发送数据 //void send(DatagramPacket p) 从此套接字发送数据报包 ds.send(dp); //关闭发送端 //void close() 关闭此数据报套接字 ds.close(); } }
UDP接收数据
-
构造方法
方法名 说明 DatagramPacket(byte[] buf, int len) 创建一个DatagramPacket用于接收长度为len的数据包 -
相关方法
方法名 说明 byte[] getData() 返回数据缓冲区 int getLength() 返回要发送的数据的长度或接收的数据的长度 public class ReceiveDemo { public static void main(String[] args) throws IOException { //创建接收端的Socket对象(DatagramSocket) DatagramSocket ds = new DatagramSocket(12345); //创建一个数据包,用于接收数据 byte[] bys = new byte[1024]; DatagramPacket dp = new DatagramPacket(bys, bys.length); //调用DatagramSocket对象的方法接收数据 ds.receive(dp); //解析数据包,并把数据在控制台显示 System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength())); } }
练习(UDP发,UDP收):
/*
UDP发送数据:
数据来自于键盘录入,直到输入的数据是886,发送数据结束
*/
public class SendDemo {
public static void main(String[] args) throws IOException {
//创建发送端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
//键盘录入数据
Scanner sc = new Scanner(System.in);
while (true) {
String s = sc.nextLine();
//输入的数据是886,发送数据结束
if ("886".equals(s)) {
break;
}
//创建数据,并把数据打包
byte[] bys = s.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.66"), 12345);
//调用DatagramSocket对象的方法发送数据
ds.send(dp);
}
//关闭发送端
ds.close();
}
}
/*
UDP接收数据:
因为接收端不知道发送端什么时候停止发送,故采用死循环接收
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建接收端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket(12345);
while (true) {
//创建一个数据包,用于接收数据
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
//调用DatagramSocket对象的方法接收数据
ds.receive(dp);
//解析数据包,并把数据在控制台显示
System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
}
//关闭接收端
// ds.close();
}
}
三种方式
单播:
单播用于两个主机之间的端对端通信
组播:
组播用于对一组特定的主机进行通信
广播:
广播用于一个主机对整个局域网上所有主机上的数据通信
组播
// 发送端
public class ClinetDemo {
public static void main(String[] args) throws IOException {
// 1. 创建发送端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
String s = "hello 组播";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("224.0.1.0");
int port = 10000;
// 2. 创建数据,并把数据打包(DatagramPacket)
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
// 3. 调用DatagramSocket对象的方法发送数据(在单播中,这里是发给指定IP的电脑但是在组播当中,这里是发给组播地址)
ds.send(dp);
// 4. 释放资源
ds.close();
}
}
// 接收端
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1. 创建接收端Socket对象(MulticastSocket)
MulticastSocket ms = new MulticastSocket(10000);
// 2. 创建一个箱子,用于接收数据
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
// 3. 把当前计算机绑定一个组播地址,表示添加到这一组中.
ms.joinGroup(InetAddress.getByName("224.0.1.0"));
// 4. 将数据接收到箱子中
ms.receive(dp);
// 5. 解析数据包,并打印数据
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
// 6. 释放资源
ms.close();
}
}
广播
// 发送端
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 1. 创建发送端Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
// 2. 创建存储数据的箱子,将广播地址封装进去
String s = "广播 hello";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("255.255.255.255");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
// 3. 发送数据
ds.send(dp);
// 4. 释放资源
ds.close();
}
}
// 接收端
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1. 创建接收端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket(10000);
// 2. 创建一个数据包,用于接收数据
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
// 3. 调用DatagramSocket对象的方法接收数据
ds.receive(dp);
// 4. 解析数据包,并把数据在控制台显示
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
// 5. 关闭接收端
ds.close();
}
}
单播换一下IP
TCP
TCP发送数据
-
Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。
-
Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
-
构造方法
方法名 说明 Socket(InetAddress address,int port) 创建流套接字并将其连接到指定IP指定端口号 Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号 -
相关方法
方法名 说明 InputStream getInputStream() 返回此套接字的输入流 OutputStream getOutputStream() 返回此套接字的输出流 -
示例代码
public class Client { public static void main(String[] args) throws IOException { //TCP协议,发送数据 //1.创建Socket对象 //细节:在创建对象的同时会连接服务端 // 如果连接不上,代码会报错 Socket socket = new Socket("127.0.0.1",10000); //2.可以从连接通道中获取输出流 OutputStream os = socket.getOutputStream(); //写出数据 os.write("aaa".getBytes()); //3.释放资源 os.close(); socket.close(); } }
TCP接收数据
-
构造方法
方法名 说明 ServletSocket(int port) 创建绑定到指定端口的服务器套接字 -
相关方法
方法名 说明 Socket accept() 监听要连接到此的套接字并接受它 -
注意事项
- accept方法是阻塞的,作用就是等待客户端连接
- 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接
- 针对客户端来讲,是往外写的,所以是输出流
针对服务器来讲,是往里读的,所以是输入流 - read方法也是阻塞的
- 客户端在关流的时候,还多了一个往服务器写结束标记的动作
- 最后一步断开连接,通过四次挥手协议保证连接终止
三次握手,四次挥手代码
public class Server {
public static void main(String[] args) throws IOException {
//TCP协议,接收数据
//1.创建对象ServerSocker
ServerSocket ss = new ServerSocket(10000);
//2.监听客户端的链接
Socket socket = ss.accept();
//3.从连接通道中获取输入流读取数据
InputStream is = socket.getInputStream();
int b;
while ((b = is.read()) != -1){
System.out.println((char) b);
}
//4.释放资源
socket.close();
ss.close();
}
}
练习题
反射
反射允许对封装类的字段,方法和构造函数的信息进行编程访问。
获取class对象的三种方式
- Class.forName("全类名");
- 类名.class
- 对象.getClass()
源代码阶段1 加载阶段2 运行阶段3
//1.第一种方式
//最常用
//余类名 : 包名 + 类名
Class clazz = Class.forName("com.itheima.myreflect1.Student");
//2.第二种方式
//一般更多的是当做参数进行传递
Class clazz2 = Student.class;
System.out.println(clazz1 == clazz2);//true
//3.第三种方式
//当我们已经有了这个类的对象时,才可以使用
Student s = new Student();
Class clazz3 = s.getClass();
获取构造方法
//2.获取构造方法
//所有公共的
Constructor[] cons1 = clazz.getConstructors();
for (Constructor con : cons1){
System.out.printIn(con);
}
//获取所有的
Constructor[] cons2 = clazz.getDeclaredConstructors();
//获取单个,顺序获取
Constructor con1 = clazz.getDeclaredConstructor();
System.out.println(con1);
//参数对应
Constructor con2 = clazz.getDeclaredConstructor(String.class);
Constructor con3 = clazz.getDeclaredConstructor(String.class,int.class);
int modifiers = con4.getModifiers();//权限修饰符,以整数形式
Parameter[] parameters = con4.getParameters();//获取所有参数
//表示临时取消权限校验
//暴力反射
con4.setAccessible(true);
Student stu = (Student) con4.newInstance("张三",23);
System.out.println(stu);
获取成员变量
//获取class字节码文件的对象
Class clazz = Class.forName("com.itheima.myreflect3.Student");
//获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields){
System.out.printIn(field);
}
//获取单个的成员变量
Field gender = clazz.getDeclaredField("name");
//获取权限修饰符
int modifiers = name.getModifiers();
//获取成员变量的名字
String n = name.getName();
//获取成员变量的数据类型
Class<?> type = name.getType();
//获取成员变量记录的值
Student s = new Student( "zhangsan", 23, "男");
name.setAccessible(true);
String value = (String) name.get(s);
//修改对象里面记录的值
name.set(s,"lisi");
获取成员方法
public class ReflectDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.获取class对象
Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");
//2.获取方法
//getMethods可以获取父类中public修饰的方法
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
//获取所有的方法(包含私有)
//但是只能获取自己类中的方法
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
//获取指定的方法(空参)
Method method3 = clazz.getMethod("sleep");
Method method4 = clazz.getMethod("eat",String.class);
//获取指定的私有方法
Method method5 = clazz.getDeclaredMethod("playGame");
// 获取方法的修饰符
int modifiers = m.getModifiers();
// 获取方法的名字
String name = m.getName();
// 获取方法的形参
Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters){
System.out.printIn(parameter);
}
//获取方法抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
/*
方法运行
Method类中用于创建对象的方法
Object invoke(Object obj,object... args): 运行方法
参数一:川bj对象调用该方法
参数二: 调用方法的传递的参数《如果没有就不写)
返回值:方法的返回值《如果没有就不写)
*/
Student s = new student();
m.setAccessible(true);
//参数一s: 表示方法的调用者
//参数二"汉堡包":表示在调用方法的时候传递的实际参数
m.invoke(s,"汉堡包");
}
}
反射的作用
- 获取一个类里面所有的信息,获取到了之后,执行其他的业务逻辑
- 结合配置文件,动态的创建对象并调用方法
练习1:
练习2:
需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。
分析:
- 通过Properties加载配置文件
- 得到类名和方法名
- 通过类名反射得到Class对象
- 通过Class对象创建一个对象
- 通过Class对象得到方法
- 调用方法
public class ReflectDemo9 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.读取配置文件的信息
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("day14-code\\prop.properties");
prop.load(fis);
fis.close();
System.out.println(prop);
String classname = prop.get("classname") + "";
String methodname = prop.get("methodname") + "";
//2.获取字节码文件对象
Class clazz = Class.forName(classname);
//3.要先创建这个类的对象
Constructor con = clazz.getDeclaredConstructor();
con.setAccessible(true);
Object o = con.newInstance();
System.out.println(o);
//4.获取方法的对象
Method method = clazz.getDeclaredMethod(methodname);
method.setAccessible(true);
//5.运行方法
method.invoke(o);
}
}
配置文件中的信息:
classname=com.itheima.a02reflectdemo1.Student
methodname=sleep
动态代理
特点:无侵入式的给代码增加额外的功能
代理里面就是对象要被代理的方法
动态代理三要素:
- 真正干活的对象
- 代理对象
- 利用代理调用方法
切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。
public class Test {
public static void main(String[] args) {
/*
需求:
外面的人想要大明星唱一首歌
1. 获取代理的对象
代理对象 = ProxyUtil.createProxy(大明星的对象);
2. 再调用代理的唱歌方法
代理对象.唱歌的方法("只因你太美");
*/
//1. 获取代理的对象
BigStar bigStar = new BigStar("鸡哥");
Star proxy = ProxyUtil.createProxy(bigStar);
//2. 调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}
/*
* 类的作用:
* 创建一个代理
* */
public class ProxyUtil {
/*
*
* 方法的作用:
* 给一个明星的对象,创建一个代理
*
* 形参:
* 被代理的明星对象
*
* 返回值:
* 给明星创建的代理
*
* 需求:
* 外面的人想要大明星唱一首歌
* 1. 获取代理的对象
* 代理对象 = ProxyUtil.createProxy(大明星的对象);
* 2. 再调用代理的唱歌方法
* 代理对象.唱歌的方法("只因你太美");
* */
public static Star createProxy(BigStar bigStar){
/* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
//参数三:用来指定生成的代理对象要干什么事情
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 参数一:代理的对象
* 参数二:要运行的方法 sing
* 参数三:调用sing方法时,传递的实参
* */
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//去找大明星开始唱歌或者跳舞
//代码的表现形式:调用大明星里面唱歌或者跳舞的方法
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
public interface Star {
//我们可以把所有想要被代理的方法定义在接口当中
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}
public class BigStar implements Star {
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
//唱歌
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}
//跳舞
@Override
public void dance(){
System.out.println(this.name + "正在跳舞");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "BigStar{name = " + name + "}";
}
}
结束!
2023.8.31 17:41
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具