知识来源于B站“黑马程序员”Java课程,笔记整理自用,也希望能帮助到更多想学习Java的人,欢迎各位大佬批评指正。本文适合有一定C语言基础的读者阅读,与C语言有相同之处的内容会简述或略过,建议零基础的读者去听视频课。涉及到计算机系统相关知识的部分也不做详细解释,之后会出计算机系统的专篇,也可能会在此文中补充。
面向对象、跨平台、开源、简单易用、多线程、安全性
桌面应用开发、企业级应用开发、移动应用开发、科学计算、大数据开发、游戏开发
Java语言的标准版,用于桌面应用的开发,是其他两个版本的基础。实际上Java语言在桌面应用开发领域不占优势,最合适的语言是C和C++。
Java语言的小型版,用于嵌入式电子设备或小型移动设备。Java ME如今已经没落,嵌入式开发最常用的是C语言。
Java语言的企业版,用于Web方向的网站开发。是这个领域的No.1。
Java虚拟机,真正运行Java程序的地方,是Java能实现跨平台开发的关键。Java提供了不同操作系统的虚拟机,根据开发需要安装即可。
Java开发工具包,包含JVM、核心类库和开发工具。
核心类库:
java语言提供的一组基础类和接口,包括IO类、网络编程类和多线程类等。
常用的开发工具:
javac:编译工具,将源代码转换成字节代码(Java代码的可执行形式)。
java:运行工具,直接从类文件执行Java应用程序代码。
jdb:调试工具。
jhat:内存分析工具。
jar:多用途的存档及压缩工具。
Java的运行环境,包含JVM、核心类库、运行工具(包含在开发工具中)。当Java应用程序开发者只把类文件发送给其他设备时,接收者只需要有JRE就可以运行。
JDK包含JRE,JRE包含JVM。
单行注释
//注释信息
多行注释
/*注释信息*/
文档注释
/**注释信息*/
关键字:被Java赋予了特定涵义的英文单词。常用的代码编辑器针对关键字有特殊的颜色标记。例如下图中标记为紫色的都是关键字:
Java中目前有53个关键字:
访问控制 | private | 私用模式,访问控制修饰符,可以应用于类、方法或字段(在类中声明的变量) |
protected | 保护模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符 | |
public | 共用模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符 | |
类、方法和变量修饰符 | abstract | 表明类或者成员方法具有抽象属性,用于修改类或方法 |
class | 声明一个类,用来声明新的Java类 | |
extends | 表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口 | |
final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量 | |
implements | 表明一个类实现了给定的接口 | |
interface | 接口 | |
native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 | |
new | 用来创建新实例对象 | |
static | 表明具有静态属性 | |
strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 | |
synchronized | 表明一段代码需要同步执行 | |
transient | 声明不用序列化的成员域 | |
volatile | 表明两个或者多个变量必须同步地发生变化 | |
程序控制 | break | 提前跳出一个块 |
continue | 回到一个块的开始处 | |
return | 从成员方法中返回数据 | |
do | 用在do-while循环结构中 | |
while | 用在循环结构中 | |
if | 条件语句的引导词 | |
else | 用在条件语句中,表明当条件不成立时的分支 | |
for | 一种循环结构的引导词 | |
instanceof | 用来测试一个对象是否是指定类型的实例对象 | |
switch | 分支语句结构的引导词 | |
case | 用在switch语句之中,表示其中的一个分支 | |
default | 默认,例如:用在switch语句中,表明一个默认的分支Java8 中也作用于声明接口函数的默认实现 | |
错误处理 | try | 尝试一个可能抛出异常的程序块 |
catch | 用在异常处理中,用来捕捉异常 | |
throw | 抛出一个异常 | |
throws | 声明在当前定义的成员方法中所有需要抛出的异常 | |
包相关 | import | 表明要访问指定的类或包 |
package | 包 | |
基本类型 | boolean | 略 |
byte | ||
char | ||
double | ||
float | ||
int | ||
long | ||
short | ||
null | 空,表示无值,不能将null赋给原始类型(byte、short、int、long、char、float、double、boolean)变量 | |
true | 略 | |
false | ||
变量引用 | super | 表明当前对象的父类型的引用或者父类型的构造方法 |
this | 指向当前实例对象的引用,用于引用当前实例 | |
void | 声明当前成员方法没有返回值,void可以用作方法的返回类型,以指示该方法不返回值 | |
保留字 | goto | 保留关键字,没有具体含义 |
const | 保留关键字,没有具体含义,是一个类型修饰符,使用const声明的对象不能更新 |
注意:关键字的字母全部小写。
例:class用于创建或定义一个类(类是Java最基本的组成单元)。 class后面的是类名(和文件名保持一致),最近的{}约束了类的范围。
字面量:数据在程序中的书写格式。
字面量的类型:整数类型、小数类型、字符串类型、字符类型、布尔类型、空类型。
以打印操作System.out.println()为例:
整数、浮点数、布尔类型可以直接打印,字符串类型需要加双引号,字符类型需要加单引号,空类型不能直接打印,只能以字符串的形式打印。
特殊字面量:'\t','\r','\n'等,可以用单引号也可以用双引号。
'\t'制表符:在打印时,把前面字符串的长度补齐到8,或者8的整数倍。最少补1个空格,最多补8个空格。
变量的赋值和运算规则与C语言相同,在此不赘述。
数据类型 | 关键字 | 内存占用(字节) |
整数 | byte | 1 |
short | 2 | |
int | 4 | |
long | 8 | |
浮点数 | float | 4 |
double | 8 | |
字符 | char | 2 |
布尔 | boolean | 没有明确规定 |
如果要定义long类型的变量,数据值需要加后缀L或l,定义float类型的变量需要加后缀F或f。其他类型定义时无特殊要求。Java各数据类型在内存中所占字节数是固定的,与操作系统无关,这也是Java能够实现跨平台开发的原因之一。
类型转换方式包括隐式类型转换和隐式类型转换,规则与C语言无异,这里不过多叙述,仅需注意byte、short、char三种类型的数据在运算时都会直接提升为int。在Java中,小数默认是double类型,整数默认是int类型,在隐式类型转换时需要考虑到这一点。
基本数据类型的变量定义和变量的值都是存储在栈内存中的,在赋值给其他变量时,赋的也是真实值。这是基本数据类型和接下来要介绍的引用数据类型的根本差别。
示例:
long n = 9999999999L;
float f = 10.1F;
除了基本数据类型之外的数据类型都叫引用数据类型,引用数据类型的变量中存储的是地址值,赋值给其他变量时,赋的也是地址值。“引用”可以理解为使用其他空间中的数据。比如数组类型变量的定义和数组的首地址存储在栈内存中,而真实的数组元素值存储在堆内存中,数组首地址即为数组第一个元素在堆内存中的地址。取数组元素时,根据首地址和索引计算出对应元素的地址,再在堆内存的对应地址读取元素的值。
标识符:给类、方法、变量等取的名字。
1.由数字、字母、下划线(_)、美元符($)组成;
2.不能以数字开头;
3.不能是关键字;
4.区分大小写。
见名知意!
小驼峰命名法:
1.标识符是一个单词的时候,全部小写。如:name。
2.标识符由多个单词组成的时候,第一个单词首字母小写,其他单词首字母大写。如:firstName。
大驼峰命名法:
1.标识符是一个单词的时候,首字母大写。如:Name。
2.标识符由多个单词组成的时候,每个单词首字母大写。如:FirstName。
用Java的Scanner类接收键盘输入的整数。(其他类型数据的接收会在后面学习)
步骤:
import java.util.Scanner; //导包的动作必须出现在类定义的上边。
Scanner sc = new Scanner(System.in); //只有sc是变量名,可以变。
int i = sc.nextInt(); //只有i是变量名,可以变。
程序示例:
//1.导包,找到Scanner类。注意要写在类定义的上面
import java.util.Scanner;
public class ScannerDemo1{
public static void main(String[] args){
//2.创建对象
Scanner sc = new Scanner(System.in);
System.out.println("请输入整数");
//3.接收数据
int i = sc.nextInt();
System.out.println(i);
}
}
补充:System.out.println();表示换行输出,如果需要在同一行输出,用System.out.print();
符号 | 作用 |
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取模 |
运算规则以及类型转换的规则与C语言基本无异,在此不做演示。细微的差别在于Java取模运算的对象和结果可以是浮点数。
注意“+”操作的三种情况:
1.作为算术运算符。
2.当“+”操作中出现字符串时,“+”是字符串连接符,而不是算术运算符。连续进行“+”操作时,从左到右逐个执行。例如,123+123+“123”的结果是“246123”。
3.当字符+字符,或字符+数字时,参与运算的是字符的ASCII码值。例如,‘a’+0的结果是97。
与C语言无异。
与C语言无异。
注意运行结果是boolean类型(true或false),不是0或1。
与C语言无异,但是逻辑运算符和位运算符很容易混淆,有必要解释一下。
逻辑运算符 | 功能 |
&& | 逻辑与 |
|| | 逻辑或 |
! | 非 |
逻辑运算的结果是boolean类型。
位运算符 | 功能 |
& | 按位与 |
| | 按位或 |
~ | 按位取反 |
^ | 异或 |
<< | 左移(补0) |
>> | 算数右移(补符号位) |
>>> | 逻辑右移(补0,适用于无符号数) |
所谓按位运算就是对数值的二进制补码形式逐位按规则进行运算。也可以对boolean类型的数据进行计算,此时运算规则与逻辑运算相同。
注意逻辑运算符&&和||有短路情况,即只执行运算符左边的表达式就可以得到最终结果时,右边的表达式将不再执行,而按位运算两边的表达式都要执行。
即C语言中的三目运算符。格式为:
关系表达式?表达式1:表达式2;
三元运算符的结果可以赋值给一个变量,也可以直接打印。注意三元运算符的结果必须被使用,否则会报错。
优先级 | 运算符 |
1 | .、 ()、 {} |
2 | !、 ~、 ++、 -- |
3 | *、 /、 % |
4 | +、 - |
5 | <<、 >>、 >>> |
6 | <、 <=、 >、 >=、 instanceof |
7 | ==、 != |
8 | & |
9 | ^ |
10 | | |
11 | && |
12 | || |
13 | ? : |
14 | =、 +=、 -=、 *=、 /=、 %=、 &= |
顺序结构语句是Java默认的执行流程,按照代码的先后顺序,从上到下依次执行。
与C语言中if的用法相同。
//第一种格式
if(关系表达式){
语句体;
}
//第二种格式
if(关系表达式){
语句体1;
}else{
语句体2;
}
//第三种格式
if(关系表达式1){
语句体1;
}else if(关系表达式2){
语句体2;
}
...
else{
语句体n+1;
}
良好的编程习惯:
1.大括号的开头可以另起一行书写,但是建议写在第一行的末尾;
2.在语句体中,如果只有一句代码,大括号可以省略不写,但是建议不要省略;
3.如果对一个boolean类型的变量进行判断,建议不用==号。
与C语言中switch的用法相同。
注意:
1.default可以省略,但不建议;
2.default可以写在任意位置,但习惯上写在最后;
3.如果没有break,会发生case穿透(程序继续执行,直到遇到break或大括号);
JDK12优化了switch语句,示例:
int number = 1;
switch (number){
case 1 ->{
System.out.println("一");
}
case 2 ->{
System.out.println("二");
}
case 3 ->{
System.out.println("三");
}
default -> System.out.println("没有这种选项");
}
语法与C语言无异。
//for循环
for(初始化语句;条件判断语句;条件控制语句){
循环体;
}
//while循环
初始化语句;
while(条件判断语句){
循环体语句;
条件控制语句;
}
//do...while循环
初始化语句;
do{
循环体语句;
条件控制语句;
}while(条件判断语句);
for(;;){
System.out.println("我爱学习");
}
while(true){
System.out.println("沉迷学习");
}
do{
System.out.println("根本停不下来");
}while(true);
以for循环为例
//1.跳过某一次循环
for(int i = 0;;i++){
if(i%2==0){
System.out.println("今天不学了,明天再说");
//结束本次循环,继续下次循环
continue;
}
System.out.println("今天是我学Java的第" + i + "天,Java真好玩");
}
//2.结束整个循环
for(int i = 0;;i++){
if(i==81){
System.out.println("小小Java,轻松拿下!");
//结束整个循环
break;
}
}
循环综合练习,猜数字小游戏:
import java.util.Random;
import java.util.Scanner;
public class LoopTest{
public static void main(String[] args){
//生成一个1~100之间的随机数字
Random r = new Random();
int number = r.nextInt(bound: 100) + 1;
//猜这个数字是多少
Scanner sc = new Scanner(System.in);
while(ture){
Scanner.out.println("请输入你猜的数字");
int guessNumber = sc.nextInt();
//猜的结果
if(guessNumber > number){
System.out.println("大了");
}else if(guessNumber < number){
System.out.println("小了");
}else{
System.out.println("猜中了");
break;
}
}
}
}
数组:是一种容器,可以用来存储同种数据类型的多个值。
注意:数组容器在存储数据时,需要结合隐式转换考虑。例如,int类型的数组容器可以存储int、short、byte类型的数据,而不能存储其他类型的数据;double类型的数组容器可以存储double、float、long、int、short、byte类型的数据。
和C语言不同,数组的定义有两种格式:
格式一:数据类型[] 数组名
例:int[] array
格式二:数据类型 数组名[]
例:int array[]
数组静态初始化的格式:数据类型[] 数组名 = new 数据类型[]{元素1,元素2,...};
例:int[] array = new int[]{11,22,33};
简化格式:数据类型[] 数组名 = {元素1,元素2,...};
例:String[] array1 = {"zhangsan","lisi","wangwu"};
定义一个double类型的数组:
double[] arr6 = {1.93,1.75,1.73,1.81};
执行以下语句:
System.out.println(arr6);
发现结果为 [D@776ec8df ,而不是数组元素值
[ 表示当前是一个数组
D 表示数组元素是double类型
@ 表示一个间隔符号(固定格式)
776ec8df 是数组真正的地址值
那访问数组元素的方式是什么呢?
与C语言一样,格式为: 数组名[索引];
同样的,把数据存储到数组中的格式为:数组名[索引]=具体数据/变量;
借助循环。
数组的长度(元素个数)可以用数组名.length的方式获得。
IDEA提供了一种自动快速生成数组遍历的方式:数组名.fori,然后回车。以arr.fori为例,生成以下结果:
for(int i = 0; i < arr.length; i++){
}
动态初始化:初始化时只指定数组长度,由系统为数组分配初始值。
格式:数据类型[] 数组名 = new 数据类型[数组长度];
例:int[] arr = new int[3];
整数类型 | 0 |
小数类型 | 0.0 |
字符类型 | '/u0000'空格 |
布尔类型 | false |
引用数据类型 | null |
Java的内存分配:
栈、堆、方法区、本地方法栈、寄存器。
栈 | 方法运行时使用的内存,比如main方法运行,进入方法栈中执行 |
堆 | 存储对象或者数组,new来创建的,都存储在堆内存 |
方法区 | 存储可以运行的class文件 |
本地方法栈 | JVM在使用操作系统功能的时候使用,与程序员无关 |
寄存器 | 给CPU使用,与程序员无关 |
注意:从JDK8开始,取消方法区,新增元空间1区,把原来方法区的多种功能进行拆分,有的功能放到了堆中,有的功能放到了元空间中。
访问不存在的索引,导致越界错误。
public class ArrTest{
public static void main(String[] args){
int[] arr = {33,5,22,44,55};
int max = arr[0];
for(int i = 0; i < arr.length; i++){
if(arr[i] > max){
max = arr[i];
}
}
System.out.println(max);
}
}
public class ArrTest{
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
for(int i = 0, j = arr.length - 1; i < j; i++,j--){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i] + " ");
}
}
}
import java.util.Random;
public class ArrTest {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
Random r = new Random();
for (int i = 0; i < arr.length; i++) {
int randomIndex = r.nextInt(arr.length);
int temp = arr[i];
arr[i] = arr[randomIndex];
arr[randomIndex] = temp;
}
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i] + " ");
}
}
}
方法是程序中最小的执行单元,可以理解为C语言中的函数。将重复的代码、具有独立功能的代码抽取到方法中可以提高代码的复用性和可维护性。
其实Java中的方法与C语言中的函数区别仅在于在定义时前面加了public static,在此不过多叙述。
1.最简单的方法定义和调用
//定义格式
public static void 方法名 (){
方法体;
}
//调用格式
方法名();
2.带参数的方法定义和调用
//定义格式
public static void 方法名 (参数1,参数2,...){...}
//调用格式
方法名(参数1,参数2,...);
3.带返回值方法的定义和调用
//定义格式
public static 返回值类型 方法名 (参数){
方法体;
return 返回值;
}
//调用格式
//直接调用
方法名(实参);
//赋值调用
整数类型 变量名 = 方法名 (实参);
//输出调用
System.out.println(方法名(实参));
注意事项:
1.方法不调用就不执行;
2.方法与方法之间是平级关系,不能互相嵌套定义;
3.方法的编写顺序和执行顺序无关;
4.方法的返回值类型为void,表示方法没有返回值,可以省略return语句不写。如果要编写return,后面不能跟具体的数据;
5.return语句下面不能编写代码,因为永远执行不到。
在同一个类中,定义了多个同名的方法,这些同名的方法具有同种的功能(比如都是对几个数的求和操作),每个方法具有不同的参数(类型、个数或顺序),这些同名的方法,就构成了重载关系。与返回值无关。
示例:
public class Test4{
public static void main(String[] args){
//调用方法
}
public static void compare(byte b1, byte b2){
System.out.println(b1==b2);
}
public static void compare(short s1, short s2){
System.out.println(s1==s2);
}
public static void compare(int i1, int i2){
System.out.println(i1==i2);
}
public static void compare(long l1, long l2){
System.out.println(l1==l2);
}
}
方法调用时压栈,返回时出栈。
由于博主时间有限(主要是太懒hhh),细节暂时不讲,以后会补充。
Java语法并不难,代码敲多了自然就熟练了。可以借助下面两个例子来练习:
import java.util.Scanner;
public class Tickets {
public static void main(String[] args) {
/*机票价格按照淡季旺季、头等舱和经济舱收费
输入机票原价、月份和头等舱或经济舱
按照如下规则计算机票价格:旺季(5-10月)头等舱9折,经济舱8.5折,淡季(11月到来年4月)头等舱7折,经济舱6.5折
*/
//1.键盘录入机票原价、月份、头等舱或经济舱
Scanner sc = new Scanner(System.in);
System.out.println("请输入机票的原价");
int ticket = sc.nextInt();
System.out.println("请输入当前的月份");
int month = sc.nextInt();
System.out.println("请输入购买的舱位:头等舱(0)/经济舱(1)");
int seat = sc.nextInt();
//2.先判断月份是淡季还是旺季
if (month >= 5 && month <= 10){ //旺季
//3.继续判断当前机票是经济舱还是头等舱
ticket = getPrice(ticket, seat, 0.9, 0.85);
} else if ((month >= 1 && month <= 4)||(month >= 11 && month <= 12)) { //淡季
ticket = getPrice(ticket, seat, 0.7, 0.65);
}else {
System.out.println("键盘录入的月份不合法");
}
System.out.println(ticket);
}
private static int getPrice(int ticket, int seat,double v1, double v2 ) {
if (seat == 0){
//头等舱
ticket = (int)(ticket * v1);
} else if (seat == 1) {
//经济舱
ticket = (int)(ticket * v2);
}else{
System.out.println("没有这个舱位");
}
return ticket;
}
}
public class PrimeNumber {
public static void main(String[] args) {
//判断101~200之间有多少个质数,并输出所有质数
int count = 0;
for (int i = 101;i <= 200; i++){
boolean flag = true;
for (int j = 2; j < i; j++){ //此处的上限为i,也可以用i的平方根来简化计算,使用方法为Math.sqrt(i),注意要导入Math类
if(i % j == 0){
flag = false;
//不是质数,跳出内层循环
break;
}
}
if(flag){
System.out.println("当前数字" + i + "是质数");
count++;
}
}
System.out.println("一共有" + count + "个质数");
}
}
专属于IDEA玩家的咒语:
1.psvm
2.sout
3.在调用未定义的方法处按Ctrl+Alt+M
试试看能召唤出什么?