十、网络编程
计算机网络:
总结:将地理位置不同的计算机设备进行连接,实现数据的共享。
网络编程:
总结:在已有网络的环境下,按照规定实现接收和发送数据即可。
中间过程对于我们不需要关注。
网络模型:
1.网络编程三要素:
1. IP地址:
计算网络中,每个计算机的唯一标识。
IPV4: 由32bit组成,即4个字节,每个字节范围0~255,4个数字组成。
IPV6: 有128bit组成,即16个字节,由8组十六进制数组成
DNS:域名解析器 记忆IP地址不方便,出现了使用名称的表示方式
负责解析名称的,最终转为IP地址形式。
本地回还地址:127.0.0.1 localhost
2. 端口号:
计算中每个应用程序的标识,范围是0~65535 ,其中0~1023被系统占用
3. 协议:
双方通信的规则。
常用:UDP 和 TCP
UDP:
用户数据报包协议
面向无连接,不需要与接收端建立连接,直接发送数据。
不可靠协议,因为数据有可能会丢失,效率高。
如:cts、会议软件、发快递
TCP:
传输控制协议
面向有连接,必须双方建立连接受才能进行数据传输。
可靠协议,不会有数据丢,效率低。
确定连接:
三次握手模式确定连接。
A 发 B 接
第一次,A--->B
第二次:B接到,并给A回信息
第三次:A接到
比如:打电话
InetAddress
在java中是面向对象思想,因此表示计算机唯一标识的这些数字也看成是一类事物,因此就有对应的对象进行描述--InetAddress。
java.net包。
方法:
InetAddress getByName(String host) :根据host获取InetAddress对象
host:可以是主机名或主机的IP地址形式
InetAddress getLocalhost() : 获取本地主机的InetAddress对象
String getHostName(): 获取主机名
String getHostAddress(): 获取主机地址
2.UDP
UDP协议
//创建InetAddress对象 //没有构造方法,只有几个静态方法可以返回这一个InetAddress 类型的对象 InetAddress inetAddress = InetAddress.getByName("baidu.com");//可以是机器名称,可以是IP地址 System.out.println(inetAddress); String hostName = inetAddress.getHostName(); System.out.println(hostName);
1.发送
两端:接收端 和 发送端
端点对象: Datagramsocket
包对象: DatagramPacket
发送端:
1.创建Datagramsocket对象
2.创建DatagramPacket对象,并将数据和接收端信息封装到对象中
3.调用send方法发送
4.关闭
//创建端点对象 DatagramSocket ds = new DatagramSocket(); //1.创建包对象 //2.设置接收端信息,内容信息 byte[] massage = "你好".getBytes(); int len = massage.length; InetAddress ip = InetAddress.getByName("local"); int port = 9999; //设置四个参数,发送的信息(字节数组类型)、长度、对方的ip、对方的端口号 DatagramPacket dp = new DatagramPacket(massage,len,ip,port); //3.发包 ds.send(dp); System.out.println("数据已发送"); //4.关闭 ds.close();
2.接收
接收段:
1.创建Datagramsocket对象,必须指定端口,要与发送端中的包中的端口号要一致
2.创建DatagramPacket对象,用于接受数据
3.调用receive()方法接包
4.拆包
5.关闭
//1.创建对象并且设置端口号 DatagramSocket ds = new DatagramSocket(9999); //2.准备接收 //创建数组准备接收 byte[] by = new byte[1024]; //获取数组长度 int len = by.length; DatagramPacket dp = new DatagramPacket(by,len); //使用这个包获取 ds.receive(dp); byte[] data = dp.getData(); String s = new String(data,0,dp.getLength()); System.out.println(s); //关闭 ds.close();
案例实现(网聊):
//先发后收,设置死循环,特定字符串退出 public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); DatagramSocket ds = new DatagramSocket(); while(true){ //发 System.out.print("小明说:"); String s = sc.next(); byte[] by = s.getBytes(); DatagramPacket dp = new DatagramPacket(by,by.length, InetAddress.getLocalHost(),8888); ds.send(dp); if ("exit".equals(s)){ System.exit(0); } //收 byte[] by1 = new byte[1024]; DatagramPacket dp1 = new DatagramPacket(by1,1024); ds.receive(dp1); byte[] data = dp1.getData(); String s1 = new String(data,0,dp1.getLength()); System.out.print("小红说:"); System.out.println(s1); } }
//现接收,后发送 public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); DatagramSocket ds = new DatagramSocket(8888); while (true){ //收 byte[] by = new byte[1024]; DatagramPacket dp = new DatagramPacket(by,1024); //创建包 ds.receive(dp); //使用这个包接收数据 byte[] data = dp.getData(); //创建字节数字并使用包获得数据 String s = new String(data,0,dp.getLength()); //转换为字符串 if ("exit".equals(s)){ System.exit(0); } System.out.print("小明说:"+s); System.out.println(); //发 System.out.print("小红说:"); String s1 = sc.next(); byte[] by1 = s1.getBytes(); DatagramPacket dp1 = new DatagramPacket(by1,by1.length, dp.getAddress(),dp.getPort()); ds.send(dp1); } }
3.广播和组(多)播
UDP协议: 单播 广播 组播
广播:
一个主机可以与该网络内中的所有主机进行数据共享。
广播地址:255.255.255.255
//广播发送 public static void main(String[] args) throws IOException { //创建对象 DatagramSocket ds = new DatagramSocket(); //准备发送 //创建数组 byte[] by = "你好".getBytes(); int len = by.length; InetAddress ip = InetAddress.getByName("255.255.255.255"); int port = 8080; DatagramPacket dp = new DatagramPacket(by,len,ip,port); ds.send(dp); System.out.println("发送完毕"); ds.close(); }
//广播接收 public static void main(String[] args) throws IOException { //创建对象 DatagramSocket ds = new DatagramSocket(8080); //准备接收 byte[] by = new byte[1024]; DatagramPacket dp = new DatagramPacket(by,1024); ds.receive(dp); //拆包 byte[] data = dp.getData(); int len = dp.getLength(); System.out.println("对方发的内容是:"+ new String(data,0,len)); System.out.println("对方的端口号是"+ dp.getPort()); System.out.println("对方的地址是:"+ dp.getAddress().getHostName()); }
4.组播
//发送 public static void main(String[] args) throws IOException { //创建对象 MulticastSocket ms = new MulticastSocket(); //准备发送 byte[] by = "网络组播".getBytes(); int len = by.length; InetAddress ip = InetAddress.getByName("224.0.0.23"); int port = 8080; DatagramPacket dp = new DatagramPacket(by,len,ip,port); //发送 ms.send(dp); //关闭 ms.close(); }
//接收 public static void main(String[] args) throws IOException { //创建对象 MulticastSocket ms = new MulticastSocket(8080); InetAddress ip = InetAddress.getByName("224.0.0.23"); //小组ip //加入组播 ms.joinGroup(ip); while (true){ //准备接收 byte[] by = new byte[1024]; DatagramPacket dp = new DatagramPacket(by,1024); ms.receive(dp); //显示数据 byte[] data = dp.getData(); int len = dp.getLength(); System.out.println(new String(data,0,len)); System.out.println("对方的ip是:"+ dp.getAddress().getHostName()); System.out.println("对方的端口号是:" + dp.getPort()); } }
3.TCP
特点:面向有连接,可靠协议。
两端:客户端和服务端。
客户端:Socket 服务端:ServerSocket
客户端:
1.创建套接字对象,邦定地址,端口号(服务端地址和端口号),socket参数一定要写服务端的ip和端口
2.获取输出流对象,向服务端写数据
3.关闭套接字
1.数据的传输与接送
//发送 public static void main(String[] args) throws IOException { //客户端发消息 Socket s = new Socket("localhost",8888); //读入写出,获取输出流对象 OutputStream os = s.getOutputStream(); os.write("你好".getBytes()); System.out.println("发送完毕"); //关闭 s.close(); os.close(); }
服务端:
1.创建服务端端点对象,并邦定端口号(与客户端的端口号要一致)
2.获取Socket对象(侦听要连接的客户端)
3.获取输入流对象,读取客户端数据
4.关闭服务端端点对象
ServerSocket构造方法:
//接收 public static void main(String[] args) throws IOException { //创建套接字对象 ServerSocket ss = new ServerSocket(8888); //创建侦听器,识别来源信息 Socket s = ss.accept(); //读入写出,创建输入流对象 InputStream is = s.getInputStream(); //创建数组准备接收 byte[] by = new byte[1024]; int len = is.read(by); //输出 String s1 = new String(by,0,len); System.out.println(s1); System.out.println("对方的ip是"+ s.getInetAddress().getHostName()); System.out.println("对方的端口号是:"+ s.getPort()); //关闭 is.close(); s.close(); }
2.文件的传输与接送
//客户端发文件 public static void main(String[] args) throws IOException { //上传文件---客户端 Socket s = new Socket("localhost",9090); //获取输出流对象 OutputStream os = s.getOutputStream(); //定义一个输入流,关联要上传的文件 FileInputStream fis = new FileInputStream("E:\\0622系统班\\day24\\a.txt"); byte[] buf = new byte[1024]; int len ; while((len = fis.read(buf)) != -1){ os.write(buf,0,len); } //客户端清楚数据都已发送,但是服务端并不知道数据都已接收 //需要客户端给服务端发送一个结束的标志 //发送结束标志给服务端 s.shutdownOutput(); //关闭流 fis.close(); System.out.println("--------文件已发送------------"); //读取服务单回送的消息 //获取输入流对象 InputStream is = s.getInputStream(); byte[] by = new byte[1024]; int num = is.read(by); System.out.println(new String(by,0,num)); //关闭 s.close(); }
//服务端 public static void main(String[] args) throws IOException { //上传文件---服务端 ServerSocket ss = new ServerSocket(9900); Socket s = ss.accept(); //定义输出流,用于保存客户端送的文件数据 //文件重名问题 String name = UUID.randomUUID().toString().replace("-", ""); File file = new File("F:\\IdeaProjects\\0622\\day23\\"+name+"txt"); if (file.exists()){ name = UUID.randomUUID().toString().replace("-", ""); file = new File("F:\\IdeaProjects\\0622\\day23\\"+name+"txt"); } //获取输入流对象 InputStream is = s.getInputStream(); byte[] buf = new byte[1024]; int len; //read是一个阻塞式方法,客户端并没有发送结束标志,就会一直等待读取数据 //因此循环不结束 while((len = is.read(buf)) != -1){ fos.write(buf,0,len); } //关闭定义的流 fos.close(); System.out.println("-----------------文件已接收-----------------"); //客户端回送消息 //获取输出流对象 OutputStream os = s.getOutputStream(); os.write("文件已上传成功".getBytes()); //关闭 s.close(); ss.close(); }
3.多线程下的服务端
public class TCPThreadServer { public static void main(String[] args) throws IOException { //多线程模式下的服务端 ServerSocket ss = new ServerSocket(8080); while(true){ //侦听连接的客户端 Socket s = ss.accept(); //侦听一定要放到循环里边,因为发送者是很多人,每个发送者的信息不一样 Thread t = new Thread(new MyRun(s)); t.start(); } //ss.close(); } } class MyRun implements Runnable{ private Socket s; public MyRun(Socket s){ this.s = s; } @Override public void run() { try { //获取输入流对象 InputStream is = s.getInputStream(); byte[] buf = new byte[1024]; int len = is.read(buf); System.out.println("客户端发送的数据是:"+new String(buf,0,len)); System.out.println("客户端的端口号是:"+s.getPort()); System.out.println("客户端的地址是:"+s.getInetAddress().getHostAddress()); System.out.println("------------------------------------------"); //回送消息 s.getOutputStream().write("数据已收到".getBytes()); }catch (IOException e){ System.out.println(e); }finally { if(s != null) { try { s.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
十一、反射
1.类加载
1.类加载过程
概念: 将class文件从磁盘到内存中,并获取class文件中的数据,对这些数据进行解析,转换等操作,最终成为可使用的java类型,这个过程叫做---类加载过程。
这些过程中: 加载、连接、初始化、使用、卸载
加载:
将class文件从磁盘内存中,为当前这个class文件自动创建一个Class对象。
连接:
验证class文件中内部结构,将一些符号名称转为对应的数据..
初始化:
对类变量进行初始化。
2.什么时候类加载
1.创建该类型对象
2.访问静态成员时
3.获取Class对象时
4.创建子类对象时,父类class文件
5.执行java.exe命令
3.类加载器
概念:类加载器是一个对象,作用就是用来加载class文件的,从磁盘到内存,
并为该class文件,创建一个Class对象。
分类:
Bootstrap ClassLoader :引导类加载器 父
Extension ClassLoader: 扩展类加载器 子
Application ClassLoader:系统类加载器 子子
自定义类加载器,定义类继承ClassLoader。
加载类时,采用的是一种委托机制。
加载类时,会逐级向上委托(App-->ext-->boot,此时不会搜索该类是否存在),boot没有父类,此时boot会查找它的包含范围中是否有该class,如果有就加载,如果没有找ext,ext会查找它的包含范围中是否有该class,如果有就加载,如果没有找app,查找它的包含范围中是否有该class,如果有就加载,如果没有抛出异常,ClassNotFoundException。
Hello.class文件要加载,先逐级委托,委托到boot,搜索boot中是否有hello,如果有就加载,没有去找ext中否是有,有就加载,没有找app,如果有加载,没有抛异常。
2.反射技术
步骤:
\1. 获取Class对象,该Class对象代表这个一个class文件
\2. 获取Class的对象表示的class文件中的内容,并对这些内容进行想要的操作。
反射技术的源头:Class对象。
1.获取Class对象:
1.已有对象的情况下: getClass()
Class c1 = p1.getClass();
2.已知类型名的情况下: 类型名.class
Class c2 = Person.class;
3.已知全类名的情况下: Class.forName(“包名.类名”)
Class c3 = Class.forName("Person");
2.构造方法:
1.获取构造方法
-
getConstructors() 获取所有公共的构造方法,返回一个Constructor数组
Constructor[] cons = c1.getConstructors();
2.getDeclaredConstructors() 获取所有权限的构造方法,返回一个Constructor数组
Constructor[] cons1 = c1.getDeclaredConstructors()
3. getConstructor(String.class,int.class) 已知参数 获取单个公共构造方法
Constructor c1 = c.getConstructor();
4.getDeclaredConstructor(String.class,int.class) 已知参数获取单个构造方法
Constructor c2 = c.getDeclaredConstructor(String.class,int.class)
2.根据构造方法创建实例对象
T newInstance(); 根据指定的构造方法创建对象
基本数据类型也是可以通过 .calss 得到相应的Class类型
1.直接通过Class对象创建实例对象
Person p1 = c1.newInstance(); //前提是这个类有构造方法
2.通过得到的构造方法对象创建实例对象
Constructor con1 = c1.getConstructor(); Person p2 = con1.newInstance();
3.根据有参构造方法创建实例对象,如果方法是private修饰过的属性,需要使用暴力反射
// c2.setAccessible(true) 参数为true代表取消访问检查,可以操作私有成员 Constructor c2 = c.getDeclaredConstructor(String.class,int.class) Person p3 = c2.newInstance("小明", 20)
3.File字段
1.获取字段
1. getFields() 获取所有公共属性,包括父类的,返回一个Field数组
Field[] f1 = c.getFields();
2. getDeclaredFields() 获取所有当前类的属性,返回一个Field数组
Field[] f2 = c.getDeclaredFields();
3. getField(String name) 获取单个公共属性
Field ff1 = c.getField("b");
4. getDeclaredField(String name) 获取单个属性,忽略权限修饰
Field ff2 = c.getDeclaredField("name");
2.使用字段赋值
-
field . set(Object obj ,Object value) 给某个实例对象的每个属性赋值
如果属性是private修饰过的属性,需要使用暴力反射
//创建类对象 Class<Person> c1 = Person.class; //获取属性 Field namefield = c1.getDeclaredField("name"); //给对象的这个属性赋值 //namefield.setAccessible(true); 暴力反射 Person p = c1.newInstance(); namefield.set(p,"小黑");
4.成员方法
1.获取成员方法
-
getMethods() 返回所有公共的成员方法,包括继承的,返回一个method数组
Class c = Class.forName("Person"); Method[] me1 = c.getMethods();
-
getDeclaredMethods() 返回本类中的所有成员方法,返回一个method数组
Method[] me2 = c.getDeclaredMethods();
-
-
getMethod("setName", String.class) 已知方法名和参数返回单个公共成员方法
Method m1 = c.getMethod("setName", String.class);
-
getDeclaredMethod("show") 根据参数返回单个成员方法,忽略权限修饰符
Method m3 = c.getDeclaredMethod("show");
2.使用方法
-
Object 方法.invoke(对象,参数) 返回Object类型
m3.invoke(p);
-
带返回值的方法
String name = (String) m2.invoke(p);
-
如果是private修饰过的方法,需要暴力反射
Method m3 = c.getDeclaredMethod("show"); m3.setAccessible(true); m3.invoke(p);
-