前端速学 java 基础语法
前端开发快速入门 java 语法(如果对 TS 和 es6 的 class 面向对象熟悉,更容易理解 java 的基础语法)。
课程来自B站黑马,本文是对学习过程中的总结和记录。
入门
IDEA、Java 环境这些就不介绍。
IDEA 部分快捷设置
- 自动导包。

- 忽略大小写(String 时有提示,string 无提示且报错,可取消该设置)

更多的 IDEA 常用设置可参考另一篇文章 IDEA常用代码规范设置
代码快捷键
.var 可以直接自动补充前缀类型和属性名
Person person = new Person();
// 可以使用 new Person().var 来自动补充前缀的类型 Person 和变量名 person
alt + 左键选中 可以快速批量选中某些代码片段,快速对其进行更改
ctrl + alt + L 快速格式化代码
shift + enter 换行
ctrl + P 快速弹出参数提示
alt + enter 可以对错误进行快速修复纠正
alt + insert 可以快速生成 get set constructor 等方法。
运算符
-
隐式转换
- 运算的取值范围:
byte(1)<short(2)<int(4)<long(8)<float(4)<double(8)float和double,浮点数类型,用于表示带有小数部分的数值,其内部存储结构和整型不同,因此不能单纯按照数值范围来比较它们的字节大小
- 运算时数据类型不一致,则会隐式转换为一致的数据类型再计算
- 小范围的会先转为大范围的,比如 float 和 double 相加,会统一提升为 double
- byte short char 运算时,会直接先提升为 int 类型,比如 byte 和 short 类型相加时,都会提升为 int 进行运算。
- 运算的取值范围:
-
强制转换
- 将大范围类型的转换为小范围类型的数据,必须依靠强制转换,但是强制转换的数据有可能出现一些错误
byte a = 100; byte b = 100; byte num = (byte)(a+b); // -57 强制转换成功,但结果错误
- 将大范围类型的转换为小范围类型的数据,必须依靠强制转换,但是强制转换的数据有可能出现一些错误
-
字符相加
- 普通字符相加,都是直接拼接的关系,比如
'a' + 'b' = "ab";。 - 当 char 字符和 数字相加时,java 会首先去 AscII 码表中查找 字符对应的数字,然后相加
char a1 = 'a'; // ASCII 对应 97 int a2 = 1; System.out.println(a1 + a2); // 98
- 普通字符相加,都是直接拼接的关系,比如
-
自增自减
++a和a++,在单独一行使用时,效果都是一样的,当赋值时,则出现区别a++是先用后加++a是先加后用int a = 10; int b = a++; // b = 10 int b = ++a; // b = 11
-
赋值运算符
a += b;等价于a = a + b;。_= *= /= % =同理
-
关系运算符:
== != > >= < <=(功能和js一致,略) -
逻辑运算符
& | ^ !^异或:即判断两个是否为不同,不同为 true。例如true ^ true打印为 false,true ^ false打印为 true。
-
短路逻辑运算符
&&||&&||和&|的效果一致,即和 js 中的&||一样。
-
逻辑/短路运算符区别
& |无论左边 true false,右边都执行&& ||如果左边能确定整个表达式结果,右边不执行&& ||和 js 中的& ||表达效果一致
-
三元运算符:
a>b?a:b(功能和js一致,略)
判断和循环
if语句,和 js 效果一致,略switch语句,和 js 效果一致- JDK 12 新特性写法
int num = 10; // 传统写法 switch (num) { case 1: System.out.println("一"); break; case 2: System.out.println("二"); break; case 3: System.out.println("三"); break; default: System.out.println("DEFAULT"); break; } // JDK 12 新写法 switch (num) { case 1 -> System.out.println("一"); case 2 -> System.out.println("二"); case 3 -> System.out.println("三"); // 只有一行的情况下,括号可省略 default -> { System.out.println("DEFAULT"); } }- swtich 匹配赋值也可以简写
// 通过 swtich 匹配,给数据赋值,正常 swtich 简写 String s = "2"; String str; switch (s) { case "1" -> str = "一"; case "2" -> str = "二"; case "3" -> str = "三"; default -> str = "no result"; } // 简写,case 匹配到的时候 return 了匹配的值,被 str 接收 String s = "2"; String str = switch (s) { case "1" -> "一"; case "2" -> "二"; case "3" -> "三"; default -> "no result"; }; System.out.println(str);for循环语句,和 js 效果一致,略while循环语句,和 js 效果一致,略
数组
- 初始化数组
String[] strArr = {"张三","李四"}; // 静态初始化
System.out.println(strArr); // [Ljava.lang.String;@776ec8df 数组的堆内存地址
String[] strs = new String[2]; // 动态初始化,固定长度不能扩展
System.out.println(strs[0]); // null 未赋值的默认为 null
System.out.println(strs[3]); // 异常:Index 3 out of bounds for length 2
-
元素访问和赋值(与 js 保持一致,略)
-
数组循环,IDEA 快捷键
[数组名].fori(其余与 js 保持一致,略) -
数组内存
- 主入口 main 方法执行时会入栈,方法执行完毕出栈
- new 出来的都在堆内存中,开辟一个内存空间,并占位。
方法
- 跟 js
[class 类]/[函数]的比较相似,但是 js 的方法命名在同模块下不能重名,同名的后边的会把前边的覆盖掉。 - java 可以在同一个类下边命名重复的方法,只要变量入参不同即可,即 方法重载
public static String getStr() {
// ...逻辑代码
return "aa";
}
// 方法重载
public static String getStr(String a) {
return a;
}
-
方法内存
- 方法在运行时,都在入栈运行,运行完毕出栈
- new 出来的,都在堆内存中有其对应的内存地址
- 方法运行完毕出栈时,其内部的变量能都会随之销毁
- 多个方法运行时,栈内存先进后出
-
基本数据类型 引用数据类型
- 基本数据类型:值存储在自己的空间,赋值时,是真实的数据值
- 引用数据类型:值存储在其他空间,赋值时,是空间的内存地址
public static void main(String[] args) { String[] strArr = {"张三", "李四"}; changeArr(strArr); // 直接修改的是数组内存地址中的值 System.out.println(strArr[1]); // 赵六 } public static void changeArr(String[] arr) { arr[1] = "赵六"; }
面向对象
js 中 class 类的使用就是面向对象。class AA const aa = new AA() aa.x。
对 es6 class 熟悉的,会更容易理解 java 中的面向对象
java 中,一个 java 文件中允许存在多个类名,但只有一个类名能用
public修饰,且public修饰的类名必须和文件名保持一致。
// Person 类
public class Person {
String name; // 属性(成员变量)
String age;
// 行为(方法)
public void printCode() {
System.out.println(name + "在码字");
}
}
// 调用/实例化 Person
Person person = new Person();
person.name = "张三";
person.printCode(); // 张三在码字
成员变量在初始化时,若未手动声明默认值,则自带一个初始化的默认值。
- 基本数据类型:byte short int long。默认值
0。float double。默认值0.0。boolean 默认值false- 引用类型:类、接口、数组、String。默认值
null
JavaBean
private
私有属性修饰符,被 private 修饰的变量,只能内部访问,无法通过 new 实例进行访问。
在 js 中,private String name 就相当于 #name。(js class 中,# 前缀的属性只能在 class 内部访问)。
私有属性想要外部访问/赋值,需要提供一个 getter/setter 方法,来确保数据的安全性。
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
Person person = new Person();
// person.name // 'name' has private access in 'com.jsonq.demo1.Person'
// private 修饰的,如果想要外部访问,就必须提供相应的 getter 方法
System.out.println(person.getName());
}
}
// Person.java
public class Person {
private String name; // 私有成员变量,无法通过外部访问
private Integer age;
public String getName() {
return name; // 私有属性必须通过提供 getter 和 setter 来获取和赋值
}
public void getName(String name) {
this.name = name;
}
}
就近原则和 this 关键字
class 类中和 方法中,字段名重复的情况下,谁近就取谁
this 用于区分 成员变量 和 局部变量
public class Person {
private int age; // 成员变量
public void test() {
int age = 20; // 局部变量
System.out.println(age); // 20 就近原则
System.out.println(this.age); // 0 this 直接指向的是 class 中的成员变量
}
}
构造方法
分为 空参构造方法 和 有参构造方法
public class Person {
private String name;
private Integer age;
// 空参构造器,如果没有写,会默认自动创建
public Person() {
}
// 有参构造器
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
// 使用
new Person() // 无参构造器,name 和 age 是默认初始值
new Person("张三", 20) // 有参构造器,初始化时直接赋参数
注意: 一旦使用了有参构造器,不会再自动创建无参构造器,必须手动声明
- 存在 有参构造器,就必须存在 无参构造器
- 没有 有参构造器,无参构造器 写不写都可以
推荐在使用时,有参构造和无参构造,都手动声明一下。
标准 JavaBean 类
JavaBean 是一个标准类的统称,常见情况下,结合的就是 private this 和构造方法来组成的 JavaBean
一个标准的 JavaBean 类,大约有如下几个特点:
- 类名见名知意
- 成员变量使用
private修饰 - 提供至少两个构造方法
- 无参构造方法
- 全参(全部参数)构造方法
- 成员方法
- 每个 private 修饰的成员变量,需要提供对应的
get和set方法 - 若有其它行为,同样需要写上
- 每个 private 修饰的成员变量,需要提供对应的
public class Person {
private String account;
private String password;
private Integer age;
// 空参构造
public Person() {
}
public Person(String account, String password, Integer age) {
this.account = account;
this.password = password;
this.age = age;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
// ...
}
IDEA 快速生成 get 和 set 方法快捷键:Alt + Ins 或者 Alt + Fn + Ins。
如果 Fn 是用来切换的快捷键方式的,记得调整 Fn,让其处于普通快捷键模式。

快捷键提示中的
Constructor可以快速生成 无参/有参/全参构造器,点击Select None生成的就是无参构造。
也可以单独生成 get 方法Getter,和 set 方法Setter
数据类型
基本数据类型
- 整数
- 浮点数
- 布尔
- 字符
基本数据类型存储再 栈内存 中,栈内存 中存储的都是真实的数据值。
引用类型
除了基本类型,剩下的都是引用类型,对 js 的数据类型熟悉的会很容易理解。
引用数据类型存储在 堆内存 中,比如
Student s = new Student(),s本身只是记录一个地址值(指针),真正的数据存储在 堆内存 中,访问s时,会根据指针找到 堆内存 中真正的数据值

- 基本数据类型
- 数据值存储在自己空间(栈)
- 赋值给其它变量时,赋值的是真实值
- 引用数据类型
- 数据值存储在其它空间(堆)
- 自己空间存储的是地址值
- 赋值给其它变量时,赋值的时地址值
成员变量和局部变量

字符串
创建方式
两种创建方式
- 直接赋值
String name="张三",一般用这种较多 - new 创建实例
String name = new String("张三");- 如果
new String()时不传任何值,创建的结果值可以理解为""空字符串 - 传入
char类型创建字符串(仅了解)

- 传入
byte类型创建字符串(仅了解)

- 如果
比较
==
== 号,基本数据类型比较的是值,而引用数据类型比较的是地址值
String a = "张三";
String b = "张三";
System.out.println(a == b); // true 比较值
String c = new String("李四");
String d = new String("李四");
System.out.println(c == d); // false 比较地址值
equals 和 equalsIgnoreCase
开发过程中,字符串比较绝大部分使用的都是 equals
equals比较两个字符串的值是否完全一样,完全一样true,否则falseequalsIgnoreCase语义上就可以看出,比较两个字符串的值是否一样(但是忽略大小写)
String a = new String("张三");
String b = new String("张三");
System.out.println(a.equals(b)); // true
String c = "AAA";
String d = "aAa";
System.out.println(c.equalsIgnoreCase(d)); // true
其它常用方法
获取字符串长度和根据索引访问字符串某一个字符
字符串变量并取对应的索引
public static void main(String[] args) {
String str = "张三zhangsan good";
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i); // 依次取字符串对应索引的字符
System.out.println(c);
}
}

str.length() 取到字符串的长度,str.chartAt(i) 取到字符串中第 i 项的字符。
字符串截取substring和替换replace
substring 有两种传入方式
substring(3):将字符串截掉前3位字符,返回剩余的字符
substring(1,3):将字符串截取索引第1项到第3项(不包含第3项),返回截取出的字符,即截取出 2 个字符
String str = "张三zhangsan good";
System.out.println(str.substring(3)); // hangsan good
System.out.println(str.substring(1, 3)); // 三z
replace 常用的就是一种
replace("CNM","***") 会将字符串中所有的 CNM 替换成 ***
String str = "别玩了,CNM,垃圾";
System.out.println(str.replace("CNM", "***")); // 别玩了,***,垃圾
字符串拼接 StringBuilder
str = str + "abc" 这种普通的字符串拼接缺陷是很大的,执行速度极慢
public static void main(String[] args) {
String str = "张三";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str = str + "abc";
}
long endTime = System.currentTimeMillis();
System.out.printf("执行时长:%d 毫秒.", (endTime - startTime)); // 执行时长:3100 毫秒.
}
使用 StringBuilder 执行
public static void main(String[] args) {
StringBuilder str = new StringBuilder("张三");
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str.append("abc");
}
long endTime = System.currentTimeMillis();
// 计算执行时间
System.out.printf("执行时长:%d 毫秒.", (endTime - startTime)); // 执行时长:16 毫秒.
}
使用 str = str + "abc" 的方式循环拼接,执行时间是 3100 毫秒(3.1s),而使用 StringBuilder 则仅仅耗时 16 毫秒(0.016 s),高低立判。
常用操作
StringBuilder 还提供了很多常用的字符串操作方法
append 拼接字符串
repeat 重复拼接的次数(循环拼接的次数)
比如刚才的 for 循环 append 添加字符串就可以简写

reverse 反转字符串
跟 js 的反转字符串效果一致。
StringBuilder str = new StringBuilder("张三abc");
System.out.println(str.reverse()); // cba三张
length 获取字符串长度
效果和普通字符串的 length 一样 sb.length()
toString 将 StringBuilder 转为 String
StringJoiner(JDK8 开始支持)
StringBuilder 补足了字符串常规拼接性能上的缺点,而 StringBuilder 本身在进行自定义字符串拼接时就比较麻烦,此时就由 StringJoiner 进行补足。
比如一个数组 {1, 2, 3, 4},想要将其转换为字符串 [1, 2, 3, 4] 的格式,两者的使用如下

创建方式
StringJoiner 由两种传参方式
StringJoiner(间隔符号),指定数组转字符串时,各项之间的拼接符号StringJoiner(间隔符号, 起始符号, 结束符号)
成员方法
常用方法和 StringBuilder 一致
add和StringBuilder的append效果一致length和StringBuilder的length效果一致toString和StringBuilder的toString效果一致
集合 ArrayList
ArrayList 和 普通数组的区别是:
- 普通数组直接打印出来的是地址值,

- 普通数组的长度是不可变的,
ArrayList数据长度可任意扩充缩减

- 普通数组是可以存储基本类型和引用类型,
ArrayList不能直接存储基本类型,需要借助包装类

增删改查
add(Entity entity)添加元素remove(Entity entity)删除指定元素remove(int index)删除指定索引的元素set(int index, Eneity entity)修改指定索引下的元素get(int index)获取指定索引的元素size()获取集合的长度(集合中元素个数)
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.remove("张三"); // 删除 ”张三“
System.out.println(list); // [李四, 王五, 赵六]
list.remove(2); // 删除索引为 2: ”赵六“
System.out.println(list); // [李四, 王五]
list.set(0,"张四"); // 设置第 0 项 “李四” -> “张四”
System.out.println(list); // [张四, 王五]
String s = list.get(1);// 获取第 1 项 ”王五“
int size = list.size(); // 长度 2
包装类
- byte -> Byte
- short -> Short
- char -> Character
- int -> Integer
- long -> Long
- float -> Float
- double -> Double
- boolean -> Boolean
面向对象进阶
[修饰符] static
static 修饰的成员变量,会在堆内存中单独开辟一块静态区域
static修饰的被所有对象共享static修饰的,不属于对象,属于类- 随着类的加载而加载,优先于对象存在
static 静态变量
正常情况下,一个 class 每次被 new,都会创建一个新的实例,每个实例的内部熟悉和方法都是初始的。
class 内部,被 static 修饰的变量,只需要被赋值一次,后续 new 的实例都会共享该数据

static修饰的变量,可以被赋值一次,后续所有的new实例都可以共享该数据static修饰的变量,如果在 class 内部访问,可以通过class.xxx或者xxx直接访问static修饰的变量,如果在其它 class 中访问和赋值,通过class.xxx进行赋值和访问,也可以new一个实例,通过实例.xxx赋值。
static 静态方法
static 静态方法常用于工具类的封装,工具类大致有以下几个特点:
- 可在内部提供一个私有构造器,防止外部对象调用时实例化。
- 内部对外放开的方法,大部分都是用
public static修饰, - 内部私有的方法,则用
private修饰
工具类例子如下:
public class ArrayUtil {
//私有化构造方法,目的时为了不让外部创建其对象
private ArrayUtil() {
}
public static String printArr(int[] arr) {
StringJoiner joiner = new StringJoiner(",", "[", "]");
for (int i = 0; i < arr.length; i++) {
joiner.add(arr[i] + "");
}
return joiner.toString();
}
}
使用时,直接调用 ArrayUtil.printArr(入参) 就可以。
static 注意事项
- 静态方法中,只能访问静态变量和静态方法
- 非静态方法中可以访问所有
- 静态方法中没有 this 关键字

其中 this 指向的就是当前调用改方法的对象,a.test(),那 this 指向的就是 a,即谁调用,this 指向谁,这点和 js 是一样的
封装
对象代表什么,就封装对应的数据,并提供对应的行为,这是一个设计思想。
就比如使用 private static final 等修饰符来对常用的变量和方法进行提取封装。
继承 extends
类与类之间存在重复共有的属性和方法,就能提取到一个公共类中,可以通过 extends 来继承公共类,和 js es6 中的 class 使用上一样。
一个类只能 extends 一个类。不能 public A extends B extends C

子类继承父类的规则
- 成员变量,不管是否私有,子类都可以继承
- 成员方法,父类私有的成员方法(
private修饰),子类无法继承 - 构造方法,子类无法继承

继承成员变量的特点
变量访问特点
- 成员变量的访问会逐层向上找,就近取值

- 继承中,如果父类中有
name属性,子类中也有name属性,但是类型不一样,则以子类的为准
重名访问特点
重名时,访问成员变量是有 3 种情况的
注:super 甚至可以理解为父类,super() 父类的空参构造,super(...args) 父类的全参构造,super.xx 父类的成员方法和变量
System.out.println(name); // 从局部位置向上查找
System.out.println(this.name); // 从本类成员位置向上查找
System.out.println(super.name); // 从父类成员位置向上查找
总结:
成员变量就近取值,成员变量重名则优先子类
继承中成员方法的特点
重名访问特点

- 方法重写
当父类种有一个成员方法,子类继承父类时,若父类方法不能满足需求,可对父类方法进行重写。 - 书写格式
子类中出现和父类种一摸一样的方法声明,那子类的这个方法就是对父类方法的重写 - @Override
建议所有重写后的方法都加上@Override,以保证重写方法的安全性(@Override可以检查重写的方法是否有语法错误,与父类方法格式不一致会标注报错)

继承的构造方法和this、super关键字
this 是本类方法调用者的地址,super 是父类的存储空间
- 父类的构造方法不会被子类继承
- 子类所有的构造方法会先访问父类的构造方法 再执行自己。
- 子类构造方法执行时,必须会执行
super()就算不写也存在,且必须在第一行 - 如果要调用父类的构造方法,必须手动写 super 进行调用。
比如子类 Student 没有有参构造,但是可以通过使用 Person 父类的有参构造来创建对象。

其输出结果是

如果是 Student 空参构造,则会先执行父类的空参构造函数,然后执行子类的空参构造

多态
多态的应用场景:
当拥有多种身份的人,需要多种不同的注册方法时,图中的写法是最笨重的写法,也可以由此引申出多态。

让多种身份的人的类都继承一个基类 Person,则所有的子类都可以被 Person 类所接受。

多态调用成员变量和方法的特点
- 变量调用:编译看左边,运行也看左边
- 方法调用:编译看左边,运行看右边
准备三个类,Animal 为父类,Cat Dog 为子类分别继承 Animal

编译看左边:

编译报错,就是 IDEA 报错,IDEA 没报错,运行后报的错是运行时错误
运行:变量运行看左边,方法运行看右边:
public static void main(String[] args) {
Animal animal = new Cat();
System.out.println(animal.name); // 动物
animal.say(); // 猫 say
}
变量看左边:animal.name 访问的是成员变量,左侧是 Animal,跟右侧无关,调用的是 Animal 中的 name。运行的结果就是 【动物】,而非 【猫】
方法看右边:animal.say() 访问的是成员方法,右边是 Cat,跟左侧无关,调用的是 Cat 中的 say 方法。则运行的结果就是 【猫 say】,而非 【动物 say】
多态的优点和弊端
- 优势:
- 父类型作为参数,可接受所有的子类对象
- 弊端:
- 不能使用子类的独有功能(比如父类中没有
say方法,但是子类中有,也不能使用 say 方法)
- 不能使用子类的独有功能(比如父类中没有
- 弊端解决方法
- 强制类型转换。一般都会做强制类型转换,从而将父类转换成真正的子类类型,从而调用子类中的独有功能。
- 转换时可以用
instanceof进行判断,根据传入的子类,将父类转成对应的子类,就可以调用子类独有功能。
public void callback(Person p) {
if (p instanceof Cat) {
Cat cat = (Cat)p;
cat.eat();
}
}
package 包 和 final 关键字
package 包:可以理解为前端的 import require
package 包跟前端 import 和 require 类似,引入别的文件才能使用这个文件中的功能

final:可以理解为 js 中的 const
基本数据类型修饰的不能更改,引用数据类型的可以更改内部属性,但地址值不能变

权限修饰符

名词说明:
- 同一个类:同一个
class内部 - 同一个包的其他类:
com.jsonq下的Cat类,com.jsonq下的Dog类,在一个 package 下。 - 不同包下的子类:
com.a和com.b就是不同包,子类就是extends继承了此类 - 不同包下的无关类:不同的 package 包,也没有任何关系
实际开发中,基本只用
private和public修饰符
抽象类抽象方法 abstract
不确定业务的具体实现,但是确定有这个业务,且该业务用于很多类中,可以使用抽象类和抽象方法。
抽象方法只声明业务方法,但是具体的业务逻辑需要子类去重写。
子类继承抽象类,则必须重写抽象类中的抽象方法(强制)。
基本抽象类、抽象方法示例:
public abstract class Person {
// 不写 {},直接分号结束,逻辑交给子类实现
public abstract void say();
}
// 子类继承抽象父类
public class Student extends Person {
@Override
public void say() {
System.out.println("Student say");
}
}
- 抽象类不能实例化,比如
Person是抽象类,则不能new Person - 抽象类中不一定存在抽象方法,比如
public abstract Person{...},通过abstract修饰成抽象类,但是 class 内没有abstract修饰的方法 - 有抽象方法的类一定是抽象类,class 中有抽象方法,class 就必须使用
abstract修饰 - 子类
extends继承抽象类@Override标注重写抽象类中的所有抽象方法(常规用法)- 子类也标识为抽象类(不推荐)
接口 implements
接口就是一种规则。implements 有点像 extends 和 abstract 抽象类的结合。
implements 可以实现多个类,且实现时必须重写内部所有 abstract 修饰的方法。

接口的成员特点
implements 可以多实现,比如 public class A implements C,D,E。
其中 extends 是单继承,implements 可以是多实现,这点和 js 中的 extends 和 ts 中的 implements 是类似的。
在 interface 接口中成员特点:
- 成员变量
- 只能是常量
- 默认修饰符
public static final String name = "张三";等价于public static final String name = "张三";
- 构造方法
- 没有
- 成员方法
- 只能是抽象方法
- 默认修饰符
public abstract void swim();等价于public abstract void swim();
接口扩展
以下都是 JDK8 开始支持的写法。
- 接口中的方法默认是
abstract抽象方法,子类必须重写,如果不让子类强制重写,可以添加default- 例如:
defailt void eat(),public可以省略,这样子类也可以不重写该方法。 - 当实现的多个接口中的方法重名时,必须重写该方法
- 例如:
static静态方法- 接口中使用
static修饰的方法,只能通过接口名调用,不能使用类名或对象名调用 - 例如:
static void eat(),只能通过该 interface 的接口名.eat()调用,其继承和实现的都不能调用。
- 接口中使用
常用 API
Math
和 js 的 Math 方法大致差不多,js 中常用的,诸如 Math.ceil、Math.floor、Math.random、Math.max 等等都是类似的
Object 和 Objects
Object是 Java 中的顶级父类。所有的类都直接或间接继承Object。Object类中的方法可以被所有子类访问。

Object
toString
- js 中的对象 toString 方法,不论什么对象,都会转成
[object Object] - Java 中 Object 的 toString 方法,打印出的是:包名 + 地址值。
如果想要 toString 时看到数据的详细内容,而不是地址值,就需要重写 class 类的 toString 方法
public class Person {
private String name;
// ....
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
alt + insert快捷键可以直接生成 toString 方法,此时再调用toString()打印出来的就是详细数据
equals
比较两个对象是否相等(对比的是地址值)
Person p1 = new Person("张三");
Person p2 = new Person("张三");
boolean isSame = p1.equals(p2);
System.out.println(isSame); // false
equals 对比的是两个对象的地址值是否相等,即是否为同一个对象,但实际场景中,对比的往往是两个对象的数据值,此时就需要重写 equals 方法。
快捷键 alt + insert,选择 equals and hashCode,然后一路回车就行,可以生成以下代码。

此时再次允许上边 p1.equals(p2) 的代码,得到的结果就是 true。
clone
-
重写 clone 方法(clone 方法是由
protected修饰,只允许在本包及其子类下使用,因此只能重写)

-
实现 Clonable 接口

此时就实现了 clone 方法,在使用时需要抛出一个异常
public void Test throws CloneNotSupportedException {
Person p1 = new Person("张三","18");
Person p2 = (Person)p1.clone();
System.out.println(p1); // Person{name='张三', age='18'}
System.out.println(p2); // Person{name='张三', age='18'}
}
注意: 该方式是属于浅克隆,对象中基本数据类型没问题,但是 clone 引用数据类型,其指向的是同一个地址值。该实现只能克隆简单的对象,含有引用数据类型的对象不适合

深克隆需要自己重写方法或者使用第三方库。
Objects

BigInteger 和 BigDecimal
BigInter
字面意思:大整数

- 其取值范围比 Long 还要广,当 Long 不足以存储和表示数据时,使用 BigInteger
- 同时,在进行大整数的加减法时,可以有效的避免精度丢失的问题
BigDecimal
大小数、

- BigDecimal 在解决浮点数/小数等运算的精度丢失上,是比较特殊的
new BigDecimal(0.01)这种表示方式,得到的不是0.01,而是精确了很多位的浮点数new BigDecimal("0.01"),传递字符串,最后得到的才是精确的数据值
正则表达式
和 js 的正则表达式规则基本一样,只是写法略微不同,Java 中的正则是纯字符串,也没有 RegExp。

System.out.println("a".matches("[abc]")); // true
时间
JDK7 及之前的时间类
Date 时间
和 js 的 Date 用法相似,空参构造就是当前时间,传入时间戳就生成国际时间,获取时间戳 getTime(),设置时间戳 setTime(Long l)
注意:Date 类虽然支持传入年月日时分秒来创建时间对象,但是此方式已被 Calendar 代替,所以参数普遍只有空参和 long 类型时间戳

SimpleDateFormat 格式化时间
- 把时间转成指定的格式,跟
momentdayjs的基础功能一样 - 把字符串表示的时间转成
Date对象

时间格式转换:年:yyyy 月:MM 日:dd 时分秒:HH mm ss 周几:EE 时间段(上下午):a

时间格式解析:字符串转 Date 对象,new 的 SimpleDateFormat 格式必须和转换的字符串格式一致

Calendar 日历
Calendar 可以说是 Date 的补足,来确定年月日,星期几这些。
Calendar 是一个抽象类,不能直接创建,必须使用方法 getInstance() 来获取当前时间的 Calendar 对象

Calendar 基本使用

月份是由
0~11,而不是1~12组成的
星期几:星期日是 1,即一周中的第一天
JDK8 时间

Instant ZonedDateTime
这两个基本不用,生成时间常用 LocalDateTime
Instant 创建的时间是不带时区的,想要正确的时区需要 +8 小时,或者使用 atZone 指定时区,就是转成了 ZonedDateTime 对象。
ZonedDateTime 创建的时间是带时区的,用法和 Instant 基本一致
基本使用

DateTimeFormatter
ofPattern 用来指定格式化的字符串格式。parse 解析,但是基本不用该方法解析
基本使用

LocalDate LocalTime LocalDateTime
now()获取当前时间of(xxx)获取指定时间的对象get 开头的方法获取年月日时分秒等isBeforeisAfter对比两个LocalDateLocalDateTime等
LocalDate 有单独的 isLeapYear 获取当前年是否是闰年,返回 true 是闰年
of 获取指定的时间,LocalDate 和 LocalTime 同理

LocalDateTime 获取年月日等

Arrays

基本使用

copyOf和copyOfRange对于基本类型是深拷贝,对于复杂类型是浅拷贝,内部对象还是指向原来的地址。
sort 排序使用

sort的排序规则和 js 的sort用法相似
Lambda 表达式
Lambda 是从 JDK8 开始支持的写法(函数式编程),可以将其理解为 js 中的箭头函数(用法有点相似)
初体验 Lambda 表达式

- 参数类型类型也可以省略不写,比如
(Integer o1, Integer o2)可以简写为(o1, o2)- 如果参数只有一个,小括号也可以省略不写
- 如果代码只有一行,函数体括号
{}关键字return结尾分号;可以省略不写,和 js 的简写规则一样
Lambda 表达式必须是一个 interface 接口,且必须有且仅有一个抽象方法才能使用 Lambda 表达式,可以在接口上添加 @FunctionalInterface 注解验证是否可以简写为 Lamda 表达式。
