【RPC】

主要内容

  1. 项目结构变化
  2. RPC简介
  3. RMI实现RPC
  4. HttpClient实现RPC
  5. Zookeeper安装
  6. Zookeeper客户端常用命令
  7. 向Zookeeper中注册内容
  8. 从Zookeeper中发现内容
  9. 手写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全栈相关推荐

  1. Java全栈(二)JavaSE:25.File类和IO流

    1 java.io.File类 1.1 概述 File类是java.io包下代表与平台无关的文件和目录,也就是说如果希望在程序中操作文件和目录都可以通过File类来完成,File类能新建.删除.重命名 ...

  2. Java全栈工程师学习

    Java全栈看似内容庞杂,只要掌握方法成功离我们很近,话不多说,上硬货! Web后端架构 后端进阶第一步,先把Web架构相关的技术学好吧,因为之前大家都做过Java Web项目,想必对这块内容还是比较 ...

  3. 各大互联网公司面经分享:Java全栈知识+1500道大厂面试真题

    这篇文章给大家分享一下我遇到的一些质量较高的面试经历,具体经过就不多说了,就把面试题打出来供各位读者老哥参考如有不全的地方,各位海涵. 猿辅导 八皇后问题 求二叉树的最长距离(任意两个节点的路径 中最 ...

  4. 各大厂面经分享:Java全栈知识+1500道大厂面试真题

    这篇文章给大家分享一下我遇到的一些质量较高的面试经历,具体经过就不多说了,就把面试题打出来供各位读者老哥参考如有不全的地方,各位海涵. 猿辅导 八皇后问题 求二叉树的最长距离(任意两个节点的路径 中最 ...

  5. java全栈系列之JavaSE--冒泡排序028

    冒泡排序是在一组数组中将相邻的两个元素进行比较,可以按由大到小或者由小到大的循序排列, 冒泡排序示意图: 每循环一次相邻的两个数进行大小比较,若由小到大的循序则大的数据在小的数据右侧 冒泡排序流程图 ...

  6. Java全栈(三)数据库技术:3.数据库之JDBC上

    第一章 JDBC概述 之前我们学习了JavaSE,编写了Java程序,数据保存在变量.数组.集合等中,无法持久化,后来学习了IO流可以将数据写入文件,但不方便管理数据以及维护数据的关系: 后来我们学习 ...

  7. 1. JAVA全栈知识体系--- Java基础

    1. JAVA全栈知识体系- Java基础 文章目录 1. JAVA全栈知识体系--- Java基础 1.1 语法基础 面向对象特性? a = a + b 与 a += b 的区别 3*0.1 == ...

  8. Java全栈知识体系

    文章目录 Java全栈 注释 特性 类图 数据类型 缓存池 String 不可变的好处 StringBuffer and StringBuilder float与double switch Objec ...

  9. Java全栈系列笔记

    Java全栈系列笔记 全部文档.项目.源码: github:https://github.com/name365/Blog-Java 码云:https://gitee.com/yang365/Blog ...

最新文章

  1. react全家桶从0到1(react-router4、redux、redux-saga)
  2. 《Cocos2d 跨平台游戏开发指南(第2版)》一1.9 添加动作到精灵
  3. 5.1 计算机网络之传输层(传输层提供的服务及功能概述、端口、套接字--Socket、无连接UDP和面向连接TCP服务)
  4. 验证视图状态 MAC 失败 的解决办法
  5. java 线性回归算法_线性搜索或顺序搜索算法在Java中如何工作? 示例教程
  6. 荷兰帝斯曼收购初创科技公司尚善 无氟光伏背板被指有利回收再利用
  7. 【Java数据结构与算法】第十一章 顺序存储二叉树、线索二叉树和堆
  8. java基础应用_Java基础(应用篇)
  9. linux查看虚拟化版本,4.15. 虚拟化 (机器翻译版本)
  10. Hexo博客系列(三)-将Hexo v3.x个人博客发布到GitLab Pages
  11. 第1章:认识Java
  12. python 有限域函数库_有限域GF(2^8)内乘法代码实现以及原理
  13. 计算机组装硬件配置清单,组装电脑配置清单
  14. 计算机病毒的常用方法,常用计算机检测病毒的方法
  15. 《全程软件测试第三版》读书笔记
  16. 16个Javascript的Web UI库、框架及工具包
  17. 学习java web感想_学了近一个月的java web 感想
  18. access denied for user root@localhost using passw
  19. stm32智能小车设计(1)——硬件选型思路
  20. 臻鑫永晖:1万存活期1年利息多少?附核算公式

热门文章

  1. Git如何修改commit信息
  2. FBTFT driver St7789v LCD 移植简介
  3. d3-绘制直线-横线
  4. Qt下实现录制麦克风声音
  5. 韩国三星android多少钱,韩国首款安卓翻盖 三星GALAXY GOLDEN赏
  6. 最受程序员欢迎的20本书
  7. 一万小时定律的数学解释-可怕的复利
  8. 使用Aura.Router在PHP中进行Web路由
  9. 设计模式 | 为什么要学设计模式?懂兵法才能当好将军
  10. 无意中看到,有一些感受。。。