本蒟蒻学习过C/C++的语法,故在学习Java的过程中,会关注于C++与Java的区别。开发前言部分,看了苏星河教程中的操作步骤。而后,主要阅读了《Java核心技术 卷1 基础知识》(第8版),本篇笔记的诞生与这本书有很大的关系。
"程序设计语言的成功,更多地取决于其支撑系统的能力,而不是优美的语法。"
Chapter 1. 开发前言
1.1 命令提示符(CMD)
无图形界面、鼠标与光标。
1.1.1 常用快捷键
作用与功能 | 快捷键 |
---|---|
启动 | win+R |
移动光标 | ←、→ |
调阅历史操作命令 | ↑、↓ |
1.1.2 常用DOS命令
作用与功能 | 命令 | 备注 |
---|---|---|
切换盘符 | X: |
X 为你指定的盘符 |
进入文件夹 | cd 文件夹1 |
|
进入多级文件夹 | cd 文件夹1\文件夹2\文件夹3 |
将文件夹部分名打出后,按Tab 键,会补全、切换 |
返回上一级 | cd .. |
|
直接回到根路径 | cd \ |
|
查看当前文件夹内容 | dir |
. 代表当前路径,.. 代表上一级路径 |
清屏 | cls |
|
退出 | exit |
1.2 Java简介
1.2.1 Java三个体系
- JavaSE,java平台标准版
- JavaEE,java平台企业版
- JavaME,java平台微型版
1.2.2 Java“白皮书”的关键术语
-
面向对象
重点放在数据(即对象)和对象的接口上。
与C++主要不同在于多继承,在Java中,取而代之为简单的接口概念及Java的元类模型。
-
简单性:
没有头文件、指针运算、结构、联合、操作符重载、虚基类等等
-
可移植性
与C/C++不同,Java规范中没有“依赖具体实现”的地方。比如,Java的
int
永远为32位整数,不像C/C++中,int
可能为16位、32位。数据类型具有固定的大小。二进制数据以固定格式进行的存储和传输,消除了字节顺序的困扰。
面向对象
-
解释型
Java解释器,可在任何移植了解释器的机器上执行Java字节码。
-
网络技能
Java有一个扩展的例程库,用于处理像HTP和FTP这类的TCP/P协议。Java应用程序能够通过URL打开和访问网络上的对象,其便捷程度就妤像访问本地文件一样。
高性能
-
健壮性
Java永远不会存取一个“坏的”指针,造成内存分配的错误,也不必防范内存泄漏。
与C++最大不同,在于Java采用的指针模型可消除重写内存和损坏数据的可能性。
多线程
-
安全性
Java是在应用程序运行时加以控制并制止恶意性破坏。使用Java可以构建防病毒、防篡改的系统。
-
动态性
与C、C++相比,Java更具动态性,能够适应不断发展的环境。
库中可*地添加新方法和实例变量,而对客户端没有任何引信。
-
体系结构中立
Java编译器通过生成与特定的计算机休系结构无关的字节码指令来实现这一特性。字节码不仅可以很容易地在任何机器上解释执行,而且还可以迅速地翻译成本地机器的代码。(当然,解释字节码,会比全速地运行机器指令慢一些)
1.3 JShell 脚本工具
JShell
脚本工具是JDK9的新特性。当你编写的代码十分少,又不愿意编写类、main方法,又不想编译和运行时,JShell工具这个轻量级的便利小工具十分适合你 使用。在命令行输入jshell
即可启动:
此后,每输入一条代码,即可直接运行并打印结果:
退出Jshell,需输入指令/exit
1.4 API
API(Application Programming Interface),应用程序编程接口。Java API是程序员的一本“字典”,是JDK中提供给我们使用的类的说明文档。这些类将底层的代码实现封装起来,我们无需关心这些类如何实现,只需要学习这些类如何使用即可。
JDK API 1.6.0 有sun公司自带的简体中文
Chapter 2. Java语言开发环境搭建
2.1 Java虚拟机——JVM
JVM(Java Virtual Machine):java虚拟机。它是运行所有Java程序的假想计算机,是Java程序的运行环境。我们编写的Java代码,均运行在JVM之上。
-
跨平台:我们用Java编写的软件可运行在任何操作系统上,该特性称为Java语言的跨平台特性,由JVM实现。我们编写的程序运行在JVM上,而JVM运行在操作系统上。
它本身不具备跨平台功能,每个操作系统下都有不同版本的虚拟机。
2.2 JRE 和 JDK
JRE(Java Runtime Environment):Java程序的运行时环境。(运行一个已有的Java程序需安装)
JDK(Java Development Kit):Java程序开发工具包。(开发一个全新的Java程序需安装)
2.3 JDK的下载与安装
前往官网,往下拉,选择JAVA那一栏的Java (JDK) for Developers进行下载。
安装路径建议不要含有中文名及空格。(亲测,如果路径存在空格,CMD难以识别)
安装完后,到CMD窗口输入你刚刚安装路径\bin\java.exe
,打开java.exe查看是否安装成功。
2.4 环境变量的配置
通过设置path,告诉cmd从哪里去寻找你新安装的java
-
系统变量中新建
-
编辑Path
2.5 使用命令行工具编译与运行第一个Java程序
创建文件HelloWorld.java
(注意,文件名需要与类名一致),然后编写如下代码:
public class HelloWorld{ //public class后面定义一个类的名称,类为Java当中所有源代码的基本组织单位
public static void main(String[] args){ //主方法入口
System.out.println(100L); //输出long类型的整数100
}
} //不同于C++,此处不需要再加分号作结尾
执行命令解析
-
javac
命令用于将java源文件编译为class字节码文件,后面紧跟java文件的文件名,如:javac HelloWorld.java
;运行该命令后,若成功编译,会出现HelloWorld.class
文件 -
java
命令运行文件,紧跟于java文件中的类名,如java HelloWorld
(注意,java
命令后面不加.class
)
javac
程序是一个Java编译器,将文件HelloWorld.java
编译成HelloWorld.class
,并发送到Java虚拟机,Java虚拟机执行编译器存放在class文件中的字节码。
2.6 开发工具Intellij IDEA
IDEA专门针对java的集成开发环境(IDE),需要有JRE运行环境并配置好环境变量。它能够自动编译,检查错误。安装教程戳此
- Java项目结构建议如此:
java.lang
是类所在的包路径。
新建项目-新建
module
-在src
中新建包Package
-右键该包- New -Java Class
- 实际存放:
-
在编写代码时,IDEA有以下便捷代码:
- 直接输入
spvm
+回车键 便能生成main方法; - 直接输入
sout
+回车键 便能生成System.out.println();
- 直接输入
IDEA有以下常用快捷键:
快捷键 | 功能 |
---|---|
Alt + Enter | 导入包,自动修正代码 |
Ctrl + alt + L | 格式化代码 |
Ctrl + Y | 删除光标所在行 |
Ctrl + D | 复制光标所在行的内容,插入光标位置下面 |
Ctrl + R | 在当前文件内容中替换 |
Ctrl + / | 单行注释,再按取消注释 |
Ctrl + Shift + / | 选中代码注释,多行注释,再按则取消注释 |
Ctrl + X | 删除行 |
- 新建module:
-
移除module:(实际上并没有将该module删除掉)
-
导入module
文件(file) - 项目结构(Project Structure),
接下来选中自己所需的module,一路点击next即可。
Chapter 3. 基本语法
3.1 关键字、标识符、修饰符
3.1.1 关键字
被Java语言赋予特殊含义,用做专门用途的字符串,不能用于常量、变量和任何标识符的名称。关键字特点是所有字母均为小写。
如abstract
、final
、public
、class
等
3.1.2 标识符
类名、变量名以及方法名都被称为标识符。
命名规则:(必须要遵守。否则,编译不通过) 由26个英文字母大小写,0
-9
,_
或$
组成 ,数字不可以开头。不可以使用关键字和保留字,但能包含关键字和保留字。Java中严格区分大小写,长度无限制。标识符不能包含空格。
命名规范:(可以不遵守,不影响编译和运行。但是要求大家遵守)
包名:多单词组成时所有字母都小写:
xxxyyyzzz
类名、接口名:多单词组成时,所有单词的首字母大写:
XxxYyyZzz
。变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:
xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接:
XXX_YYY_ZZZ
3.1.3 修饰符
Java使用修饰符来修饰类中方法和属性。与C++,有较大的差异,日后再作总结。
有两类:
- 访问控制修饰符:
default
,public
,protected
,private
; - 非访问控制修饰符:
final
,abstract
,static
,synchronized
;
final关键字
-
当
final
关键字修饰一个类的时候,当前这个类不能有任何的子类,且其中所有的成员方法都无法进行覆写。public final class 类名称{
//...
} -
当
final
关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆写。public class Fu{
public final void method(){
//...
}
}对于类、方法而言,
abstract
关键字和final
关键字不能同时使用,因为矛盾。 -
当
final
关键字用来修饰局部变量,这个变量不能再进行更改对于基本类型而言,变量当中的数值不可变;对于引用类型而言,变量当中的地址值不可变。
注意,对于一个被
final
修饰的类,仍然可以通过它的Setter
去修改成员变量final int num2 = 233;
final int num3;
num3 = 666; //只要保证有唯一一次赋值即可
final Student stu2 = new Student("Luffy");
stu2.setName("Nami"); //正确写法! -
当
final
关键字用来修饰成员变量,这个变量不能再进行更改。成员变量本身具有默认值,但若使用
final
修饰该成员变量时,必须手动进行赋值(直接在声明处赋值 或者 构造方法赋值)必须保证类当中所有重载的构造方法,都最终会对
final
修饰的成员变量进行赋值。public class Person{
private final String name = "Luffy";
private final String name2;
public Person(){
name2 = "Nami";
}
}
权限修饰符
public |
protected |
(default) |
private |
|
---|---|---|---|---|
同一个类(“我自己”) | ️ | ️ | ️ | ️ |
同一个包的不同.java 文件(“我邻居”) |
️ | ️ | ️ | |
不同包的子类(“我儿子”) | ️ | ️ | ||
不同包的非子类(“陌生人”) | ️ |
在定义一个类的时候,允许使用的权限修饰符:
- 外部类:
public
、(default)
- 成员内部类:
public
、protected
、(default)
、private
- 局部内部类:什么都不能修饰
在Java中,不区分变量的声明与定义。
3.2.1 按数据类型分类
较于C/C++的注意事项:
-
对于
float
和long
类型,字母后缀F
和L
不能丢!long num = 3000000000L; //记得用"L"后缀声明long型常量
float num_2 = 2.5F;
double num_3 = 1.2;
System.out.println(num); -
byte
、short
、char
三种类型在运算时,会首先提升为int
类型再计算,如果运算结果要存进该三种类型时需要强制转换。short num_1 = 50, num_2 = 40;
short rev = (short)(num_1 + num_2); boolean
类型不能发生数据类型转换!在C++中,整数0
相当于布尔型true
,在Java中则不行。long
类型为64位,不同于C/C++的32位;char
类型为16
位,不同于C/C++的8位。Java没有任何无符号类型(unsigned type)
引用数据类型,在传递至方法中时,实际上传递的是该数据的地址值。
3.2.2 按声明的位置分类
3.3 常量
与C++中的关键字const
类似,在Java中,利用关键字final
声明常量。如:
final double CM_PER_INCH = 2.54;
关键字final
表示这变量只能被赋值一次,而后便不能再更改。若希望某个常量在一个类中的多个方法使用,通常将该常量称为类常量。使用类常量的示例:
public class Constant2{
public static void main(String[] args){
System.out.println(CM_PER_INCH);
}
public static final double CM_PER_INCH = 2.54;
//常量被声明为public,那么其他类的方法也可使用该常量。
}
编译器的常量优化
在为变量进行赋值时,若右侧的表达式当中全为常量,无任何变量,那么编译器javac将会直接将若干个常量表达式计算得到结果。
short a = 5, b = 8;
short result_2 = a + b; //编译器会报错,因为short + short --> int + int --> int(见上面写的变量注意事项)
short result = 5 + 8; //编译之后,得到的.class字节码文件当中相当于:short result = 13;
3.4 运算符
-
>>>
运算符将用0填充高位(没有<<<
);>>
运算符用符号位填充高位。在C/C++中,
>>
运算符实际上只是为非负数定义的。 逻辑运算符得到的结果均为
boolean
类型。-
instanceof
运算符用于操作对象实例,检查该对象是否为一个特定类型(类类型或接口类型)。该运算符左侧的变量所指的对象,是操作符右侧类或接口的一个对象,结果为真。
在对象多态当中,需要用到。
String name = "J_StrawHat";
boolean flag = name instanceof String; //返回真。public static void method(Animal animal){
if(animal instanceof Dog){
Dog dog = (Dog) animal;
dog.watchHouse();
}
}
3.5 控制流程
3.5.1 块作用域
块(即复合语句)指由一对花括号括起来的若干条简单的Java语句,它确定了变量的作用域。
在C++中,可以嵌套的块中重定义一个变量,在内层定义的变量会覆盖在外层定义的变量。但是,在Java中,不能在嵌套的两个块中声明同名的变量。如下:
public static void main(String[] args){
int n;
...
{
int n; //错误!!!!
}
}
3.5.2 条件语句(略)
3.5.3 循环与确定循环(略)
3.5.4 多重选择:switch语句
注意事项:
- 多个
case
后面的数值不能重复。 -
switch
后面有且仅可以有以下数据类型:- 基本数据类型:byte/short/char/int
- 引用数据类型:String字符串/enum枚举
3.5.5 中断控制流程语句
在Java中除了与C++相同的break
语句,还增加了一条带标签的break
,用于跳出多重嵌套的循环语句,希望跳到嵌套的所有循环语句之外。举例使用:
Scanner in = new Scanner(System.in);
int n;
read_data: //标签必须放到希望跳出的最外层循环之前,且紧跟一个冒号。
while( ... ){
...
for( ... ){
System.out.print(...);
n = in.nextInt();
if(n < 0){
break read_data; //跳出read_data的循环
}
}
}
还有continue
语句,它将中断正常的控制流程,将控制转移到最内层循环的首部。当然也有一种带标签的continue
语句,将跳到与标签匹配的循环首部。
3.6 输入与输出
暂时先咕咕咕(在书66页)
3.6.2 printf、print、println的区别
println
:换行输出,但不能用于格式转换。
System.out.println("Hello, Java");
System.out.println("Hello, Java");
运行结果为:
print
:一般输出,但不能用于格式转换、换行输出。
System.out.print("Hello, Java");
System.out.print("Hello, Java");
运行结果为:
printf
:格式化输出,用法与C的printf
基本一致。
String a = "PI"; double b = Math.acos(-1);
System.out.printf("%s:%.2f\n", a, b);
运行结果为:
3.7 方法
3.8.1 方法概述
-
方法:将某个功能抽取出来,将代码单独地定义在一个大括号内,形成一个单独的功能。
优点:实现代码的复用性,解决代码冗余的现象。
在其它语言中方法指过程和函数。一个返回非void类型返回值的方法称为函数;一个返回void类型返回值的方法叫做过程。
3.8.2 方法的定义与调用
方法定义格式有:
修饰符 返回值类型 方法名(参数列表){
...
方法体
...
return 返回值;
}
修饰符:可选的,定义了该方法的访问类型,告诉编译器如何调用该方法。目前固定写法为
public static
方法的调用与C++的函数调用基本相同。
3.8.3 重载
如果多个方法,有相同的名字、不同的参数,便产生了重载(overloading)。编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的 参数类型 与特定方法调用所使用的 值类型 进行匹配来挑选出相应的方法。若编译器找不到匹配的参数,或者找出多个可能的匹配,就会产生编译时错误(该过程称为重载解析(overloading resolution))。
要完整描述一个方法,需要指出方法名以及参数的类型,还叫做方法的签名(signature)。注意,其返回类型不是方法签名的一部分,也就说,不能有两个名字相同、参数类型也相同却返回不同类型值的方法。eg:
String
类有4个称为indexOf
的公有方法,他们的签名是:indexOf(int)
、indexOf(int, int)
、indexOf(String)
、indexOf(String, int)
3.8.4 可变参数
JDK 1.5 之后,若定义一个方法需要接收多个类型一致的参数,我们可以如此简化方法的参数列表:修饰符 返回值类型 方法名(参数类型... 形参名){ }
注意...
号,用在参数上,称之为可变参数。(此写法等价于修饰符 返回值类型 方法名(参数类型[] 形参名){ }
,相当于直接将数组中元素作为实际参数进行传递,编程成的.class
文件,将这些元素封装到一个数组中再进行传递,这些动作在编译.class
文件时就自动完成了)
public class ChangeArgs{
public static void main(String[] args){
int[] arr = {1, 2, 3, 4, 5};
System.out.println(getSum(arr)); //可以传入数组
int sum2 = getSum(5, 3, 2, 1); //也可直接传入多个类型一致的参数
System.out.println(sum2);
}
public static int getSum(int...arr){
int sum = 0;
for(int a: arr) sum += a;
return sum;
}
}
注意:若某个方法需要传入多个参数,且参数列表包含可变参数时,可变参数一定要写在参数列表的末尾位置。
3.8 数组
数组为引用数据类型,其长度在程序运行期间不可改变。
直接打印数组名称,得到的是数组对应的,内存地址哈希值。
Java中的[]
运算符被预定义为检查数组边界,而且没有指针运算。
3.8.1 数组初始化
-
动态初始化(指定长度)
动态初始化时,所有元素将会自动拥有一个默认值。对于浮点类型,默认为
0.0
。对于字符类型,默认为'\u0000'
(Unicode,16进制)。若为引用类型,默认为null
。格式:
数据类型[] 数组名称 = new 数据类型[数组长度];
举例使用:
double[] arrayB = new double[300];
String[] arrayC = new String[5]; //二维数组初始化
double[][] balances;
balances = new double[NYEARS][NRATES]; -
静态初始化(指定内容)
格式:
数据类型[] 数组名称 = new 数据类型[] {元素1, 元素2, 元素3, ...}
举例使用:
int[] arrayA;
arrayA = new int[]{5, 4, 3, 2};
String[] arrayB = new String[]{"Hello", "World", "Java"};
int[] arrayC = {10, 20, 30}; //静态初始化的省略格式,但注意,一旦使用了省略格式,就不能像定义arrayA那样拆分成两个步骤。
C++与Java的对比:
int[] a = new int[100]; //Java
等价于
int* a = new int[100]; //C++
3.8.2 数组拷贝
Java中,允许将一个数组变量拷贝给另一个数组变量。
直接使用赋值运算符,则此时两个变量将引用同一个数组。(浅拷贝)
int[] a = b;
a[5] = 12; //now b[5] is also 12.
如果仅将一个数组的所有值(非地址)拷贝到另一新数组中,可以使用Arrays
类的copyOf
方法。(深拷贝)
int[] c = Arrays.copyOf(b, b,length()); //第二个参数,为新数组的长度。可以用于增加数组的大小。
c = Arrays.copyOf(c, 2 * c.length()); //数值型的数组,多余的元素将被赋值为0。若长度小于原始数组的长度,则只拷贝最前面的数据元素。
3.8.3 命令行参数
我们看到,每一个Java应用程序都有一个带String arg[]
参数的main
方法,这个参数表明main
方法将接收一个字符串数组,也就是命令行参数。
public class Message{
public class void main(String[] args){
if(args[0].equals("-h")) System.out.print("Hello,");
else if(args[0].equals("-g")) System.out.print("Goodbye,");
//打印命令行参数
for(int i = 1; i < args.length(); i++)
System.out.print(" " + args[i]);
System.out.println("!");
}
}
输入命令 | 运行结果 | 实际储存 |
---|---|---|
java Message -g cruel world |
Goodbye, cruel world! | args[0]: "-g"; args[1]: "cruel"; args[2]: "world" |
Java应用程序的
main
方法中,程序名并没有存储在args
数组中。比如上面的args[0]
是"-g
",而不是"Message
"或"java
"
3.8.4 不规则数组
Java中,数组的每一行有不同的长度。
举例如下,创建一个二维数组,第i
行长度为i
。代码如下:
int[][] odds = new int[MAXN + 1][];
for(int n = 0; n <= MAXN; n++)
odds[n] = new int[n + 1];
3.8.5 数组工具类Arrays相关API
导包:java.util.Arrays
里面提供大量的静态方法,用于实现数组常见的操作。
将参数数组变成字符串(输出的默认格式为
[元素1, 元素2, ... ]
):public static String toString(数组);
-
按照默认升序对数组元素进行排序:
public static void sort(数组);
若数组存的是自定义类,那么该自定义类需要有
Comparable
或者Comparator
接口的支持。
[附]Java内存划分
Java内存划分为5个部分:
- 栈(Stack):存放的都是方法中的局部变量。方法的运行一定要在栈当中运行。其中,局部变量,为方法的参数,或者方法
{}
内部的变量。 - 堆(Heap):但凡
new
出来的东西,都在堆当中。而堆内存里面的东西均有一个(16位)地址值。 - 方法区(Method Area):存储
.class
相关信息,包含方法的信息。 - 本地方法栈(Native Method Stack):与操作系统相关。
- 寄存器(pc Register):与CPU相关。
public class ArrayOne{
public static void main(String[] args){
int[] array = new int[3]; //动态初始化
System.out.println(array); //地址值
array[1] = 66;
array[2] = 233;
}
}