2021最新java基础总结(收藏版)

Java是一门面向对象的编程语言,它诞生于1995年,原属于SUN公司,2009年,美国甲骨文公司(Oracle)收购了SUN公司。

Java不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承(虽然Java的类不可以多继承,但是接口可以多继承)、指针等概念,因此Java语言具有功能强大简单易用两个特征。

Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。

一、简单易学

Java语言的语法与C语言和C++语言很接近,凡是有过计算机基础,或者学过这两种语言的同学再来学java,都会很容易上手的。

二、面向对象

这里所说的面向对象面向过程是一种软件开发思想。

面向过程就是分析出解决问题所需要的步骤,然后用函数按这些步骤一一实现,使用的时候再依次调用就可以了。

面向对象则是把构成问题的事务逐个分解成各个对象,并分别设计这些对象,然后将他们组装成有完整功能的系统。

面向过程一般只用函数实现,面向对象是用实现各个功能模块。

三、具有平立性和移植性

Java有一句口号:Write once, run anywhere,一次编写、到处运行

这是java最重要的特性之一,其实现是使用一种名为JVM的java虚拟机。

已编译的Java程序可以在任何带有JVM的平台上运行。例如你可以在windows平台编写代码后,放到linux上运行。

只要你在编写完代码后,将代码编译成.class文件,再把class文件打成Java包,这个jar包就可以在不同的平台上运行了。

四、具有稳健性。

Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,从而生成更可靠的程序。

异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。

五、支持多线程

多线程机制使应用程序在同一时间并行执行多项任务,这是其他部分语言所不具备的。

JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,而JRE是可以独立安装的。

JDK(Java Development Kit)

除了上面所说的JRE,JDK还包含了其他一些东西 ,它们的作用有一部分是帮助我们编译Java代码, 还有就是监控Jvm的一些工具。

JDK是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。

所以安装了JDK,就无需再单独安装JRE了。其中包含的开发工具有:编译工具(javac.exe),打包工具(jar.exe)等等。

JREJava Runtime Environment

JRE大部分都是 C 和 C++ 语言编写的,它是我们在编译java时必不可少的,因为其包含了Java虚拟机和Java程序所需的核心类库等。

核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统默认加载这个包。

如果想要运行一个已经开发好了的Java程序,计算机中只需要安装JRE即可。

JVM(Java Virtual Machine

Java虚拟机本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。

Java语言的可移植性正是建立在Java虚拟机的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。

上面是JAVA的分层架构图,有兴趣的童鞋可以仔细看看。

上面我们聊得失Java本身的架构,这里我们来说说java的三大平台与各自特点和区别。

Java SE( Java Platform Standard Edition)标准版

是为开发普通桌面和商务应用程序提供的解决方案。

JavaSE是三个平台中最核心的部分, JavaEE和 JavaME都是从 JavaSE的基础上发展而来的, JavaSE平台中包括了Java最核心的类库,如集合、IO、数据库连接以及网络编程等。

Java EE( Java Platform Enterprise Edition)企业版

是为开发企业级应用程序提供的解决方案。

JavaEE可以被看作一个技术平台,该平台用于开发、装配以及部署企业级应用程序,其中主要包括 Servlet、JSP、 Javabean、JDBC、EJB、Web等技术。

Java ME( Java Platform Micro Edition)小型版

是为开发电子消费产品和嵌入式设备提供的解决方案。

JavaME主要用于小型数字电子设备上软件程序的开发例如,为家用电器增加智能化控制和联网功能,为手机增加新的游戏和通讯录管理功能。

此外,JavaME提供了HTTP等高级Internet协议,使移动电话能以Client/ Server方式直接访问 Internet的全部信息,提供最高效率的无线交流。

java.lang:这个是系统的基础类;

java.io:这里面是所有输入输出有关的类,比如文件操作等;

java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;

java.net:这里面是与网络有关的类;

java.util:这个是系统辅助类,特别是集合类;

java.sql:这个是数据库操作的类。

Java中的数据类型可以分为四类八种,如下图:

一、整型(byte、short、int、long)

byte、short、int、long 四种数据类型虽然都是表示整数的,但它们的取值范围可不一样。

由上可以看出 byte、short 的取值范围比较小,而long的取值范围是最大的,所以占用的空间也是最多的。

int 取值范围基本上可以满足我们的日常计算需求了,所以 int 也是我们使用得最多的一个整型类型。

二、浮点型(float、double)

float 和 double 都是表示浮点型的数据类型,它们之间的区别在于精确度的不同。

double 类型比float 类型存储范围更大,精度更高。

通常的浮点型数据在不声明的情况下都是double型的,如果要表示一个数据时float 型的,可以在数据后面加上 \”F\” 。

浮点型的数据是不能完全精确的,有时候在计算时可能出现小数点最后几位出现浮动,这时正常的。

三、字符型(char)

char 有以下三种初始方式:

Java是用unicode 来表示字符,“中” 这个中文字符的unicode 就是两个字节。

String.getBytes(encoding) 方法获取的是指定编码的byte数组表示。

通常gbk / gb2312 是两个字节,utf-8 是3个字节。

如果不指定encoding 则获取系统默认encoding 。

四、布尔型(boolean)

boolean 的取值就两个:true 、false ,表示真和假。这个基本没啥好说的。

Java运算符按功能可分为:算数运算符、关系运算符、逻辑运算符、位运算符、赋值运算符和条件运算符。

具体作用、优先级等介绍大家可以从下方图中表格对比参照了解:

用于解释说明程序的文字,主要分为一下三种:

一、单行注释

格式: // 注释文字

二、多行注释

格式: /* 注释文字 */

三、文档注释

格式:/** 注释文字 */

作用

在程序中,特别是复杂的程序中,适当地加入注释可以增加程序的可读性,有利于程序的修改、调试和交流。

注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。

注意事项:多行和文档注释都使用。

在Java中,可以使用访问修饰符来保护对类、变量、方法和构造方法的访问。

Java 支持 如下 4 种不同的访问权限:

public (公共的):

对所有类可见。

使用对象:类、接口、变量、方法

protected(受保护的) :

对同一包内的类和所有子类可见。

使用对象:变量、方法。 注意:不能修饰类(外部类)。

private (私有的):

在同一类内可见。

使用对象:变量、方法。 注意:不能修饰类(外部类)

default (即缺省,什么也不写,不使用任何关键字):

在同一包内可见,不使用任何修饰符。

使用对象:类、接口、变量、方法。

具体对比和说明可参考上图。

用于修饰类、属性和方法

  • 被final修饰的类不可以被继承
  • 被final修饰的方法不可以被重写
  • 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,
  • 引用指向的内容是可以改变的

final:

final可以修饰类、变量、方法:

  • 修饰类表示该类不能被继承
  • 修饰方法表示该方法不能被重写
  • 修饰变量表 示该变量是一个常量不能被重新赋值。

finally:

finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块最终都会执行。

一般用来存放一些关闭资源的代码。

finalize:

finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用。

当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的 最后判断。

this 代表对象本身,可以理解为:指向对象本身的一个指针。

this 的用法在java中大体可以分为3种:

1、普通的直接引用,this相当于是指向当前对象本身。

2、形参与成员名字重名,用this来区分:

3、引用本类的构造函数

super关键字可以理解为:指向自己超(父)类对象的一个指针(这里可以和上面的this进行对比理解),而这个超类指的是离自己最近的一个父类。

super 的三种用法

1、普通的直接引用与this类似,super相当于是指向当前对象的父类的引用,这样就可以用super.xxx来引用父类的成员。

2、子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分

3、引用父类构造函数:

其实这个在2的演示代码中也有提现,具体请看2中我的注释。

一、作用简介

static 的主要意义是在于创建独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属性和调用方法!

static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。

static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

static块为什么可以用来优化程序性能呢?,答案是因为它的特性:即只会在类加载的时候执行一次。

因此,很多时候我们会将一些只需要进行一次的初始化操作都放在static代码块中进行。

二、应用场景

  1. 修饰成员变量
  2. 修饰成员方法
  3. 静态代码块
  4. 修饰类【只能修饰内部类也就是静态内部类】
  5. 静态导包

三、注意事项

  1. 静态只能访问静态。
  2. 非静态既可以访问非静态的,也可以访问静态的。

一、break ,continue ,return 的区别及作用

break:

跳出上一层循环,不再执行循环(结束当前的循环体)

continue:

跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)

return:

程序返回,不再执行下面的代码(结束当前的方法 直接返回)

二、跳出多重循环的方法

在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。

例如:

封装:

把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。

但是换句话说,如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

继承:

是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功

能,也可以用父类的功能,但不能选择性地继承父类。

通过使用继承我们能够非常方便地复用以前的代码。

关于继承如下 3 点请记住:

  1. 子类拥有父类非 private 的属性和方法。
  2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  3. 子类可以用自己的方式实现父类的方法。(以后介绍)。

多态:

父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。

上面我们说三大特性提到了多态,那么这里索性就详细讲解下吧。

一、什么是多态?

所谓的多态,其实就是指程序中定义的引用变量所指向的具体类型,以及通过该引用变量发出的方法调用,在编译时并不确定,而是在程序运行期间才确定

即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。

因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而实现该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性

多态分为编译时多态运行时多态

其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态

而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

二、多态的实现

Java实现多态有三个必要条件:继承、重写、向上转型。

  1. 继承:在多态中必须存在有继承关系的子类和父类。
  2. 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
  3. 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。

对于Java而言,它的多态实现机制遵循一个原则:就是当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

一、单一职责原则SRP(Single Responsibility Principle)

类的功能要单一,不能包罗万象,跟杂货铺似的。

二、开放封闭原则OCP(Open-Close Principle)

一个模块对于拓展是开放的,对于修改是封闭的。就是说只能在原有基础上扩展,而不能去修改本身存在,以避免影响之前的调用。

三、里式替换原则LSP(the Liskov Substitution Principle LSP)

这里的LSP不是你所想的老什么批哦[憨笑]

子类可以替换父类出现在父类能够出现的任何地方。

四、依赖倒置原则DIP(the Dependency Inversion Principle DIP)

高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。

抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

五、接口分离原则ISP(the Interface Segregation Principle ISP)

设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。就比如一个手机拥有打电话,看视频,玩游戏等功能,把这几个功能拆分成不同的接口,比在一个接口里要好的多。

说区别前我们先看下它们各自的简介:

  • 变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域。
  • 成员变量:方法外部,类内部定义的变量
  • 局部变量:类的方法中的变量。

一、作用域

成员变量:针对整个类都有效。

局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)

二、存储位置

成员变量:存储在堆内存中。

局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。

三、生命周期

成员变量:随着对象的创建而存在,随着对象的消失而消失

局部变量:当方法调用完,或者语句结束后,就自动释放。

四、初始值

成员变量:有默认初始值。

局部变量:没有默认初始值,使用前必须赋值。

静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。

实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。

值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。

引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。

好了,这篇文章就到这里了,由于篇幅和想到哪写到哪的原因,可能有些知识点不是太详尽和结构化,请谅解哈。

最后,希望对你的学习有帮助。

别忘了点赞、收藏、关注三连哦。

三十分钟入门基础Go(Java小子版)

Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态、强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全,GC,结构形态及 CSP-style 并发计算。

本篇文章适用于学习过其他面向对象语言(Java、Php),但没有学过Go语言的初学者。文章主要从Go与Java功能上的对比来阐述Go语言的基础语法、面向对象编程、并发与错误四个方面。

Go语言的基础语法与常规的编程语言基本类似,所不同的有声明变量的方式,数组、切片、字典的概念及功能与Java不太相同,不过Java中这些数据结构都可以通过类比功能的方式在Go中使用。

Go语言中有两种方式

1.使用var关键字声明,且需要注意的是,与大多数强类型语言不同,Go语言的声明变量类型位于变量名称的后面。Go语句结束不需要分号。

var num int

var result string = \”this is result\”

2.使用:=赋值。

num := 3 等同于 var num int = 3

其中变量的类型会根据右侧的值进行匹配,例如\”3\”会匹配为int,\”3.0\”会匹配为float64,\”result\”会匹配为string。

使用const来声明一个常量,一个常量在声明后不可改变。

const laugh string = \”go\”

只声明未赋值的变量,其值为nil。类似于java中的“null”

没有明确初始值的变量声明会被赋予它们的 零值

零值是:

  • 数值类型为 0,
  • 布尔类型为 false,
  • 字符串为 \”\”(空字符串)。

使用func关键字来定义一个方法,后面跟方法名,然后是参数,返回值(如果有的话,没有返回值则不写)。

func MethodName(p1 Parm, p2 Parm) int{}

Go 函数与其他编程语言一大不同之处在于支持多返回值,这在处理程序出错的时候非常有用。例如,如果上述 add 函数只支持非负整数相加,传入负数则会报错。

在 Go 语言中,无论是变量、函数还是类属性和成员方法,它们的可见性都是以包为维度的,而不是类似传统面向编程那样,类属性和成员方法的可见性封装在所属的类中,然后通过 private、protected 和 public 这些关键字来修饰其可见性。

Go 语言没有提供这些关键字,不管是变量、函数,还是自定义类的属性和成员方法,它们的可见性都是根据其首字母的大小写来决定的,如果变量名、属性名、函数名或方法名首字母大写,就可以在包外直接访问这些变量、属性、函数和方法,否则只能在包内访问,因此 Go 语言类属性和成员方法的可见性都是包一级的,而不是类一级的。

假如说一个名为domain的文件夹下有3个.go文件,则三个文件中的package都应为domain,其中程序的入口main方法所在的文件,包为main

对于学过C语言来说,指针还是比较熟悉的,我所理解的指针,其实就是一个在内存中实际的16进制的地址值,引用变量的值通过此地址去内存中取出对应的真实值。

与Java语言的if基本相同

数组功能与Java语言类似,都是长度不可变,并且可以使用多维数组,也可以通过arrays[i]来存储或获取值。

数组使用较为简单,但是存在着难以解决的问题:长度固定

例如当我们在程序中需要一个数据结构来存储获取到的所有用户,因为用户数量是会随着时间变化的,但是数组其长度却不可改变,所以数组并不适合存储长度会发生改变的数据。因此在Go语言中通过使用切片来解决以上问题。

切片相比于Java来说是一种全新的概念。在Java中,对于不定长的数据存储结构,可以使用List接口来完成操作,例如有ArrayList与LinkList,这些接口可以实现数据的随时添加与获取,并没有对长度进行限制。但是在Go中不存在这样的接口,而是通过切片(Slice)来完成不定长的数据长度存储

切片与数组最大的不同就是切片不用声明长度。但是切片与数组并非毫无关系,数组可以看作是切片的底层数组,而切片则可以看作是数组某个连续片段的引用。切片可以只使用数组的一部分元素或者整个数组来创建,甚至可以创建一个比所基于的数组还要大的切片:

切片的长度就是它所包含的元素个数。

切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。

切片 s 的长度和容量可通过表达式 len(s) 和 cap(s) 来获取。

切片的长度从功能上类比与Java中List的size(),即通过len(slice)来感知切片的长度,即可对len(slice)进行循环,来动态控制切片内的具体内容。切片的容量在实际开发中运用不多,了解其概念即可。

模拟上述提到的问题使用切片解决方案

字典也可称为 ‘键值对’ 或 ‘key-value’,是一种常用的数据结构,Java中有各种Map接口,常用的有HashMap等。在Go中通过使用字典来实现键值对的存储,字典是无序的,所以不会根据添加顺序来保证数据的顺序。

众所周知,在面向对象的语言中,一个类应该具有属性、构造方法、成员方法三种结构,Go语言也不例外。

Go语言中并没有明确的类的概念,只有struct关键字可以从功能上类比为 面向对象语言中的“类” 。比如要定义一个学生类,可以这么做:

Go中的成员方法声明与其他语言不大相同。以Student类为例,

接口在 Go 语言中有着至关重要的地位,如果说 goroutine 和 channel 是支撑起 Go 语言并发模型的基石,那么接口就是 Go 语言整个类型系统的基石。Go 语言的接口不单单只是接口,下面就让我们一步步来探索 Go 语言的接口特性。

和类的实现相似,Go 语言的接口和其他语言中提供的接口概念完全不同。以 Java、PHP 为例,接口主要作为不同类之间的契约(Contract)存在,对契约的实现是强制的,体现在具体的细节上就是如果一个类实现了某个接口,就必须实现该接口声明的所有方法,这个叫「履行契约」:

这个时候,如果有另外有一个接口 iTemplate2 声明了与 iTemplate 完全一样的接口方法,甚至名字也叫 iTemplate,只不过位于不同的命名空间下,编译器也会认为上面的类 Template 只实现了 iTemplate 而没有实现 iTemplate2 接口。

这在我们之前的认知中是理所当然的,无论是类与类之间的继承,还是类与接口之间的实现,在 Java、PHP 这种单继承语言中,存在着严格的层级关系,一个类只能直接继承自一个父类,一个类也只能实现指定的接口,如果没有显式声明继承自某个父类或者实现某个接口,那么这个类就与该父类或者该接口没有任何关系。

我们把这种接口称为侵入式接口,所谓「侵入式」指的是实现类必须明确声明自己实现了某个接口。这种实现方式虽然足够明确和简单明了,但也存在一些问题,尤其是在设计标准库的时候,因为标准库必然涉及到接口设计,接口的需求方是业务实现类,只有具体编写业务实现类的时候才知道需要定义哪些方法,而在此之前,标准库的接口就已经设计好了,我们要么按照约定好的接口进行实现,如果没有合适的接口需要自己去设计,这里的问题就是接口的设计和业务的实现是分离的,接口的设计者并不能总是预判到业务方要实现哪些功能,这就造成了设计与实现的脱节。

接口的过分设计会导致某些声明的方法实现类完全不需要,如果设计的太简单又会导致无法满足业务的需求,这确实是一个问题,而且脱离了用户使用场景讨论这些并没有意义,以 PHP 自带的 SessionHandlerInterface 接口为例,该接口声明的接口方法如下:

用户自定义的 Session 管理器需要实现该接口,也就是要实现该接口声明的所有方法,但是实际在做业务开发的时候,某些方法其实并不需要实现,比如如果我们基于 Redis 或 Memcached 作为 Session 存储器的话,它们自身就包含了过期回收机制,所以 gc 方法根本不需要实现,又比如 close 方法对于大部分驱动来说,也是没有什么意义的。

正是因为这种不合理的设计,所以在编写 PHP 类库中的每个接口时都需要纠结以下两个问题(Java 也类似):

  1. 一个接口需要声明哪些接口方法?
  2. 如果多个类实现了相同的接口方法,应该如何设计接口?比如上面这个 SessionHandlerInterface,有没有必要拆分成多个更细分的接口,以适应不同实现类的需要?

接下我们来看看 Go 语言的接口是如何避免这些问题的。

在 Go 语言中,类对接口的实现和子类对父类的继承一样,并没有提供类似 implement 这种关键字显式声明该类实现了哪个接口,一个类只要实现了某个接口要求的所有方法,我们就说这个类实现了该接口

例如,我们定义了一个 File 类,并实现了 Read()、Write()、Seek()、Close() 四个方法:

假设我们有如下接口(Go 语言通过关键字 interface 来声明接口,以示和结构体类型的区别,花括号内包含的是待实现的方法集合):

尽管 File 类并没有显式实现这些接口,甚至根本不知道这些接口的存在,但是我们说 File 类实现了这些接口,因为 File 类实现了上述所有接口声明的方法。当一个类的成员方法集合包含了某个接口声明的所有方法,换句话说,如果一个接口的方法集合是某个类成员方法集合的子集,我们就认为该类实现了这个接口。

与 Java、PHP 相对,我们把 Go 语言的这种接口称作非侵入式接口,因为类与接口的实现关系不是通过显式声明,而是系统根据两者的方法集合进行判断。这样做有两个好处:

  • 其一,Go 语言的标准库不需要绘制类库的继承/实现树图,在 Go 语言中,类的继承树并无意义,你只需要知道这个类实现了哪些方法,每个方法是干什么的就足够了。
  • 其二,定义接口的时候,只需要关心自己应该提供哪些方法即可,不用再纠结接口需要拆得多细才合理,也不需要为了实现某个接口而引入接口所在的包,接口由使用方按需定义,不用事先设计,也不用考虑之前是否有其他模块定义过类似接口。

这样一来,就完美地避免了传统面向对象编程中的接口设计问题。

对于任何一个优秀的语言来说,并发处理的能力都是决定其优劣的关键。在Go语言中,通过Goroutine来实现并发的处理。

Go语言中没有像Java那么多的锁来限制资源同时访问,只提供了Mutex来进行同步操作。

多协程之间通过Channel进行通信,从功能上可以类比为Java的volatile关键字。

ch := make(chan int) 声明一个int型的Channel,两个协程之间可以通过ch进行int数据通信。

通过Channel进行数据传输。

Go 语言错误处理机制非常简单明了,不需要学习了解复杂的概念、函数和类型,Go 语言为错误处理定义了一个标准模式,即 error 接口,该接口的定义非常简单:

其中只声明了一个 Error() 方法,用于返回字符串类型的错误消息。对于大多数函数或类方法,如果要返回错误,基本都可以定义成如下模式 —— 将错误类型作为第二个参数返回:

然后在调用返回错误信息的函数/方法时,按照如下「卫述语句」模板编写处理代码即可:

非常简洁优雅。

defer用于确保一个方法执行完成之后,无论执行结果是否成功,都要执行defer中的语句。类似于Java中的try..catch..finally用法。例如在文件处理中,无论结果是否成功,都要关闭文件流。

Go语言中没有太多的异常类,不像Java一样有Error、Exception等错误类型,当然也没有try..catch语句。

Panic(恐慌),意味在程序运行中出现了错误,如果该错误未被捕获的话,就会造成系统崩溃退出。例如一个简单的panic:a := 1/0。

就会引发panic: integer divide by zero。

其中第一行表示出问题的协程,第二行是问题代码所在的包和函数,第三行是问题代码的具体位置,最后一行则是程序的退出状态,通过这些信息,可以帮助你快速定位问题并予以解决。

当有可以预见的错误时,又不希望程序崩溃退出,可以使用recover()语句来捕获未处理的panic。recover应当放在defer语句中,且该语句应该在方法中前部,避免未能执行到defer语句时就引发了系统异常退出。

可以看到,虽然会出现异常,但我们使用recover()捕获之后,就不会出现系统崩溃退出的情形,而只是将该方法结束。其中fmt.Printf(\”%d / %d = %d\\n\”, i, j, k)语句并没有执行到,因为代码执行到他的上一步已经出现异常导致该方法提前结束。4 recover

当有可以预见的错误时,又不希望程序崩溃退出,可以使用recover()语句来捕获未处理的panic。recover应当放在defer语句中,且该语句应该在方法中前部,避免未能执行到defer语句时就引发了系统异常退出。

可以看到,虽然会出现异常,但我们使用recover()捕获之后,就不会出现系统崩溃退出的情形,而只是将该方法结束。其中fmt.Printf(\”%d / %d = %d\\n\”, i, j, k)语句并没有执行到,因为代码执行到他的上一步已经出现异常导致该方法提前结束。

通过以上的学习,大家可以以使用为目的的初步了解到go的基础语法,但是仅凭本文想要学明白go是完全不够的。例如go的最大优势之一“协程”,由于文章目的就没有特别详细展开,有兴趣的同学可以继续学习。

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。