软件结构:client/serve browser/serve
网络编程:一定协议下,两台计算机通信。
网络四层结构:物理层/数据链路层 --> 网络层 --> 传输层 --> 应用层
网络通信分类:
网络编程三要素:
协议
IP地址
cmd --> ipconfig ip配置信息
ping ip地址 ping通可以互发信息;ping失败,不可以
ping 127.0.0.1 本地ip地址
ping localhost 本地服务器ip地址
端口号
网络软件一打开,操作系统回味网络软件分配一个随机的端口号
端口号两个字节组成:0-65535
1024之前的端口号不能使用,已经被系统分配给已知软件
网络软件端口号不能重复使用
常用端口号:
1. 80端口,网络端口
2. 数据库端口号,mysql:3306 oracle:1521、
3. Tomcat服务器:8080
客户端: ip:端口号 Socket类
① OutputStream:发送“你好服务器” ④InputStream
连接通路:IO流对象
服务器端: ip:端口号 ServeSocket类
②InputStream:读取 “你好服务器” ③outputStream:回复
(c/s交互一次需要四个流对象)
实际情况:
1.多个客户端和服务器进行交互,服务器必须明确和哪个客户端进行交互
在服务器端有一个方法,叫accept客户端获取到请求的客户端对象。
2.多个客户端同时和服务器进行交互,就需要使用多个IO流对象
服务器是没有IO流的,服务器可以获取到请求的客户端对象Socket
使用每个客户端Socket提供的IO流和客户端进行交互。
Socket s1 = serve.accept(); Socket s2 = serve.accept();
一言概之:服务器使用客户端的流和客户端交互。
socket
类
TCP通信客户端:向服务器发送连接请求,给服务器发送数据,读取写回数据。
表示客户端的类:Java.net.Socket,此类实现客户端“套接字”。套接字是两台机器间通信的端点。
套接字:包含了IP地址和端口号的网络单位。
构造方法:
Socket(String host, int port):服务器主机名称/服务器的IP地址 服务器的端口号
成员方法:
OutputStream getOutputStream():返回此套接字的输出流。
InputStream getInputStream():返回此套接字的输入流。
void close();关闭此套接字。
使用步骤:
1.创建一个客户端对象Socket,构造方法中绑定服务器的IP地址和端口号。
2.使用Socket对象的方法getOutputStream获取网络字节输出流对象OutputStream。
3.使用网络字节输出流OuStream中的方法write,给服务器发送数据。
4.使用Socket对象的方法getInputStream()获取网络字节输入流InputStream对象
5.使用网络字节输入流的方法read,读取服务器写回的数据。
6.释放资源(socket)
注意事项:
1.客户端和服务器端进行交互必须使用socket中提供的网络流,不能使用自己创建的流对象。
2.创建客户端对象Socket的时候,就回去请求服务器,进行三次握手,建立连接通路。
如果服务器没有启动:会抛出异常。
如果服务器启动了:可以正常建立连接。
TCP服务器端:接收客户端请求,读取客户端数据,回写给客户端数据。
表示服务器的类:java.net.ServeSocket 此类实现服务器套接字。
构造方法:
ServeSocket(int port) 创建绑定到特定端口的服务器套接字。 指定端口号。
服务器端必须明确,是那个客户端请求的服务器,所以可以用accept方法获取到客户端。
成员方法:
Socket accept() 展厅并接受到此套接字的连接。
服务器实现步骤:
1.创建服务器对象 ServeSocket,和系统要指定端口号。
2.使用ServeSocket对象中的方法accept,获取到请求的客户端对象Socket。
3.使用Socket对象的方法getInputStream获取网络字节输入流对象InputStream。
3.使用网络字节输出流InputStream中的方法read,读取客户端发送的数据。
4.使用Socket对象的方法getOutputStream()获取网络字节输出流OutputStream对象
5.使用网络字节输出流的方法write,服务器向客户端写回数据。
6.释放资源(Socket, ServeSocket)
TCP通信文件上传原理
读取本地文件上传到服务器,服务器把上传的文件保存到服务器的硬盘上。
客户端使用本地字节输入流,读取要上传的文件。
客户端使用网络字节输出流,把读取到的文件上传到服务器上。
服务器使用网络字节输入流,读取客户端上传的文件
服务器使用本地字节输出流,把读取到的文件保存到服务器硬盘上,
服务器使用网络字节输入流,给客户端写一个“上传成功”。
客户端使用网络字节输入流,读取服务器写回的数据。
释放资源。
注意:客户端和服务器对本地硬盘读写,需要使用自己创建的字节流对象(本地流)。
客户端和服务器之间进行读写,必须使用Socket中提供的字节流对象(网络流)。
文件上传即文件复制:需要明确 : 数据源+数据目的地。
模拟BS服务器
创建BS版本TCP服务器
概念:有且只有一个抽象方法的接口。函数式接口就是适用于函数式编程场景的接口。
Java中函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利进行推导。 但是可以包含其他的(默认,静态,私有)方法。
格式
修饰符 interface 接口名称{
public abstract 返回值类型 方法名称;//抽象方法的 public abstract可以省略
//其他非抽象方法内容
}
@FunctionalInterface注解
@FunctionalInterface
用来检测是否是一个函数式接口。
自定义函数式接口
函数式接口使用:一般可以作为方法的参数和返回值类型。
浪费的日志案例。(有些代码不会执行,会产生性能浪费。)
体验lambda的更优写法。
lambda特点:延迟加载。
使用前提:必须存在函数式接口。
简言之:只有满足条件了,才会执行Lambda表达式方法体内容!
证明lambda的延迟
jdk提供的函数式接口都放在java.util.function包中
java.util.function.Supplier<T> 接口仅包含一个无参的方法,:T get() .用来获取一个泛型参数指定类型的对象数据。
Supplier<T> 接口称之为生产型接口,指定接口泛型。特有方法:T get();方法产生什么类型数据。
使用lambda表达式。
求int数组最大值。
在函数式接口中写出主要函数体。
java.util.function.Consumer<T> 接口与supplier接口相反,消费一个数据。
特有抽象方法:void accept(T t) 消费一个指定类型的数据。
使用:使用lambda方式实现主要函数体。
默认方法:andThen
作用:需要两个consumer接口,可以把两个consumer接口组合到一起,在对数据进行消费。
例如:consumer<String> con1; consumer<String> con1; String s = "hello";
con1.accept(s); con2.accept(s);
可以使用lambda表达式方法来写,写lambda表达式时需要写两个。。
使用andThen方法: con1.andThen(con2).acccept(s);
谁写前面,谁先消费。可以使用lambda表达式方法来写,写lambda表达式时需要写两个。
练习:格式化打印信息。
将字符串数组的信息分隔开,再拼接打出。
原始数组信息 string[] arr = {"古力娜扎,25","迪丽热巴,23","马儿扎哈,29"};
分割以后按照格式 姓名:name;年龄:age. 打印输出。
【注意区分原始数组中的 逗号 是中文输入还是英文输入。】
java.util.function.Predicate<T>接口。 用来对数据类型进行判断,得到一个boolean值。
抽象方法:boolean test(T t);用来对指定数据类型及逆行判断。真true假false。
对传递参数进行判断。
默认方法:and
表示并且关系,也可以用于连接两个判断条件。&&等价
默认方法:or
表示或关系。等价||。
默认方法:negate
表示取反关系。等价!。
练习:集合信息筛选。
数组中有多条“姓名+性别”信息,通过predicator接口的拼装条件将符合要求的字符串选到集合AeeayList中,需要同时满足两个条件:必须为女生;姓名为四个字。
java.util.function.Function<T,R> 接口用来根据一个类型的数据的到另一个类型的数据,
前者称为前置条件,后者称为后置条件。
抽象方法:R apply(T t)
根据类型T的参数获取类型R的结果。
使用场景:例如将String类型转换为Integer类型。
使用步骤:
1.定义一个方法,方法参数传递一个字符串类型的整数;
2.方法参数传递一个Function接口,泛型使用<String,Integer>
3.使用Function接口中的方法apply,把字符串类型的整数转换为Integer类型的整数。
默认方法:andThen
用来进行组合操作。
案例要求:把string类型转换为Integer类型,转换后的结果+10; 把增加之后的数据转换为String类型。
第一次:string->Integer
第二次:Integer->String
在这里就可以使用andThen方法,把两次转换组合在一起使用。fun1.apply完成第一次转换;fun2.apply完成第二次转换。
练习:自定义函数模型拼接。
使用Function
进行函数模型的拼接,按照顺序需要执行的多个函数操作为:
String str = “古丽娜,20”;
jdk1.8之后出现的
使用stream流,遍历集合,对集合中的数据进行过滤。
list.stream().filter(predicate).filter().....forEach(lambda表达式);
过滤 --> 映射 --> 跳过 —> 计数 -->
先建立模型,等到所有步骤解析完以后才会执行;都得益于lambda表达式的延迟执行。Java中的sttream不会存储元素,而是按需计算。
数据的来源:数组,集合。
与collection操作不同,Stream操作还有两个基本特征:
使用流时通常包括三个步骤:获取一个操作数据源 → 数据转换 → 执行操作获取想要的结果,每次原有的stream对象不变,返回一个新的stream对象(可以有多次转换),流就像链条一样,变成一个管道。
java.util.stream.Stream<T>时java8新加入的常用的个流接口。(这不是一个函数式接口,有许多抽象方法。)
获取一个流的方式:
所有的collection
集合都可以通过Stream
默认方法获取流。
只有collection(list set)集合中有此方法,map集合中没有。
stream<E> stream();
Stream stream1 = list.stream();
Stream stream2 = set.stream();
Set keySet = map.keySet(); //对于map集合,先获取key值的set集合,再获取流
Stream stream3 = keySet.stream();
Collection<String> values = map.values(); //对于map集合,先获取values值的collection集合,再获取流
Stream stream4 = values.stream();
Set<Map.Entry<String,String>> entries = map.entrySet(); //获取键值对(鉴于值得映射关系 entrySet)
Stream<Map.Entry<String,String>> stream5 = entries.stream();
stream
接口的静态方法of
可以获取对应数组的流。
//把数组转换为流
Stream<Integer> stram1 = Stream.of(1,2,3,4,5);
Integer[] arr = {
1,2,3,4,5};
Stream<Integer> stram2 = Stream.of(arr);
延迟方法:返回值类型依旧是本身,支持链式调用。
终结方法:返回值类型不再是本身,不在支持链式调用。本小节中包括方法count
和forEach
。
属于管道流,只能被使用一次。第一个stream调用完毕即关闭,数据就会流转到下一个流。
1. 逐一处理:forEach
void forEach(Consumer<T> con)
方法接受一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
consumer接口是一个消费型的函数式接口,可以传递lambda表达式,消费数据。
概言之:用来遍历流中数据,是一个终结方法,遍历之后不能继续调用stream流中的其他方法。
2. 过滤:filter
Stream filter(Predicate<T> pre);
predicate用于判断的接口,有一个方法boolean test(T t);
3. 映射:map
将流中的元素映射到另外一个流中,需要使用map映射方法
Stream map(Function<T> fun); F接口中有一个方法:R apply(T t).
这种转换就称之为映射。
4. 统计个数:count
如同collection集合当中的size方法一样,流提供count方法来数一数其中的元素个数。
long count(); 终结方法,不能再调用其他stream方法。
5. 取用前几个:limit
limit方法对流进行截取,只截取前几个。延迟方法,只是对流中元素进行截取,返回新流,可以继续调用其他方法。
集合当前长度较小时不进行操作。
Stream limit(long MaxSize);
6. 跳过前几个:skip
Stream skip(long n); //跳过前n个,从第n+1个元素开始。
7. 组合:concat
把两个流组合为一个流。
Stream concat(Stream s1,Stream s2)
返回一个新流,原来两个流的拼接。
题目:现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤︰
1.第一个队伍只要名字为3个字的成员姓名﹔存储到一个新集合中。
2.第一个队伍筛选之后只要前3个人;存储到一个新集合中。
3.第二个队伍只要姓张的成员姓名;存储到一个新集合中。
4.第二个队伍筛选之后不要前2个人;存储到一个新集合中。
5.将两个队伍合并为一个队伍;存储到一个新集合中。
6.根据姓名创建Person对象﹔存储到一个新集合中。
7.打印整个队伍的Person对象信息。
冗余的 lambda
方法引用
改进代码
方法引用符:::
是引用运算符,它所在的表达式称为方法引用。如果lambda表达式要表达的函数已经存在某个方法的视线中,那么可以通过引用双冒号来引用该方法作为lambda的替代者。
lanbda表达式写法:s -> System.out.println(s);
语义:拿到参数经lambda之手,继而传递给System.out.println方法去处理。
方法引用写法:System.out::println;
语义:直接让system.out类中的println方法来取代Lambda。
【注意】:lambda中传递的参数一定是方法引用中那个方法可以接受的类型,否则会抛异常,
通过对象名引用成员方法
使用前提:对象名是已经存在的,成员方法也是已经存在的。就可以使用对象名来引用成员方法。
通过类名称引用静态成员方法。
使用前提:类已经存在;静态成员方法也已经存在。就可以使用类名称引用静态成员方法。
通过super引用成员方法。
存在继承关系,当lambda表达式中需要super调用时,也可以使用方法引用进行替代。
使用super引用父类的成员方法。
super已经存在(子父类继承关系存在);父类成员方法已经存在。
通过this引用成员方法。
this已经存在;本类的成员方法已经存在。
this::本类成员方法。
类的构造器使用。
构造方法的引用。
构造器名称与类名称完全一样,并不固定。构造器的引用使用类名称::new
的格式表示。
构造方法已知;创建对象已知 new。就可以使用person引用new创建对象。
数组的构造器使用。
数组的长度已知;已知创建的数组类型;
int[]::new
方法引用。
测试分类:
Junit白盒测试
使用步骤:
定义一个测试类。(测试用例)
这个类所在的包和被测试程序包平行;
测试类名:xxxxTest
测试包名:xx.xx.test
定义测试方法,可以独立运行。
方法名:test测试的方法名 testAdd
返回值:void
参数列表:空参。
给方法前加上@Test。
导入Junit环境。
判定结果:
Assert.assertEquals(期望结果,实际结果); 红色失败;绿色成功。
@Before
一般用于资源申请。
所有方法执行前都会执行此方法。一般命名public void init(){...}
@After
一般用于资源释放。
所有方法执行后比执行此方法。一般命名public void close(){...}
Java程序的三个阶段:
Source 源代码阶段 Class 类对象阶段 Runtime 运行时阶段
获取class对象的三种方式:
1.class.forName("全类名"):第一个阶段。将字节码文件加载进内,返回class对象。
2.类名.class:通过类名的属性class获取。
3.对象.getClass():getClass()方法在Object类中定义。所有的额对象都可以调用此方法。
class对象功能:
1.获取功能
获取成员变量们
Field getField(String name) //只能获取 指定名称的public修饰的成员变量
Field[] getFields() //只能获取public修饰的成员变量
Field getDeclaredField(String name) // 获取指定名称的成员变量,不考虑修饰符
Field[] getDeclaredFields() // 获取所有的成员变量,不考虑修饰符
获取到成员变量以后,成员变量可以进行的操作有
set() //设置成员变量的值
get() //获取成员变量的值
setAccessible(true) //忽略访问权限修饰符的安全检查。暴力反射。
//【所有的获取方法中都包含暴力反射方法】暴力反射方法一般都跟随declared方法后面使用,会涉及到安全修饰符问题。
获取构造方法们
Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<?>[] getConstructors()
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
获取到构造方法后,可以创建对象:
创建对象: T newInstance(Object... initargs)
空参构造时,可以直接调用personClass.Instance()。得到一个空参构造。空参构造都是用这种方法。
setAccessible(true) //也包含暴力发射方法。
获取成员方法们
Method getMethod(String name, Class<?>... parameterTypes) // 获取指定名称和参数的方法
Method[] getMethods() //获取所有方法,包括object类中的方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
Method[] getDeclaredMethods()
获取到方法以后:一件事,执行方法。
方法名.invoke(类名);执行方法。
获取类名
String getName()
Package getPackage()
反射案例:
需求:写一个“框架”,可以帮我们创建任意类的对象,并且执行其中任意方法。
实现:1.配置文件 2.反射
步骤:
1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中。
2.在程序中加载读取配置文件。
3.使用反射技术来加载类文件进内存。
4.创建对象。
5.执行方法。
注释:三种注释,单行,多行和文档。用文字描述程序,方便阅读。
注解:说明程序,给计算机看的。Annotation,元数据。jdk1.5之后引入的。
与类,接口,枚举在同一个层次。它可以声明在包,类,字段,方法,局部变量,方法参数等前面,用来对这些元素进行说明,注释。
使用注解:@注解名称。
注解的三种功能
主要学习: