java基础知识大全(持续更新中···)


基础知识


关键字

控制访问类

关键字说明
private私有的
public公共的
protected受保护的
default默认的

类、方法和变量修饰符

关键字说明
new创建( 对象 )
class
interface接口
extends继承于
abstract抽象的
final最终的( 不可继承 / 重写 / 重新赋值 )
implements实现( 接口 )
static静态的
void无返回值的
synchronized线程同步的( 同一时间只能被一个线程访问 )
native本地 / 原生方法( 由c / c++实现 )
strictfp(strict float point)严格的 ( 遵守 IEEE 浮点数算术标准,保证不同硬件平台所执行的结果一致 )
transient临时的( 只能修饰成员变量,使其不参与序列化过程 )
volatile易变的( 保证任何时刻,两个不同线程访到的某个成员变量总是同一个值 )

程序控制语句

关键字说明
break跳出当前循环
continue跳转到下一次循环的迭代
switch根据值选择执行
case定义一个供 switch 选择的值
default定义一个供 switch 返回的默认值
do执行
if如果
else否则
forfor 循环
whilewhile循环
instanceof测试左边的对象是否是右边的类的实例 ,返回 Boolean
return返回

错误处理

关键字说明
assert断言
try尝试
catch捕捉异常
finally在 try 和 catch 块之后、方法完成之前运行,不管是否抛出或捕获异常
throw抛出一个异常对象
throws用在声明方法时,表示该方法可能要抛出异常

assertion( 断言 )是一种调试程序的方式

  • 默认是不运行的,除非增加 jvm 参数 -ea / -enableassertions

基本语法:

assert expression1 ;
assert expression1 : expression2 ;
  • expression1 表示一个 boolean 表达式,expression2 表示一个基本类型 / 表达式 / 一个对象,用于在失败的时候的输出信息
  • 当 expression1 为 false 的时候,程序将会停止并抛出 java.lang.AssertionError 对象,如果含有 expression2 则附带 expression2 内容

包相关

关键字说明
package
import导入

基本类型

关键字说明
boolean布尔型
byte字节型
short短整型
int整型
long长整型
float单精度浮点
double双精度浮点
char字符型

变量引用

关键字说明
super父类的
this当前类的

保留关键字

关键字说明
goto是关键字,但无法使用
const是关键字,但无法使用

基本数据类型

类型最大值最小值大约
byte127( 27-1 )-128( -27100
short32767( 215 - 1 )-32768( -2153 * 104
int2,147,483,647( 231 - 1 )-2,147,483,648( -2312 * 109
long9,223,372,036,854,775,807( 263 -1 )-9,223,372,036,854,775,808( -2639 * 1018
类型大小说明
char16 位Unicode 字符( \u0000 - \uFFFF )( 0 - 65535 )
float32位符合 IEEE 754 标准的单精度浮点数
double64 位符合 IEEE 754 标准的双精度浮点数
boolean/只有两个取值( true / false )

ASCII 码表

ASCII ( American Standard Code for Information Interchange ): 美国信息交换标准代码。

标准 ASCII 码使用 1字节( 第一位为0 )来表示字符。

  • ASCII 控制字符( 字符编码 : 0 - 31)
  • ASCII 打印字符( 字符编码 : 32 - 127)
十进制符号十进制符号十进制符号十进制符号
32空格56880P104h
33!57981Q105i
34"58:82R106j
35#59;83S107k
36$60<84T108l
37%61=85U109m
38&62>86V110n
39'63?87W111o
40(64@88X112p
41)65A89Y113q
42*66B90Z114r
43+67C91[115s
44,68D92\116t
45-69E93]117u
46.70F94^118v
47/71G95_119w
48072H96`120x
49173I97a121y
50274J98b122z
51375K99c123{
52476L100d124|
53577M101e125}
54678N102f126~
55779O103g127删除

Unicode 和 UTF-8

Unicode( 统一码 )仅仅是一个字符集,码点空间为 U+000000 - U+10FFFF,用数字来映射不同的字符,仅仅规定了特定字符对应的二进制代码,至于这个二进制代码如何存储则没有任何规定。

  • Unicode 使用17个平面,每个平面的码点范围为 U+xx0000 - U+xxFFFF,其中xx表示16进制的 0x00 到 0x10,每个平面有 216 = 65536个码位,所以 Unicode 总共有 17 * 65536 = 1114112个码位。
  • 所有最常见的字符都放在基本平面 BMP( Basic Multilingual Plane ),这是 Unicode 最先定义和公布的一个平面,编码从 U+000000 到 U+00FFFF。
  • 剩下的字符都放在辅助平面,码点范围从 U+010000 到 U+10FFFF。

UTF-8 以字节为单位对Unicode进行编码,使用 1 - 4 个字节表示一个字符,根据字符的不同变换长度。

Unicode 字符集范围(十六进制)UTF-8 编码(⼆进制)
0000 0000 - 0000 007F0xxxxxxx
0000 0080 - 0000 07FF110xxxxx 10xxxxxx
0000 0800 - 0000 FFFF1110xxxx 10xxxxxx 10xxxxxx
0001 0000 - 0010 FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

UTF-16 : 基本平面的字符占用2个字节,辅助平面的字符占用4个字节。
UTF-32 : 每个字符均占用四个字节。



Character 类

  • 常见方法
方法解释
booleanisLetter(charch)判断字符是否为字母
booleanisDigit(charch)判断字符是否为数字
booleanisLetterOrDigit(charch)判断字符是否为字母或数字
booleanisSpaceChar(charch)判断字符是否为空格
boolean isLowerCase(char ch)判断字符是否为小写字母
booleanisUpperCase(charch)判断字符是否为大写字母
char toLowerCase(charch)返回对应字符的小写
char toUpperCase(charch)返回对应字符的大写

字母大小写判断 / 转换

  • 判断

  • 转换


String类


StringJoiner类


Map 类


value 值为 List 的初始化

例 : 统计数组 [ n u m s ] [ nums ] [nums] 中数字的出现位置。

public class MapTest
{
    Map<Integer, List<Integer>> map;
    
    public void setMap(int[] nums)
    {
        map = new HashMap<>();
        
        for (int i = 0; i < nums.length; i++)//方法1
        {
            map.computeIfAbsent(nums[i], Key -> new ArrayList<>()).add(i);
        }
        
        for (int i = 0; i < nums.length; ++i)//方法2
        {
            map.putIfAbsent(nums[i], new ArrayList<>());
            map.get(nums[i]).add(i);
        }
        
        for (int i = 0; i < nums.length; i++)//方法3
        {
            List<Integer> list = map.getOrDefault(nums[i], new ArrayList<>());
            list.add(i);
            map.put(nums[i], list);
        }
    }
}

算法


前缀和

Prefix sum( 前缀和 )

用于解决原数组不变,多次求区间和的问题。

输入整数数组 [ n u m s ] [ nums ] [nums] 和查询数组 [ f i n d S u m ] [ findSum ] [findSum],每个查询包含两个整数 l l l r r r ,表示查询第 l l l 个数到第 r r r 个数的和
(注意 : 是第 l l l 个数到第 r r r 个数,也就是 n u m s [ 0 ] nums [ 0 ] nums[0] 为第一个数)

  • 对于 n n n 个数,先创建一个长度为 n + 1 n+1 n+1 的前缀和数组 [ p r e f i x S u m ] [prefixSum] [prefixSum]
  • p r e f i x S u m [ 0 ] = 0 prefixSum[0]=0 prefixSum[0]=0
  • p r e f i x S u m [ i ] = p r e f i x S u m [ i − 1 ] + n u m s [ i − 1 ] prefixSum[i]=prefixSum[i-1]+nums[i-1] prefixSum[i]=prefixSum[i1]+nums[i1]
  • s u m ( l , r ) = p r e f i x S u m [ r ] − p r e f i x S u m [ l − 1 ] sum(l,r)=prefixSum[r]-prefixSum[l-1] sum(l,r)=prefixSum[r]prefixSum[l1]
证明

p r e f i x S u m 简 写 为 p r e prefixSum简写为pre prefixSumpre
p r e [ l − 1 ] = p r e [ 0 ] + p r e [ 1 ] + … + p r e [ l − 2 ] + p r e [ l − 1 ] pre[l-1]=pre[0]+pre[1]+ \ldots+pre[l-2]+pre[l-1] pre[l1]=pre[0]+pre[1]++pre[l2]+pre[l1]
p r e [ r ] = p r e [ 0 ] + p r e [ 1 ] + … + p r e [ l − 1 ] + p r e [ l ] + p r e [ l + 1 ] … + p r e [ r − 1 ] + p r e [ r ] pre[r]=pre[0]+pre[1]+ \ldots+pre[l-1]+pre[l]+pre[l+1] \ldots+pre[r-1]+pre[r] pre[r]=pre[0]+pre[1]++pre[l1]+pre[l]+pre[l+1]+pre[r1]+pre[r]
p r e [ r ] − p r e [ l − 1 ] = p r e [ l ] + p r e [ l + 1 ] + … + p r e [ r − 1 ] + p r e [ r ] pre[r]-pre[l-1]=pre[l]+pre[l+1]+ \ldots+pre[r-1]+pre[r] pre[r]pre[l1]=pre[l]+pre[l+1]++pre[r1]+pre[r]

输入 :
[ n u m s ] [ nums ] [nums] = [ 1,2,3,4,5,6,7,8,9,10,11 ]
[ f i n d S u m ] [ findSum ] [findSum] = [ [ 1,5 ],[ 6,11 ],[ 1,11 ] ]

输出 :
[ 15,51,66 ]

i n d e x index index01234567891011
[ n u m s ] [ nums ] [nums]1234567891011/
[ p r e f i x S u m ] [ prefixSum ] [prefixSum]01361015212836455566
public void getSum(int[] nums, int[][] findSum)
{
    int[] prefixSum = new int[nums.length + 1];

    for (int i = 1; i < prefixSum.length; i++)
    {
        prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
    }

    for (int[] find : findSum)
    {
        int l = find[0];
        int r = find[1];

        System.out.print(prefixSum[r] - prefixSum[l - 1]+" ");

		//如果 findSum 中的 l 和 r 为原数组下标而不是第 l / r 个数,可以 :
		//int l = find[0] + 1; 例 : 原数组下标 0 对应第 1 个数
		//int r = find[1] + 1;
		//或者 :
		//prefixSum[r +1] - prefixSum[l]
    }
}
补充

[ p r e f i x S u m ] [prefixSum] [prefixSum] 长的度比 [ n u m s ] [nums] [nums] 多 1 是为了设置 p r e f i x S u m [ 0 ] = 0 prefixSum[0]=0 prefixSum[0]=0,然后让 p r e f i x S u m [ 1 ] prefixSum[1] prefixSum[1] 保存 [ n u m s ] [nums] [nums] 的第一个数的前缀和。

因为如果 p r e f i x S u m [ 0 ] prefixSum[0] prefixSum[0] 保存的是 [ n u m s ] [nums] [nums] 第一个数的前缀和,在计算 s u m ( l , r ) sum(l,r) sum(l,r) 的时候需要减去第 l l l 个数的上一个数的前缀和,当 l = 1 l=1 l=1 时,因为 [ p r e f i x S u m ] [prefixSum] [prefixSum] 的第一个数就是 [ n u m s ] [nums] [nums] 第一个数的前缀和,所以在寻找 [ n u m s ] [nums] [nums] 第一个数上一个数的前缀和时就会导致数组越界。

另一种解决方法是在计算 p r e f i x S u m [ r ] − p r e f i x S u m [ l − 1 ] prefixSum[r] - prefixSum[l - 1] prefixSum[r]prefixSum[l1] 前进行判断,如果 l = 1 l=1 l=1 则直接返回 p r e f i x S u m [ r ] prefixSum[r] prefixSum[r],由于每次查询都要多进行一次判断,所以不建议采用这种写法。


水塘抽样算法

Reservoir Sampling( 水塘抽样 / 蓄水池抽样 )

用于解决内存无法加载全部数据时,从包含未知大小的数据流中随机选取 k 个数据,并且要保证每个数据被抽取到的概率相等的问题。

  • k = 1 k = 1 k=1

对 于 第 i 个 数 据 , 以 1 i 的 几 率 保 留 , 作 为 最 终 的 结 果 。 对于第 i 个数据,以 \frac{1}{i} 的几率保留,作为最终的结果。 ii1

证明

总共有 1 1 1 个数时 :

p ( n 1 ) = 1 1 = 1 p(n_1) = \frac{1}{1} = 1 p(n1)=11=1

总共有 2 2 2 个数时 :

结 果 为 n 1 的 概 率 = 选 择 n 1 的 概 率 × 不 保 留 n 2 的 概 率 结果为n_1的概率 = 选择n_1的概率 \times 不保留n_2的概率 n1=n1×n2

p ( n 1 ) = p ( n 1 ) × ( 1 − p ( n 2 ) ) = 1 × ( 1 − 1 2 ) = 1 2 p(n_1) = p(n_1) \times (1-p(n_2)) = 1 \times ( 1 - \frac{1}{2}) = \frac{1}{2} p(n1)=p(n1)×(1p(n2))=1×(121)=21

p ( n 2 ) = 1 2 p(n_2) = \frac{1}{2} p(n2)=21

总共有 3 3 3 个数时 :

结 果 为 n 1 的 概 率 = 选 择 n 1 的 概 率 × 不 保 留 n 2 的 概 率 × 不 保 留 n 3 的 概 率 结果为n_1的概率 = 选择n_1的概率 \times 不保留n_2的概率 \times 不保留n_3的概率 n1=n1×n2×n3

p ( n 1 ) = p ( n 1 ) × ( 1 − p ( n 2 ) ) × ( 1 − p ( n 3 ) ) = 1 × ( 1 − 1 2 ) × ( 1 − 1 3 ) = 1 3 p(n_1) = p(n_1) \times (1-p(n_2)) \times (1-p(n_3)) = 1 \times ( 1 - \frac{1}{2}) \times ( 1 - \frac{1}{3}) = \frac{1}{3} p(n1)=p(n1)×(1p(n2))×(1p(n3))=1×(121)×(131)=31

结 果 为 n 2 的 概 率 = 选 择 n 2 的 概 率 × 不 保 留 n 3 的 概 率 结果为n_2的概率 = 选择n_2的概率 \times 不保留n_3的概率 n2=n2×n3

p ( n 2 ) = p ( n 2 ) × ( 1 − p ( n 3 ) ) = 1 2 × ( 1 − 1 3 ) = 1 3 p(n_2) = p(n_2) \times (1-p(n_3)) = \frac{1}{2} \times ( 1 - \frac{1}{3}) = \frac{1}{3} p(n2)=p(n2)×(1p(n3))=21×(131)=31

p ( n 3 ) = 1 3 p(n_3) = \frac{1}{3} p(n3)=31

总共有 n n n 个数,对于第 m m m 个和第 n n n 数 :

结 果 为 n m 的 概 率 = 选 择 n m × 不 保 留 n m + 1 × 不 保 留 n m + 2 × … × 不 保 留 n n − 1 × 不 保 留 n n 结果为n_m的概率 = 选择n_m \times 不保留n_{m+1} \times 不保留n_{m+2} \times\ldots \times 不保留n_{n-1} \times 不保留n_{n} nm=nm×nm+1×nm+2××nn1×nn

p ( n m ) = 1 m × m m + 1 × m + 1 m + 2 × … × n − 2 n − 1 × n − 1 n = 1 n p(n_m) = \frac{1}{m} \times \frac{m}{m+1} \times \frac{m+1}{m+2} \times \ldots \times \frac{n-2}{n-1} \times \frac{n-1}{n} = \frac{1}{n} p(nm)=m1×m+1m×m+2m+1××n1n2×nn1=n1

p ( n n ) = 1 n p(n_n) = \frac{1}{n} p(nn)=n1

public int getRandom(Sample sample)
{
    int index = 1;
    int choice = 0;

    while (sample != null)
    {
        if (random.nextInt(index) == 0)
        {
            choice = sample.val;
        }

        index++;
        sample = sample.nextSample;
    }

    return choice;
}
  • k > 1 k > 1 k>1

对 于 前 k 个 数 据 , 全 部 保 留 。 对于前 k 个数据,全部保留。 k
对 于 第 k + i 个 数 据 , 以 k k + i 的 几 率 保 留 , 以 1 k 的 几 率 替 换 当 前 保 留 的 k 个 数 中 的 一 个 , 作 为 最 终 的 结 果 。 对于第 k+i 个数据,以 \frac{k}{k+i} 的几率保留,以 \frac{1}{k}的几率替换当前保留的k个数中的一个,作为最终的结果。 k+ik+ikk1k

证明

总共有 k k k 个数时 :

p ( n r [ r ∈ 1 : k ] ) = 1 p(n_r[r\in1:k])=1 p(nr[r1:k])=1

总共有 k + 1 k+1 k+1 个数时 :

结 果 为 n r [ r ∈ 1 : k ] 的 概 率 = 选 择 n r × n k + 1 不 替 换 n r 结果为n_r[r\in1:k]的概率 = 选择n_r \times n_{k+1}不替换n_r nr[r1:k]=nr×nk+1nr

n k + 1 不 替 换 n r 的 概 率 = 不 保 留 n k + 1 + 保 留 n k + 1 × 不 替 换 n r n_{k+1}不替换n_r的概率 = 不保留n_{k+1} +保留n_{k+1} \times 不替换n_r nk+1nr=nk+1+nk+1×nr

p ( n r ) = 1 × ( 1 k + 1 + k k + 1 × k − 1 k ) = k k + 1 p(n_r) =1\times (\frac{1}{k+1} + \frac{k}{k+1} \times \frac{k-1}{k}) = \frac{k}{k+1} p(nr)=1×(k+11+k+1k×kk1)=k+1k

p ( n k + 1 ) = k k + 1 p(n_{k+1}) =\frac{k}{k+1} p(nk+1)=k+1k

总共有 n n n 个数,对于第 m m m 个和第 n n n 个数 :

结 果 为 n m 的 概 率 = 选 择 n m × n m + 1 不 替 换 n m × n m + 2 不 替 换 n m × … × n n − 1 不 替 换 n m × n n 不 替 换 n m 结果为n_m的概率 = 选择n_m \times n_{m+1}不替换n_m \times n_{m+2}不替换n_m \times\ldots \times n_{n-1}不替换n_m \times n_n不替换n_m nm=nm×nm+1nm×nm+2nm××nn1nm×nnnm

p ( n m ) = k m × m m + 1 × m + 1 m + 2 × … × n − 2 n − 1 × n − 1 n = k n p(n_m) = \frac{k}{m} \times \frac{m}{m+1} \times \frac{m+1}{m+2} \times \ldots \times \frac{n-2}{n-1} \times \frac{n-1}{n} = \frac{k}{n} p(nm)=mk×m+1m×m+2m+1××n1n2×nn1=nk

p ( n n ) = k n p(n_n) = \frac{k}{n} p(nn)=nk

public int[] getRandom(Sample sample, int k)
{
    int index = 1;
    int[] choice = new int[k];

    for (int i = 0; i < k; i++)
    {
        choice[i] = sample.val;
        sample = sample.nextSample;
    }

    while (sample != null)
    {
        int randomNum = random.nextInt(index);

        if (randomNum < k)
        {
            choice[randomNum] = sample.val;
        }

        index++;
        sample = sample.nextSample;
    }

    return choice;
}

经典题目


数字的字典序

题目 : 输入一个整数 n n n ,按字典序返回范围 [ 1 , n n n ] 内所有整数。

递归思路 :

  • 从 1 到 9 开始,在每个数的末位后添加 0 到 9(不超过 n n n)。

迭代思路 :

  • 字典序中一个数的下一个数一定是在这个数的末位后加上 0(不超过 n n n)。

  • 如果因为 n n n 的限制不能在末尾后加 0,那么这个数的下一个数就是这个数加上1(不超过 n n n 且末位不是9)。

  • 末位是 9 不能加 1 的原因是例如在 [ 1 , 1000 ] 的字典序中,119 的下一个数应该是 12 而不是 120,所以在遇到末位是 9 的时候,需要将这个数末尾的所有 9 都去掉后加 1 以此得到下一个数。

输入 :
n n n = 13

输出 :
[ 1,10,11,12,13,2,3,4,5,6,7,8,9 ]

  • 解法1 : 递归
public List<Integer> lexicalOrder(int n)
{
    List<Integer> ans = new ArrayList<>();

    if (n < 10)
    {
        for (int i = 1; i <= n; i++)
        {
            ans.add(i);
        }

        return ans;
    }

    for (int i = 1; i <= 9; i++)
    {
        dfs(i, n, ans);
    }

    return ans;
}

void dfs(int cur, int max, List<Integer> ans)
{
    ans.add(cur);

    for (int i = 0; i <= 9; i++)
    {
        int newNum = cur * 10 + i;

        if (newNum > max)//如果已经大于max则不需要获得之后的num了
        {
            return;
        }

        dfs(newNum, max, ans);
    }
}
  • 解法2 : 迭代
public List<Integer> lexicalOrder(int n)
{
    List<Integer> ans = new ArrayList<Integer>();

    int number = 1;

    for (int i = 0; i < n; i++)
    {
        ans.add(number);

        if (number * 10 <= n)
        {
            number *= 10;//进入下一层
        }
        else
        {
            while (number % 10 == 9 || number + 1 > n)//如果末位已经到9或者已经到达最大值
            {
                number /= 10;//返回上一层
            }

            number++;//获得下一个合法的数字
        }
    }

    return ans;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值