目录
一、引言
Java是当今世界中最受欢迎的编程语言之一,其有着一系列计算机软件和规范形成的技术体系,被广泛运用于各个领域之中。它的强大灵活和可靠性是许多开发者的首选。JavaSE是Java编程的核心和基础。学好JavaSE对后续的学习有着巨大的帮助。
二、初始Java
2.1Java工作领域
1、企业级应用系统
大型复杂的企业级软件系统、微服务等。
2、服务器系统
应用的后台(后端)
3、移动应用开发
android
4、大数据平台开发
大数据相关的各类框架,比如:Hadoop、spark、storm、flink等,
5、游戏开发
我的世界
2.2Java简史
Java语言之父——詹姆斯·高斯林(James·Gosling)
Java语言源于1991年4月,Sun公司James·Gosling博士领导的绿色计划(Green Project)开始启动,此计划初的目标是开发一种能够在各种消费性电子产品(机顶盒,冰箱,收音机——智能家居)上运行的程序架构。这个就是Java的前身:Oak。
1995年代互联网的发展Sun公司看见Oak在互联网上的应用前景,于是改造Oak,于1995年5月以Java的名称正式发布,并提出”Write once,Run anywhere”的口号。
2009年sun公司被Oracle公司收购。
2.3Java的语言特性
1、简单性
Java是纯面向对象语言。不包含头文件,指针运算联合、操作符重载、虚基类等复杂特性。
2、面向对象
在Java的世界里一切皆对象,所谓面向对象,就是依靠对象之间的交互来完成事情。
3、分布式(微服务)
Java有丰富的例程库,用于处理像HTTP和FTP之类的TCP/IP协议,能够通过URL打开和访问网络上的对象。
4、健壮性
Java与C++最大的不同在于Java采用的指针模型可以消除重写内存和损坏数据的可能性。其编译器可以检测许多在其它语言中仅在运行时才能检测出来的问题。
5、安全性
删除指针防止用户破环自己进程空间之外的内存,不允许未经授权读写文件,满足强制转换规则的情况下才能强转成功。
6、体系结构中立
编译器生成一个体系结构中立的目标文件格式,按照该中规范生成的文件,只要有Java运行时系统,这些编译后的代码就可以在许多处理器上运行。而且其它语言编写地程序,在编译后如果能够严格按照字节码文件地规范生成.class文件,也可以在JVM上运行
7、可移植性
与C/C++不同,Java规范中没有“依赖具体实现的地方”,基本数据类型地大小以及有关运算都做了明确地说明,例如Java中的int永远都是32位的整数。在Java中数据类型具有固定的大小,这消除了代码移植时令人头疼的主要问题
8、解释性
Java为了实现与平台无关,自己维护了一套基于栈架构的指令集,其源代码经过编译之后,字节码文件中的指令就是按照自己的指令集来组织的,但是在具体硬件环境中运行时,系统并不能识别,因为Java程序在执行时Java解释器会逐条的将字节码文件中的指令翻译成CPU的指令集。
9、高性能
边解释边执行。Java代码运行效率偏低,但近年来JVM也在不断的优化,比如JIT(即时编译器),热点代码探测,让Java程序的执行效率大幅提高。
10、多线程
Java在当时很超前。它是第一个支持并发程序设计的主流语言,多线程可以带来更好的交互响应和实时行为。
11、动态性
Java与C/C++相比更加具有动态性。它能够适应不断发展的环境。库中可以自由的添加新方法和实例变量,而对客户端没有任何影响。
12、Java不仅仅是一门编程语言,也是一个由一系列计算机软件和规范组成的技术体系。
2.4java程序构成
public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello,world");
}
}
1、源文件(扩展名为*.java):源文件带有类的定义。类用来表示程序的一个组件,小程序或许只会构成一个类,类的内容必须包含在花括号里面
2、类:类中带有一个或多个方法,方法必须在类的内部声明
3、方法:在方法的花括号中编写方法应该执行的语句。
总结:类存在于源文件中,语句存在于方法中
注意:在一个源文件中只能有一个public修饰的类,而且源文件名字必须与public修饰的类的名字相同
2.5Java的工作方式
1、编写源代码将其保存为.java文件;
2、通过执行javac程序来编译.java文件将其转为字节码文件,在这个过程中编译器会检查错误,如果出现错误就需要改正才会产生.class这个文件;
3、通过启动Java虚拟机(JVM)运行.class文件。
2.6注释
1、单行注释
public class HelloWorld{
public static void main(String[] args){
//这是单行注释
}
}
以"//"标识,只能注释单行内容
2、多行注释
public class HelloWorld{
public static void main(String[] args){
/*
这是多行注释
*/
}
}
包含在“/*”和“*/”之间,能注释很多行的内容。
3、文档注释
/**
*@author
*/
public class HelloWorld{
public static void main(String[] args){
}
}
可以被javadoc工具解析,生成一套以网页文件形式体现的程序说明文档
4、注释要求
1、内容准确:注释内容要和代码匹配一致,并在代码修改时及时更新;
2、篇幅合理:注释即不应该太精简,也不应该长篇大论;
3、使用中文:一般中国公司都要求使用中文写注释;
4、积极向上:注释中不要包含负能量。
2.7标识符
Java语言中,对于变量,常量,函数,语句块也有名字,我们统统称之为Java标识符。标识符是在程序中由用户用来给类、对象、方法、变量、接口和自定义数据类型命名的。
标识符可以由字母、数字以及下划线和$符号等构成,但不能以数字开头,也不能是关键字,并且严格区分大小写。
类名:采用大驼峰的格式命名,即每个单词首字母大写。
方法名/变量名:首字母小写,后面每个单词的首字母大写(小驼峰)。
2.8关键字
关键字是Java语言里事先定义好的,具有特别意义的标识符,又叫保留字,还有特别意义的变量。Java的关键字对Java的编译器有特殊的意义,它们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数。
常用关键字
三、Java的数据类型
3.1分类
3.2基本数据类型
3.3类型转换
1、自动类型转换(隐式)
自动类型转换:代码不需要经过任何处理,在代码编译时,编译器会自动进行处理。特点:数据范围小的转为数据范围大的时会自动进行
2、强制类型转换(显示)
强制类型转换:当进行操作时,代码需要经过一定的格式处理,不能自动完成。特点:数据范围大的到数据范围小的
注意事项:
1、不同数字类型的变量之间赋值,表示范围更小的类型能转换成范围较大的类型
2、如果需要把范围大的类型赋值给范围小的需要进行强制类型转换,但是可能精度值丢失
3、将一个字面值常量进行赋值的时候Java会自动针对数字范围进行检查
4、强制类型转换不一定能成功,不相干的类型不能互相转换
四、逻辑控制
4.1顺序结构
public class Test{
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
System.out.println(3);
System.out.println(4);
}
}
程序按照顺序依次执行。
4.2分支结构
1、if-else语句
if(布尔表达式){
语句1
}
若布尔表达式为true则执行语句1。
if(布尔表达式){
语句1
}else{
语句2
}
若布尔表达式为true则执行语句1,否则执行语句2。
if(布尔表达式1){
语句1
}else if(布尔表达式2){
语句2
}else{
语句3
}
布尔表达式1 成立,执行语句 1 ,否则判断布尔表达式 2是否 成立,若成立执行语句 2 ,否则执行语句 3
2、switch语句
switch(表达式){
case 常量值1:
语句1
break;
case 常量值2:
语句2
break;
default:
break;
}
执行流程先计算表达式的值和 case 依次比较,一旦有响应的匹配就执行该项下的语句,直到遇到 break 时结束当表达式的值没有与所列项匹配时,执行 default
4.3顺序结构
while(循环条件){
语句
}
循环条件为 true, 则执行循环语句 ; 否则结束循环。eg:打印1-10;
while(i <= 10){
System.out.println(i);
i++;
}
for (表达式1;表达式2;表达式3){
语句
}
表达式 1: 用于初始化循环变量初始值设置,在循环最开始时执行,且只执行一次表达式 2: 循环条件,满则循环继续,否则循环结束表达式 3: 循环变量更新方式eg:打印1-10的和
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
}
System.out.println(sum);
五、方法
5.1背景
在编程我们经常会遇到某段功能的代码被频繁使用到,如果在每个位置都重新实现一遍,会使程序变得繁琐,降低开发效率,使代码维护变的困难,并且不利于代码的复用。因此,在编程中,我们将可以频繁使用的代码封装成方法,需要时直接拿来链接使用即可,避免了一遍一遍的累赘。
5.2概念
方法就是一个代码块,类似于C语言中的函数。
方法存在的意义:
1、模块化的组织代码。
2、做到代码的复用, 使一份代码可以在多个位置使用。
3、让代码更好理解更简单。
4、直接调用现有方法开发。
5.3方法的定义及使用
修饰符 返回值类型 方法名(参数类型 形参){
方法体;
return 返回值;
}
1、返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成void。
2、方法名字:采用小驼峰命名。
3、参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开。
4、方法体:方法内部要执行的语句。
5、在Java当中没有方法声明一说(只有方法名称和参数,没有具体的实现),其必须写在类中,且不能嵌套定义。
实现一个两数相加的方法,代码实例如下:
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int sum = add(a,b);
System.out.println(sum);
}
public static int add(int x,int y){
return x + y;
}
}
5.4方法的调用
定义方法的时候,,不会执行方法的代码,只有调用的时候才会执行,此时会将实参传给形参,在参数传递完成后会执行被调方法的方法体,直至方法的代码运行结束,返回到主调方法并向下继续执行。
一个方法可以被多次调用
5.5方法的重载
在自然语言中,一个词语如果有多重含义,那么就说该词语被重载了,具体代表什么含义需要结合具体的场景。在Java中方法也是可以重载的。如果多个方法的名字相同,参数列表不同,则称该几种方法被重载了。
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 30;
double d = 20.1;
double e = 10.2;
int sum1 = add(a,b);
int sum2 = add(a,b,c);
double sum3 = add(d,e);
System.out.println(sum1);
System.out.println(sum2);
System.out.println(sum3);
}
public static int add(int x,int y){
return x + y;
}
public static int add(int x,int y,int z){
return x + y + z;
}
public static double add(double x,double y){
return x + y;
}
}
5.6方法的递归
一个方法在执行过程中调用自身,就称为“递归”。递归相当于数学上的“数学归纳法”,有一个起始条件,然后有一个递推公式。
递归的必要条件:
1、将原问题划分成其子问题,且子问题必须要与原问题的解法相同
2、递归出口
求N的阶乘代码实例
public class Test {
public static void main(String[] args) {
int N = 5;
int ret = fac(N);
System.out.println(ret);
}
public static int fac(int n){
if (n == 1) {
return 1;
}
return n * fac(n - 1);
}
}
运行结果
代码执行过程图:
六、数组
6.1数组的概念
在程序开发过程总会存储大量的相同类型数据,如果要存5个学生的成绩,那便只需创建五个变量,但如果要存100个学生的成绩,难道需要创建100个学生的成绩吗,这种做法太过繁琐,为解决上述问题,我们可以使用数组来批量存储相同数据类型的的元素。
数组是一个由相同类型元素组成的集合。在内存中是一段连续的空间,它们的存储空间是相邻的,每个空间都有自己的唯一编号从0开始递增,这个编号就是数组的下标。数组是引用数据类型。
数组的创建:数据类型[] 数组名 = new 数据类型(数组元素个数);
int[] arr1 = new int[10];//声明一个名为arr1可以容纳10个int类型的数组
double[] arr2 = new double[10];//声明一个名为arr2可以容纳10个double类型的数组
6.2数组的初始化
1、静态初始化
在创建数组时并不直接指定数据元素个数,而是直接将具体数据直接指定
int[] nums = {1,2,3,4,5,6};
注意在静态初始化虽然没有指定数组的长度,但当编译器开始编译时会根据{}中元素个数来确定数组的长度。并且{}中数据类型必须与[]前数据类型一致。
2、动态初始化
数组在创建数组时,直接指定数组中元素的个数
3、数组的默认值
在未对数组进行初始时,数组中元素会有其对应的默认值
类型
默认值
byte
0
short
0
int
0
long
0
float
0.0f
double
0.0
char
/u0000
boolean
false
当数组为引用数据类型时,数组的默认值为null。
6.3数组的使用
数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素。下标从0开始,介于[0, N)之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
int[] arr = {1,2,3,4,5,6,7,8,9,0};
System.out.println(arr[0]);
System.out.println(arr[2]);
System.out.println(arr[9]);
数组的遍历
在数组中可以通过.leagth来获取数组的长度
int[] arr = {1,2,3,4,5,6,7,8,9,0};
System.out.println(arr.length);
所谓 "遍历" 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作,以打印为例:我们可以通过循环来实现来解决
int[] arr = {1,2,3,4,5,6,7,8,9,0};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
也可以使用for-each来实现遍历数组
int[] arr = {1,2,3,4,5,6,7,8,9,0};
for (int i: arr) {
System.out.println(arr[i]);
}
6.4数组在内存中的分布
1、Java虚拟机(JVM)的内存划分
内存是计算机中用来存储程序运行数据的连续存储空间。在程序运行过程中,代码、运行产生的中间数据以及程序中的常量都需要加载或保存到内存中。JVM要运行程序就必须对内存进行空间的分配和管理。
程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址。
虚拟机栈(VM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域
2、数组在内存中的存储
数组是引用数据类型,引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。
arr是数组类型的引用变量,其内部保存的内容可以简单理解成是数组在堆空间中的首地址。
6.5数组的练习
1、数组转字符串
int[] arr = {1,2,3,4,5,6,7,8,9,0};
String str = Arrays.toString(arr);
System.out.println(str);
//运行结果:[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
2、数组排序(冒泡排序)
对数组进行升序排序,将数组中相邻元素从前往后依次进行比较,如果前一个元素比后一个元素大,则交换,一趟下来后最大元素就在数组的末尾。依次从上上述过程,直到数组中所有的元素都排列好
int[] arr = {7,3,9,2,8,6,1,5,4,0};
for (int i = 0; i < arr.length; i++) {
for (int j = 1; j < arr.length - i; j++) {
if (arr[j - 1] > arr[j]) {
int t = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = t;
}
}
}
System.out.println(Arrays.toString(arr));
3、查找数组中指定元素(二分查找)
以升序数组为例, 二分查找的思路是先取中间位置的元素, 然后使用待查找元素与数组中间元素进行比较:
如果相等,即找到了返回该元素在数组中的下标;
如果小于,以类似方式到数组左半侧查找;
如果大于,以类似方式到数组右半侧查找。
int[] arr = {1,2,3,4,5,6,7,8,9,0};
int Find = 6;
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (Find < arr[mid]) {
// 去左侧区间找
right = mid - 1;
} else if (Find > arr[mid]) {
// 去右侧区间找
left = mid + 1;
} else {
// 相等, 说明找到了
System.out.println(mid);
break;
}
}