一、说明
声明:本文为原创文章,未经许可禁止转载
虽然有点标题党,但本文旨在帮助读者增加薪酬谈判的底气和筹码。
众所周知,现在前端的招聘加分项要求里多半会有一条:至少熟悉或了解一门后端语言(java
/python
/golang
),如何让自己在这一项脱颖而出,顺利拿到offer,本文将会给你答案。
另外注意,简历上只需写自己最熟悉的一门后端语言就行,同时也从下面选择一个自己容易理解的语言。
开始前,给我的新公众号打个广告,欢迎关注:萌萌哒草头将军
二、怎么问你
作为前端,虽然经常和后端打交道,但是很难有机会系统学习某个后端语言,所幸,前端面试的时候一般会将JavaScript和Java两门语言的比较作为面试题目。比如:说说JavaScript和Java的异同点、JavaScript和Java相比有啥优点或缺点。
所以我们在面试前多做这类问题的准备。
全文包括代码7000+
字太长了,所以将文章分为了上、下两篇。
上篇主要对比基础语法、面向对象编程的区别,下篇对比垃圾回收、异步编程和跨平台,下篇有大量的示意图辅助理解,所以下篇更精彩,下篇请轻戳我。
下面就是你需要提前准备并且熟悉的知识点,
我将知识点分为了
青铜
、黄金
、铂金
,分别代表基础语法、语言特性、语言设计,面试的时候分别说出一两个点(十分推荐带星号标记的),这样粗中有细,有深有浅,面试官就会觉得你是真的了解而不是滥竽充数。如果被问道这个问题,你再假装思索一下(睁大眼睛,眼球向右上微微瞅一两秒)然后娓娓道来,效果更佳哦。
三、这么回答
青铜
1.语言类型
JavaScript是弱类型语言,边解释边执行,一个非const声明的变量可以保存多种类型的值。
// js
let num = 2022
let str = num.toString()
console.log(str)
Python作为弱类型语言,一个变量也可以保存任何类型的值,并且Python里没有定义常量的修饰符(可以自己实现)。
# python
num = 2022
# str = num.__str__()
# 等价于
str = str(num)
print(str)
而Java是强类型语言,先编译后执行,在编译前所有变量类型都是确定的,Java中如果一个变量接受另外类型的值,需要强制类型转换。另外,Java中使用final
关键字定义不可变的常量
// java
// 声明前需要指定变量类型
Integer num = 2022;
String str = num.toString();
System.out.Println(str);
Golang亦然。Golang中也是用const
定义常量
// go
package main
import (
"fmt"
"strconv"
)
func main() {
var a int = 2022
// Golang数据类型自身没有方法(下面会提到),将数字转化为字符串,需要调用内建包`strconv`
var b string = strconv.Itoa(a)
fmt.Println(b)
}
2.包装类*
JavaScript基本类型和包装类是一样的,基本类型可以调用类方法。
// js
let a = 2022
a.toString()
而Java基本类型无法调类方法,必须自动装箱成为包装类才能调用类型方法。
// java
int num = 2022
num.toString() // error
下面说明下Java的自动装箱和拆箱
// 没有自动装箱,声明一个包装类是这样的
Integer num1 = new Integer(2022);
// 有自动装箱,声明一个包装类是这样的
Integer num2 = 2022;
// 反过来就是自动拆箱
int num3 = num1;
// num1、num2是可以调用Integer类型方法的,比如toString,因为它们是包装类。
// num3是没法调用任何方法的,因为是基本类型。
Golang没有包装类的概念,基本类型(标量类型)没有方法可调用。
// go
var a int = 2022
// Golang数据类型自身没有方法(下面会提到),将数字转化为字符串,需要调用内建包`strconv`
var b string = strconv.Itoa(a)
Python虽然没有包装类的概念,但是基本变量和JavaScript一样是可以调用类的内建方法。
# python
num = 2022
num = num.__str__()
3.number类型的差异*
JavaScript中数字类型只有number类型一种,而Java中数据类型字节数从小到大分为byte、short、int、long、double类型。所以JavaScript数字的转换是自动的,而Java中,小字节转换为大字节是自动转换,但是大字节转换为小字节,需要强制转换。
// java
byte a = 1;
// 由小到大
short b = a;
int c = 100;
// 由大到小需要手动指定
byte d = (byte)c;
如果说Java中的number类型很杂,那Golang中的number就是非常杂了,放个图感受下吧。
Golang中的数字转换必须是显示的。
// golang
var n1 = int32(8)
var n2 = int64(8)
var n3 = int64(n1) + n2
fmt.Println(n3) // 16
Python中的数字类型只有三种:int、float、complex,互相转换将数据类型作为函数名即可。
# python
a = 1.0
int(a)
print(a) # 1
4.==
的区别*
JavaScript的==
会进行隐式类型转换,然后按值比较。如果都是引用类型,只比较引用地址,如果是不同的类型,则会都会转换为同类型比较;
// js
0 == false // true
'1' == 1 // true
'hello' == 'he' + new String('llo') // true
而Java中==
是严格按地址比较,地址相同时才相等,Java中的equals是严格按值比较的;同时Java中没有===
;
// java
0 == false // false
'1' === 1 // false
"hello" == "he" + new String("llo") // false
Golang中的==
比较有意思,因为它既不像JavaScript一样可以隐式类型转换(不同类型比较会报错),也不完全像Java一样都按地址比较,对于基本类型,按值比较,对于聚合类型比较只有当字面完全相同时才相等,对于引用类型按地址比较,对于接口类型,则按接口类型(动态类型)
和类型值(动态值)
比较,二者完全相同才相等;
package main
import (
"fmt"
)
func main() {
// golang
// 基本类型
var num1 = 1
var num2 = 1
fmt.Println(num1 == num2) // true
// 聚合类型 数组
a := [4]int{1, 2, 3, 4}
b := [4]int{1, 2, 3, 4}
c := [4]int{1, 3, 4, 5}
fmt.Println(a == b) // true
fmt.Println(a == c) // false
// 聚合类型 结构体
type A struct {
a int
b string
}
type B struct {
a int
b string
}
var d = A { a : 1, b : "1" }
var e = A { a : 1, b : "1" }
var f = A { a : 1, b : "2" }
fmt.Println(d == e) // true
fmt.Println(d == f) // false
// 引用类型
var h = &A { a : 1, b : "1" }
var j = &A { a : 1, b : "1" }
var k = h
fmt.Println(h == j) // false
fmt.Println(h == k) // true
}
Python中的==
是按值比较的,is
是按地址比较的。
# py
num1 = 444
num2 = 444
print(num1 == num2) # True
print(num1 is num2) # True
arr1 = [1, 2, 3]
arr2 = [1, 2, 3]
print(arr1 == arr2) # True
print(arr1 is arr2) # False
5.数组的异同
JavaScript和Python中的数组是任意长度的,并且可以存放各种类型。
// js
const arr = []
arr.push(...[1, '2', false, {}])
# python
arr = []
arr.append(1)
arr.append('2')
Java中的数组是固定长度的固定类型的(强类型语言的传统),如果要像JavaScript使用数组,需要使用Java集合框架里的ArrayList或者LinkedList。
// java
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;
List<Object> list = new ArrayList<Object>();
list.add(1);
list.add("2");
Golang也有这个“毛病”,所以Golang推出了切片
的概念:长度可变的数组就是切片,所以也被成为动态数组
。
// golang中,空接口泛指一切类型,所以golang没有泛型
type iface interface {}
// 定义数组的话需要指定长度
// var arr [3]iface
// 或者用...代表长度
// var arr [...]iface{1, 2, 3}
// 定义切片就不需要指定长度了
var nums []iface
nums = append(nums, 1)
nums = append(nums, '2')
黄金
6.面向对象*
JavaScript虽然可以面向对象编程,但是它不符合面向对象编程的编程方式,class语法仅仅是prototype的语法糖,而Java是标准的面向对象的编程语言,天生具有面向对象特性:封装、继承、多态(多态的表现:重载、重写),JavaScript中没有封装和多态,所以没有重载,JavaScript的“重写”仅仅是prototype“短路”假象(因为子类有这个方法就不会沿着prototype属性向上查询)
// java
class Animal {
String name;
int age;
}
class Cat extends Animal {
int fish;
public Cat (String name, int age, int fish) {
this.name = name;
this.age = age;
this.fish = fish;
}
public void call () { System.out.println("I am "+ this.name +", I have " + this.fish + "小鱼干"); }
}
class Dog extends Animal {
int bone;
public void Cat (String name, int age, int bone) {
this.name = name;
this.age = age;
this.bone = bone;
}
public void call () { System.out.println("I am "+ this.name +", I have " + this.bone + "小鱼干"); }
}
public static void main (String []args) {
Cat cat = new Cat("cat", 1, 2);
cat.call();
Dog dog = new Dog("dog", 2, 3);
dog.call();
}
Python没有extends
关键字,继承的时候只需要在子类后面指定父类,同时支持多继承。Python的__init__
和JavaScript的constructor
不仅在写法上十分相似,而且都是实例化时自动执行的。
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
class Cat(Animal):
def __init__(self, name, age, fish):
Animal.__init__(self, name, age)
self.fish = fish
def call(self):
print("I am " + self.name + ", I have " + str(self.fish) + "小鱼干")
class Dog(Animal):
def __init__(self, name, age, bone):
Animal.__init__(self, name, age)
self.bone = bone
def call(self):
print("I am " + self.name + ", I have " + str(self.bone) + "大骨头")
cat = Cat("cat", 1, 2)
cat.call() # I am cat, I have 2小鱼干
dog = Dog("dog", 2, 3)
dog.call() # I am dog, I have 3大骨头
Golang没有类的概念,只有结构体strut
和接口interface
,通过面向接口编程、结构体嵌入式开发达到面向对象的效果。嵌入式继承的写法确实很简洁,而且成员方法在类外部定义的。
package main
import "fmt"
type AnimalInterface interface {
call()
}
type Animal struct {
name string
age int32
}
type Cat struct {
Animal
fish int32
}
type Dog struct {
Animal
bone int32
}
func (this Cat) call() {
fmt.Println("I am", this.name, "I have ", this.fish, "小鱼干")
}
func (this Dog) call() {
fmt.Println("I am", this.name, "I have ", this.bone, "大骨头")
}
func main() {
var cat AnimalInterface = Cat{Animal{"cat", 1}, 2}
cat.call() // I am cat I have 2 小鱼干
var dog AnimalInterface = Dog{Animal{"dog", 2}, 3}
dog.call() // I am dog I have 3 大骨头
// Go里面可以面向struct编程,但是Go不鼓励这么做
var cat2 = Cat{Animal{"cat", 1}, 2}
cat2.call() // I am cat I have 2 小鱼干
}
7. 私有属性和公有属性
JavaScript使用对象的defineProperty
或者proxy
方法可以限制对象属性私有还是公有。
// js
Object.defineProperty(window, 'pi', {
writable:false,
value: 3.14
})
Java中使用修饰符private或者public控制。
// java
// 公有
public float pi = 3.14
// 私有
符private float pi = 3.14
Python可以通过在变量前面加双下划线__
表示私有。
# python
pi = 3.14 #公有
__pi = 3.14 #私有
Golang中通过大写表示共有属性。小写或者下划线开头表示私有。
// go
var pi float32 = 3.14 // 私有属性
var _pi float32 = 3.14 // 私有属性
var PI float32 = 3.14 // 公有属性
好了,上篇到此就结束了,更多有趣的对比欢迎戳我下篇,或者关注我的微信公众号:萌萌哒草头将军
谢谢各位的捧场