Java 编程的逻辑

编程基础

程序:基本上就是告诉计算机要操作的数据和执行的指令序列,即对什么数据做什么操作

数据类型和变量

  • 整数类型:byte/short/int/long(取值范围1/2/4/8)
  • 小数类型:float/double(不同的取值范围和精度)
  • -字符类型:char(单个字符)
  • 真假类型:boolean(真假)

变量:给数据起名字,方便找不同的数据,它的值可以变,但含义不应变。

数组类型

三种赋值形式

1
2
3
int[] arr = {1,2,3};
int[] arr = new int[]{1,2,3};
int[] arr = new int[3]; //3可以为变量,生成动态的长度的数组

数组长度确定后不可变

默认值:数值类型的值为0, boolean为false, char为空字符

数组内存空间:一个基本类型变量,内存中只会有一块对应的内存空间。但数组有两块:一块用于存储数组内容本身,另一块用于存储内容的位置。

基本运算

算术运算

  1. 运算时要注意结果的范围,使用恰当的数据类型
  2. 小数计算结果不精确

比较运算

  1. (a++)是先用原来的值进行其他操作,然后再对自己做修改
  2. (++a)是先对自己做修改,再用修改后的值进行其他操作。

逻辑运算

  • 与(&):两个都为true才是true,只要有一个是false就是false;

  • 或(|):只要有一个为true就是true,都是false才是false;

  • 非(!):针对一个变量,true会变成false,false会变成true;

  • 异或(^):两个相同为false,两个不相同为true;

条件执行

if语句

1
2
3
4
5
if(判断条件){
代码块1
else{
代码块2
}

在if/else if/else中,判断的顺序是很重要的,后面的判断只有在前面的条件为false的时候才会执行

三元运算符

1
判断条件?表达式1:表达式2

switch

1
2
3
4
5
6
7
switch(表达式){
case 值1:
break;
case 值2:
break;
default:代码n+1
}

表达式值的数据类型只能是byte、short、int、char、枚举和String(Java 7以后 ) break是指跳出switch语句

实现原理

程序最终都是一条条的指令,CPU有一个指令指示器,指向下一条要执行的指令.有一些特殊的指令,称为跳转指令,这些指令会修改指令指示器的值,让CPU跳到一个指定的地方执行。跳转有两种:一种是条件跳转;另一种是无条件跳转。条件跳转检查某个条件,满足则进行跳转,无条件跳转则是直接进行跳转。

if、if/else、if/else if/else、三元运算符都会转换为条件跳转和无条件跳转,

switch的转换和具体系统实现有关。

  • 分支比较少:跳转指令
  • 分支比较多:跳转表( key为条件值(编译器会自动排序,方便二分查找),value 为跳转地址)

跳转表中如果值是连续的,则优化为一个数组,连找都不用找了

因为需要排序所以switch类型需要支持排序(byte/short/int/string(通过hashCode))

不支持Long(跳转表值的存储空间一般为32位,容纳不下long)

条件执行的本质依赖于条件跳转、无条件跳转和跳转表。

循环

多次重复执行某些类似的操作

while

1
2
3
while(条件语句){
代码块
}

do/while

1
2
3
do{
代码块;
}while(条件语句)

条件语句是什么,代码块都会至少执行一次

for

1
2
3
for(初始化语句;循环条件;步进操作){
循环体
}

循环条件必须返回一个boolean类型外,其他语句没有什么要求(甚至可以为空 for(;;))

执行的流程:

  1. 执行初始化指令;
  2. 检查循环条件是否为true,如果为false,则跳转到第6步;
  3. 循环条件为真,执行循环体;
  4. 执行步进操作;
  5. 步进操作执行完后,跳转到第2步,即继续检查循环条件;
  6. for循环后面的语句。

foreach

1
2
3
for(int element :arr){
System.out.println (element);
}

循环控制

break:提前结束循环

continue:跳过循环体中剩下的代码,然后执行步进操作

实现原理

和if一样,循环内部也是靠条件转移和无条件转移指令实现的,在if中,跳转只会往后面跳,而for会往前面跳,第6行就是无条件跳转指令。break/continue语句也都会转换为跳转指令。

image-20210607224455526

解决复杂问题的基本策略是分而治之,将复杂问题分解为若干相对简单的子问题,然后子问题再分解为更小的子问题……

函数的用法

使用函数来减少重复代码和分解复杂操作。

1
2
3
4
修饰符 返回值类型 函数名字(参数类型 参数名字,...){
操作
return 返回值;
}

返回值:函数可以没有返回值,如果没有返回值则类型写成void,如果有则在函数代码中必须使用return语句返回一个值,这个值的类型需要和声明的返回值类型一致。

Main函数表示程序的入口;

在函数中修改数组元素内容和在调用者中修改是完全一样的。

return可以用于函数内的任意地方

需要的返回值是一种复合结果时可以使用对象;

同一个类里,函数可以重名,但参数不同(重载).Java编译器会自动进行类型转换,并寻找最匹配的函数

调用自己的函数就叫递归函数(递归层数过多会爆栈,栈溢出错误)

递归其实是有开销的,而且使用不当,可能会出现意外的结果,可以通过循环实现

通过函数来减少重复代码、分解复杂操作是计算机程序的一种重要思维方式。

基本原理

  1. 从main函数开始顺序执行
  2. 函数调用可以看作一个无条件跳转
  3. 跳转到对应函数的指令处开始执行
  4. 碰到return语句或者函数结尾的时候,再执行一次无条件跳转
  5. 跳转回调用方,执行调用函数后的下一条指令

计算机系统主要使用内存中的来存放函数调用过程中需要的数据,包括参数返回地址,以及函数内定义的局部变量

使用CPU内的一个存储器存储返回值

代码及对于栈执行情况

image-20210607235453570

image-20210607234315250

数组和对象类型,实际的内容空间一般不是分配在栈上的,而是分配在堆。

image-20210607234331934

递归调用

image-20210607234340878

递归函数的执行过程,函数代码虽然只有一份,但在执行的过程中,每调用一次,就会有一次入栈,生成一份不同的参数、局部变量和返回地址。

每一次调用都需要分配额外的栈空间用于存储参数、局部变量以及返回地址,需要进行额外的入栈和出栈操作。

递归的次数比较多时,应该考虑其他方式。

个人思考

第一章整体看下来发现还是有一些比较基础的东西都遗忘了,比如说居然都忘记还有short这个数据类型,主要是平时用的比较少,像循环基本都是用for,其他两种真的很少用。蛮喜欢基本原理这一部分,即使是最基础的,一章看下来也能学到东西。

理解数据背后的二进制

字符的编码与乱码

常见非Unicode编码

ASCII:美国大概只需要128个字符,所以就规定了128个字符的二进制表示方法。这个方法是一个标准,称为ASCII编码

ISO 8859-1:它也是使用一个字节表示一个字符,其中0~127与ASCII一样,128~255规定了不同的含义。在128~255中,128~159表示一些控制字符,以及被Windows-1252取代。

Windows-1252:与ISO 8859-1基本是一样的,区别只在于数字128~159.

GB2312:GB2312标准主要针对的是简体中文常见字符,包括约7000个汉字和一些罕用词和繁体字。

GBK:在GB2312的基础上,向下兼容GB2312。GBK增加了14 000多个汉字。