网络编程
一、TCP编程
1.TCP客户端和服务器的通信流程
客户端:Socket s = new Socket("IP地址",端口号)
客户端将数据写入通道内 OutputStream out = s.getOutputStram()
一次写入一个字节数组,发送给服务器端
客户端获取通道内的字节流 InputStream is = s.getInputStream()
一次读取一个字节数组,展示客户端的反馈信息
服务器端:ServerSocket ss = new ServerSocket(端口号)
监听客户端连接 Socket s = ss.accept();
获取监听到指定客户端的通道内的字节输入流对象 InputStream in = s.getInputStream()
一次读取一个字节数组--->获取对象
服务器在获取监听到客户端通道内的字节输出流对象,反馈给客户端消息
OutputStream out = s.getOutputStream()
2.TCP的三次握手
SYN:同步序列编号
ACK:应答机制(消息确认符-->表示数据已经准确接收无误
客户端给服务器发送一个握手信号SYN
服务器端反馈给客户端SYN+ACK:数据已经收到
客户端反馈给服务器SYN+ACK:客户端数据已收到,反馈给服务器端
当这些结束之后,客户端和服务器建立通信。
3.客户端
创建TCP协议这种方式的客户端的socket对象
java.net.Socket -->public Socket(String host,int port)
4.服务器端
创建服务器端的Socket对象-->绑定端口
public ServerSocket(int port)throws IOException
5.案例
需求:客户端上传图片文件—>读取当前项目xxx.jpg图片文件—写给服务器‘
服务器端读取传过来的xx.jpg图片文件—复制到指定磁盘上的 copy.jpg文件中,服务器端加入反馈(反馈客户端)!
public class Client {
public static void main(String[] args) throws IOException {
//创建客户端的Socket
Socket s = new Socket("10.35.165.17",2222) ;
//创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("bug2.jpg")) ;
//获取通道内的字节输出流,----封装字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream()) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
//给通道内的输出流写数据
bos.write(bytes,0,len) ;
//刷新
bos.flush();
}
//告知服务器端,没有流数据写过去
s.shutdownOutput();
//读取反馈
//获取客户端通道内的字节输入流
InputStream inputStream = s.getInputStream();
//一次读取一个字节数组
byte[] buffer = new byte[1024] ;
int length = inputStream.read(buffer);
String str = new String(buffer,0,length) ;
System.out.println("str:"+str) ;
//释放资源
bis.close();
s.close();
//关闭资源
}
}
public class Server {
public static void main(String[] args) throws IOException {
//服务器端的Socket
ServerSocket ss = new ServerSocket(2222) ;
//监听客户端连接
Socket s = ss.accept() ;
//获取通道内字节输出流---->封装字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(s.getInputStream()) ;
//创建字节缓冲输出流---写数据到指定文件中
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("D:\\EE_2302\\day29\\code\\my.jpg")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
//刷新
bos.flush();
}
//服务器加入反馈
//获取通道内字节输出流
OutputStream outputStream = s.getOutputStream();
outputStream.write("图片上传完毕".getBytes()) ;
//释放资源
bos.close();
ss.close();
}
}
序列化
序列化:就是使用ObjectOutputStream将一个Java对象的信息写入流中
对象--->数据流(流数据)
构造方法:public ObjectOutputStream(OutputStream out)
成员方法:public final void writeObject(Object obj) 将指定的对象写入到序列化流中,变成流数据
反系列化:就是使用ObjectInputStream 将数据流中的内容--->还原成--->对象
构造方法:
public ObjectInputStream(InputStream in)
成员方法:
public final Object readObject():从反序列流中将数据流的内容----解析成对象
举例
//反序列化
public static void read() throws IOException, ClassNotFoundException {
//创建反序列化流对象ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;
// public final Object readObject():从反序列流中将数据流的内容----解析成对象
Object object = ois.readObject();
//强转学生类型
Student s = (Student) object;
System.out.println(s.getName()+"---"+s.getAge());
}
//序列化
public static void write() throws IOException {
//创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ;
//public final void writeObject(Object obj) :将指定的对象写入到序列化流中,变成流数据
//创建一个学生对象
Student s = new Student("高圆圆",44) ;
oos.writeObject(s) ;
//java.io.NotSerializableException: com.qf.io_01.Student 当前这个类未实现序列化异常
}
反射
1.什么是反射
什么是反射? (jvm正在编译的时候,就可以获取这个类所有的信息!)
(Reflection),一个类在加载运行状态的时候,可以动态获取正在运行的类(Class)
创建正在运行类的对象(构造器-Constructor)
使用正在运行的这个类的对象去给 成员变量赋值(Field)
调用正在运行的这个类中成员方法(Method)
反射应用----Java设计模式
正射 ----就是我们自己书写的代码的时候, 类名 对象名 = new 类名() ;
2.获取字节码文件的方式
1)调用Object类的getClass方法
2)任意Java类型的class属性
3)第三种方式:(推荐)
Java提供类Class:表示正在运行的Java应用程序中的类和接口
有一个静态方法:
参数:类的完全限定名称。 (包名.类名) String类型:经常用在配置文件中xx.properties
public static Class<?> forName(String className):
3.获取构造器的Constructor对象并创建实例
1)获取指定公共的构造方法的Constructor对象
public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定的公共的构造方法的Constructor对象,参数:指定参数类型的Class类对象,如果没有参数,可以不写
举例:String类型的参数---java.lang.String
int---->java.lang.Integer
2)获取所有的指定有参构造构造方法的Constructor对象
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
参数是:当前参数类型的字节码文件 String--->String.class
3)创建当前类实例
public T newInstance(Object... initargs)
参数:通过构造器里面给当前正在运行的类的成员变量赋值:实际参数
返回T---Type:代表任意Java对象
4)当设置非公共属性时应当设置取消java语言访问检查
public void setAccessible(boolean flag)
参数为true:取消Java语言访问检查
4.通过Class字节码文件对象获取属性(成员变量)/设置值
1)获取所有的公共字段(成员变量)
public Field[] getFields()
2)获取"这个类"所有的字段所在的Field类对象,公共的/默认的/私有的/受保护的
public Field[] getDeclaredFields()
public Fileld[] getDeclaredFields(String name)
参数为:这个类的属性名称
3)将指定的value实际参数作用在指定的当前类的实例上--->为字段赋值
public void set(Object obj,Object value)
5.获取成员方法/调用方法
1)获取这个类的所有的公共的成员方法的类对象Method(包括继承过来的以及实现接口的方法)
public Methods[] getMethods()
2)获取类所有的的指定的成员方法的类对象Method,不包括继承过来的
public Methods[] getDeclaredMethods()
3)获取指定的公共成员方法
public Method getMethod(String name,Class<?>... parameterTypes)
参数1:方法的名称
参数2:当前方法中参数类型的Class
4)获取指定的成员方法所在的类对象Method
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
参数1:方法的名称
参数2:当前方法中参数类型的Class
5)调用方法
public Object invoke(Object obj,Object... args)
参数1:这个类的实例
参数2:方法中传入的实际参数
JDK动态代理
1.代理思想/静态代理
代理设计模式---属于"结构型设计模式"
代理核心思想:代理角色帮助真实角色对他的业务功能进行增强!
静态代理
特点:代理角色和真实角色必须实现同一个接口!
弊端:业务功能和增强的功能没有完全分离!
2.动态代理
jdk动态代理--->jdk提供的
java.lang.reflect.Proxy:动态代理类
1)静态方法:
public static Object newProxyInstance(
ClassLoader loader, //获取接口实例的类加载器
Class<?>[] interfaces,//实现的接口列表的字节码文件(代理类要的实现接口列表的Class)
InvocationHandler h)//有代理实例调用 接口方法的处理程序:接口类型
抛出异常 throws IllegalArgumentException
2)InvocationHandler:接口
public Object invoke(Object proxy, Method method, Object[] args)
参数1:调用该方法的代理实例
参数2:调用该代理实例的接口方法的Method类对象
参数3:调用接口方法中所有的参数,组成数组,没有参数,可不写
举例
//测试类中
UserDao ud = new UserDaoImple() //真实角色
InvocationHandler handler = new MyInvocationHandler(ud) ;
UserDao ud3 = (UserDao) Proxy.newProxyInstance(
ud.getClass().getClassLoader(),//代理实例要实现的接口的字节码文件---类加载
//代理实例要实现接口列表的Class--->Class类中--public 类<?>[] getInterfaces()
ud.getClass().getInterfaces(),
//代理实例调用接口方法的处理程序
handler);
ud3.add() ;
//InvocationHandler接口类
/*
* @param proxy 调用该方法的代理实例
* @param method 调用该代理实例的接口方法的Method类对象
* @param args 调用接口方法中所有的参数,组成数组,没有参数,可不写
* @return 从代理实例上的方法调用返回的值
* @throws Throwable 可能调用方法失败!
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("操作业务功能之前,先权限校验!" );
//反射的写法调用接口方法--add()/delete()/udate()/findAll()
Object obj = method.invoke(target, args);
System.out.println("操作业务功能之后,产生日志记录!");
return obj; //方法的返回值
}