一、Java概述
1、Java三大块(三个不同的版本)
Java的三个版本Java ME、Java SE、Java EE,并不是分隔的单独的三大块,从学习的角度来说,它们的关系类似于基础、进阶、高级,但也不完全是这个关系,通常学习都是先学习Java SE,然后再决定学习Java ME、Java EE或者其他Java方向。
- Java SE:Java标准版,这是学习java的入门和基础,包含的Java语言本身以及常用的API和库,通常只有学了Java SE,才会去考虑选择Java的学习方向,Java ME、Java EE或者其他Java方向。
- Java EE:Java企业版,这是在Java SE的基础上添加了一系列的API和库,这些API和库都是为了方便企业级应用的开发而设计的,所以Java EE其实就是面向企业级应用的Java版本。
- Java ME:Java微型版,也是Java SE的瘦身版,是专门针对嵌入式设备开发的一个版本。这也是“最小”的一个版本,之所以放在最后说,是因为这个版本并没有流行起来,如果没有特殊的需要,不建议学习这个版本。
2、Java跨平台特性
Java有许多的特性,但它的跨平台特性尤为重要,是非常重要的一个特性,也是以后深入学习Java非常重要的一个点。顾名思义,Java跨平台特性指的是同一个Java程序可以在编译一次之后可以运行在Windows系统或Linux系统等不同的平台上。我们知道,不同系统执行程序指令时的原理是不同的,所以同一个程序的编译版本是不可能直接运行在不同的系统的,Java为了解决这个问题,另外开发出了一个Java虚拟机,即JVM(Java Virtual Machine),由此,Java程序不需要直接和系统本身打交道,而是运行在JVM之上,所以运行Java程序之前只需要先安装好对应系统的JVM,然后就能将Java程序运行起来,这样就解决了Java跨平台的问题。但是,因为不同平台的原理不同,所以不同的平台需要安装不同版本的JVM,比如Windows版本或Linux版本等,但是需要注意的是JVM并不是单独存在,即JVM并不能单独安装,通常,它是包含在了JDK(Java开发工具包)中,所以想要运行Java程序,平台上需要先安装对应版本的JDK。
3、JDK、JRE、JVM
JDK(Java Development Kit,Java开发工具包)是Java开发所必须的工具包,平常所说的Java版本其实就是JDK的版本,如当提到Java14和JDK14,其实都是一样的。JRE(Java Runtime Environment,Java运行时环境),其实想要运行一个Java程序,并不需要完整的JDK,只需要有JRE即可,而JRE是可以单独下载安装的。JDK、JRE和JVM之间的关系其实是包含关系,JDK包含了JRE(所以JRE的体积小于JDK),而JRE又包含了JVM,因为Java程序最终是要运行在JVM上的,而JVM又不能单独安装,所以运行Java程序最少也要安装一个JRE,当然,安装了JDK肯定也是能运行Java程序的。其实如果安装了JDK的话,可以发现JDK的安装目录下有一个jre的目录,其实它就是JRE,当某个软件需要用到JRE时,而它又没有JRE或者JRE版本不对或者因为其他原因受限不能使用自带的JRE,就可以将对应版本JDK的这个jre目录拷贝过去直接使用。
4、Java面向对象编程
Java是一门纯面向对象的编程语言,这是很特别的,至少对于我这样先学习Python再回去学习Java的人来讲是特别的,Java中没有单独写一个函数来执行的概念,Java中所有需要执行的代码都是从一个类开始的,哪怕只是一个简单的hello world打印也需要先有一个类才能执行。
5、Java编译与执行
包含Java源代码的“.Java”文件需要经过编译之后生成对应的特殊的“.class”字节码文件才能放入JVM中执行,在编译的过程中会检查源代码的语法,如果发现语法错误就会报编译错误。但需要注意,不同于C、C++等语言,生成的“.class”文件是特殊的字节码文件,而不是二进制码文件,是不能直接运行在操作系统上的,其实,这个“.class”字节码文件如果是二进制文件的话就不需要到JVM中执行了,可以直接由对应的系统来执行了。Java程序的编译(只语法检查,不进行运算)其实使用的是JDK中自带的一个“javac.exe”程序来进行的,这个exe程序也就是Java程序的编译器。使用的时候可以在DOS中执行“javac yourfile.java”命令来编译你的“.Java”程序文件。
编译好的“.class”字节码文件也可以使用JDK中自带的java.exe来执行,例如,想要执行字节码文件“A.class”,可以在DOS中执行命令“java A”,其中A既是文件名,也是类名,因为此文件中有一个与文件名同名的类,而且编写java程序时,这个类名和也必须文件名一样,所以称其为类名或者文件名都一样。
6、classpath环境变量
学习Java时,我们通常会在电脑的环境变量中添加一个名为classpath的环境变量,因为在执行Java程序时通常会用到它。使用java.exe执行“.class”字节码文件时,会先启动JVM,然后JVM再启动ClassLoader搜索对应的“.class”文件,如果没有配置classpath环境变量,则从当前目录下搜索“.class”文件,如果配置了classpath,则只会从classpath指定的路径下搜索,也就是说,classpath其实是专门给JVM中的ClassLoader使用的。当然,是可以给classpath指定多个路径的,只需要使用分号分隔开即可,通常,classpath最少也需要配置当前路径,即配置成“.;”,点号则表示当前路径。
二、基础语法
1、hello world程序
println和print的区别在于,前者打印输出之后会自动换行,而后者不会自动换行,我们通常使用的是前者。
//定义一个公开的类 public class HelloWorld{ // 定义一个程序的主方法,也是程序的入口, // 并且这个方法的写法也是固定的,除了args可以变外,其他的都不能有任何改动,语法规定就必须写成这样 public static void main(String[] args){ // java的语句是以分号标识一个语句的结束 // 并且在方法之外类以内的范围是不可以编写java语句的,但是可以声明变量 System.out.println("Hello World!"); } }
2、注释
Java中注释的写法有三种:
- 单行注释:以双斜杠//开头的内容。
- 多行注释:被包含在/*和*/之间的内容。
- 文档注释:被包含在/**和*/之间的内容,并且如果有多行,则中间的每行都以星号*开头。
// 这是单行注释 /* 这是多行注释 这是多行注释 这是多行注释 ... */ /** * 这是javadoc注释 * 写成这样的注释会被javac.exe解析为帮助文档 * ... */
3、文件中class类的定义
在一个文件中定义类时,应注意并遵循以下几点:
- 一个java文件中可以定义多个class,但是编译时每个class会单独生成一个对应的“xxx.class”字节码文件。
- 一个java文件中public的class不是必须的。
- 一个java文件中最多只能定义一个public类,如果一个java文件中定义的有public的类,那么这个类的名称必须和文件名相同。
- 每个类都可以编写main入口方法(不只是public类),运行时只需要执行“java YourClassName”即可,在执行这个命令时会自动调用main方法,此时,如果这个类你没有定义main方法,那么就会报错。
- 推荐一个Java文件中只编写一个类。
4、标识符
通常,可以由程序员自己定义的名称就称之为标识符,在Java中定义标识符时应当注意以下几点:
- 一个合法的标识符只能由数字、字母、下划线和美元符组成,不能有其他的特殊符号。
- 标识符不能以数字开头。
- 标识符严格区分大小写。
- 关键字不能做标识符。
- 理论上无长度限制,但是最好不要太长。
- 标识符常见命令规范:
- 见名知意。
- 遵守驼峰命名方式。
- 类名和接口名:首字母大写,后面每个单词首字母大写。
- 变量名和方法名:首字母小写,后面每个单词首字母大写。
- 常量名:全部大写,单词之间使用下划线隔开。
5、字面值
字面值,顾名思义,其实就是字面上我们看见的值,其实就是数据本身的直观表示,比如“int a = 10;”,其中10这个数字就是一个字面值,在程序中,可以使用的字面值类型包括整型、浮点型、布尔型、字符型、字符串型。
true/false为布尔型的字面值,这里需要注意,虽然底层false的值存储的是0,但是不允许给布尔型的变量直接赋值1或0,如“boolean flag = 0”则会报错。
使用双引号括起来的内容为字符串型的字面值,使用单引号括起来的单个字符(如'a'、'字'等)为字符型的字面值,需要注意的是,Java中是不允许给一个变量赋予空字符的,如“char a = ''”则会报错。
整数的数字字面值默认为int类型,如果想要声明某个数字字面值为long类型,则需要在数字末尾添加l或L(推荐使用大写的L)。
浮点的数字字面值默认为double类型,如果想要声明某个浮点数字为float类型,则需要在数字末尾添加f或F。
6、变量
内存空间:变量其实就是表示内存中的一块空间,而这块空间有对应的数据类型、名称和字面值(数据),即变量包含三个部分:数据类型、名称和字面值(数据)。
声明定义:变量的声明定义使用格式为“数据类型 变量名”,给变量赋值的格式为“变量名=字面值”,在声明定义一个变量的同时,也可以给它赋值,如“数据类型 变量名=字面值”。
变量值:变量的值是可以改变的,但前提是赋值或改变之后的数据值必须和定义时声明的数据类型一致,需要注意改变值的时候改变的是原先那块内存空间的值,而不是另起一块内存空间。
变量访问:变量在赋值之前是不可以访问的,因为在没有赋值之前,这个变量对应的内存空间实际上还没有开辟出来,自然就无法访问了。
就近原则:在同一作用域中,变量名不能重名,即不能重复声明定义,但是能重新赋值。对于在不同作用域中的重名的变量,Java遵循“就近原则”进行访问。
局部变量:在方法体中的变量可以称之为局部变量,因为方法体中的Java语句是自上而下顺序执行的,所以对于局部变量的声明和定义是有顺序关系的。
成员变量:在方法体之外、类体之内的变量称之为成员变量,对于成员变量的声明定义是没有顺序关系的,并且对于方法体之外、类体之内的空间除了可以定义变量之外是不能定义其他Java语句的。
7、基本数据类型
Java中数据类型一般分为基本数据类型和引用数据类型,比如字符串、实例对象就属于引用数据类型,而基本数据类型则分为8中四类:
- 整数型:byte(1个字节)、short(2个字节)、int(4个字节)、long(8个字节)。默认值为0。
- 浮点型:float(4个字节)、double(8个字节)。默认值为0.0。
- 布尔型:boolean(1个字节)。默认值为false(在c语言中,1是true,0是false)。
- 字符型:char(2个字节)。默认值为u0000(不是字符0,也不是空格,而是空白字符)。
可以发现,对于基本数据类型的默认值,遵循“一切向0看齐”。但需要注意的是,默认值是对于成员变量来说的,成员变量在生命定义后即使没有给它赋值也是可以访问的,因为系统会自动给它赋予一个默认值,但是局部变量是没有默认值的,当你访问一个没有被赋值的局部变量时则会编译报错。
8、UTF-8编码
Java默认采用的编码格式是Unicode,但在实际开发中通常采用的是UTF-8编码,可以理解为Unicode是一种可以容纳所有国家语言的编码方式的统称,即只要可以容纳所有国家的语言的编码方式都可以叫Unicode,而UTF-8则是Unicode实现方式中的一种,其他实现Unicode的编码方式还有UTF-16、UTF-32等。
9、不同进制的数字表示
Java中数字的表示默认是以十进制的方式表示,对于八进制的数字则以0开头表示,如010表示十进制的8,对于十六进制的数字则以0x开头表示,如0x10表示十进制的16。
不同进制之间的数字是可以直接进行运算的,但是最终的结果则是以十进制的方式来进行表示的。
10、基本数据类型之间的互相转换
Java的8种基本数据类型之间,除了布尔类型之外,其他的数据类型都是可以互相转换的,但是需要在进行转换时需要注意它们各自的取值范围和“容量”大小关系,避免发生精度损失的情况。
7种数据类型的“容量”大小关系为:byte < short = char < int < long < float < double。这里需要注意的是,long类型虽然是8个字节,float类型和double类型是4个字节和8个字节,但由于long类型属于整数类型,float类型和double类型属于浮点数类型,所以在“容量”的大小关系判断上,应该是long类型小于float类型和double类型的。对于short类型和char类型,它们虽然都是2个字节,但是由于char类型没有符号位,所以char类型可以表示更大的正整数。
小“容量”到大“容量”之间的转换可以由系统自动完成,称之为自动类型转换,如“long m = 10”,其中10为int类型,运行时将自动转换为long类型。而反过来,大“容量”到小“容量”之间的转换,则需要程序员添加强制类型转换符,称之为强制类型转换,需要注意的是强制类型转换存在精度损失的风险,请谨慎使用,如“int n = (int)10L”,其中10L为long类型,“(int)”为强制类型转换符,表示将10L强制转换为int类型,此时如果转换的值的范围超过了int类型的取值范围,强制转换之后将发生精度损失。
当多种不同类型的数据进行运算时,系统会自动将小“容量”的数据转换为表达式中“容量”最大的那个数据的相同“容量”之后再进行运算。
数据类型之间的转换原理:对于小“容量”到大“容量”之间的转换,不足的字节数在左边补上相应位数的0即可,而大“容量”到小“容量”之间的转换,则从左边开始直接“砍掉”多出的字节数,剩下的内容就是最终的值。
自动类型转换示例:如“int i = 123”中123字面值默认就是int类型,赋值给变量i时就不需要转换,但是“long j = 123”中123是int类型,赋值给j时存在类型转换,将j自动转换为long类型。而“long j = 2147483648”则会出错,因为2147483648被当成int类型处理,但是int类型的取值范围为-2147483648~2147483647,2147483648已经超出了int类型的取值范围,所以编译报错,此时需要指定2147483648为long类型才能正确编译“long j = 2147483648L”。
11、自增和自减运算
算术运算符有两个单目运算符,自增++和自减--,表示将变量进行加1和减1操作。但是如果此时有赋值操作,则需要注意,如果自增或自减在变量之后,则先进行赋值操作,再进行自增或自减操作,如“int m = 10; int n = m++;”中n的值为10,而m的值为11,如果自增或自减在变量之前,则先进行自增或自减操作,再进行赋值操作,如“int m = 10; int n = ++m;”中n的值为11,m的值也为11。
12、逻辑运算符(布尔运算符)
逻辑运算符两边的算子都必须是布尔类型,并且运算结果也是布尔类型。
- &:逻辑与,两边的算子都是true,结果才是true。
- |:逻辑或,两边的算子只要有一个是true,结果就是true。
- !:逻辑取反,是一个单目运算符,!true就是false,!false就是true。
- ^:逻辑异或,两边的算子只要不一样就为true,一样就是false。
- &&:短路与,与逻辑与的运算结果是相同的,只不过存在短路现象,左边的算子为false时,就不会去执行右边的算子了。
- ||:短路或,与逻辑或的运算结果是相同的,只不过存在短路现象,左边的算子为true时,就不会去执行右边的算子了。
注意:&逻辑与等不是短路的运算符,会将两边的算子都运算一次之后才出结果,不会发生短路现象。
13、三元运算符
语法为:布尔表达式 ? 表达式1 : 表达式2
当布尔表达式的结果为真时,选择表达式1作为整个表达式的执行结果,当布尔表达式的结果为假时,选择表达式2作为整个表达式的执行结果。但注意,这只是个表达式,并不是完整的Java语句。
三、流程控制
1、if语句
// 第一种 if(布尔表达式){ java 语句; ... } // 第二种 if(布尔表达式){ java 语句; ... }else{ java 语句; ... } // 第三种 if(布尔表达式){ java 语句; ... }else if{ java 语句; ... }else if{ ... } // 第四种 if(布尔表达式){ java 语句; ... }else if{ java 语句; ... }else if{ ... }else{ java 语句; ... }
注意,如果大括号中如果只有一条Java语句,那么这个大括号是可以不用写的(不推荐),但同时也就意味着如果这个if语句之后没有大括号,却有多个Java语句,那么只有第一个Java语句会被作为if语句的条件执行语句,其他的语句则是与if语句平行的语句。
2、switch语句
// 正常语法 switch(int或String类型的字面值或变量){ case int或String类型的字面值或变量: java语句; ... break; case int或String类型的字面值或变量: java语句; ... break; ... default: java语句; ... } // case合并语法 switch(int或String类型的字面值或变量){ // 直接将多个case写在一起即可 case int或String类型的字面值或变量:case int或String类型的字面值或变量: java语句; ... break; case int或String类型的字面值或变量: java语句; ... break; ... default: java语句; ... }
// 语法 for(初始化表达式; 布尔表达式; 更新表达式){ // 需要重复执行的代码片段,也称为循环体 Java语句; }
注意,初始化表达式、布尔表达式、更新表达式都不是必须的,但是括号中的两个分号是必须的,不能少。
执行步骤:
- 先执行初始化表达式,并且该表达式只执行一次。
- 判断布尔表达式的结果是true还是false,如果是false,则退出循环;如果是true,则执行循环体。
- 执行更新表达式式。
- 重新开始第二步。
foreach语法:foreach语法是普通for循环的一个增强版本,在代码编写上也简便了许多,但是因为不能使用下标,所以还是需要根据实际情况来使用。
// 语法:for(元素类型 变量名 : 数组或集合){循环体} int[] array = {1, 2, 3, 4}; for (int e : array){ System.out.println(e); }
4、while循环和dowhile循环
// 语法 while(布尔表达式){ 循环体; }
执行步骤:判断布尔表达式的结果,如果结果为true则执行循环体,否则退出循环,然后反复执行这一步骤。
// 语法 do{ 循环体; }while(布尔表达式); // 这里的分号不要忘记了
特点:dowhile结构的特点是循环体至少会执行一次。
5、break和continue