Java-网络编程、反射

目录

1 网络编程

1.1 网络相关的概念

1.2 InetAddress类

1.3 Socket

 1.4 TCP网络通信编程

1.5 netstat

1.6 UDP网络通信编程

2 反射

2.1 反射原理

2.2 Class类

2.3 类加载

2.4 通过反射获取类的结构信息

2.5 通过反射创建对象

2.6 通过反射访问类中成员


1 网络编程

1.1 网络相关的概念

1):网络通信,两台设备之间通过网络实现数据传输,将数据通过网络从一台设备传输到另一台设备

2):java.net包下提供了一系列的类或接口,完成网络通信

3):局域网、城域网、广域网(代表,万维网)

4):ip地址,用于唯一标识网络中的每台计算机

5):查看ip地址,ipconfig

6):ipv4地址的新式,点分十进制 xxx.xxx.xxx.xxx(8*4=32位二进制)xxx:0-255

7):ipv6,128位,冒分十六进制x:x:x:x:x:x:x:x(16*8=128)x:4位的16进制,16位二进制转换成4位二进制

8):IP地址分类

  • A:0.0.0.0-127.255.255.255
  • B:128.0.0.0-191.255.255.255
  • C:192.0.0.0-223.255.255.255
  • D:224.0.0.0-239.255.255.255
  • E:240.0.0.0-247.255.255.255

9):域名,将IP地址映射为域名方便记忆

10):端口号,用于标识计算机上某个特定的网络程序,范围:0-65535,0-1024已经被占用

11):常见的端口号

  • tomcat:8080
  • mysql:3306
  • oracle:1521
  • sqlserver:1433

1.2 InetAddress类

1)相关方法

  • 1.获取本机InetAddress对象getLocalHost
  • 2.根据指定主机名/域名获取ip地址对象getByName
  • 3.获取InetAddress对象的主机名getHostName
  • 4.获取InetAddress对象的地址getHostAddress
    //获取本机 InetAddress 对象 getLocalHost
    InetAddress localHost = InetAddress.getLocalHost();
    System.out.println(localHost);
    //根据指定主机名/域名获取 ip 地址对象 getByName
    InetAddress host2 = InetAddress.getByName("ThinkPad-PC");
    System.out.println(host2);
    InetAddress host3 = InetAddress.getByName("www.hsp.com");
    System.out.println(host3);
    //获取 InetAddress 对象的主机名 getHostName
    String host3Name = host3.getHostName();
    System.out.println(host3Name);
    //获取 InetAddress 对象的地址 getHostAddress
    String host3Address = host3.getHostAddress();
    System.out.println(host3Address);

    1.3 Socket

  • 1.套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准。
  • 2.通信的两端都要有Socket,是两台机器间通信的端点
  • 3.网络通信其实就是Socket间的通信。
  • 4.Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
  • 5.一般主动发起通信的应用程序属客户端,,等待通信请求的为服务端
     

 1.4 TCP网络通信编程

1):编写一个服务端,和一个客户端服务端在9999端口监听
客户端连接到服务端,发送"hello, server”,并接收服务端回发的"hello,client"再退出
服务端接收到客户端发送的信息,输出,并发送“hello, client",再退出

//1. 在本机 的 9999 端口监听, 等待连接
// 细节: 要求在本机没有其它服务在监听 9999
// 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在 9999 端口监听,等待连接..");
//2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
// 如果有客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务端 socket =" + socket.getClass());
//
//3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
InputStream inputStream = socket.getInputStream();
//4. IO 读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf, 0, readLen));//根据读取到的实际长度,显示内容. }
//5. 获取 socket 相关联的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello, client".getBytes());
// 设置结束标记   
//字符流还可以使用结束标记writer.newLine(),另一方使用readLine()才可以读取到结束标记
socket.shutdownOutput();
//6.关闭流和 socket
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();//关闭


//1. 连接服务端 (ip , 端口)
//解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket 返回=" + socket.getClass());
//2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
// 得到 和 socket 对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3. 通过输出流,写入数据到 数据通道
outputStream.write("hello, server".getBytes());
// 设置结束标记
socket.shutdownOutput();
//4. 获取和 socket 关联的输入流. 读取数据(字节),并显示
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf, 0, readLen));
}
//5. 关闭流对象和 socket, 必须关闭
inputStream.close();
outputStream.close();
socket.close();
System.out.println("客户端退出.....");

2):编写一个服务端,和一个客户端服务器端在8888端口监听
客户端连接到服务端,发送一张图片e:llqie.png
服务器端接收到客户端发送的图片,保存到src下,发送"收到图片”再退出
客户端接收到服务端发送的“收到图片",再退出

public class TCPFileUploadServer {
public static void main(String[] args) throws Exception {
//1. 服务端在本机监听 8888 端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端在 8888 端口监听....");
//2. 等待连接
Socket socket = serverSocket.accept();
//3. 读取客户端发送的数据
// 通过 Socket 得到输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//4. 将得到 bytes 数组,写入到指定的路径,就得到一个文件了
String destFilePath = "src\\abc.mp4";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
bos.write(bytes);
bos.close();
// 向客户端回复 "收到图片"
// 通过 socket 获取到输出流(字符)
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("收到图片");
writer.flush();//把内容刷新到数据通道
socket.shutdownOutput();//设置写入结束标记
//关闭其他资源
writer.close();
bis.close();
socket.close();
serverSocket.close();
}
}


public class TCPFileUploadClient {
public static void main(String[] args) throws Exception {
//客户端连接服务端 8888,得到 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
//创建读取磁盘文件的输入流
//String filePath = "e:\\qie.png";
String filePath = "e:\\abc.mp4";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
//bytes 就是 filePath 对应的字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//通过 socket 获取到输出流, 将 bytes 数据发送给服务端
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
bis.close();
socket.shutdownOutput();//设置写入数据的结束标记
//=====接收从服务端回复的消息=====
InputStream inputStream = socket.getInputStream();
//使用 StreamUtils 的方法,直接将 inputStream 读取到的内容 转成字符串
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);
//关闭相关的流
inputStream.close();
bos.close();
socket.close();
}
}

1.5 netstat

1):netstat -an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况

2):netstat -an| more 可以分页显示,输入空格显示下一页

3):要求在dos控制台下执行,LISTENING :监听 ,ESTABLISHED: 建立了连接

1.6 UDP网络通信编程

1):类DatagramSocket(两端的端口)和 DatagramPacket(两端传送的数据包最大64k)[数据包/数据报]实现了基于UDP协议网络程序。

2):UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。

3):DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。

4):UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接

5):DatagramSocket可以指定在哪个端口接受数据,只有接收端发送端,没有服务端客户端

6):编写一个接收端A,和一个发送端B
接收端A在9999端口等待接收数据(receive)
发送端B向接收端A发送数据"hello,明天吃火锅~"
接收端A接收到发送端B发送的数据,回复"好的,明天见".再退出发送端接收回复的数据,再退出

public class UDPReceiverA {
public static void main(String[] args) throws IOException {
//1. 创建一个 DatagramSocket 对象,准备在 9999 接收数据
DatagramSocket socket = new DatagramSocket(9999);
//2. 构建一个 DatagramPacket 对象,准备接收数据
// 在前面讲解 UDP 协议时,老师说过一个数据包最大 64k
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//3. 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
// 填充到 packet 对象
//老师提示: 当有数据包发送到 本机的 9999 端口时,就会接收到数据
// 如果没有数据包发送到 本机的 9999 端口, 就会阻塞等待. System.out.println("接收端 A 等待接收数据..");
socket.receive(packet);
//4. 可以把 packet 进行拆包,取出数据,并显示. int length = packet.getLength();//实际接收到的数据字节长度
byte[] data = packet.getData();//接收到数据
String s = new String(data, 0, length);
System.out.println(s);
//===回复信息给 B 端
//将需要发送的数据,封装到 DatagramPacket 对象
data = "好的, 明天见".getBytes();
//说明: 封装的 DatagramPacket 对象 data 内容字节数组 , data.length , 主机(IP) , 端口
packet =
new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9998);
socket.send(packet);//发送
//5. 关闭资源
socket.close();
System.out.println("A 端退出...");
}
}


public class UDPSenderB {
public static void main(String[] args) throws IOException {
//1.创建 DatagramSocket 对象,准备在 9998 端口 接收数据
DatagramSocket socket = new DatagramSocket(9998);
//2. 将需要发送的数据,封装到 DatagramPacket 对象
byte[] data = "hello 明天吃火锅~".getBytes(); //
//说明: 封装的 DatagramPacket 对象 data 内容字节数组 , data.length , 主机(IP) , 端口
DatagramPacket packet =
new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9999);
socket.send(packet);
//3.=== 接收从 A 端回复的信息
//(1) 构建一个 DatagramPacket 对象,准备接收数据
// 在前面讲解 UDP 协议时,老师说过一个数据包最大 64k
byte[] buf = new byte[1024];
packet = new DatagramPacket(buf, buf.length);
//(2) 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
// 填充到 packet 对象
//老师提示: 当有数据包发送到 本机的 9998 端口时,就会接收到数据
// 如果没有数据包发送到 本机的 9998 端口, 就会阻塞等待. socket.receive(packet);
//(3) 可以把 packet 进行拆包,取出数据,并显示. int length = packet.getLength();//实际接收到的数据字节长度
data = packet.getData();//接收到数据
String s = new String(data, 0, length);
System.out.println(s);
//关闭资源
socket.close();
System.out.println("B 端退出");
}
}

2 反射

2.1 反射原理

1):加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射

 2):反射机制可以完成:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时得到任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

3):反射相关的主要类:

  • java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法

4):反射使用灵活,但是使用反射基本是解释执行,对执行速度有影响

5):反射优化-关闭访问检查

  • Method和Field、Constructor对象都有setAccessible()方法setAccessible作用是启动和禁用访问安全检查的开关
  • 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。
  • 参数值为false则表示反射的对象执行访问检查
     

2.2 Class类

1):基本介绍:

  • Class也是类,因此也继承Object类[类图]
  • Class类对象不是new出来的,而是系统创建的[演示]
  • 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次[演示]
  • 每个类的实例都会记得自己是由哪个Class 实例所生成
  • 通过Class可以完整地得到一个类的完整结构,通过一系列API
  • Class对象是存放在堆的
  • 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,变量名,方法名,访问权限等等)

2):常用方法:

  • static Class forName(String name),返回指定类名name的Class对象
  • Object newlnstance(),调用缺省构造函数,返回该Class对象的一个实例
  • getName(),返回此Class对象所表示的实体(类、接口、数组类、基本类型等)名莉
  • Class [] getlnterfaces(),获取当前Class对象的接口
  • ClassLoader getClassLoader(),返回该类的类加载器
  • Class getSuperclass(),返回表示此Class所表示的实体的超类的Class
  • Constructor[] getConstructors(),返回一个包含某些Constructor对象的数组
  • Field[] getFields(),返回Field对象的一个数组
  • Method getMethod(),返回一个Method对象,此对象的形参类型为paramType

3):如下类型有Class对象:

  • 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  • interface:接口 
  • 数组 
  • enum:枚举 
  • annotation:注解基本数据类型 
  • void 
Class<String> cls1 = String.class;//外部类
Class<Serializable> cls2 = Serializable.class;//接口
Class<Integer[]> cls3 = Integer[].class;//数组
Class<float[][]> cls4 = float[][].class;//二维数组
Class<Deprecated> cls5 = Deprecated.class;//注解
Class<Thread.State> cls6 = Thread.State.class;//枚举
Class<Long> cls7 = long.class;//基本数据类型
Class<Void> cls8 = void.class;//void 数据类型
Class<Class> cls9 = Class.class;

2.3 类加载

1):静态加载:编译时加载相关的类,某个类不存在就报错,依赖性太强,new Dog()就是静态加载

2):动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性,反射就是动态加载,Class.forName("Dog")

3):类加载的时机:

  • 当创建对象的时候//静态加载
  • 当子类被加载时父类也加载//静态加载
  • 调用类中的静态成员时//静态加载
  • 通过反射//动态加载
  • 类只加载一次(三个阶段:加载[不同的数据源转成二进制字节流加载到内存中]->连接(验证[确保Class文件的字节流符合要求]、准备[默认初始化,准备的时候:只有静态的才分配空间、常量直接赋值]、解析[符号引用替换为地址的直接引用])->初始化[真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程。])
  • <clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。

2.4 通过反射获取类的结构信息

1):java.lang.Class类

  1. getName:获取全类名
  2. getSimpleName:获取简单类名
  3. getFields:获取所有public修饰的属性返回Field[],包含本类以及父类的
  4. getDeclaredFields:获取本类中所有属性包括私有的包括私有的
  5. getMethods:获取所有public修饰的方法返回Method[],包含本类的
  6. getDeclaredMethods:获取本类中所有方法包括私有的
  7. getConstructors:获取所有public修饰的构造器,包含本类以及父类的
  8. getDeclaredConstructors:获取本类中所有构造器包括私有的
  9. getPackage:以Package形式返回包信息
  10. getSuperClass:以Class形式返回父类信息.
  11. getlnterfaces:以Class[]形式返回接口信息
  12. getAnnotations:以Annotation[]形式返回注解信息

2):java.lang.reflect.Field

  1. getModifiers: 以int形式返回修饰,[说明:默认修饰符是0,public是1,private是2,protected是4,static是8 ,final是16]多个就是相加
  2. getType:以Class形式返回类型
  3. getName:返回属性名

3):java.lang.reflect.Method

  1. getModifiers:以int形式返回修饰,[说明:默认修饰符是0,public是1,private是2,protected是4static是8, final是16]
  2. getReturnType:以Class形式获取返回类型
  3. getName:返回方法名
  4. getParameterTypes:以Class[]返回参数类型数组

4):java.lang.reflect.Constructor

  1. getModifiers: 以int形式返回修饰符
  2. getName:返回构造器名(全类名)
  3. getParameterTypes:以Class[]返回参数类型数组

2.5 通过反射创建对象

1):调用类中的public修饰的无参构造器

Class相关方法:

  • newlnstance :调用类中的无参获取对应类的对象

2):抵用类中的指定构造器

Class相关方法:

  • getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对
  • getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器对象

3):Constructor类相关方法:

  • setAccessible:暴破(破除私有的方法)
  • newlnstance(Object...obj):调用构造器
//1. 先获取到 User 类的 Class 对象
Class<?> userClass = Class.forName("com.hspedu.reflection.User");
//2. 通过 public 的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
//3. 通过 public 的有参构造器创建实例
/*
constructor 对象就是
public User(String name) {//public 的有参构造器
this.name = name;
}
*/
//3.1 先得到对应构造器
Constructor<?> constructor = userClass.getConstructor(String.class);
//3.2 创建实例,并传入实参
Object hsp = constructor.newInstance("hsp");
System.out.println("hsp=" + hsp);
//4. 通过非 public 的有参构造器创建实例
//4.1 得到 private 的构造器对象
Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
//4.2 创建实例
//暴破【暴力破解】 , 使用反射可以访问 private 构造器/方法/属性, 反射面前,都是纸老虎
constructor1.setAccessible(true);
Object user2 = constructor1.newInstance(100, "张三丰");
System.out.println("user2=" + user2);

2.6 通过反射访问类中成员

1):访问属性:o表示对象

  • 根据属性名获取Field对象:Field f = Class对象.getDeclaredField(属性名);
  • 暴破: f.setAccessible(true);//f 是Field
  • 访问:f.set(o,值)设置值、f.get(o);
  • 如果是静态属性,则set和get中的参数o,可以写成null
  • //1. 得到 Student 类对应的 Class 对象
    Class<?> stuClass = Class.forName("com.hspedu.reflection.Student");
    //2. 创建对象
    Object o = stuClass.newInstance();//o 的运行类型就是 Student
    System.out.println(o.getClass());//Student
    //3. 使用反射得到 age 属性对象
    Field age = stuClass.getField("age");
    age.set(o, 88);//通过反射来操作属性
    System.out.println(o);//
    System.out.println(age.get(o));//返回 age 属性的值
    //4. 使用反射操作 name 属性
    Field name = stuClass.getDeclaredField("name");
    //对 name 进行暴破, 可以操作 private 属性
    name.setAccessible(true);
    //name.set(o, "老韩");
    name.set(null, "老韩~");//因为 name 是 static 属性,因此 o 也可以写出 null
    System.out.println(o);
    System.out.println(name.get(o)); //获取属性值
    System.out.println(name.get(null));//获取属性值, 要求 name是static

    2):访问方法

  • 根据方法名和参数列表获取Method方法对象: Method m =
  • clazz.getDeclaredMethod(方法名,XX.class);//得到本类的所有方法
  • 获取对象:Object o=clazz.newlnstance);
  • 暴破:m.setAccessible(true);
  • 访问:Object returnValue = m.invoke(o,实参列表);//o就是对象
  • 注意:如果是静态方法,则invoke的参数o,可以写成null!
  • 1. 得到 Boss 类对应的 Class 对象
    Class<?> bossCls = Class.forName("com.hspedu.reflection.Boss");
    //2. 创建对象
    Object o = bossCls.newInstance();
    //3. 调用 public 的 hi 方法
    //Method hi = bossCls.getMethod("hi", String.class);//OK
    //3.1 得到 hi 方法对象
    Method hi = bossCls.getDeclaredMethod("hi", String.class);//OK
    //3.2 调用
    hi.invoke(o, "韩顺平教育~");
    //4. 调用 private static 方法
    //4.1 得到 say 方法对象
    Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
    //4.2 因为 say 方法是 private, 所以需要暴破,原理和前面讲的构造器和属性一样
    say.setAccessible(true);
    System.out.println(say.invoke(o, 100, "张三", '男'));
    //4.3 因为 say 方法是 static 的,还可以这样调用 ,可以传入 null
    System.out.println(say.invoke(null, 200, "李四", '女'));
    //5. 在反射中,如果方法有返回值,统一返回 Object , 但是他运行类型和方法定义的返回类型一致
    Object reVal = say.invoke(null, 300, "王五", '男');
    System.out.println("reVal 的运行类型=" + reVal.getClass())
    //在演示一个返回的案例
    Method m1 = bossCls.getDeclaredMethod("m1");
    Object reVal2 = m1.invoke(o);
    System.out.println("reVal2 的运行类型=" + reVal2.getClass());//Monster
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值