本文档完全参照廖雪峰的官方网站之Java教程
1.Java基础
1.数据类型
1.基本数据类型
整数类型:byte:1字节,short:2字节,int:4字节,long:8字节
浮点数类型:float:4字节,double:8字节
字符类型:char:2字节
布尔类型:boolean
2.运算符
四则运算:+,-,*,/
求余运算:%
移位运算:>>:右移,<<:左移。左移就是不断地×2,右移就是不断地÷2,还有一种无符号的右移运算,使用>>>,它的特点是不管符号位,右移后高位总是补0。
位运算:&:与运算的规则是,必须两个数同时为1,结果才为1。
|:或运算的规则是,只要任意一个为1,结果就为1。
~:非运算的规则是,0和1互换。
^异或运算的规则是,如果两个数不同,结果为1,否则为0。
布尔运算:永远只有true和false两个值
- 比较运算符:>,>=,<,<=,==,!=
- 与运算 &&
- 或运算 ||
- 非运算 !
3.引用类型
字符串类型:例如:String i = “你好”;
数组类型:例如:int[] ns = new int[5];
2.流程控制
1.if
if (条件) {
// 条件满足时执行
}
if (条件) {
// 条件满足时执行
} else {
// 条件不满足时执行
}
if (条件) {
// 条件满足时执行
} else if (条件) {
// 条件满足时执行
} else {
// 条件都不满足时执行
}
2.switch
switch (option) {
case 3:
...
break;
case 2:
...
break;
case 1:
...
break;
default:
...
break;
}
3.while
while (条件表达式) {
循环语句
}
// 继续执行后续代码
4.do while
do {
执行循环语句
} while (条件表达式);
5.for
for (初始条件; 循环条件; 更新计数器) {
// 执行语句
}
6.break
7.continue
3.数组操作
1.遍历
方式一:
public class Main {
public static void main(String[] args) {
int[] array = { 1, 2, 3, 6, 5 };
for (int i=0; i<ns.length; i++) {
int n = ns[i];
System.out.println(n);
}
}
}
方式二:
public class Main {
public static void main(String[] args) {
int[] array = { 1, 2, 3, 6, 5 };
for (int arr : array) {
System.out.println(arr);
}
}
}
2.排序
冒泡排序:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] array = { 8, 2, 9, 3, 5, 1, 6, 0, 18, 95 };
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - i - 1; j++) {
if (array[j] > array[j+1]) {
// 交换array[j]和array[j+1]:
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
// 排序后:
System.out.println(Arrays.toString(array));
}
}
3.n维数组
public class Main {
public static void main(String[] args) {
int[][] array = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11,1}
};
}
}
2.面向对象基础
1.方法
修饰符 方法返回类型 方法名(方法参数列表) {
若干方法语句;
return 方法返回值;
}
方法返回值通过return语句实现,如果没有返回值,返回类型设置为void,可以省略return。
1.this变量
在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。
如果没有命名冲突,可以省略this。
2.方法参数
方法可以包含0个或任意个参数。方法参数用于接收传递给方法的变量值。调用方法时,必须严格按照参数的定义一一传递。
3.可变参数
4.参数绑定
调用方把参数传递给实例方法时,调用时传递的值会按参数位置一一绑定。
2.构造方法
创建实例的时候,我们经常需要同时初始化这个实例的字段
// 有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 无参构造
public Person() {
}
3.方法重载
方法名相同,但各自的参数不同,称为方法重载(Overload)。
注意:方法重载的返回值类型通常都是相同的。
方法重载的目的是,功能类似的方法使用同一名字,更容易记住。
String类提供了多个重载方法indexOf(),可以查找子串:
- int indexOf(int ch):根据字符的Unicode码查找;
- int indexOf(String str):根据字符串查找;
- int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;
int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。
4.继承
Java使用extends关键字来实现继承:
class Student extends Person {
}
在OOP的术语中,我们把Person称为超类(super class),父类(parent class),基类(base class),把Student称为子类(subclass),扩展类(extended class)。
Java只有单继承。
为了让子类可以访问父类的字段,我们需要把private改为protected。用protected修饰的字段可以被子类访问。1.super
super关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName。
5.多态
在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。
多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
多态的特性就是,运行期才能动态决定调用的子类方法。对某个类型调用某个方法,执行的实际方法可能是某个子类的覆写方法。这种不确定性的方法调用。1.final
继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override。
6.抽象类
如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类。
因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
面向抽象编程的本质就是:上层代码只定义规范(例如:abstract class Person);
- 不需要子类就可以实现业务逻辑(正常编译);
- 具体的业务逻辑由不同的子类实现,调用者并不关心。
通过abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范;
定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;
如果不实现抽象方法,则该子类仍是一个抽象类;
面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。
7.接口
如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:interface。
所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。
在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface。
抽象类和接口的对比如下:
abstract class | interface | |
---|---|---|
继承 | 只能extends一个class | 可以implements多个interface |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义抽象方法 | 可以定义抽象方法 |
非抽象方法 | 可以定义非抽象方法 | 可以定义default方法 |
1.接口继承
一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。
2.default方法
在接口中,可以定义default方法。
default void run() {
System.out.println("run");
}
实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。
8.静态字段和静态方法
1.静态字段
在一个class中定义的字段,我们称之为实例字段。实例字段的特点是,每个实例都有独立的字段,各个实例的同名字段互不影响。
还有一种字段,是用static修饰的字段,称为静态字段:static field。
实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。
推荐用类名来访问静态字段。可以把静态字段理解为描述class本身的字段(非实例字段)。
2.静态方法
有静态字段,就有静态方法。用static修饰的方法称为静态方法。
调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用。静态方法类似其它编程语言的函数。
3.接口的静态字段
因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型。
9.包
package com.test.demo
10.作用域
1.public
定义为public的class、interface可以被其他任何类访问。
2.private
定义为private的field、method无法被其他类访问。
3.protected
protected作用于继承关系。定义为protected的字段和方法可以被子类访问,以及子类的子类。
4.package
包作用域是指一个类允许访问同一个package的没有public、private修饰的class,以及没有public、protected、private修饰的字段和方法。
5.局部变量
在方法内部定义的变量称为局部变量,局部变量作用域从变量声明处开始到对应的块结束。方法参数也是局部变量。
6.final
Java还提供了一个final修饰符。final与访问权限不冲突,它有很多作用。
- 用final修饰class可以阻止被继承。
- 用final修饰method可以阻止被子类覆写。
- 用final修饰field可以阻止被重新赋值。
-
11.内部类
1.Inner Class
如果一个类定义在另一个类的内部,这个类就是Inner Class:
class Outer {
class Inner {
// 定义了一个Inner Class
}
}
上述定义的Outer是一个普通类,而Inner是一个Inner Class,它与普通类有个最大的不同,就是Inner Class的实例不能单独存在,必须依附于一个Outer Class的实例。
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
2.Anonymous Class
还有一种定义Inner Class的方法,它不需要在Outer Class中明确地定义这个Class,而是在方法内部,通过匿名类(Anonymous Class)来定义。示例代码如下:
Runnable r = new Runnable() {
// 实现必要的抽象方法...
};
3.Static Nested Class
最后一种内部类和Inner Class类似,但是使用static修饰,称为静态内部类(Static Nested Class)。
Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种: Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Class的private访问权限;
Static Nested Class是独立类,但拥有Outer Class的private访问权限。
3.Java核心类
1.String
String是一个引用类型。
字符串在String内部是通过一个char[]数组表示的。
Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的private final char[]字段,以及没有任何修改char[]的方法实现的。1.字符串比较
当我们想要比较两个字符串是否相同时,要特别注意,我们实际上是想比较字符串的内容是否相同。必须使用equals()方法而不能用==。
两个字符串比较,必须总是使用equals()方法。
要忽略大小写比较,使用equalsIgnoreCase()方法。// 是否包含子串:
"dajiahao".contains("jia"); // true
// 提取子串
"Hello".substring(2); // "llo"
"Hello".substring(2, 4); "ll"
2.去除首尾空白字符
使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格,\t,\r,\n:
" \tHello\r\n ".trim(); // "Hello"
注意:trim()并没有改变字符串的内容,而是返回了一个新字符串。
另一个strip()方法也可以移除字符串首尾空白字符。它和trim()不同的是,类似中文的空格字符\u3000也会被移除:"\u3000Hello\u3000".strip(); // "Hello"
" Hello ".stripLeading(); // "Hello "
" Hello ".stripTrailing(); // " Hello"
3.替换子串
要在字符串中替换子串,有两种方法。一种是根据字符或字符串替换:
String s = "hello";
s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'
s.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"
另一种是通过正则表达式替换:
String s = "A,,B;C ,D";
s.replaceAll("[\\,\\;\\s]+", ","); // "A,B,C,D"
4.分割字符串
要分割字符串,使用split()方法,并且传入的也是正则表达式:
String s = "A,B,C,D";
String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}
5.拼接字符串
拼接字符串使用静态方法join(),它用指定的字符串连接字符串数组:
String[] arr = {"A", "B", "C"};
String s = String.join("***", arr); // "A***B***C"
6.格式化字符串
字符串提供了formatted()方法和format()静态方法,可以传入其他参数,替换占位符,然后生成新的字符串:
public class Main {
public static void main(String[] args) {
String s = "Hi %s, your score is %d!";
System.out.println(s.formatted("Alice", 80));
System.out.println(String.format("Hi %s, your score is %.2f!", "Bob", 59.5));
}
}
有几个占位符,后面就传入几个参数。参数类型要和占位符一致。我们经常用这个方法来格式化信息。常用的占位符有:
%s:显示字符串;
- %d:显示整数;
- %x:显示十六进制整数;
- %f:显示浮点数。
占位符还可以带格式,例如%.2f表示显示两位小数。如果你不确定用啥占位符,那就始终用%s,因为%s可以显示任何数据类型。
7.类型转换
要把任意基本类型或引用类型转换为字符串,可以使用静态方法valueOf()。这是一个重载方法,编译器会根据参数自动选择合适的方法:
String.valueOf(123); // "123"
String.valueOf(45.67); // "45.67"
String.valueOf(true); // "true"
String.valueOf(new Object()); // 类似java.lang.Object@636be97c
字符串转换为int类型:
int n1 = Integer.parseInt("123"); // 123
int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255
8.转换为char[]
String和char[]类型可以互相转换,方法是:
char[] cs = "Hello".toCharArray(); // String -> char[]
String s = new String(cs); // char[] -> String
2.StringBuilder
为了能高效拼接字符串,Java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder中新增字符时,不会创建新的临时对象。
StringBuilder和StringBuffer接口完全相同,现在完全没有必要使用StringBuffer。
用指定分隔符拼接字符串数组时,使用StringJoiner或者String.join()更方便;
用StringJoiner拼接字符串时,还可以额外附加一个“开头”和“结尾”。
3.包装类型
因为包装类型非常有用,Java核心库为每种基本类型都提供了对应的包装类型:
基本类型 | 对应的引用类型 |
---|---|
boolean | java.lang.Boolean |
byte | java.lang.Byte |
short | java.lang.Short |
int | java.lang.Integer |
long | java.lang.Long |
float | java.lang.Float |
double | java.lang.Double |
char | java.lang.Character |
1.Auto Boxing
因为int和Integer可以互相转换。
这种直接把int变为Integer的赋值写法,称为自动装箱(Auto Boxing),反过来,把Integer变为int的赋值写法,称为自动拆箱(Auto Unboxing)。
2.不变类
所有的包装类型都是不变类。
一旦创建了Integer对象,该对象就是不变的。
对两个Integer实例进行比较要特别注意:绝对不能用==比较,因为Integer是引用类型,必须使用equals()比较。
3.进制转换
Integer类本身还提供了大量方法,例如,最常用的静态方法parseInt()可以把字符串解析成一个整数。
4.JavaBean
JavaBean是一种符合命名规范的class,它通过getter和setter来定义属性;
属性是一种通用的叫法,并非Java语法规定;
可以利用IDE快速生成getter和setter;
使用Introspector.getBeanInfo()可以获取属性列表。
5.枚举类
Java使用enum定义枚举类型,它被编译器编译为final class Xxx extends Enum { … };
通过name()获取常量定义的字符串,注意不要使用toString();
通过ordinal()返回常量定义的顺序(无实质意义);
可以为enum编写构造方法、字段和方法
enum的构造方法要声明为private,字段强烈建议声明为final;
enum适合用在switch语句中。
6.记录类
从Java 14开始,提供新的record关键字,可以非常方便地定义Data Class:
- 使用record定义的是不变类;
- 可以编写Compact Constructor对参数进行验证;
-
7.BigInteger
BigInteger用于表示任意大小的整数;
BigInteger是不变类,并且继承自Number;
将BigInteger转换成基本类型时可使用longValueExact()等方法保证结果准确。8.BigDecimal
BigDecimal用于表示精确的小数,常用于财务计算;
比较BigDecimal的值是否相等,必须使用compareTo()而不能使用equals()。9.常用工具类
Java提供的常用工具类有:
Math:数学计算
- Random:生成伪随机数
- SecureRandom:生成安全的随机数