IDEA Git操作(三)使用 cherry-pick、交互式 rebase 自由修改提交树

说明

本教程按照 git在线练习 顺序进行,将在线测试的命令操作落地到 IDEA,使用开发工具来实现所有在线练习中的操作。

你可以结合 git在线练习 来学习本教程,先在线学习git命令,再在 IDEA 中实现相同操作。



 

cherry-pick



 

准备提交结构


 

  • 切换到 master,并重置到第二次提交
  • 删除 pushed 和 local 分支

新建 bugFix、side 和 another 分支

  • 对bugFix、side和another分支,分别做两次提交
  • 切换到 master

上面的提交历史对照 learngitbranching.js.org 的结构图

cherry-pick bufFix 2、side 1和another 2三个提交到 master 分支




选中这三个提交

右键选择 cherry-pick

如果出现代码合并提示,选择 merge,将 bufFix 2 提交的代码合并到 master 的代码

按照正确代码逻辑,向中间合并代码

完成后,生成了一个新的提交

接下来,按照相同的操作,完成 side 1 和 another 2 的代码合并操作,同样也会生成两个新的提交

上面的提交历史对照 learngitbranching.js.org 的结构图

交互式 rebase



 

准备提交结构


 

  • 重置master到第二次提交
  • 删除 bugFix、side 和 another 分支

  • 做四次提交

上面的提交历史对照 learngitbranching.js.org 的结构图

用交互式 rebase 操作自由调整提交




在 master 分支,右键点击 “添加打印3”,选择交互式 rebase

在交互界面中,丢弃“添加打印3”

向上移动一步 “添加打印6”

查看将要执行的命令

在这里看到,会按以下顺序执行:

  1. 丢弃“添加打印3”
  2. 选取“添加打印4”
  3. 选取“添加打印6”
  4. 选取“添加打印5”

点击开始执行 rebase 操作
 

过程中可能会出现3次合并代码的操作提示,可以按照三步的顺序,合并三句打印代码

上面的提交历史对照 learngitbranching.js.org 的结构图

用交互 rebase 将 4,6,5 压缩成一个分支

learngitbranching.js.org 中略过了压缩分支的操作




右键点击“添加打印4”的提交,执行交互式 rebase

将 5 和 6 设置为 Fixup

设置完的状态如下

rebase 完成后,三次提交被压缩为一次提交

笔记汇总目录

Spring Cloud 微服务

Spring Cloud入门操作手册(Hoxton)



 

RabbitMQ

RabbitMQ
RabbitMQ - Spring boot 整合

Lucene Solr

Lucene Solr 811



 

Docker

Docker
Docker案例



 

Kubernetes

Kubernetes
k8s部署Spring Cloud应用

分布式事务

分布式事务(一)Mysql本地事务和事务隔离级别
分布式事务(二)分布式事务方案
分布式事务(三)Seata分布式事务框架-AT模式介绍
分布式事务(四)Seata AT模式-Spring Cloud微服务案例
分布式事务(五)Seata AT模式-Spring Cloud微服务添加 AT 分布式事务
分布式事务(六)Seata TCC模式-TCC模式介绍
分布式事务(七)Seata TCC模式-Spring Cloud微服务添加 TCC 分布式事务
分布式事务(八)Spring Cloud微服务系统基于Rocketmq可靠消息最终一致性实现分布式事务



 

RocketMQ

RocketMQ (一) 安装
RocketMQ (二) 双主双从同步复制集群方案
RocketMQ (三) 基本原理
RocketMQ (四) 使用RocketMQ原生API收发消息代码样例
RocketMQ (五) Springboot 整合 RocketMQ 收发消息样例

RocketMQ 发送事务消息原理分析和代码实现

Java基础

java编程基础(一)二进制
java编程基础(二)位运算符
java编程基础(三)冒泡排序
java编程基础(四)选择排序
java编程基础(五)二分法查找
java编程基础(六)递归

2006

就业

简历:自我介绍: 接收短期或长期出差, 接收高强度加班抗压能力极强
项目:
自研项目: 主题一致,
外包:
人力:类型一致
项目:类型一致
国企:

复习

1.做题,打桩: Java面试宝典,码出高效
2.mysql(多表联查)\servlet()SSM(图)
3.SSM(图) shiro
4.jt业务 redis ( 是什么, 在项目里解决什么问题, ) \Nginx\Mycat\SSO\zookeeper\dubbo
5.cloud mq 分布式事务

课程安排

  1. 大数据理论基础,项目介绍架构设计
  2. 基础环境搭建,Hadoop分布式海量数据存储处理系统详解
  3. Hadoop\Flume日志收集工具
  4. 项目第一次重构,实现海量数据的自动收集, Hive数据仓库\数据湖\数据集市
  5. 基于Hive实现海量数据离线分析项目第二次重构
  6. KafkaMQ\Flink海量数据流式处理系统
  7. Flink实战项目第三次重构
  8. Python

大数据

指海量数据及其处理方式
大数据(big data),IT行业术语,是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。
在维克托·迈尔-舍恩伯格及肯尼斯·库克耶编写的《大数据时代》 [1] 中大数据指不用随机分析法(抽样调查)这样捷径,而采用所有数据进行分析处理。大数据的5V特点(IBM提出):Volume(大量)、Velocity(高速)、Variety(多样)、Value(低价值密度)、Veracity(真实性)。

大数据相关技术

数据收集
爬虫,python,jsoup,Nucth
数据仓库 :Hive ELK
日志:Flume分布式日志收集工具,log4j
IOT:物联网
数据清洗
SQL: id name age gender addr 姓名 性别 18岁及以上的 select name, gender from user where age >= 18
逻辑: if(person != null){…}
数据存储
Hadoop.HDFS (Google File System)
数据处理
离线处理: Hive
流式处理: Flink,Spark,Storm

MQ: kafka
数据传输: Sqoop
状态监控:zookeeper

环境准备

8G 克隆三台虚拟机
hadoop01 1G
hadoop02 512M
hadoop03 512M

4G 克隆一台虚拟机
hadoop01 1G

hadoop

分布式的海量数据存储和处理系统,同时具有集群资源管理和任务调度的能力.

历史

Google:全文检索,海量数据存储,海量数据处理,爬虫
2004年《google file system》(GFS)《Mapreduce》(MR)
2006年《Big Table》(上亿行,上百万列这样的高表或者宽表)
Doug cutting(Java): Nutch(爬虫) Lucene、HDFS、Mapreduce Hadoop
Hbase(k,v)
大数据技术发源自搜索引擎领域,直到现在很多大数据技术中都会有搜索引擎的影响.

DataSet: 离线数据
DataStream: 流式数据

概念

Hadoop允许使用简单的编程模型跨计算机集群对大型数据集进行分布式处理

版本

hadoop1.x: HDFS(海量数据存储) Mapreduce(海量数据的离线计算,集群资源管理,任务调度)
hadoop2.x: HDFS(海量数据存储) Mapreduce(海量数据的离线计算) Yarn(集群资源管理,任务调度) 2.7.1
hadoop3.x: HDFS(海量数据存储) Mapreduce(海量数据的离线计算) Yarn(集群资源管理,任务调度)

HDFS

海量数据存储,无限存储

Hadoop的安装

  1. 单机模式: 一台服务器,上传安装包解压,即可. JDK, 缺点,只能用于MapReduce程序的测试,HDFS和Yarn的功能都不支持.
  2. 伪分布式模式: 一台服务器,需要一定配置修改, JDK 缺点是性能较低,但是支持Hadoop的所有功能
  3. 完全分布式模式: 多台服务器,真正生产环境下的模式,所有功能都支持.需要有JDK和zookeeper的环境支持.

可能出现的错误
提示命令找不到 环境变量配置错误或者没有source /etc/profile

Flume

定义

是一个分布式的海量日志收集工具.用于收集,聚合,移动大量的日志数据. 具备简单的流式数据处理模型的特点.但是不需要计算.所以说他简单.

重要概念 RegExp

log : 江哥今天没来上课,不知道去哪了
Event:事件,JSON = {“headers” : “value”, “body” : “江哥今天没来上课,不知道去哪了”},Flume在接收到日志数据之后,会先将其进行封装,每一条日志都会被封装为一个Event.然后再进行传输.
Agent: 代理, flume是一个分布式的软件,就像Hadoop中不同的节点都有自己的名字一样.flume的所有节点都叫Agent,每一个Agent中都包含三个部分.sourcechannel和sink
Source: 数据源,是Flume用来接收数据的组件.日志数据首先会进入到source模块,并在改模块进行封装(Event).输出给Channel进行缓存.
Channel:缓存,被动接收source传来的Event并进行缓存.等待Sink对event的消费.一般情况下,为了追求速度,都使用内存资源来缓存数据,那么此时就要注意:需要给缓存设置最大值.
Sink:输出, 指定数据输出的位置,消费Channel中的event

灵活的特性

多级流动
扇入
扇出

知识回顾

Flume是一个分布式的日志收集工具.

重要概念

Event:事件,Flume在收集到日志数据之后不是直接传输,而是先封装其为
Event(JSON) : {“headers”:“自定义内容”,“body”:“日志本身”}
Agent:Flume本身是分布式的,他所有的节点都叫做Agent,代理. 其中每个agent内部都会有三个组件,分别是,source\channel\sink
Source:Flume的数据源,负责数据的接入,当日志数据进入到source时,source会先将其进行封装(Event),然后将event输出到Channel中进行缓存.
Channel: 被动接收source传来的数据,对所有数据进行缓存,等待Sink的消费.一般情况为了追求速度,使用内存来做缓存,所以必须设置最大容量.
Sink:指定数据的输出位置,消费Channel中缓存的数据.

灵活特性

多级流动: 两台以上的服务器串联传输数据.
扇入: 多个服务器同时将数据发送给一台服务器
扇出: 一台服务器讲数据分发给多台

Hive

MapReduce在离线数据处理中遇到的问题

  1. MR程序开发调试非常复杂不适合要求快速得出结果的工作场景.
  2. Hadoop使用JAVA开发,MapReduce天生对JAVA支持最好,对其他语言使用者不够友好.学习成本高
  3. 想要开发一个优秀的MR程序,必须要有非常扎实的java基础和对Hadoop底层的一定了解.

概念

Hive是一个基于Hadoop的数据仓库客户端. 使用简单的SQL方式来处理海量离线数据,避免开发MR程序的成本过高的问题.内部集成了非常丰富的函数库.并且在内置函数不能满足开发需求时.Hive也允许用户自定义函数(UDF)来解决更为复杂的问题(很少)

数据仓库

仓库(有用,但不常用的)
数据仓库是一个面向主题的,稳定的,集成的,反映历史数据的数据存储系统.一般用于为管理者的决策分析提供数据的支持.

Hive表的类型

内部表和外部表

MANAGED_TABLE 内部表(托管表): 先创建表,然后在表中写数据,内部表指的是数据和表结构都属于Hive当Hive要删除这个表时,元数据和表数据都会被删除.
create table tb_book1 (id int, name string) row format delimited fields terminated by ‘\t’;

EXTERNAL_TABLE 外部表: 先有数据已经保存在HDFS中,根据数据文件的格式,创建Hive的表结构,然后将表结构映射在数据上,来管理相关数据.
create external table tb_book2 (id int,name string) row format delimited fields terminated by ‘\t’ location ‘/2006book2’;

实验

  1. 证明Hive中所有的库和表等元数据只会记录目录,而不会关心具体的文件.
  2. 观察内部表和外部表在被删除的时候的不同表现.

分区表

分区表时Hive作为数据仓库实现面向主题的唯一方式

分桶表

分桶的应用场景: 1.HashMap 2.数据库分库 3.Nginx负载均衡 IPhash

只在测试时.数据量大并且随意抽取样本数据不准确时.使用分桶表.

知识回顾

Hive是什么? 是一个基于Hadoop的数据仓库客户端,使用SQL的方式操作.内置了非常丰富的函数库.并且支持自定义函数.
数据仓库? 一个面向主题的,稳定的,集成的,反应历史数据的数据存储系统,一般为管理者提供决策分析的数据支持.
内部表: 先创建表然后在表中写数据
外部表托管表:先有数据,然后根据数据文件的格式,创建表结构,然后将表结构映射在数据上
内部表删除数据时,元数据和表数据都会被删除,外部表删除时,元数据会被删除,表数据则不会被删除
分区表:Hive作为数据仓库实现面向主题的唯一方式.
分桶表:只在测试阶段使用,为了解决,数量大,并且数据依据时间或者类似其他维度有规律.测试时如果全量原始数据加载测试,耗时太长,如果随意抽取部分数据结果不具有代表性,可能会影响判断. 此时,使用分桶表来解决这个数据抽样问题

项目第二次重构: 海量日志数据的离线分析

  1. 重新整理收集日志的规则.

Sqoop

是连接基于HDFS的文件存储系统,和关系型数据库的数据桥梁.能够轻松实现将HDFS中存储的数据输出到Mysql等关系型数据,或者从关系型数据库中导入数据到HDFS.

Kafka

MQ,消息队列, 海量数据存储(HDFS),和流式数据处理(Spark,Flink)
ActiveMQ(6k)\RabbitMQ(1.2w)\RocketMQ(3-5w):更适合于业务系统,能够保证数据一致性(数据不会丢失)
Kafka(25-50w)\ZeroMQ(Storm)速度非常快,但是有小概率的数据丢失情况(十亿分之一)

重要概念

消费者\生产者客户端: 连接消费者和生产者,达到解耦的目的.同时保证Kafka的可伸缩性.
Broker: kafka中,所有的机器节点都叫做broker按照int类型数据进行编号默认从0开始.主要的任务就是保存队列中的数据.
Topic: 将不同业务的数据,用主题(文件夹)隔离开,避免数据错误消费.一个主题上可以挂在多个生产者和多个消费者.
Partition: 按照0-n来编号,实际上就是在不同的Broker中来分布存放Topic的一部分数据,具有不同事件键的数据会被分配到不同的分区中,来隔离不同子业务的数据.在分区内部,所有数据都会被进行编号0-n 来保证数据消费的顺序是FIFO.并且与Offset配合来使消费者避免重复消费的问题,或者消费指定的数据.
Leader\Follower: kafka为了保障数据的安全.在分区层面上对数据进行了备份.原始的分区为Leader 复制出的备份分区为Follower,Follower存放在与Leader不同的Broker上.在正常情况下,所有的读写数据的工作全部由Leader完成,Follower只负责实时同步Leader中的数据变化.当Leader不可用时.两个Follower会由Zookeeper组织完成选举体态Leader.
ConsumerGroup:消费者组,用于解决发布订阅中多个消费者共享一份数据负载均衡的问题.一个组在消费者客户端中注册一个offset组内成员的消费行为都会使offset发生改变.

Flink

Flink是一个分布式的计算引擎,能够处理流式数据和离线数据.(无边界/有边界)

核心API
DataSet : 用于处理离线数据
DataStream : 一般用于处理流式数据,同时也支持将离线数据以流的形式来处理

Scala学习宝典(保姆级)

前言

为什么要学习Scala

分布式高并发语言Go、R、Erlang等等为何选择Scala?
Spark是大数据处理的核心方式,用scala语言编写!
Kafka分布式发布订阅消息系统,由LinkedIn捐给Apache,以极高的吞吐量著称,是目前最火爆的MQ,用scala语言编写!
Flink最新一代分布式海量数据计算框架,Alibaba收购并开源,自己开发Blink分支,Scala语言编写!

学前寄语

在我个人看来,Scala是一门非常优雅的语言,但优雅的背后要付出的辛苦也很多,比如学习Scala的人都会说,Scala语法非常简洁,但这也意味着抽象级别比较高,对初学者而言不好理解。也会有人说,Scala语法非常灵活,一个功能可以有非常多的实现方法,可以说条条大路通罗马,那么代价就是对于初学者来说,路多了反而不好选择。
所以在这里我对初学者的忠告是:在学习Scala前期,先走通一条路,屏蔽掉多余的干扰项,可能我们第一次使用Scala实现的项目看起来是非常笨拙的,但是没关系,任何的学习都是循序渐进的,不要追求一蹴而就。走通之后再回头,去寻找更好的替代方案。这样才能享受学习。

Scala简介

Scala combines object-oriented and functional programming in one concise, high-level language. Scala’s static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.
Scala是一个将面向对象和函数式编程结合在一起的简洁的高级语言。Scala的静态类型有助于避免复杂应用程序中的错误,其可以运行在JVM和JavaScript的特点使您可以轻松访问庞大的生态系统库来构建高性能系统。

Scala的诞生

Martin OrderSky马丁 奥德斯基 是JVM开发团队核心成员,是JDK1.5泛型,增强for循环,自动类型转换,JDK1.8Lambda等重要特性的作者.
在对JAVA的维护过程中,马丁非常推崇JAVA的面向对象及垃圾回收,是James Gosling的小迷弟. 但作为编辑器开发的狂热爱好者的他,又觉得JAVA的语法过于繁琐.所以相继自己开发了另外两种语言Pizza和Scala.其中Scala被用于实验室科研后推广开源.在上边提到JDK1.5和1.8的两个版本中的新特性就是从Pizza和Scala中迁移而来.
Scala诞生于2001年,但真正被人们所熟知并广泛应用于大数据开发是在十多年之后.可以说是Spark和Kafka带火了Scala.
James Gosling曾在采访中说,如果让他选择“今天使用的语言不是Java,那就是Scala”。

函数式编程

C:面向过程编程
Java:面向对象编程
Scala:面向函数编程
函数式编程:将所有复杂的问题的解决拆分为若干函数的处理,每一个函数可以去实现一部分功能,利用很多次函数的处理最终解决问题。函数式编程相对于面向对象编程更加的抽象,好处是代码可以非常的简洁,更多的采用常量而不是变量来解决问题,这样额外带来的好处是在线程并发时可以减少甚至杜绝多线程并发安全问题,特别适合于应用在处理高并发场景、分布式场景下的问题。函数式编程可以使用高阶函数,函数在scala中是一等公民,可以更加灵活的进行程序的编写。
函数式编程并不是面向对象编程的发展,而是另外一种解决问题的思路,两者之间也并没有绝对的好坏之分,在不同的场景中各有各的优缺点。
总结:Scala是一个面向对象的函数式编程语言.

Scala和java的关系

java是由C语言开发,scala基于java,现在看来大部分情况下,青出于蓝而胜于蓝!
分布式开发中会面对很多棘手的问题,如面临服务器之间的通信、远程调用、序列化、反序列化等等。但有了scala这一切变得轻松,它全部都做了内部实现。让我们访问分布式集群环境就和访问单机一样简单。
同时Scala无缝集成Java,Scala编译器会先把源文件编译成.class文件再运行。Scala可以调用几乎所有的Java类库,也可以从Java应用程序中调用Scala的代码。它也可以访问现存的数之不尽的Java类库,这让用户(潜在地)迁移到Scala更加容易。
Java => 编译 => .class => JVM
Scala => 编译 => .class => JVM
Scala => JVM(支持,但不推荐,不是解释执行)
扩展:groovy、clojure(storm)都可以编译成.class,利用JVM。

Scala的特点

Scala环境搭建

SDK下载安装及测试

提示:Scala基于Java,需要提前安装JDK并配置环境变量
官网:https://www.scala-lang.org/

说明: 编写本篇教程时,Scala的最新版本为2.13.3,但是基于后续Spark\Kafka\Flink三个主流框架的版本及对Scala的版本要求.我们最终选择Scala-2.12.7这个版本作为基础环境. 2.11版本之后差别并不大,所以该文所有代码预计在2.11\2.12\2.13版本环境中都可以正常运行.
1.访问Scala官网,找到所需安装包进行下载.https://www.scala-lang.org/download/all.html
在这里,我们选择的是2.12.7这个版本进行下载.

2.进入2.12.7版本下载页后,在尾部找到对应系统安装包进行下载.

3.安装Scala,双击msi包,一路下一步即可.注意中间在安装目录选择时,路径不要有中文或空格.

选择合适的目录

4.检查环境

5.Hello World
 

IDEA插件安装

在IDEA的File菜单中打开Settings窗口

选择Plugins在Marketplace中搜索Scala,找到Scala插件后点击Install.

等待插件下载完成,点击重启IEDA

如果网络环境不佳无法自动下载插件.此时需要手动导入,具体导入方法可以参考我的另一篇文章: https://blog.csdn.net/dcc15011003101/article/details/107805938

第一个Scala工程

创建一个新的工程

创建一个Object(至于什么是Object之后会详细解释)

命名为: cn.tedu.scalabasic.HelloWorld
选择Object选项

编写代码

右键运行

结果

主方法

Scala基础语法

注释

Scala中注释的写法与Java一致

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
  def main(args: Array[String]): Unit = {
    /*
    1.注释
    Scala中的注释形式与Java中一致.包括文档注释,多行注释,单行注释
     */
    /**
     * 这是一个文档注释
     */
    /*
    这是一个多行注释
     */
    //这是一个单行注释
  }
}

代码分隔

在Scala代码中每行代码的结尾不需要分号";"作为结束符(自动推断),也可以加分号.

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
  def main(args: Array[String]): Unit = {
    /*
    2.代码分隔符
    在Scala中,代码行与行之间的分隔符与Java相同,使用分号;,但是一般不需要写,Scala实现了自动推断.
     */
    println("再见,刘沛霞");
    println("你好,张慎政")
    println("么么哒,董长春")
  }
}

变量和常量定义

在Scala中变量和常量分别使用var和val来定义.可以声明类型但不必要(类型自动推断).
其中,官方建议尽量不要使用var而是使用val来定义,有利于内存回收.和线程的安全.

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
  def main(args: Array[String]): Unit = {
    /*
    3.常量变量
    在Scala中,变量使用var来定义,常量使用val定义
    并且在定义变量和常量时一般都不写类型.Scala按照上下文可以自动推断
    **重要**:在Scala中我们更推荐大家尽可能的使用常量来定义属性,这更利于内存回收.
     */
    var a = 12
    val b = 10
    val c: Int = 11
    a += 1
    //    b += 2  //编译报错,常量不可变
    println(a)
    println(b)
  }
}

标识符

Scala 可以使用两种形式的标志符,字符数字和符号。
字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号"$“在 Scala 中也看作为字母。然而以”$“开头的标识符为保留的 Scala 编译器产生的标志符使用,应用程序应该避免使用”$"开始的标识符,以免造成冲突。
同时,Scala与Java相同的是同样建议使用驼峰规则命名.

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
  def main(args: Array[String]): Unit = {
    /*
    4.标识符
    Scala标识符规则与Java大致相同.不能以符号和数字开头(_和$可以).
    其中以$例外一般不作为标识符的开头使用,暂时不必深究.
    命名格式推荐使用驼峰命名法:AaaBbb,aaaBbb,AAA_BBB
    不能使用关键字和保留字命名
     */
    val userName = "董长春"
    //    val if = 12 //编译报错,关键字和保留字不能用作标识符

  }
}

关键字保留字

方法和操作符

在Scala中有这样一句话:操作符即方法,方法即操作符
意思是方法可以按照操作符的使用规则来使用,操作符也可以按照方法的方式使用

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
  def main(args: Array[String]): Unit = {
    /*
    5.操作符和方法
    在Scala中有这样一句话:操作符即方法,方法即操作符
    意思是方法可以按照操作符的使用规则来使用,操作符也可以按照方法的方式使用
     */
    val str = "123_456_789"
    val strs = str split "_" //这里以操作符的形式(1 + 2)使用split方法
    for (s <- strs) {
      println(s)
    }

    println(2.*(4)) //这里以方法的形式(a.split("_"))使用+操作符

    //对于没有参数的方法,也可以不写括号
    val num: Int = 123
    println(num.toString())
    println(num.toString)
  }
}

Scala数据类型

在Java中,分为基本数据类型(8种) 和 引用类型(继承自Object).其中基本数据类型直接以字面量的形式赋值,没有属性和方法.
在Scala中数据类型与Java的设计类似,并且针对面向对象和函数式编程做了更进一步的优化.

其中:

  1. Scala中有两种类型,AnyVal(值类型)和AnyRef(引用类型)都继承自Any(顶级父类).
  2. AnyVal类型也是以"对象"形式存在,有属性和方法.声明时以字面量的形式声明.其中包含了类似Java中的8大基本类型:Byte\Short\Int\Long\Float\Double\Boolean\Char.除此之外还有一个特殊的Unit类型,表示空值(类似Java中的void).
  3. AnyRef类型中包括所有的Scala内置对象和自定义的Scala对象,集合等,以及所有引用的Java对象.还定义了一个Null类型来表示空对象,他的默认值为null.
  4. 在Scala中还有一个非常特殊的Nothing类型.他是所有Scala类型的子类.被定义为abstract final class Nothing extends Any.不可被继承,不可被实现.
    官方图谱:

AnyVal

这里我们先对AnyVal做详细讲解
值类型的定义

package cn.tedu.scalabasic

import scala.collection.immutable.StringOps

/**
 * AnyVal值类型的声明
 */
object DataTypes {

  def main(args: Array[String]): Unit = {
    val a: Byte = 123
    println(a,a.getClass)

    val b: Short = 123
    println(b,b.getClass)

    val c: Int = 123
    println(c,c.getClass)

    val d: Long = 123L  //此处加不加L都可以
    println(d,d.getClass)

    val e: Float = 1.23f  //为区别Double,Float类型后加f
    println(e,e.getClass)

    val f: Double = 1.23
    println(f,f.getClass)

    val g: Char = '@'
    println(g,g.getClass)

    val h: Boolean = false
    println(h,h.getClass)

    val i: Unit = "123" //不管这里赋值什么,他的值始终是()这是Unit的默认值.
    println(i,i.getClass)

    /*
      特殊说明
      虽然String是java.lang.String.在Scala中属于AnyRef类型,
      但是在实际操作是会被隐式转换为StringOps类型属于AnyVal
      通过以下实验可以证明
     */
    val j: String = "123"
    println(j,j.getClass) //(123,class java.lang.String)
    println(j.length(),j.length)  //(3,3) 这里我们知道,java.lang.String类中是没有length属性的只有length()方法

    val k: StringOps = "123"
    println(k,k.getClass) //(123,class scala.collection.immutable.StringOps) length在StringOps中定义
  }
}

值类型的转换

自动类型转换

在Scala中类型的转换包括两种,一种是值类型转换,另外一种是引用类型转换,这里我们先重点讲解值类型的转换.
值类型可以通过以下方式进行转换:

其转换方向取决于长度,只能由范围小的类型自动转换为范围大的,不能大变小.

package cn.tedu.scalabasic

/**
 * 类型转换
 */
object TypeTrans {
  def main(args: Array[String]): Unit = {
    /*
    自动类型转换
    在计算的过程中,值类型会进行自动转换,根据范围由小到大
    Byte->Short->Int->Long->Float->Double
                  ↑
                 Char
     */
    var a: Int = 12
    var b: Byte = 1
    var c: Char = 'a'
    var d: Long = 12
    var e: Double = 11.1
    var f: Short = 3
    var g: Float = 3.2f
    println(a+b,(a+b).getClass)
    println(a+c,(a+c).getClass)
    println(a+d,(a+d).getClass)
    println(a+e,(a+e).getClass)
    println(a+f,(a+f).getClass)
    println(d+g,(d+g).getClass)

  }
}
 

说明:浮点数32位的范围大于Long的64位,由转换公式决定.

强制类型转换

范围大的数据类型通过 .toxxx 实现向范围小的数据类型强制转换,注意可能会造成精度的缺失.

package cn.tedu.scalabasic

/**
 * 类型转换
 */
object TypeTrans {
  def main(args: Array[String]): Unit = {
    /*
    强制类型转换
    范围大的数据类型通过 .toxxx 实现向范围小的数据类型强制转换,注意可能会造成精度的缺失.
     */
    val h: Int = 12
    val i: Double = 2.12
    val j: Int = (h + i).toInt
    println(j)
  }
}
 

值类型和String类型之间的相互转换

  1. 值类型转换为String类型有两种方式,分别是a+"" 和 a.toString
  2. String类型数据转换为值类型的前提是可转,使用.toXxx

package cn.tedu.scalabasic

/**
 * 类型转换
 */
object TypeTrans {
  def main(args: Array[String]): Unit = {
    /*
    值类型和String类型之间的相互转换
     */
    //值类型转换为String类型有两种方式,分别是a+"" 和 a.toString
    val k:Int = 12
    println(k+"",(k+"").getClass)

    val l:Int = 12
    println(l.toString,(k.toString).getClass)

    //String类型数据转换为值类型的前提是可转,使用.toXxx
    val m:String = "123"
//    val m:String = "12a"  //报错:java.lang.NumberFormatException
    println(m.toInt,m.toInt.getClass)
  }
}
 

运算符

算数运算符

Scala中的算数运算符与Java基本一致,唯一不同是没有++和–.

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
  def main(args: Array[String]): Unit = {
    /*
    算数运算符
     */
    val a = 13
    val b = 3
    println(a + b)
    println(a - b)
    println(a * b)
    println(a / b)
    println(a / 3.0)  //如果在除法运算时需要精确值,需要有至少一个浮点型
    println(a % b)
  }
}

赋值运算符

Scala中的赋值运算符与Java基本一致

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
  def main(args: Array[String]): Unit = {
    /*
    赋值运算符
     */
    var c = 3 //将3这个值赋值给变量a
    c += 3
    println(c)
    c -= 3
    println(c)
    c *= 3
    println(c)
    c /= 3
    println(c)
    c %= 3
    println(c)
  }
}

关系运算符

Scala中的关系运算符与Java基本一致

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
  def main(args: Array[String]): Unit = {
    /*
    关系运算符
     */
    println(a>b)
    println(a>=b)
    println(a<b)
    println(a<=b)
    println(a==b)
    println(a!=b)
  }
}

逻辑运算符

Scala中的逻辑运算符与Java基本一致,但是在取反操作时不能像Java一样多层取反.

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
  def main(args: Array[String]): Unit = {
    /*
    逻辑运算符
     */
    var bool1 = true
    var bool2 = false
    println(bool1 && bool2)
    println(bool1 || bool2)
    println(!bool2) //Scala不支持多层取反(!!!!!true)
  }
}

位运算符

Scala中的位运算符与Java基本一致,了解即可.

流程控制

程序的执行顺序需要根据需求进行相应的控制,比如执行的顺序,执行条件,执行次数等.

顺序结构

Scala像大部分语言一样,代码一般按照从上到下的顺序执行.
一行代码中安装从左到右的顺序执行,同一行内如果有括号,先算括号里.
数学计算中遵循先乘除后加减.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
  def main(args: Array[String]): Unit = {
    /*
    顺序结构
     */
    println("Hello World")
    println(1+2+"Hello World"+3+2*2+(4+5))
  }
}

分支结构

在Scala分支结构中,只有if…else if…else 没有Java中的switch…case(模式匹配)
需要注意的是if分支结构是可以有返回值的.所以可以使用if语句替代Scala中没有的三元运算符.
与Java一致的是,分支结构可以多层嵌套.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
  def main(args: Array[String]): Unit = {
    /*
    分支结构
     */
    val age = 19
    if (age >= 18) {
      println("你已经成年了")
    } else if (age >= 500) {
      println("你已经成精了")
    } else {
      println("你还是个孩子")
    }
    //在Scala中没有三元运算符 a?b:c,使用if...else代替
    var result = if (age >= 18) "你已经成年了" else "你还是个孩子"
    println(result)
  }
}

循环结构

for循环

在Scala中for循环用于控制程序执行次数,与Java一致,同时具备了更加丰富的功能.

  1. for循环表达式可以同时定义多个.
  2. for循环内可以加条件.
  3. for循环可以有返回值.
  4. for循环有返回值,返回每次执行的结果.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
  def main(args: Array[String]): Unit = {
    /*
    循环结构-for
     */
    for (i <- 1 to 10) {
      println(i)
    }
    //九九乘法表
    for (i <- 1 to 9) {
      for (j <- 1 to i) {
        //print(j + "*" + i + "=" + i * j + "\t")
        //在Scala中变量和字符串的拼接可以简化为占位符
        print(s"${j} * ${i} = ${i*j}\t")
      }
      println()
    }

    //简写
    for (i <- 1 to 9; j <- 1 to i) {
      print(s"${j} * ${i} = ${i*j}\t")
      if (i == j) println()
    }

    //循环条件,守卫,步长
    for(i <- 1 to 10 if i % 2 == 0) println(i)

    //推导式:生成一个2到10的偶数集合
    var nums = for(i <- 1 to 10 if i % 2 == 0) yield i
    println(nums)
  }
}
 

while循环

Scala中while与Java中写法和用法都一致.但是更推荐使用for,因为for更加简洁.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
  def main(args: Array[String]): Unit = {
    /*
    循环结构-while
     */
    var num1 = 0
    while (num1 <= 10) {
      num1 += 1
    }
    println(num1)

    var num2 = 0
    do {
      num2 += 1
    } while (num2 <= 10)
    println(num2)
  }
}
 

break()和breakable

在Scala中没有break和continue想要使用类似功能只能用scala.util.control.Breaks._工具

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
  def main(args: Array[String]): Unit = {
    /*
    break()和breakable
     */
     //导包
    import scala.util.control.Breaks._
    //break
    breakable {
      for (i <- 1 to 10) {
        println(i)
        if (i == 6) break()
      }
    }

    //continue
    for (i <- 1 to 10) {
      breakable {
        if (i % 2 == 0) break()
        println(i)
      }
    }
  }
}
 

代码块

在Java中我们学过静态代码块,在Scala中也有代码块的概念,就是用大括号括起来的一段代码.并且在Scala中代码块是可以有返回值的.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
  def main(args: Array[String]): Unit = {
    /*
    代码块
    */
    val block = {
      println("代码")
      val v1 = 12
      val v2 = 14
      v1 + v2  //最后一行的表达式为返回值
    }
    println(block)
  }
}

方法和函数

在Scala中方法和函数是不一样的.一般情况下在使用时并不需要特殊区分.

方法

Scala中的方法定义与Java不同更加简洁.
一般情况下返回值类型可以省略.递归方法除外.
return可以省略,默认方法体中最后一行被返回.
只有一行代码的时候大括号也可以省略.
当返回值类型为Unit(没有返回值)时,=也可以省略不写,也就是说如果不写等号该方法不会返回任何数据.这种形式在Scala中叫做"过程"

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
  def main(args: Array[String]): Unit = {
    /*
    方法的定义
     */
    def getSum1(a:Int,b:Int):Int = {
      return a+b
    }
    //返回值类型可以省略(自动推断)
    //return可以省略(默认返回最后一行)
    //{}可以省略(只有一行代码)
    def getSum2(a:Int,b:Int)= a + b
    
    val sum1 = getSum1(1,2)
    val sum2 = getSum2(3,4)
    
    println(sum1)
    println(sum2)

    //没有返回值时,=等号可以不写.这种形式在Scala中叫做:过程
    def sayHello() {
      println("Hello,World")
      return 1  //即使写了返回1,因为没有等号= 方法也不会返回任何数据.
    }
    val hello = sayHello()
    println(hello)
  }
}
 

递归

方法内部调用自己,即为递归
递归方法定义时不能省略返回值类型

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
  def main(args: Array[String]): Unit = {
    //递归方法定义时必须声明返回值
    def getFactorial(n: Int): Int = {
      if (n == 1) n else n * getFactorial(n - 1)
    }

    val factorial = getFactorial(5)

    println(factorial)
  }
}
 

方法的参数

默认参数

在方法的定义时,我们有时会遇到这样的需求: 某些参数通常是不变的数据只有少数情况下才会变化.如果大多数情况下都需要手动传入同样值不符合编程中复用的原则.默认参数就可以解决这类问题.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
  def main(args: Array[String]): Unit = {
    /*
    默认参数
     */
    def getUserInfo(name:String,age:Int,addr:String = "张家口"):String = {
      s"${name}来自${addr},今年${age}岁了"
    }
    val info1 = getUserInfo("董长春",12)
    val info2 = getUserInfo("马梦诗",12)
    val info3 = getUserInfo("刘沛霞",12,"天津")

    println(info1)
    println(info2)
    println(info3)
  }
}
 

指定参数

指定参数时指,在调用方法是通过指定参数名来改变参数传递的前后顺序.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
  def main(args: Array[String]): Unit = {
    /*
    指定参数
     */

    def getProduct(a: Int = 5, b: Int = 10) = (a + 3) * b
    //改变参数传入的顺序
    println(getProduct(b = 4, a = 3))
    //避免在参数有默认值时错传
    println(getProduct(b = 1))
  }
}

可变参数

当方法的参数列表中有多个不确定的参数时,可以是用可变参数,与Java类似.
可变参数用数组来保存,可以直接调用数组的方法.
如果参数列表中既有普通参数,也有可变参数,可变参数必须写在最后.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
  def main(args: Array[String]): Unit = {
    /*
    可变参数
     */
    def getSum4(nums:Int*) = nums.sum //可变参数是用数组保存的,可以直接使用数组的方法.
    //需要注意的是,如果参数列表中既有普通参数,也有可变参数,可变参数需要写在最后.
    //def getLogs(logs:String*,id:Int) = {} //编译错误:*-parameter must come last
  }
}

方法的调用

在Scala中方法的调用也是非常灵活的,我们来总结一下.

后缀调用

最普通的调用方式就是后缀调用,这与Java中是一致的.都是对象.methodName(参数)的形式.

中缀调用

中缀调用其实在前边已经接触过,就是方法即运算法,运算符及方法的体现.
比如:1 to 10.就是1.to(10)

大括号调用

当参数只有一个时,也可以将小括号替换为大括号.比如: 对象.methodName{参数},用的不多.

无括号调用

当没有参数时,方法的调用可以不写括号,比如:array.sum

惰性加载

为了解决资源推迟加载,优化启动速度,区别加载顺序等问题,Scala中设计了一个惰性加载的概念.
所有被lazy修饰的val所指向的资源不会直接加载,而是延迟到使用时才会加载.
需要注意的是,lazy只能修饰val 不能修饰var
当用于接收方法返回值得val被lazy修饰是,这个方法会惰性加载.叫做惰性方法.
这种惰性加载的形式在Java中也有,只不过Java没有原生支持,而是通过代码逻辑来实现,叫做懒加载,比如单例中的懒汉式.在Spring等对象管理框架中也有懒加载的概念.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
  def main(args: Array[String]): Unit = {
    /*
    惰性方法(类似Java中的懒加载)
     */
    def getSum3(a:Int,b:Int) = {
      println("getSum3已经加载")
      a + b
    }
//    val sum3 = getSum3(1,2)
    lazy val sum3 = getSum3(1,2)
    println(sum3)
  }
}

函数

在Scala中函数与方法是不同的.方法依附于对象,而函数本身就是对象.
函数的定义方式也与方法不同.

object Function {
  def main(args: Array[String]): Unit = {
    /*
    函数的定义
    1.函数本身是一个对象.
    2.函数参数列表和函数体之间是 => 来链接
    3.函数不需要写返回值类型.
    4.函数定义时的变量名就是函数名,也就是这个对象的引用,调用时使用函数名(参数)的形式调用.
    5.函数可以直接用lazy修饰作为惰性函数.
     */
    val getSum = (a:Int,b:Int) => {a + b}
    println(getSum(1,2))

    val printMsg = () => {println("Hello")}
    printMsg()

    lazy val getMax = (v1:Int,v2:Int) => {if (v1>v2) v1 else v2}
    println(getMax(2, 3))
    
    /*
    方法转函数
    有时候我们需要将一段逻辑作为参数传递,但是方法不支持传递,可以将其转化为函数.
    通过本案例的输出结果,我们也可验证,函数是对象,而方法不是.
     */
    
    def getInfo() = {println("我叫董长春")}
    println(getInfo)
    val toFunction = getInfo _
    println(toFunction)
  }
}

类和对象

回顾面向对象

在Java的学习过程中,面向对象无疑是重中之重.而Scala是一门面向对象的函数式编程语言,所以面向对象的概念或者思想在Scala学习过程中也是不能忽视的.

什么是面向对象

面向对象是一种编程思想,是相对于面向过程的又一层高级抽象.
我们将一系列事物的特性或者功能抽象出来并对其进行封装,这个封装结果就是类,而属于这个类的所有个体就是这个类的对象.

面向对象的特点

封装: 保证安全性
继承: 保证复用性
多态: 保证拓展性

类的定义和对象创建

Scala中简单的类定义与Java相同,内部可以定义成员属性和成员方法.

package cn.tedu.scalaoop

/**
 * 定义一个类
 */
class Person {
  //初始化属性有三种方式
  var name:String = ""  //姓名
  var age:Int = _       //年龄
  var addr = ""         //地址

  def sayHello(){println(s"大家好我是${name},今年${age}岁了,来自${addr}.")}
}

对象的创建方式与Java也类似,通过new关键字实现对象的创建
在使用空参构造时,类名后的括号可以省略.
对象可以访问类的公共属性和方法.

package cn.tedu.scalaoop

/**
 * 创建对象
 */
object PersonObject {
  def main(args: Array[String]): Unit = {
    val p1 = new Person()
    //构造方法为空时,括号可以省略
    val p2 = new Person
    p2.name = "董长春"
    p2.age = 18
    p2.addr = "张家口"
    p2.sayHello()
  }
}

访问权限修饰符

Scala中访问权限修饰符与Java稍有不同,但是功能是一致的.
Scala中共有四种访问权限修饰符
包括:private \ private[this] \ protected \ 默认

修饰符 权限
private[this] 作用域私有的,只能在范围内中访问,对于其他私有
private 类私有的,只能在本类访问
protected 受保护的,只能在本类及其子类中访问
默认 公共的(public)全局可访问

一般情况下,开发中更多的使用到默认的,和private私有的.

package cn.tedu.scalaoop

/**
 * 定义一个动物类
 */
class Animal {
  var name = ""
  private var age = 0
  protected var color = ""
  private[this] var eat = ""

  def sayHello(){println(name,age,color,eat)}
}
 

package cn.tedu.scalaoop

object AnimalObject {
  def main(args: Array[String]): Unit = {
    val animal = new Animal
    animal.name = "驴"
    //非公有不能访问
//    animal.age
//    animal.color
//    animal.eat

  }
  class Donkey extends Animal{
    var animal = new Animal
    animal.name
//    animal.age  //私有不能访问
    animal.color  //子类可以访问protected修饰的属性
//    animal.eat  //私有不能访问
  }
}
 

构造器

像Java创建对象一样,Scala在创建对象的时候也是调用构造器,而Scala中构造器的写法与Java不用,直接定义在类名之后的小括号里.

单例对象

伴生类和伴生对象

private[this]权限修饰

apply()方法

继承

特质Trait

包和样例

容器

数组

元组

列表

集合

映射

迭代器

函数式编程

模式匹配

IO

高阶函数

隐式转换

泛型

集合

Actor

Akka

  •  

猜你喜欢

转载自blog.csdn.net/abu1216/article/details/111010012
今日推荐