RPC通信协议远程服务调用(25)Java全栈
【RPC】
主要内容
- 项目结构变化
- RPC简介
- RMI实现RPC
- HttpClient实现RPC
- Zookeeper安装
- Zookeeper客户端常用命令
- 向Zookeeper中注册内容
- 从Zookeeper中发现内容
- 手写RPC框架
学习目标
知识点 | 要求 |
---|---|
项目架构变化 | 掌握 |
RPC简介 | 掌握 |
RMI实现RPC | 掌握 |
HttpClient实现RPC | 了解 |
Zookeeper安装 | 掌握 |
Zookeeper客户端常用命令 | 掌握 |
向Zookeeper中注册内容 | 掌握 |
从zookeeper中发现内容 | 掌握 |
手写RPC框架 | 掌握 |
1 项目架构变化
现在学习RPC。因为后期学习的Dubbo是一个RPC框架,学习好现在的内容,学习Dubbo将会变得容易一些。
1.1 单体架构
1. 架构图
单体架构就是一个项目里面包含这个项目中全部代码。一个应用搞定全部功能。
DNS 服务器可以是单映射,也可以配置多个映射。
2. 软件代码结构
在单体架构项目中,团队都是通过包(package)进行区分每个模块。
总体包结构:com.uestc.*.
分层包。
项目名:-- com--uestc-- common-- utils--user-- controller-- service-- mapper-- sys-- controller-- service-- mapper
3. 优缺点
优点:(1)部署简单;(2)维护方便;(3)成本低。
缺点:当项目规模大、用户访问频率高、并发量大、数据量大时,会大大降低程序执行效率,甚至出现服务器宕机等情况。
4. 适用项目:传统管理项目,小型互联网项目。
1.2 分布式架构
1. 架构图(简易版)
分布式架构会把一个项目按照特定要求(多按照模块或功能)拆分成多个项目,每个项目分别部署到不同的服务器上。
2. 软件代码结构
项目1:--com.uestc.xxx-- controller-- service-- mapper
项目2--com.uestc.mmm-- controller-- service-- mapper
3. 优缺点
优点:(1)增大了系统可用性。减少单点故障,导致整个应用不可用;(2)增加重用性。因为模块化,所以重用性更高;(3)增加可扩展性。有新的模块增加新的项目即可;(4)增加每个模块的负载能力。因为每个模块都是一个项目,所以每个模块的负载能力更强。
缺点:(1)成本更高;(2)架构更加复杂(3)整体响应之间变长,一些业务需要多项目通信后给出结果;(4)吞吐量更大。吞吐量= 请求数/秒。
4. 待解决问题:分布式架构中各个模块如何进行通信?
可以使用Http协议,也可以使用RPC协议通信,也可以使用其他的通信方式。我们本阶段使用的是RPC协议,因为它比HTTP更适合项目内部通信。
2 RPC简介
2.1 RFC
RFC(Request For Comments) 是由互联网工程任务组(IETF)发布的文件集。文件集中每个文件都有自己唯一编号,例如:rfc1831。目前RFC文件由互联网协会(Internet Society,ISOC)赞助发型。
RPC就收集到了rfc 1831中。可以通过下面网址查看:
https://datatracker.ietf.org/doc/rfc1831/
2.2 RPC
RPC在rfc 1831中收录 ,RPC(Remote Procedure Call) 远程过程调用协议
RPC协议规定允许互联网中一台主机程序调用另一台主机程序,而程序员无需对这个交互过程进行编程
。在RPC协议中强调当A程序调用B程序中功能或方法时,A是不知道B中方法具体实现的
。
RPC是上层应用层协议,底层可以基于TCP协议,也可以基于HTTP协议
。一般我们说RPC都是基于RPC的具体实现,如:Dubbo框架
。从广义上讲只要是满足网络中进行通讯调用都统称为RPC,甚至HTTP协议都可以说是RPC的具体实现,但是具体分析看来RPC协议要比HTTP协议更加高效,基于RPC的框架功能更多。
RPC协议是基于分布式架构而出现的,所以RPC在分布式项目中有着得天独厚的优势。
2.3 RPC和HTTP对比
(1)具体实现
RPC:可以基于TCP协议,也可以基于HTTP协议。
HTTP:基于HTTP协议。
(2)效率
RPC:自定义具体实现可以减少很多无用的报文内容,使得报文体积更小。
HTTP:如果是HTTP 1.1 报文中很多内容都是无用的。如果是HTTP2.0以后和RPC相差不大,比RPC少的可能就是一些服务治理等功能。
(3)连接方式
RPC:长连接支持。
HTTP:每次连接都是3次握手。
(4)性能
RPC可以基于很多序列化方式。如:thrift
HTTP 主要是通过JSON,序列化和反序列效率更低。
(5)注册中心
RPC :一般RPC框架都带有注册中心。
HTTP:都是直连。
(6)负载均衡
RPC:绝大多数RPC框架都带有负载均衡测量。
HTTP:一般都需要借助第三方工具。如:nginx
(7)综合结论
RPC框架一般都带有丰富的服务治理等功能
,更适合企业内部接口调用。而HTTP更适合多平台之间相互调用。
3 HttpClient实现RPC
3.1 HttpClient简介
在JDK中java.net
包下提供了用户HTTP访问的基本功能,但是它缺少灵活性或许多应用所需要的功能。
HttpClient起初是Apache Jakarta Common 的子项目。用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本。2007年成为顶级项目。
通俗解释:HttpClient可以实现使用Java代码完成标准HTTP请求及响应。
3.2 代码实现
(1)服务端
新建项目HttpClientServer
(2)新建控制器
com.uestc.controller.DemoController
@Controller
public class DemoController {@RequestMapping("/demo")@ResponseBodypublic String demo(String param){return "demo"+param;}
}
(3)新建启动器
新建启动器
:com.msb.HttpClientServerApplication
@SpringBootApplication
public class HttpClientServerApplication {public static void main(String[] args) {SpringApplication.run(HttpClientServerApplication.class,args);}
}
(4)客户端
新建HttpClientDemo项目
(5)添加依赖
<dependencies><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.10</version></dependency>
</dependencies>
(6)新建类
新建com.uestc.HttpClientDemo,编写主方法。
(7)使用GET方法访问
public static void main(String[] args) {try {//创建http工具(理解成:浏览器) 发起请求,解析响应CloseableHttpClient httpClient = HttpClients.createDefault();//请求路径URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/demo");uriBuilder.addParameter("param", "get123");//创建HttpGet请求对象HttpGet get = new HttpGet(uriBuilder.build());//创建响应对象CloseableHttpResponse response = httpClient.execute(get);//由于响应体是字符串,因此把HttpEntity类型转换为字符串类型,并设置字符编码String result = EntityUtils.toString(response.getEntity(), "utf-8");//输出结果System.out.println(result);//释放资源response.close();httpClient.close();} catch (URISyntaxException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
}
(8)使用POST方式访问
public class HttpClientDemo {public static void main(String[] args) {try {//创建http工具(理解成:浏览器) 发起请求,解析响应CloseableHttpClient httpClient = HttpClients.createDefault();//创建HttpPOST请求对象HttpPost post = new HttpPost("http://localhost:8080/demo");//所有请求参数List<NameValuePair> params = new ArrayList<>();params.add(new BasicNameValuePair("param","123"));//创建HttpEntity接口的文本实现类的对象,放入参数并设置编码HttpEntity httpEntity = new UrlEncodedFormEntity(params,"utf-8");//放入到HttpPost对象中post.setEntity(httpEntity); //创建响应对象CloseableHttpResponse response = httpClient.execute(post);//由于响应体是字符串,因此把HttpEntity类型转换为字符串类型String result = EntityUtils.toString(response.getEntity());//输出结果System.out.println(result);//释放资源response.close();httpClient.close();} catch (IOException e) {e.printStackTrace();}}
}
3.3 Jackson用法
简单来说,从服务器上写好的对象或者对象集合
想要发送给浏览器,就要转化为json字符串
,而浏览器中的数据发送给服务器也要从json字符串
解析为对象或者对象集合
以供后续使用。
1. 把对象转换为json字符串
ObjectMapper objectMapper = new ObjectMapper();
People peo = new People();
objectMapper.writeValueAsString(peo);
2. 把json字符串转换为对象
ObjectMapper objectMapper = new ObjectMapper();
People peo = objectMapper.readValue(content, People.class);
3. 把json字符串转换为List集合
ObjectMapper objectMapper = new ObjectMapper();
JavaType javaType = objectMapper.getTypeFactory().constructParametricType(List.class, People.class);
List<People> list = objectMapper.readValue(content, javaType);
3.4 HttpClient请求包含JSON
public class HttpClientDemo {public static void main(String[] args) {try {CloseableHttpClient httpClient = HttpClients.createDefault();HttpPost post = new HttpPost("http://localhost:8080/demo");HttpEntity httpEntity= null;
String json = "{}";StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);post.setEntity(entity);CloseableHttpResponse response = httpClient.execute(post);String result = EntityUtils.toString(response.getEntity());System.out.println(result);response.close();httpClient.close();} catch (IOException e) {e.printStackTrace();}}
}
3.5 控制器接口参数
@RequestBody
把请求体中流数据
转换为指定的对象。多用在请求参数是json数据且请求的Content-Type=”application/json”
(注意:流数据是指,除了文本类型之外的数据都统称为流数据)
@RequestMapping("/demo4")
@ResponseBody
public String demo4(@RequestBody List<People> list) {System.out.println(list);return list.toString();
}
3.6 Ajax发送json参数写法
var json = '[{"id":123,"name":"msb"},{"id":123,"name":"mashibing"}]';$.ajax({url:'/demo5',type:'post',success:function(data){alert(data);for(var i = 0 ;i<data.length;i++){alert(data[i].id +" "+data[i].name);}},contentType:'application/json',//请求体中内容类型dataType:'json',//响应内容类型。data:json});
3.7 跨域
跨域:协议、ip、端口中只要有一个不同就是跨域请求。
同源策略:浏览器默认只允许ajax访问同源(协议、ip、端口都相同)内容。
解决同源策略:在控制器接口上添加@CrossOrigin
。表示允许跨域。本质在响应头中添加Access-Control-Allow-Origin: *
var json = '[{"id":123,"name":"msb"},{"id":456,"name":"mashibing"}]';$.ajax({url:'/demo5',type:'post',success:function(data){alert(data);for(var i = 0 ;i<data.length;i++){alert(data[i].id +" "+data[i].name);}},contentType:'application/json',//请求体中内容类型dataType:'json',//响应内容类型。data:json});
4 RMI实现RPC
4.1 RMI简介
RMI(Remote Method Invocation) 远程方法调用。
RMI是从JDK1.2推出的功能,它可以实现在一个Java应用中可以像调用本地方法一样调用另一个服务器中Java应用(JVM)中的内容。
RMI 是Java语言的远程调用,无法实现跨语言。
4.2 执行流程
Registry(注册表)是放置所有服务器对象的命名空间。 每次服务端创建一个对象时,它都会使用bind()或rebind()方法注册该对象。 这些是使用称为绑定名称的唯一名称注册的。
要调用远程对象,客户端需要该对象的引用。即通过服务端绑定的名称从注册表中获取对象(lookup()方法)。
4.3 API介绍
(1)Remote
java.rmi.Remote
定义了此接口为远程调用接口。如果接口被外部调用,需要继承此接口。
public interface Remote{}
(2)RemoteException
java.rmi.RemoteException
继承了Remote接口的接口中,如果方法是允许被远程调用的,需要抛出此异常。
(3)UnicastRemoteObject
java.rmi.server.UnicastRemoteObject
此类实现了Remote接口和Serializable接口。
自定义接口实现类
除了实现自定义接口还需要继承此类。
(4)LocateRegistry
java.rmi.registry.LocateRegistry
可以通过LocateRegistry在本机上创建Registry,通过特定的端口就可以访问这个Registry。
(5)Naming
java.rmi.Naming
Naming定义了发布内容可访问RMI名称。也是通过Naming获取到指定的远程方法。
4.4 代码实现
4.4.1 服务器端
(1)服务端创建
创建RmiServer项目
(2)编写接口
com.uestc.service.DemoService
编写
public interface DemoService extends Remote {String demo(String demo) throws RemoteException;
}
(3)编写实现类
com.uestc.service.impl.DemoServiceImpl
编写。
注意:构造方法是public的。默认生成protected
public class DemoServiceImpl extends UnicastRemoteObject implements DemoService {public DemoServiceImpl() throws RemoteException {}@Overridepublic String demo(String demo) throws RemoteException {return demo+"123";}
}
(4)编写主方法
编写com.uestc.DemoServer
类,生成主方法
public class DemoServer {public static void main(String[] args) {try{//创建接口实例DemoService demoService = new DemoServiceImpl();//创建注册表LocateRegistry.createRegistry(8989);//绑定服务Naming.bind("rmi://localhost:8989/demoService", demoService);System.out.println("服务器启动成功");}catch(Exception e ){e.printStackTrace();}}
}
(5)运行项目
运行后项目,项目一直处于启动状态,表示可以远程访问此项目中的远程方法。
4.4.2 客户端
(1)创建客户端代码
创建项目RmiClient
(2)复制服务端接口
把服务端com.uestc.service.DemoService
粘贴到项目中
(3)创建主方法类
新建com.uestc.DemoClient
public class ClientDemo {public static void main(String[] args) {try {DemoService demoService =(DemoService)Naming.lookup("rmi://localhost:8989/demoService");String result = demoService.demo("jayden ");System.out.println(result);} catch (NotBoundException e) {e.printStackTrace();} catch (MalformedURLException e) {e.printStackTrace();} catch (RemoteException e) {e.printStackTrace();}}
}
(4)运行项目然后用浏览器访问查看结果。
5 Zookeeper安装
5.1 Zookeeper简介
zookeeper分布式管理软件。常用它做注册中心(依赖zookeeper的发布/订阅功能)、配置文件中心、分布式锁配置、集群管理等。
zookeeper一共就有两个版本。主要使用的是java语言写的。
5.2 安装
(1)使用你虚拟机或者云服务器的linux服务器上传压缩文件
到官网https://zookeeper.apache.org/找到download下载zookeeper压缩包apache-zookeeper-3.5.5-bin.tar.gz
,然后上传到 /usr/local/tmp
中。
(2)解压
tar zxf apache-zookeeper-3.5.5-bin.tar.gz
cp -r apache-zookeeper-3.5.5-bin ../zookeeper
(3)新建data目录
进入到zookeeper中
cd /usr/local/zookeeper
mkdir data
(4)修改配置文件
进入conf中
cd conf
cp zoo_sample.cfg zoo.cfg
vim zoo.cfg
修改dataDir为data文件夹路径
dataDir=/usr/local/zookeeper/data
(5)启动zookeeper
进入bin文件夹
cd /usr/local/zookeeper/bin
./zkServer.sh start
通过status查看启动状态。稍微有个等待时间
./zkServer.sh status
5.3 Zookeeper客户端常用命令
进入到./zkCli.sh
命令行工具后,可以使用下面常用命令
1 ls
ls [-s][-R] /path
-s 详细信息,替代老版的ls2
-R 当前目录和子目录中内容都罗列出来
例如:ls -R / 显示根目录下所有内容
2 create
create /path [data]
[data] 包含内容
创建指定路径信息
例如:create /demo 创建/demo
3 get
get [-s] /path
[-s] 详细信息
查看指定路径下内容。
例如: get -s /demo
null:存放的数据
cZxid:创建时zxid(znode每次改变时递增的事务id)
ctime:创建时间戳
mZxid:最近一次更新的zxid
mtime:最近一次更新的时间戳
pZxid:子节点的zxid
cversion:子节点更新次数
dataversion:节点数据更新次数
aclVersion:节点ACL(授权信息)的更新次数
ephemeralOwner:如果该节点为ephemeral节点(临时,生命周期与session一样), ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是ephemeral节点, ephemeralOwner值为0.
dataLength:节点数据字节数
numChildren:子节点数量
4 set
set /path data
设置节点内容
5 delete
delete /path
删除节点
5.4 向Zookeeper中注册内容
新建项目ZookeeperClient
(1)创建/demo
使用zookeeper的客户端命令工具创建/demo
./zkCli.sh
create /demos
(2)添加依赖
<dependencies><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.5.5</version></dependency>
</dependencies>
(3)编写代码
创建类com.uestc.MyApp
。
ZooDefs.Ids.OPEN_ACL_UNSAFE
表示权限,具体完全开放的ACL,任何连接的客户端都可以操作该属性znode。
CreateMode.PERSISTENT_SEQUENTIAL
永久存储,文件内容编号递增。
public static void main(String [] args){try {ZooKeeper zookeeper = new ZooKeeper("192.168.32.128:2181", 10000, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {System.out.println("获取连接");}});String content = zookeeper.create("/demo/nn", "content".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);System.out.println("content"+content);} catch (IOException e) {e.printStackTrace();} catch (KeeperException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}
}
(4)查看上传数据
ls -R / :查看列表 get /demo/nn0000000002 :查看内容
5.5 从zookeeper中发现内容
在原有项目中新建一个类,类中编写主方法。
public static void main(String[] args) {try {ZooKeeper zookeeper = new ZooKeeper("192.168.32.128:2181", 10000, new Watcher() {@Overridepublic void process(WatchedEvent watchedEvent) {System.out.println("获取连接");}});//获取列表List<String> list = zookeeper.getChildren("/demo", false);for (String child : list) {byte[] result = zookeeper.getData("/demo/" + child, false, null);System.out.println(new String(result));}} catch (IOException e) {e.printStackTrace();} catch (KeeperException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}
}
6 手写RPC框架
使用Zookeeper作为注册中心,RMI作为连接技术,手写RPC框架。
6.1 创建项目ParentDemo
创建父项目ParentDemo
。
包含4个聚合子项目。
pojo
: service和consumer中需要的实体类
service
:包含被serviceimpl和consumer依赖的接口。
provider
:provider中写两个方法,一个是实现service的接口,第二个是作为服务器向zookeeper注册内容,并且用Naming绑定zookeeper内容和具体实现的实现类。
consumer
:消费者,调用服务内容。
6.2 创建pojo项目
略,就是写一个可能用到的实力类,记得这个类要实现序列化Serializable
。
6.2 创建service项目
主要就是编写服务器provider需要实现的接口集合,之后服务器只需要针对service实现各个接口即可,方便管理。这里要用到pojo,所以要在依赖中依赖pojo项目。
<dependencies><dependency><groupId>com.uestc</groupId><artifactId>pojo</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
创建com.uestc.DemoService
public interface MyPersonService extends Remote {// 找到所有的Personpublic List<Person> findAll() throws RemoteException;
}
6.3 创建provider项目
此项目编写service项目中接口的具体实现,RMI服务发布和把信息发送到Zookeeper中。
项目结构如下:
(1)在pom.xml
中添加对service项目的依赖
<dependencies><dependencies><dependency><groupId>com.uestc</groupId><artifactId>service</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.5.7</version></dependency></dependencies>
</dependencies>
(2)创建接口的实现类DemoServiceImpl,实现service的接口方法
创建com.uestc.service.impl.DemoServiceImpl
public class MyPersonServiceImpl extends UnicastRemoteObject implements MyPersonService {public MyPersonServiceImpl() throws RemoteException {}public List<Person> findAll() throws RemoteException {List<Person> personList = new ArrayList<Person>();personList.add(new Person(1, "jayden"));personList.add(new Person(2, "kim"));return personList;}
}
(3)创建RmiRun
创建com.uestc.ProviderRun
。实现RMI服务的发布和Zookeeper消息的发布。
public class ProviderRun {public static void main(String[] args) {try {MyPersonService myPersonService = new MyPersonServiceImpl();LocateRegistry.createRegistry(8089);String url = "rmi://localhost:8089/myPersonService";Naming.bind(url, myPersonService);System.out.println("RMI服务启动成功");//创建zookeeper并发布信息ZooKeeper zooKeeper = new ZooKeeper("82.156.182.26:2181", 100000, new Watcher() {public void process(WatchedEvent watchedEvent) {System.out.println("获取链接");}});//注意要在zookeeper注册一个文件夹/rpczooKeeper.create("/rpc/provider",url.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);System.out.println("注册成功");} catch (Exception e) {e.printStackTrace();}}
}
6.4 创建consumer项目
新建consumer项目,此项目需要从zookeeper中获取rmi信息,并调用rmi服务
(1)在pom.xml
中添加对service项目的依赖,因为service已经依赖过pojo项目了,所以根据继承Consumer项目也依赖了pojo项目
<dependencies><dependency><groupId>com.uestc</groupId><artifactId>service</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.6.6</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.5.7</version></dependency></dependencies>
(2) 创建接口和实现类
创建com.uestc.service.PersonService
接口
创建com.uestc.service.impl.PersonServiceImpl
实现类
public interface PersonService {public List<Person> show();
}
@Service
public class PersonServiceImpl implements PersonService {public List<Person> show() {try {ZooKeeper zooKeeper = new ZooKeeper("82.156.182.26:2181", 100000, new Watcher() {public void process(WatchedEvent watchedEvent) {System.out.println("链接成功");}});byte[] result = zooKeeper.getData("/rpc/provider", false, null);MyPersonService myPersonService = (MyPersonService) Naming.lookup(new String(result));return myPersonService.findAll();} catch (Exception e) {e.printStackTrace();}return null;}
}
(3)创建控制器
创建com.uestc.controller.PersonController
控制器
@Controller
public class PersonController {@Autowiredprivate PersonService personService;@RequestMapping("/show")@ResponseBodypublic List<Person> show(){return personService.show();}
}
(4)创建启动器
创建com.uestc.ConsumerApplication
@SpringBootApplication
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class,args);}
}
6.5 测试
在浏览器输入:http://localhost:8080/show
。
观察是否返回相应的json对象List
注意provider如果第二次启动可能会出错,显示KeeperErrorCode = NodeExists for /rpc/provider
,这个时候需要在zookeeper将这个/rpc/provider
路径删除了。
RPC通信协议远程服务调用(25)Java全栈相关推荐
- Java全栈(二)JavaSE:25.File类和IO流
1 java.io.File类 1.1 概述 File类是java.io包下代表与平台无关的文件和目录,也就是说如果希望在程序中操作文件和目录都可以通过File类来完成,File类能新建.删除.重命名 ...
- Java全栈工程师学习
Java全栈看似内容庞杂,只要掌握方法成功离我们很近,话不多说,上硬货! Web后端架构 后端进阶第一步,先把Web架构相关的技术学好吧,因为之前大家都做过Java Web项目,想必对这块内容还是比较 ...
- 各大互联网公司面经分享:Java全栈知识+1500道大厂面试真题
这篇文章给大家分享一下我遇到的一些质量较高的面试经历,具体经过就不多说了,就把面试题打出来供各位读者老哥参考如有不全的地方,各位海涵. 猿辅导 八皇后问题 求二叉树的最长距离(任意两个节点的路径 中最 ...
- 各大厂面经分享:Java全栈知识+1500道大厂面试真题
这篇文章给大家分享一下我遇到的一些质量较高的面试经历,具体经过就不多说了,就把面试题打出来供各位读者老哥参考如有不全的地方,各位海涵. 猿辅导 八皇后问题 求二叉树的最长距离(任意两个节点的路径 中最 ...
- java全栈系列之JavaSE--冒泡排序028
冒泡排序是在一组数组中将相邻的两个元素进行比较,可以按由大到小或者由小到大的循序排列, 冒泡排序示意图: 每循环一次相邻的两个数进行大小比较,若由小到大的循序则大的数据在小的数据右侧 冒泡排序流程图 ...
- Java全栈(三)数据库技术:3.数据库之JDBC上
第一章 JDBC概述 之前我们学习了JavaSE,编写了Java程序,数据保存在变量.数组.集合等中,无法持久化,后来学习了IO流可以将数据写入文件,但不方便管理数据以及维护数据的关系: 后来我们学习 ...
- 1. JAVA全栈知识体系--- Java基础
1. JAVA全栈知识体系- Java基础 文章目录 1. JAVA全栈知识体系--- Java基础 1.1 语法基础 面向对象特性? a = a + b 与 a += b 的区别 3*0.1 == ...
- Java全栈知识体系
文章目录 Java全栈 注释 特性 类图 数据类型 缓存池 String 不可变的好处 StringBuffer and StringBuilder float与double switch Objec ...
- Java全栈系列笔记
Java全栈系列笔记 全部文档.项目.源码: github:https://github.com/name365/Blog-Java 码云:https://gitee.com/yang365/Blog ...
最新文章
- react全家桶从0到1(react-router4、redux、redux-saga)
- 《Cocos2d 跨平台游戏开发指南(第2版)》一1.9 添加动作到精灵
- 5.1 计算机网络之传输层(传输层提供的服务及功能概述、端口、套接字--Socket、无连接UDP和面向连接TCP服务)
- 验证视图状态 MAC 失败 的解决办法
- java 线性回归算法_线性搜索或顺序搜索算法在Java中如何工作? 示例教程
- 荷兰帝斯曼收购初创科技公司尚善 无氟光伏背板被指有利回收再利用
- 【Java数据结构与算法】第十一章 顺序存储二叉树、线索二叉树和堆
- java基础应用_Java基础(应用篇)
- linux查看虚拟化版本,4.15. 虚拟化 (机器翻译版本)
- Hexo博客系列(三)-将Hexo v3.x个人博客发布到GitLab Pages
- 第1章:认识Java
- python 有限域函数库_有限域GF(2^8)内乘法代码实现以及原理
- 计算机组装硬件配置清单,组装电脑配置清单
- 计算机病毒的常用方法,常用计算机检测病毒的方法
- 《全程软件测试第三版》读书笔记
- 16个Javascript的Web UI库、框架及工具包
- 学习java web感想_学了近一个月的java web 感想
- access denied for user root@localhost using passw
- stm32智能小车设计(1)——硬件选型思路
- 臻鑫永晖:1万存活期1年利息多少?附核算公式