网络编程
软件结构
- C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
- B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机
的通信的程序。
网络通信协议
- 网络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。这就
好比在道路中行驶的汽车一定要遵守交通规则一样,协议中对数据的传输格式、传输速率、传输步骤等做了
统一规定,通信双方必须同时遵守,最终完成数据交换。 - TCP/IP协议: 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是
Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它
的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的
协议来完成自己的需求。
协议分类
通信的协议还是比较复杂的, java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这
些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net
包中提供了两种常见的网络协议的支持:
- TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,
在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。 - UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。每次发送的数据最大为64kb
TCP
TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
步骤:
- 服务端程序,需要事先启动,等待客户端的连接。
- 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
在Java中,提供了两个类用于实现TCP通信程序:
- 客户端: java.net.Socket 类表示。创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建
立连接开始通信。 - 服务端: java.net.ServerSocket 类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端
的连接。
Socket类
Socket
类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
构造方法
public Socket(String host, int port)
:创建套接字对象并将其连接到指定主机上的指定端口号。如果指
定的host是null ,则相当于指定地址为回送地址。
成员方法
public InputStream getInputStream()
: 返回此套接字的输入流。
- 如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
- 关闭生成的InputStream也将关闭相关的Socket。
public OutputStream getOutputStream()
: 返回此套接字的输出流。
- 如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
- 关闭生成的OutputStream也将关闭相关的Socket。
public void close()
:关闭此套接字。
- 一旦一个socket被关闭,它不可再使用。
- 关闭此socket也将关闭相关的InputStream和OutputStream 。
public void shutdownOutput()
: 禁用此套接字的输出流。
- 任何先前写出的数据将被发送,随后终止输出流。相当于给服务端那边发送了个EOF
ServerSocket类
ServerSocket
类:这个类实现了服务器套接字,该对象等待通过网络的请求。
构造方法
public ServerSocket(int port)
:使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指
定的端口号上,参数port就是端口号。
ServerSocket server = new ServerSocket(6666);
成员方法
public Socket accept()
:侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法
会一直阻塞直到建立连接。
Junit
Junit是一个Java语言的单元测试框架,属于白盒测试,简单理解为可以用于取代java的main方法。Junit属于第三
方工具,需要导入jar包后使用。
使用
- 编写测试类,简单理解Junit可以用于取代java的main方法
- 在测试类方法上添加注解 @Test
- @Test修饰的方法要求:public void 方法名() {…} ,方法名自定义建议test开头,没有参数。
- 添加Junit库到lib文件夹中,然后进行jar包关联
- 使用:点击方法左侧绿色箭头,执行当前方法(方法必须标记@Test)。执行结果红色:代表失败;执行结果
绿色:代表成功
哪个方法想使用单元测试,就在方法上,添加注解: @Test
注意:
- 该方法的返回值类型,必须写为void
- 该方法必须没有参数列表
- 不能用static修饰
运行:方法上右键运行,运行的是含有@Test注解的方法。类上右键运行,运行的是类当中含有@Test注解的所有方
法
绿条: 正常运行
红条: 出现问题,异常了
常用注解
- @Test,用于修饰需要执行的测试方法
- @Before,修饰的方法会在测试方法之前被自动执行
- @After,修饰的方法会在测试方法执行之后自动被执行
另外,IDEA本身就集成了这个功能
反射
类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进
行初始化。
加载
- 就是指将class文件读入内存,并为之创建一个Class对象。
- 任何类被使用时系统都会建立一个Class对象
连接
- 验证是否有正确的内部结构,并和其他类协调一致
- 准备负责为类的静态成员分配内存,并设置默认初始化值
- 解析将类的二进制数据中的符号引用替换为直接引用
初始化
详情见下面
初始化
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来
使用这些class文件中的内容呢? 这就是我们反射要研究的内容。
概述
框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
反射:将java代码的各个组成部分封装为其他对象,可以在程序运行过程中操作这些对象,这就是java的反射
机制,如下图。
反射的好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
注意:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的
Class对象都是同一个。
获取Class对象的方式
获取class对象方式 | 作用 | 应用场景 |
---|---|---|
Class.forName("全类名") | 通过指定的字符串路径获取 | 多用于配置文件,将类名定义在配置文件中。读取文件,加载类 |
类名.class | 通过类名的属性class获取 | 多用于参数的传递 |
对象.getClass() | 通过对象的getClass()方法获取 | 多用于对象的获取字节码的方式 |
建议使用第一种方式获取Class对象
Class对象相关方法
- String getSimpleName(); 获得简单类名,只是类名,没有包
. String getName(); 获取完整类名,包含包名+类名 - T newInstance() ;创建此 Class 对象所表示的类的一个新实例。要求:类必须有public的无参数构造方法
Constructor类
Constructor是构造方法类,类中的每一个构造方法都是Constructor的对象,通过Constructor对象可以实例化对
象。
成员方法
public Constructor[] getConstructors()
: 获取public修饰的所有的构造方法每个构造方法被封装成了一个Constructor类型的对象,被存储数组中 注意: Constructor类,专门用来描述构造方法的
public Constructor<T> getConstructor(Class... parameterTypes)
: 获取指定参数类型的public修饰的构造方法如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常。 参数: Class... parameterTypes: 必须传递数据类型对应的Class对象,而且是可变参数,可以传递数组,参数列表,不传递(获取空参)
比如: 获取 构造方法 public Person(String name, int age) { ...} 对应的Construtor对象
参数列表: String.class,int.class
Constructor<?> con2 = c.getConstructor(String.class, int.class);
System.out.println(con2);
私有方法也能通过其他方法反射出来,但最好不要这么做,因为破坏了封装性
获取步骤
- 获取Class类型的对象(三种方式,建议使用第三种forName)
- 通过Class类型的对象获取构造方法对象
利用反射得到的构造方法构造对象
T newInstance(Object... initargs):根据指定参数创建对象。
public class Demo06NewInstance { public static void main(String[] args) throws Exception { //1.获取Class类型的对象(三种方式,建议使用第三种forName) Class<?> c = Class.forName("domain.Person"); //2.通过Class类型的对象获取带参构造方法对象 //获取满参构造方法对象 Constructor<?> con = c.getConstructor(String.class, int.class); //3.执行带参构造方法对象,创建一个具体的对象 Person p = (Person)con.newInstance("张三",20); System.out.println(p); } }
T newInstance():空参构造方法创建对象。返回值:Object类型: 被创建出来的对象,提升为Object类型
public class Demo05NewInstance { public static void main(String[] args) throws Exception { //1.获取Class类型的对象(三种方式,建议使用第三种forName) //domain是包名 Class<?> c = Class.forName("domain.Person"); //2.通过Class类型的对象获取空参构造方法对象 Constructor<?> con = c.getConstructor(); //3.执行构造方法对象,创建一个具体的对象 //执行的是空参构造方法对象,不需要传递参数 Person p = (Person)con.newInstance(); System.out.println(p);//Person{name='null', age=0} } }
上面两个是Constructor类的方法,这个是Class类的方法,直接调用的空参构造
public T newInstance(): 执行空参构造方法,创建一个对象
内部原理:
1.内部会获取空参构造方法对象,要求类中必须定义空参构造 2.空参构造方法对象调用newInstance(Construct类中的),创建一个对象
public class Demo07NewInstance { public static void main(String[] args) throws Exception { //1.获取Class类型的对象(三种方式,建议使用第三种forName) Class<?> c = Class.forName("domain.Person"); //2.java.lang.Class类 成员方法: newInstance Person p = (Person)c.newInstance(); System.out.println(p); } }
Method类
Method是方法类,类中的每一个方法都是Method的对象,通过Method对象可以调用方法。
成员方法
- public Method[] getMethods(): 获取所有public修饰的成员方法,包含继承下来的每个成员方法被封装成了一个Method对象,存储Method数组中 java.lang.reflect.Method类: 是用来描述成员方法的
- public Method getMethod(String name, Class... parameterTypes):获取public修饰的指定方法名称,指定参数类型对应的成员方法对象
参数:
String name: 方法名称
Class... parameterTypes: 必须传递数据类型对应的Class对象,而且是可变参数,可以传递数组,参数列表,不传递(获取空参) 例子
public class Demo04GetMethod { public static void main(String[] args) throws Exception { //1.获取Class类型的对象(三种方式,建议使用第三种forName) Class<?> c = Class.forName("domain.Person"); //2.获取所有public修饰的成员方法,包含继承下来的 Method[] ms = c.getMethods(); for (Method m : ms) { System.out.println(m); } System.out.println("--------------"); //获取public修饰的名称为toString的没有参数的方法 Method m1 = c.getMethod("toString"); System.out.println(m1); //获取public修饰的名称为setName的参数为String类型的方法 Method m2 = c.getMethod("setName", String.class); System.out.println(m2); // 获取public修饰的名称为getSum的参数为两个int类型的方法 //getSum是private修饰的,无法获取,报出异常 //Method m3 = c.getMethod("getSum", int.class, int.class); //System.out.println(m3); //获取public修饰的名称为my2CharArray的参数为String类型的方法 Method m4 = c.getMethod("my2CharArray", String.class); System.out.println(m4); } }
获取步骤
- 获取Class类型的对象(三种方式,建议使用第三种forName)
- 用上一步获取的Class对象使用成员方法获取Method对象
利用反射回来的对象调用方法
public Object invoke(Object obj,Object... args)
: 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。(invoke 中文表示调用的意思)参数:
1.Object obj: 成员方法的执行,必须有对象的支持
2.Object... args: 执行方法时,该方法需要的具体的参数
返回值: java.lang.Object类型
方法执行的结果,被封装成一个Object返回
1.没有返回值的方法: 返回一个null
2.有返回值的方法: Object对象中,封装了具体的结果
public class Demo05Invoke { public static void main(String[] args) throws Exception { //1.获取Class类型的对象(三种方式,建议使用第三种forName) Class<?> c = Class.forName("domain.Person"); //成员方法的调用,需要对象的支持 //快捷方式,创建一个对象 Object obj = c.newInstance(); //2.获取setName方法对象 Method setNameMethod = c.getMethod("setName", String.class); //3.执行setName方法对象 Object result = setNameMethod.invoke(obj, "柳岩"); System.out.println(result);//null //System.out.println(obj); //2.获取getName方法对象 Method getNameMethod = c.getMethod("getName"); //3.执行getName方法对象 result = getNameMethod.invoke(obj); System.out.println(result); } }