Java零基础入门11.0
- 网络编程
- 1. 初识网络编程
- 2. 网络编程三要素
- 3.IP三要素
- 3.1 IPV4的细节
- 3.1.1特殊的IP地址
- 3.1.2 常用的CMD命令
- 3.2 InetAddress 的使用
- 3.3 端口号
- 3.4 协议
- 3.4.1 UDP协议
- 3.4.1 - I UDP 发送数据
- 3.4.1 - II UDP 接收数据
- 3.4.1 - III UDP 练习(聊天室)
- 3.4.2 UDP的三种通信方式
- 3.4.3 TCP协议
- 3.4.3 - I TCP 发送和接收数据
- 3.4.3 - II TCP 中文乱码问题
- 3.4.3 - III TCP 代码细节
- 3.4.4 TCP通信程序
- 3.4.4 - I 三次挥手
- 3.4.4 - II 四次挥手
- 4. 综合练习
- 4.1 多发多收
- 4.2 接收并反馈
- 4.3 上传文件
- 4.4 文件名重复
- 4.5 多线程版的服务端上传文件
- 4.6 线程池的服务端
- 4.7 BS架构模型
- 反射
- 5. 初识反射
- 6. 获取class对象的三种方式
- 7. 反射获取构造方法
- 8. 反射获取成员变量
- 9. 反射获取成员方法
- 10. 综合练习
- 10.1 保存信息
- 10.2 跟配置文件结合动态创建
- 动态代理
- 11. 动态代理的思想分析
- 12. 动态代理的代码实现
网络编程
1. 初识网络编程
Java中可以使用java.net包
下的技术秦颂开发出常见的网络应用程序。
常见的软件架构:bs和cs
BS架构:
优点:
缺点:
CS架构:
优点:
缺点:
小结:
2. 网络编程三要素
1、确定对方电脑在互联网上的地址(IP)
2、确定接收数据的软件(端口号)
一个端口号只能被一个软件绑定使用
3、确定网络传输的规则(协议)
IP:
- 设备在网络中的地址,是唯一的标识 端口号:应用程序在设备中唯一的标识(设备包括电脑、笔记本、手机等)
协议:
- 数据在网络中传输的规则,常见的
协议:
- 数据在网络传输的规则,常见的协议有UDP、TCP、http、ftp
小结:
3.IP三要素
IP:
IPV4:
IPV6:
小结:
3.1 IPV4的细节
比如:
在网吧里面的很多台电脑,不是都在连接外网的时候都有一个公网的IP,它们往往是共享同一个公网IP,再由路由器给每一台电脑分配局域网IP,这样就可以实现节约IP的效果
3.1.1特殊的IP地址
127.0.0.1
,也就是localhost:
是回送地址也称本地回环地址,也称本机IP
,永远只会寻找当前所在本机
提问:
假设
192.168.1.100
是我电脑的IP,那么这个IP跟127.0.0.1
是一样的吗?
不一样
案例:
- 现在以
192.168.1.100
发送数据的时候,此时是先发到路由器,路由器再找到你当前的IP,这样子才能实现数据的发送,但是此时会有个小细节——每一个路由器给你分配的IP是不一样的
所以在不同的地方上网,局域网IP有可能会不一样
- 如果要往
127.0.0.1
发送数据,是不经过路由器的,数据在经过网卡的时候,网卡会发现要往127.0.0.1
发送就会直接把这个数据给自己发送回来
3.1.2 常用的CMD命令
ipconfig:查看本机IP地址
ping:检查网络是否连通
小结:
3.2 InetAddress 的使用
此类表示互联网协议(IP)地址
public static void main(String[] args) throws IOException {
//获取InetAddress的对象
//IP的对象 一台电脑的对象
InetAddress address = InetAddress.getByName("Javris");
System.out.println(address);
String name = address.getHostName();
System.out.println(name);
String ip = address.getHostAddress();
System.out.println(ip);
}
3.3 端口号
端口说白了就可以理解成为——电脑往外发送数据或者是接收数据的出口or入口
案例:
电脑A向电脑B发送数据
3.4 协议
OSI模型:
TCP/IP模型:
在该模型当中把OSI的应用层、表示层、会话层给合并了——应用层,最后两层数据链路层、物理层合并了——物理链路层
3.4.1 UDP协议
面向无连接什么意思?
假设左边的电脑要给右边的电脑发送数据,在发送数据之前按道理来讲,要先检查两台电脑之间的网络是否畅通。但是,UDP协议是不会检查的,能收到就收到,收不到就算了
应用场景:适用于丢一点数据,也不会产生影响的情况。比如:网络会议、语音通话、看视频
3.4.1 - I UDP 发送数据
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
//打包数据
String s1 = "李在赣神魔?";
byte[] bytes = s1.getBytes();
InetAddress address = InetAddress.getByName("127.0.01");
//端口
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
ds.send(dp);
ds.close();
}
程序运行完毕,控制台没有消息是因为UDP的特点:不管你接不接得到,发了就行
3.4.1 - II UDP 接收数据
接收数据的时候只要给它传递一个数组就可以了
注意:在运行的时候要先运行接收端,再运行发送端
发送端:
public class Test1 {
public static void main(String[] args) throws IOException {
//创建DatagramSocket对象
//细节:
//绑定端口:以后我们就是通过这个端口往外发送
//空参:所有可用的端口中随机一个进行使用
//有参:指定端口号进行绑定
DatagramSocket ds = new DatagramSocket();
//打包数据
String s1 = "李在赣神魔?";
byte[] bytes = s1.getBytes();
InetAddress address = InetAddress.getByName("127.0.01");
//端口
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
ds.send(dp);
ds.close();
}
}
接收端:
public class Test2 {
public static void main(String[] args) throws IOException {
//1.创建DatagramSocker对象
//细节:
//在接收的时候,一定要绑定端口
//而且绑定的端口一定要跟发送的端口保持一致
DatagramSocket ds = new DatagramSocket(10086);
//2.接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);//后者是用多大的空间去接收数据
//相当于在接收数据的时候,还是把这个数据放到bytes这个数组里面
//该方法是阻塞的
//程序执行到这一步的时候,会在这里等死
//等发送端发送消息
ds.receive(dp);
//3.解析数据 包
//获取包的数据、长度、地址、端口
byte[] data = dp.getData();
int len = dp.getLength();
InetAddress address = dp.getAddress();
int port = dp.getPort();
System.out.println("接收到数据"+new String(data,0,len));
System.out.println("该数据是从"+address+"这台电脑"+"port"+"端口发出的");
//4.释放资源
ds.close();
}
}
3.4.1 - III UDP 练习(聊天室)
修改IDEA设置:
在发送端选择编辑7
修改选项
勾选多个实例:
一定要勾选,不然没法同时运行多个
接收端:
public static void main(String[] args) throws IOException {
//创建对象接收端口
DatagramSocket ds = new DatagramSocket(10086);
//接收数据包,用数组
byte[] bytes = new byte[1024];//为什么该数组和数据包不写入循环里面呢?
// 因为每一次都拿同一个数组去接收数据,第二次可以把第一次的覆盖
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
while (true){
ds.receive(dp);
//解析数据包
byte[] data = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String name = dp.getAddress().getHostName();
//打印
System.out.println("IP为:"+ip+",主机民为"+name+"的人,发送了数据"+new String(data,0,len));
}
}
发送端:
发送端设置完成了之后,可以运行多个
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();//相当于一个快递公司,其不肯能进行循环
//输入数据,进行打包
Scanner sc = new Scanner(System.in);
//下面的快递可以无休止地进行循环
while(true){
System.out.println("请输入想要说出的话:");
String str = sc.nextLine();
byte[] bytes = str.getBytes();
if("886".equals(str)){
break;
}
//建立地址、端口,然后打包进去
InetAddress address = InetAddress.getByName("127.0.01");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);
//3.发送数据
ds.send(dp);
}
//4.释放资源
ds.close();
}
3.4.2 UDP的三种通信方式
单播、组播和广播
1、单播
一对一
以前的代码就是单播
2、组播
一对多(一组)、
组播地址:224.0.0.0~239.255.255.255
其中224.0.0.0~224.0.0.255
为预留的组播地址
3、广播
一对全(全部)
广播地址:255.255.255.255
3.4.3 TCP协议
它在发数据的时候,会先去检查两台电脑之间的网络是否畅通,就是确保连接成功才会发送数据——面向连接
应用场景:下载软件、文字聊天、发送邮件
package socketnet.Dome4;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class dome2 {
public static void main(String[] args) throws IOException {
MulticastSocket ms = new MulticastSocket();
//打包数据
String str = "你好啊。。。";
byte[] bytes = str.getBytes();
//发送的地址
InetAddress address = InetAddress.getByName("224.0.0.1");
//要发送的端口号
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
//发送数据
ms.send(dp);
//释放资源
ms.close();
}
}
package socketnet.Dome4;
import java.io.IOException;
import java.net.*;
public class dome1 {
public static void main(String[] args) throws IOException {
MulticastSocket ms = new MulticastSocket(10000);
//将当前主机,添加到224.0.0.1的这一组当中
InetAddress address = InetAddress.getByName("224.0.0.1");
ms.joinGroup(address);
//接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//该方法是堵塞的
//程序执行到这一步的时候,会在这里死等
//等发送端发送消息
ms.receive(dp);
//解析数据包
byte[] data = dp.getData();
int len = dp.getLength();
int port = dp.getPort();
System.out.println("接收到的数据"+new String(data,0,len));
System.out.println("该数据是从"+address+"这台电脑中的"+port+"这个端口发出的");
//释放资源
ms.close();
}
}
package socketnet.Dome4;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class dome {
public static void main(String[] args) throws IOException {
MulticastSocket ms = new MulticastSocket(10000);
//将当前主机,添加到224.0.0.1的这一组当中
InetAddress address = InetAddress.getByName("224.0.0.1");
ms.joinGroup(address);
//接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//该方法是堵塞的
//程序执行到这一步的时候,会在这里死等
//等发送端发送消息
ms.receive(dp);
//解析数据包
byte[] data = dp.getData();
int len = dp.getLength();
int port = dp.getPort();
System.out.println("接收到的数据"+new String(data,0,len));
System.out.println("该数据是从"+address+"这台电脑中的"+port+"这个端口发出的");
//释放资源
ms.close();
}
}
package socketnet.Dome5;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class dome {
public static void main(String[] args) throws IOException {
MulticastSocket ms = new MulticastSocket();
//打包数据
String str = "你好啊。。。";
byte[] bytes = str.getBytes();
//发送的地址
InetAddress address = InetAddress.getByName("255.255.255.255");
//要发送的端口号
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
//发送数据
ms.send(dp);
//释放资源
ms.close();
}
}
3.4.3 - I TCP 发送和接收数据
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象
通信之前要保证连接已经建立
通过Socket产生IO流来进行网络通信
客户端(Socket)
- 创建客户端的Socket对象
(Socket)
与指定服务端连接
Socket(String host , int port)
- 获取输出流,写数据
OutputStream getOutputstream()
- 释放资源
void close
服务器(ServerSocket)
-
创建服务器端的Socket对象
(ServerSocket)
ServerSocket(int port)
-
监听客户端连接,返回一个Socket对象
Socket accept()
-
获取输入流,读数据,并把数据显示在控制台
InputStreamReader getInputStreamReader(socket.getInputStream )
-
释放资源
void close
先来运行服务端(接收的):
public static void main(String[] args) throws IOException {
//TCP协议,接收数据
//1.创建 ServerSocker对象
ServerSocket ss = new ServerSocket(1000);
//2.监听客户端的链接
Socket socket = ss.accept();
//3.从连接通道中获取输入流读取数据
InputStream is = socket.getInputStream();
int b ;
while((b=is.read())!=-1){
System.out.println((char) b);
}
socket.close();
ss.close();
}
再运行客户端(发送的):
public static void main(String[] args) throws IOException {
//TCP协议,发送数据
//1.创建Socket对象
//细节:在创建对象的同时会连接服务器
// 如果连接不上,代码会报错
Socket socket = new Socket("127.0.01",1000);
//2.可以从连接通道中获取输出流
OutputStream os = socket.getOutputStream();
os.write("aaa".getBytes());//这种方式不能传中文
//3.释放资源
os.close();
socket.close();
}
3.4.3 - II TCP 中文乱码问题
为什么会出现乱码问题呢?
在使用IDEA默认的编码表(UTF-8),此时就会把一个中文变成3个字节,服务器在读的时候是一个字节一个字节地读,所以每次转码的时候只是1/3个中文,造成了乱码
解决方案:
- 不要拿
InputStream
字节流去读了,把其变成字符流InputStreamReader
,就不会出现乱码
- 如果说想要提高读取的效率,那么就再把字符流变成缓冲流
BufferedReader
3.4.3 - III TCP 代码细节
3.4.4 TCP通信程序
3.4.4 - I 三次挥手
3.4.4 - II 四次挥手
多了一次:需要保证已经把连接通道里面的数据处理完毕了,这个时候的连接才能断开
4. 综合练习
4.1 多发多收
public static void main(String[] args) throws IOException {
//服务器
ServerSocket ss = new ServerSocket(10000);
//等待客户端连接
Socket socket = ss.accept();
//读取数据
InputStreamReader isr = new InputStreamReader(socket.getInputStream());
int b;
while((b=isr.read())!=-1){
System.out.print((char)b);
}
socket.close();
ss.close();
}
public static void main(String[] args) throws IOException {
//客户端
Socket socket = new Socket("127.0.01", 10000);
Scanner sc = new Scanner(System.in);
OutputStream os = socket.getOutputStream();
while(true){
System.out.println("请输入信息:");
String str = sc.nextLine();
if("886".equals(str)){
break;
}
os.write(str.getBytes());//getBytes(): 使用平台的默认字符集将字符串编码为 byte
}
os.close();
}
4.2 接收并反馈
思路:
服务器:
public static void main(String[] args) throws IOException {
//服务器
ServerSocket ss = new ServerSocket(10000);
//等待客户端连接
Socket socket = ss.accept();
//socket中获取输入流读取数据
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int b;
//细节:
//read方法会从连接通道中读取数据
//但是,需要有一个结束标记,此处的循环才会停止
//否则,程序会一致停在reead方法这里,等待读取下面的数据
while((b=isr.read())!=-1){
System.out.println((char)b);
}
//回写数据
String str = "乖乖站好";
OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
socket.close();
ss.close();
}
客户端:
public static void main(String[] args) throws IOException {
//客户端
Socket socket = new Socket("127.0.01", 10000);
//写出数据
String str = "李在赣神魔?";
OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
//写出一个结束标记
socket.shutdownOutput();
//接收服务端回写的数据
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int b;
while((b= isr.read())!=-1){
System.out.println((char) b);
}
socket.close();
}
4.3 上传文件
结合本地IO流与网络IO流结合起来
服务器:
public static void main(String[] args) throws IOException {
//服务器
ServerSocket ss = new ServerSocket(10000);
//等待客户端连接
Socket socket = ss.accept();
//读取数据并保存到本地文件当中
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("javaprogram1\\b.txt"));
int len;
byte[] bytes = new byte[1024];
while((len=bis.read())!=-1){
bos.write(bytes,0,len);
}
//回写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
socket.close();
ss.close();
}
客户端:
public static void main(String[] args) throws IOException {
//客户端
Socket socket = new Socket("127.0.01", 10000);
//读取本地文件中的数据,并写到服务器当中
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("javaprogram1\\a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] bytes = new byte[1024];
int len;
while((len=bis.read())!=-1){
bos.write(bytes,0,len);//在本地读了多少,就立马把多少数据写到客户端当中
}
//往服务器写出结束标记
socket.shutdownOutput();
//接受服务端的回写数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//相当于就是先获取到连接通道里面的输入流,字节流->字符流->再用缓冲流进行包裹
String line = br.readLine();//这个line就是服务器回写的数据
System.out.println(line);
socket.close();
}
4.4 文件名重复
Java里面有个UUID类
- 可以使用该类生成一个随机的文件名
- 可以使用静态的randomUUID方法,返回一个UUID对象
public static void main(String[] args) throws IOException {
System.out.println(UUID.randomUUID());
String str = UUID.randomUUID().toString().replace("-", "");//随机生成的UUID
System.out.println(str);
}
以此修改上方的服务端代码文件:
public static void main(String[] args) throws IOException {
//服务器
ServerSocket ss = new ServerSocket(10000);
//等待客户端连接
Socket socket = ss.accept();
//读取数据并保存到本地文件当中
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String name = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("javaprogram1\\+"+name+"txt"));
int len;
byte[] bytes = new byte[1024];
while((len=bis.read())!=-1){
bos.write(bytes,0,len);
}
//回写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
socket.close();
ss.close();
}
4.5 多线程版的服务端上传文件
MyRunnable类:
public class MyRunnable implements Runnable{
Socket socket;
public MyRunnable(Socket socket){
this.socket = socket;
}
//有一个问题里面原本是没有socket的,这时该怎么办?
//当开启一条线程的时候,需要把客户端的连接对象传递给线程,那么就可以通过构造方法来传递
@Override
public void run() {
try {
//读取数据并保存到本地文件当中
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String name = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("javaprogram1\\+" + name + "txt"));
int len;
byte[] bytes = new byte[1024];
while ((len = bis.read()) != -1) {
bos.write(bytes, 0, len);
}
//回写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器:
public static void main(String[] args) throws IOException {
//服务器
ServerSocket ss = new ServerSocket(10000);
while (true){
//等待客户端连接
Socket socket = ss.accept();
//开启一条线程
//一个用户就对应服务端的一条线程
new Thread(new MyRunnable(socket)).start();
}
}
4.6 线程池的服务端
public static void main(String[] args) throws IOException {
//创建线程池对象,7个参数,ctrl+p提示
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//核心线程数量
16,//线程池总大小
60,//空闲时间
TimeUnit.SECONDS,//单位空闲时间
new ArrayBlockingQueue<>(2),//2个队列
Executors.defaultThreadFactory(),//线程工厂,让线程池如何创建线程对象
new ThreadPoolExecutor.AbortPolicy()//阻塞队列
);
//服务器
ServerSocket ss = new ServerSocket(10000);
while (true){
//等待客户端连接
Socket socket = ss.accept();
//开启一条线程
//一个用户就对应服务端的一条线程
// new Thread(new MyRunnable(socket)).start();
pool.submit(new MyRunnable(socket));
}
}
多次运行后:
4.7 BS架构模型
- 运行该代码然后直接在浏览器中打开该网站
- 打开网站之后会将数据返回到IDEA控制台中
比如:
运行服务端
public static void main(String[] args) throws IOException {
//创建对象绑定端口
ServerSocket ss = new ServerSocket(10000);
//等待客户端来连接
Socket socket = ss.accept();
//读取数据
InputStreamReader is = new InputStreamReader(socket.getInputStream());
int len;
while ((len=is.read())!=-1){
System.out.print((char) len);
}
socket.close();
ss.close();
}
在IDEA返回的数据:
反射
5. 初识反射
什么是反射?
反射允许对成员变量,成员方法和构造方法的信息进行编程访问
能把这个类扒的干干净净,一点都不剩
IDEA里面的提示功能就是个反射
利用反射就可以获取这个类里面的所有信息
问题:既然是从里面拿东西,为什么一定要有反射,用IO流不行吗?
IO流是从上往下一行一行地去读程序
- 当读到构造方法和普通成员方法的时候很难进行区分
- 如果说想用返回值去区分,那么成员变量和局部变量应该无法去区分
那么反射要学什么东西呢?
学习反射的获取和解剖
反射的作用:
6. 获取class对象的三种方式
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.第一种方式,最为常用的
//全类名:包名+类名
Class<?> clazz1 = Class.forName("test6.Student");
//2.第二种方式
//一般更多的是当做参数进行传递
Class clazz2 = Student.class;
System.out.println(clazz1==clazz2);//true
//3.第三种方式
//当已经有了这个类的对象的时候,才可以进行时候
Student stu = new Student();
Class clazz3 = stu.getClass();
System.out.println(clazz2==clazz3);//true
}
7. 反射获取构造方法
student类:
public class Student {
private static final long serialVersionUID = 650157805827481085L;
private String name;
private int age;
private String address;
public Student() {
}
public Student( String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public Student(String name){
this.name = name;
}
protected Student(int age){
this.age = age;
}
private Student(String name,int age){
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", address = " + address + "}";
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.获取class字节码文件对象
Class<?> clazz1 = Class.forName("test6.Student");
//2.获取构造方法
Constructor[] cons1 = clazz1.getConstructors();
for (Constructor con : cons1) {
System.out.println(con);
}
System.out.println("====================");
Constructor[] cons2 = clazz1.getDeclaredConstructors();
for (Constructor con : cons2) {
System.out.println(con);
}
System.out.println("===================");
//获取单个的,getDeclaredConstructor只是能让我们看到这个构造,但是还是无法直接用这个构造去创建对象
Constructor con3 = clazz1.getDeclaredConstructor();
System.out.println(con3);
Constructor con4 = clazz1.getDeclaredConstructor(String.class);
System.out.println(con4);
Constructor con5 = clazz1.getDeclaredConstructor(int.class);
System.out.println(con5);
Constructor con6 = clazz1.getDeclaredConstructor(String.class,int.class);
System.out.println(con6);
int modifiers = con6.getModifiers();
System.out.println(modifiers);//返回当前方法的权限修饰符个数
System.out.println("======================");
Parameter[] parameters = con4.getParameters();//获取该构造方法的参数
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
//暴力反射:表示临时取消权限校验
con6.setAccessible(true);
Object stu = (Student)con6.newInstance("麻瓜", 25);
System.out.println(stu);
}
8. 反射获取成员变量
Student类:
public class Student {
private static final long serialVersionUID = 650157805827481085L;
private String name;
private int age;
private String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return address
*/
public String getAddress() {
return address;
}
/**
* 设置
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", address = " + address + "}";
}
}
测试类:
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.获取class字节码文件的对象
Class clazz = Class.forName("test6.Student");
//2.获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("============================");
//获取单个的成员变量
// Field address = clazz.getField("address");//获取不到私有的
Field address = clazz.getDeclaredField("address");//可以获取到私有的
System.out.println(address);
System.out.println("==============================");
//获取权限修饰符
Field name = clazz.getDeclaredField("name");
System.out.println(name);
//获取成员变量的名字
String n = name.getName();
System.out.println(n);
//获取成员变量的数据类型
Class<?> type = name.getType();
System.out.println(type);
System.out.println("===================================");
//获取成员变量记录的值,这个值和对象有关系
//所以想要获取值就要先创建一个对象
Student stu = new Student("里皮",26,"a");
name.setAccessible(true);//允许私有访问
Object value = name.get(stu);
System.out.println(value);
//修改对象里面记录的值
name.set(stu,"麻瓜");
System.out.println(stu);
}
9. 反射获取成员方法
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.获取class字节码文件的对象
Class clazz = Class.forName("test6.Student");
//2.获取里面所有方法对象,包括继承了父类中的所有公共方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("===================================");
//获取里面所有的方法对象,不能获取父类的,能获取本类中私有的方法
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("========================================");
//获取指定的单一方法
Method m = clazz.getDeclaredMethod("eat", String.class);//获取指定的方法避免重复
System.out.println(m);
//获取方法的修饰符
int modifiers = m.getModifiers();
System.out.println(modifiers);
//获取方法的名字
String name = m.getName();
System.out.println(name);
//获取方法的形参
Parameter[] parameters = m.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
//获取方法抛出的异常
Class<?>[] exceptionTypes = m.getExceptionTypes();
for (Class<?> exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
//方法运行
/*
Method类中用于创建对象的方法
object invoke(object obj, object. . . args):运行方法
*/
Student stu = new Student();
m.setAccessible(true);
//参数一:表示方法的调用者
//参数二:表示在调用方法的时候传递的时间参数
Object result = m.invoke(stu, "小老板");
System.out.println(result);//获取返回值
}
10. 综合练习
10.1 保存信息
public class Test {
public static void main(String[] args) throws IllegalAccessException, IOException {
Student stu = new Student("麻瓜",26,'男',10.11,"装老师傅");
Teacher t = new Teacher("小老板",60);
saveObject(stu);
}
// 把对象里面所有的成员变量名和值保存到本地文件当中
public static void saveObject(Object obj) throws IllegalAccessException, IOException {
//1.获取字节码文字的对象
Class clazz = obj.getClass();
//IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("javaprogram1\\b.txt"));
//2.获取所有的成员变量
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//获取成员变量的名字
String name = field.getName();
//获取成员变量的值
Object value = field.get(obj);
bw.write(name+"="+value);
bw.newLine();
}
bw.close();
}
}
10.2 跟配置文件结合动态创建
Techaer类:
public class Teacher {
private String name;
private int salary;
public Teacher() {
}
public Teacher(String name, int salary) {
this.name = name;
this.salary = salary;
}
public void teach(){
System.out.println("老师在睡觉!");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return salary
*/
public int getSalary() {
return salary;
}
/**
* 设置
* @param salary
*/
public void setSalary(int salary) {
this.salary = salary;
}
public String toString() {
return "Teacher{name = " + name + ", salary = " + salary + "}";
}
}
配置文件:
测试类:
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//1.读取配置文件中的信息
Properties prop = new Properties();
//把配置文件的信息加载到IO流
FileInputStream fis = new FileInputStream("javaprogram1\\prop.properties");
prop.load(fis);//把配置文件的信息读取到了集合当中
fis.close();
System.out.println(prop);
//2.获取全类名
String classsName = (String) prop.get("classname");
String methodName = (String) prop.get("method");
System.out.println(classsName);
System.out.println(methodName);
//3.利用反射创建对象并运行方法
//获取其字节码文件对象
Class clazz = Class.forName(classsName);
//获取构造方法
Constructor con = clazz.getDeclaredConstructor();
Object o = con.newInstance();//获取其对象
System.out.println(o);
//获取成员方法并运行
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
method.invoke(o);
}
}
小结:
动态代理
11. 动态代理的思想分析
假设给下面这个方法去增加其他的功能
按照以前所学,只能直接修改代码,把其写在该方法当中——侵入式修改
但是在一个成熟的项目中很少会这么去做,因为一旦修改了可能就会引起连锁反应
这时就需要用到动态代理:
比如:
那么这个代理是如何能知道用户要代理唱歌和跳舞的呢?
通过接口来搞定
代码展示:
star类:
public class star implements mystar{
private String name;
public star() {
}
@Override
public String sing(String name){
System.out.println(this.name+"唱:"+name);
return "谢谢大家";
}
@Override
public void dance(String name){
System.out.println(this.name+"正在跳"+name);
}
public star(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "star{name = " + name + "}";
}
}
接口:
public interface mystar {
//把所有想要代理的方法写到接口当中
public abstract String sing(String name);
public abstract void dance(String name);
}
小结:
12. 动态代理的代码实现
如何为Java对象创建一个代理对象?
MyStar接口:
public interface MyStar {
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}
BigStar类:
public class BigStar implements MyStar{
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = 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 + "}";
}
@Override
public String sing(String name) {
System.out.println(this.name+"正在唱"+name);
return "Thanks";
}
@Override
public void dance() {
System.out.println(this.name+"正在跳舞");
}
}
代理:
public class ProxyUtil {
/*
该方法的作用:
给一个明星的对象,创建一个代理
形参:被代理的明星对象
返回值:给明星创建的代理
需求:
外面的人想要大明星唱一首个
1.获取代理的对象
代理对象 = ProxyUtil.createProxy(大明星的对象)
2.再调用代理的唱歌方法
代理对象.唱歌的方法
*/
public static MyStar createProxy(BigStar bigStar){
/*
参数一:用于指定哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情
*/
MyStar star = (MyStar) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//类加载器加载字节码文件
new Class[]{MyStar.class},//以数组的方式来体现,中介它可以代MyStart这个接口的所有方法
new InvocationHandler() {
//当没有强转成接口的类时,Proxy.newProxyInstance返回的是Object类型,而用Mystart去接收就肯定会报错
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
参数一:代理的对象
参数二:
*/
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 class Test {
public static void main(String[] args) {
//1.获取代理的对象
BigStar bigStar = new BigStar("小黑子");
MyStar proxy = ProxyUtil.createProxy(bigStar);
//2.调用唱歌的方法
String res = proxy.sing("只因");
System.out.println(res);
//3.调用跳舞的方法
proxy.dance();
}
}
代码思路: