1、9月1日开始学习 8月底提了辞职,开始搜集相关资料,包括知乎、B站等,9月底办完离职手续后开始了正式学习,第一部分学习了一个月时间,使用的是WizNote Lite记笔记(因为可以实时渲染markdown,还可以在手机上随时查看),但是在两台电脑的数据传输时有问题,遂逐渐搬到csdn上来,方便自己查看,同时也方便同跟我一样的人相互学习。
2、使用ieda写代码
3、使用jdk版本:jdk8
Why?因为目前国内绝大部分公司,使用的jdk版本仍然是jdk8。商业公司求稳,从jdk9开始每半年发布一次,分长期支持版本(至少3年)和短期支持版本(半年)
LTS:long term support长期支持版
学习优先级:Jdk8->jdk11->jdk17
相关细节和代码会在后续迁移补上
语言版本即jdk(oracle) vs openjdk
2009年被oracle收购之前,将jdk源代码开源,形成了openjdk。但是,在sun开源jdk源代码的时候,其中有一部分源代码(小部分非核心功能),因为产权问题,无法开源,就被其他有同样功能的开源代码取代。
Openjdk中,只包含jdk最最核心的功能,还有其他一些第三方实现的功能或者是插件,openjdk是jdk的极简版本。
关于openjdk和jdk源代码是有关系的:包含在openjdk源代码中的绝大部分代码和oreclejdk一模一样。Jdk可以理解为openjdk的一个分支:不仅两者的代码是相同的,而且,oraclejdk还会和openjdk保持同步。同时,一旦oraclejdk发现openjdk中的一些bug,oracle在修复之后,把这些修复bug的代码同步到openjdk。
IBM、Google、Facebook等都是从openjdk中拿到源代码,然后经过修改,增加自己的功能,形成自己的jdk版本。
JAVASE(Java platform standard edition)标准版。为开发普通桌面和商务应用程序提供的解决方案。
JAVAME(Java platform to micro edition)小型版。为开发电子消费产品和嵌入式设备提供的解决方案。
JAVAEE(Java platform to enterprise edition)企业版。为开发企业环境下的应用程序提供的一套解决方案。
高级语言计算机本身是不认识的,计算机只认识二进制01。一定有一个工具,帮助我们将代码转换为计算机可以识别的对应到机器指令级别的二进制序列。
编译型语言:用这种语言写出的代码,首先通过编译器的编译,转换成目标代码(二进制可执行文件.exe),然后依次在操作系统中执行。
解释型语言:转换一句,执行一句。
Java是解释型:.java->.class->java虚拟机(jvm)。
JRE(Java runtime environment)包括Java虚拟机,运行时核心类库(rt.jar),JRE主要是给已经写好的Java程序使用,换句话说Java程序能在操作系统中运行,必须有JRE。
JDK(Java develop kit)包含JRE,除此之外,JDK中海包含一些供开发者使用的工具,比如javac、javap等,仅仅只供开发者在开发阶段使用的工具。
在java语言中,一个Java文件中只能定义一个被public修饰的类,且被public修饰的类的类名必须和Java文件的文件名相同。(没有public可以不一样)
不管计算机中运行的什么样的程序,本质上都是在运算(广义的运算)。
计算机中储存的所有东西都是二进制01数据。
运算—运算对象和运算符
运算对象(参与运算的数量),Java语言中数量分为常量和变量。
只需记住格式就行,后面会慢慢学习。
public class HelloWorld {
public static void main(String[] args) {
System.out.println("HelloWorld!");
}
}
//第一:public表示公开的(关键字,固定写法)
//第二:class用来声明一个类(关键字,固定写法)
//第三:HelloWorld是一个类名(既然是一个名字,就可以改成其它的名字)
//第四:public class HelloWorld表示声明一个公共的类HelloWorld
//第五:在java编程中,一定要注意成对儿的符号要成对儿写,以上HelloWorld当中成对儿的符号包括:小括号(),中括号[],大括号{},双引号""。这些符号在编写的时候建议成对儿编写。
//注意:所有程序都是从main()方法开始执行的!!!!!
Java使用DOS命令运行
d:
查看当前目录下的文件及文件夹:dir
进入文件夹:cd 文件夹的名字
返回上一级目录:cd ..
清空屏幕:cls
删除文件:del 文件名
删除文件夹:rd 文件夹名称
退出:exit
javac HelloWorld.java
命令编译得到HelloWorld.class
文件夹java HelloWorld
命令加载class文件并运行。用于解释说明的文字。千万不要忽视注释的作用,尤其是对于初学者。方便写代码和项目维护、阅读代码。初学者最好先写注释再写代码。
单行注释格式//
多行注释格式 /* 注释内容 */,多行注释不能嵌套
文档注释格式 /** 文档注释内容 */
关键字是被Java语言赋予特殊含义的单词。
关键字的特点:组成关键字的字母全部小写。
注意事项:高级的编辑器或者专门的开发工具中,关键字会有高亮效果;goto和const作为保留字存在,目前并不使用。
用于定义数据类型的关键字:class、interface、byte、short、int、long、float、double、char、boolean、void
用于定义数据类型值的关键字:true、false、null
用于定义流程控制的关键字:if、else、switch、case、default、while、do、for、break、continue、return
用于定义访问权限修饰符的关键字:private、protected、public
用于定义类、函数、变量修饰符的关键字:abstract、final、static、synchronized
用于定义类与类之间关系的关键字:extends、implements
用于定义建立实例及引用实例、判断实例的关键字:new、this、super、instanceof
用于异常处理的关键字:try、catch、finally、throw、throws
用于包的关键字:package、import
其他修饰符关键字:native、strictfp、transient、assert
标识符就是给类、接口、方法、变量等起名字时使用的字符序列。
组成规则:英文大小写字母、数字字符、$和_
注意事项:不能以数字开头、不能是Java中的关键字(保留字)、区分大小写。
常见命名规则:驼峰命名
在实际开发当中,命名要遵行的一个核心原则:见名知意。
常量:在运行过程中其值不会发生改变的量。
进制转化一般用趋于法计算。
负数在计算机中如何表示呢?
约定0表示正号,1表示负号,最高位假设字长为8位,8位二进制来表示一个数0,0001100。
计算机中如何计算?
引入补码来计算,一旦二进制用补码表示,符号位和数值位可以一起参与数值运算。
二进制的补码表示和原码表示
1.对于一个正数的原码表示,其原码表示就是补码表示。
2.对于一个负数的原码表示,其补码表示:原码表示的符号位不变,其余各位依次取反(0变1,1变0),末尾+1。
3.原码和补码互补,对补码求补就得到了原码。
所以整数在计算机中其实都是以补码形式存在。
变量定义的格式:数据类型 变量名(标识符) = 变量值
数据类型:一个数据集合和基于这个数据集合一组操作。
变量数据类型:基本数据类型、引用数据类型。
基本数据类型
\u0000
引用数据类型
输入的整数默认类型是int,当输入的数超过int表示范围就会报错,通过给整数字面量加后缀L或l(不推荐小写l,容易与数字1混淆),来声明字面量为long类型。
输入的小数默认类型是double,超过表示范围时,通过给小数字面量加F或f来声明类型是float类型。
关于字符的问题,一个字符要被保存到计算机中,只能以数值的形式。一个字符对应的数值,也就是字符和字符对应的数值关系,就是编码表,ASCII码表。比如(‘a’ 97)。
使用变量注意事项
1.作用域:变量有效的时间范围,这个范围通常用{}来界定。
2.初始化值:变量在使用之前,由编译器强制必须赋值。
3.建议一行定义一个变量(从代码的可读性角度来考虑)
如何实现键盘录入数据呢?
导包语句(放到class定义的上面):import java.util.Scanner;
创建对象:Scanner sc = new Scanner(System.in);
从键盘读取数据:int x = sc.nextInt();
.nextInt();//获得整数
.nextDouble();//获得小数
.next();//获得字符串
.next().charAt(0);//获得单个字符(数值控制第几个字符,0则是第一个,依次类推)
+正号,-负号,+加,-减,*乘
/除(整数相除只能得到整数),%取模,++自增,--自减,+字符串相加
1、对于++
和--
:单独使用效果,若参与运算,位置不同,效果不同。++
和--
既可以放在变量之后,也可以放在变量之前。当++
和--
不参与任何其他运算时,++
和--
在变量之前和之后时没有区别的;但是一旦++
和--
参与其他运算的时候,他们就会有一点区别。
2、当++
在前时,参与运算的时候,首先是自增,再参与运算。对于--
而言,在前和在后与++
相同
符号:=、+=、-=、*=、/=、%=
==相等于、!=不等于、<小于、>大于、<=小于等于、>=大于等于
instanceof检查是否是类的对象
关系运算符的结果只有两种true or false,注意和“=”区分,不要把“==”写成“=”。
关系运算符的结果:true or false都是boolean类型。
& AND(与) 全真为真
| OR(或) 全假为假
^ XOR(异或) 相同为false不同为true
! NOT(非)
&& AND(短路)
|| ORtrue(短路)
&&有短路效果。即当第一个操作数为false,直接得出与运算的结果。
<<左移
空位补0,被移除的高位丢弃
相当于给操作数*2(左移后的结果仍然在相应数据类型的表示范围内)
>>右移
被移位的二进制空缺位补符号位
最高位补符号位,相当于给操作数/2(只针对未溢出且能被除尽的情况)
>>>无符号右移
被移位的二进制空缺位补0
移位的动作和带符号的一模一样,右移之后,高位补0。无符号右移通常只用来处理正整数,对正整数而言,无符号右移一位相当于/2
&与运算
任何二进制位和0进行&结果是0,和1进行&是原值
只针对二进制的一位,0或1,只有两个二进制都为1时结果为1,其余为0
|或运算
任何二进制位和0进行|结果是原值,和1进行|是1
只有两个全为0的时候结果才为0
^异或运算
任何相同二进制位进行^结果是0,不同二进制位^结果是1
相同为0,不同为1。两个性质:任何数与本身异或为0(a^a=0),0和任何数做异或运算都是本身(0^a=a)。满足交换律
~按位取反
包括符号位
格式:关系表达式?表达式1 : 表达式2
1.如果关系表达式结果为true,运算后的结果是表达式1
2.如果关系表达式结果为false,运算后的结果是表达式2
顺序结构描述的是Java语言之间,从上到下(从左到右)依次执行的执行顺序。
选择结构中的代码顺序和顺序结构中代码的执行顺序有明显的不同
1.选择结构与特定的语法规则,代码要执行具体的逻辑运算进行判断。
2.逻辑运算的结果有两个,所以产生选择,按照不同的选择执行不同的代码。
3.Java中,选择结构有两种实现形式,if语句和switch语句。
if (条件判断语句) {
语句;
}
if语句如果满足条件就执行。
switch (表达式) {
case 常量表达式或枚举常量:
语句;
break;
case 常量表达式或枚举常量:
语句;
break;
......
default: 语句;
break;
}
switch语句首先计算出表达式的值,其次和case依次比较,一旦有对应的值,就执行相应语句,在执行的过程中,遇到break就会结束。最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后结束程序。
for (初始化语句; 判断条件语句; 控制条件语句) {
循环体语句;
}
for循环适合针对一个范围判断进行操作(操作次数比较明确)。
while (条件判断语句) {
循环体语句(包含条件控制语句)
}
while循环适合判断次数不明确操作。
do {
循环体语句(包含条件控制语句)
} while (条件判断)
do while执行流程和for、while稍有不同:1.执行初始化语句;2.不管是否满足循环条件,都首先执行一次循环体;3.之后执行流程就几乎一样。
for循环和while循环等价,do while循环与其他两种循环结构相比,可保证循环体至少执行一次。
break(中断)
break使用场景:在选择结构switch语句中、在循环语句中使用。离开使用场景的存在是没有意义的。
break的作用:跳出(终止)单层循环(如果有多层循环,只跳出内层),结束switch语句。
continue(继续)
continue使用场景:在循环语句中使用,离开使用场景的存在是没有意义的。
continue的作用:退出循环的一次迭代过程。也可以使用标签。
return(返回)
return关键字不是为了跳出循环体,更常用的功能是结束一个方法(函数),也就是退出一个方法。跳转到上层调用的方法。
概念:相同数据类型的数据元素的有序集合。
数据类型[] 数组名 = new 数据类型[数组长度];
声明数组int[] arr;是一个声明在方法体中的局部变量,存储了一个内存首地址。
比如System.out.println(arr);
输出的是内存地址[I@4554617c,其中[表示一维数组,I表示int,@没意义起分割作用,后面是16进制内存地址
根据索引进行访问数据。数组必须先初始化,然后才能使用。
所谓初始化:为数组中的数据元素分配内存空间,并给每一个数据元素赋初值。
动态初始化:初始化时只指定数组长度,由系统为数组分配初始值。
静态初始化:初始化时指定每个数组元素的初始值,由系统分配长度。
数组操作中,常见的两个问题
要想理解这两个问题需要理解jvm虚拟机内存模型:(重要)
1、一个Java程序在虚拟机运行的过程中,在内存中需要保存很多种类型的数据。
2、比如局部变量(声明在方法体中变量),数组等等。不同类型的数据,其使用方式和生命周期都不相同。
3、为了更好的管理这些不同类型的数据,jvm将自己的内存空间划分为不同的内存区域,各个区域针对不同类型的数据,其内存空间有不同的管理方式。
目前先只了解栈
和堆
,而方法区
、还有其他的空间在后面学习。
数组作为引用类型之一,其变量中存储的是数组的地址。
栈和堆的区别:
栈堆区别 | 栈 | 堆 |
---|---|---|
存储内容 | 存储局部变量 | new出来的东西 |
初值 | 手动赋初值 | jvm自动赋予默认初值 |
生命周期 | 局部变量的内存,会随着方法的执行完毕而被回收(销毁) | 堆上数据的内存空间的回收,和方法的执行没有直接关系 |
栈:每个方法在执行的时候会创建一个栈帧,主要存储局部变量。
堆:是被所有线程共享的一块区域,在虚拟机启动时创建。
方法区:存储已被虚拟机加载的类信息、常量、静态变量。
数组操作练习
int[] a = new int[] {1, 2, 3, 4, 5};
System.out.println(a.length);
int[] a = new int[] {1, 2, 3, 4, 5};
String st = Arrays.toString(a);
System.out.println(st);
//使用的是方法,在下面一节有讲
//假设知道一个数组中的值都小于等于100
public static void countingSort(int[] targetArr) {
//1.遍历原数组
int[] countingArray = new int[101];
for (int i = 0; i <targetArr.length; i++) {
countingArray[targetArr[i]]++;
}
//2.利用辅助数组排序
//排序位置
int originIndex = 0;
for (int i = 0; i < countingArray.length; i++) {
if (countingArray[i] != 0) {
for (int j = 0; j < countingArray[i]; j++) {
targetArr[originIndex] = i;
originIndex++; //每赋一个值,排序位置就+1
}
}
}
String reverse = Arrays.toString(targetArr);
System.out.println(reverse);
}
初始化格式
数据类型[][] 变量名 = new 数据类型[m][n];
数据类型[][] 变量名 = new 数据类型[m][];
数据类型[][] 变量名 = new 数据类型[][]{{元素…},{元素…},{元素…}};
简化版格式:数据类型[][] 变量名 = {{元素…},{元素…},{元素…}};
二维数组的实质:一维数组的数组
练习
Scanner sc = new Scanner(System.in);
System.out.print("请输入杨辉三角型的行数:");
int m = sc.nextInt();
int[][] nums = new int[m][];
for (int i = 0; i < m; i++) {//i控制行数
//创建内层数组(二维数组中每一个值都是一维数组)
nums[i] = new int[i + 1];//元素个数,根据行数增加元素的个数
//遍历赋值
for (int j = 0; j < nums[i].length; j++) {//j控制列
if (j == 0 || j == i) {//j == 0是左边全为1,j == i是右边全为1
nums[i][j] = 1;
} else {
nums[i][j] = nums[i - 1][j - 1] + nums[i - 1][j];
}
}
}
//遍历:增强for循环,不懂得可以去百度,很简单的
for (int[] num : nums) {
for (int a : num) {
System.out.print(a + " ");
}
System.out.println(); //换行
}
public static 返回值类型 方法名(参数类型 参数变量, ...) {
函数体(本方法要执行的若干操作);
return 返回值;
}
修饰符:现在先认为是固定的 public static
函数体内要与一个返回值,要有return语句,需要跟上面的返回值类型一致。(特例:void不需要)
返回值:返回该方法的运算结果。基本的数据类型;引用数据类型(类);void没有返回一个值。
方法名:一个标识符,作用是调用的时候根据方法名进行调用。见名知意。
参数/参数列表:有0个或多个,每一参数先写类型,再写参数名。多个参数用逗号分隔开
方法定义:方法就是完成特定功能的代码块(在有些其他语 言中,也被成为函数)。
方法调用:接收调用,输出调用,直接调用
方法调用注意事项:
方法重载:同一个类中,可以定义多个同名方法,同名方法之间相互重载,便于调用。
方法的区分:编译器按方法签名来唯一确定一个方法。即方法名+参数列表。
因为没有返回值类型的判断,可以直接调用。
当调用重载方法的时候,如果没有完全匹配的方法,此时会选择最近的方法匹配调用(这种情况极少,不用)。
方法的参数传递:研究的问题是:修改形式参数,会不会影响实际参数。1、方法参数为基本数据类型;2、方法参数为引用数据类型。
方法练习
递归方法的定义:方法定义中调用方法本身的现象。
public class Demo1 {
public static void main(String[] args) {
//计算1~100的和
int sum = sum(100);
System.out.println(sum);
}
public static int sum(int i) {
if (i == 1) {
return 1;
}
return i + sum(i - 1);
}
}
递归容易产生的问题:递归一定要有出口!!次数不能太多,否则就出现stackoverflow
要理解这个问题先理解:栈空间内存管理
1、栈内存中,存储定义在方法体中的局部变量
2、一定是,运行中的方法,在运行过程中,其局部变量才会占用栈内存
3、所以,即一个运行中的方法,才会占用栈内存
4、所以栈空间,会给每一个运行中的方法,分配一个其独有的内存空间(栈帧stack frame)
栈空间:
生命周期:
stackoverflow产生的原因
所以递归的注意事项:1、递归一定要有递归出口;2、递归的深度不能太深,否则还是可能出现,stackoverflow
递归算法的核心思想:把一个大规模的问题的解决,转化为若干相似规模更小的子问题的求解。
递归算法练习
Part1 基本概念部分
Part2 面向对象部分
Part3 Object和String部分