Java语法
安装环境:IDEA
- 下载
- 官方下载地址:https://www.jetbrains.com/zh-cn/idea/download/#section=windows
- 下载专业版(Ultimate版)
- 安装
- 下面的页面可以全选,其他默认(路径可以自己改)
- 激活
- 试用30天:免费
- 学校教育邮箱(@后有edu的邮箱)激活(实测哈工大的不行,挺多中国高校的邮箱都不行)
- 邮箱用不了就看@zst_2001的 学信网激活教程
- 去官网买个人用激活码,1000rmb左右,可以买当前版本的永久权限,更新需要重新购买
- 去淘宝或拼多多买激活码
- 其他(破解工具之类的)
- 新建项目
- 选项:
- Language:Java
- Build system:Maven
- JDK:1.8 (Amazon Corretto version 1.8.0_332)选JDK的地方有一个Download JDK,可以下载。JDK 1.8也叫JDK 8,是长期支持的版本(LTS),而且是目前支持到期年份最远的
- Add sample code:勾选上
- 切换成中文界面:左上角点击File -> settings -> plugins -> 搜索chinese -> 选择Chinese (simplified) Language Pack -> 下载后重启IDEA即可
- 切成白色界面:设置 -> 外观与行为 -> 外观 -> 主题选项里换成 IntelliJ Light
基本概念
- JDK、JRE、JVM的关系:
- JDK:Java Development Kit,Java开发工具包
- JRE: Java Runtime Environment,Java运行环境
- JVM:Java Virtual Machine,Java虚拟机
- JDK包含JRE,JRE包含JVM
- JDK版本选择
- 目前JDK1.8(也叫JDK8,注意不是JDK18)用得最多
- Java代码的编译运行流程
- 将Java源码编译成Java字节码。
- 使用JVM将Java字节码转化成机器码(不同平台或操作系统的指令集不同,同一个程序的机器码也不同)。
- JVM作用:跨平台、内存管理、安全、垃圾回收等等。
- JSE、JEE、JME的区别
- JSE: Java Standard Edition,标准版
- JEE:Java Enterprise Edition,企业版
- JME: Java Mirco Edition,移动版
- Spring是JEE的轻量级替代品
- SpringBoot是Spring + 自动化配置
Java语法
Java
和 C#
等等一些语言,都是纯面向对象的语言,所有变量和函数都要定义在类里面,程序入口是名称为 Main
的类里的 main
函数
变量、运算符、输入与输出
内置数据类型
类型 | 字节数 | 举例 |
---|---|---|
byte | 1 | 123 |
short | 2 | 12345 |
int | 4 | 123456789 |
long | 8 | 1234567891011L(常量类型的long) |
float | 4 | 1.2F |
double | 8 | 1.2, 1.2D |
boolean | 1 | true, false |
char | 2 | ‘A’ |
常量
使用 final
修饰:
final int N = 110;
类型转化
- 显示转化:
int x = (int)'A';
- 隐式转化:
double x = 12, y = 4 * 3.3;
- 只能低精度转到高精度。高精度转低精度要显示转化
表达式
与 C++
、Python3
类似:
int a = 1, b = 2, c = 3;
int x = (a + b) * c;
x ++;
int y = a > b ? a : b;
输入
方式1,效率较低,输入规模较小时使用。
Scanner sc = new Scanner(System.in);
String str = sc.next(); // 读入下一个字符串(到空格或回车停止)
int x = sc.nextInt(); // 读入下一个整数
float y = sc.nextFloat(); // 读入下一个单精度浮点数
double z = sc.nextDouble(); // 读入下一个双精度浮点数
String line = sc.nextLine(); // 读入下一行
方式2,效率较高,输入规模较大时使用。注意需要抛异常。
package com.yxc; // 这句是项目自定义的groupID,一般创建项目后的Main文件里会自带,不能照搬
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String str = br.readLine();
System.out.println(str);
}
}
// String有按字符分隔字符串和字符串转整数的方法,可以用来处理一行数的输入
输出
方式1,效率较低,输出规模较小时使用。(直接输入sout看提示符,按回车就能打出 System.out.println()
)
System.out.println(123); // 输出整数 + 换行
System.out.println("Hello World"); // 输出字符串 + 换行
System.out.print(123); // 输出整数
System.out.print("yxc\n"); // 输出字符串
System.out.printf("%04d %.2f\n", 4, 123.456D); // 格式化输出,float与double都用%f输出
方式2,效率较高,输出规模较大时使用。注意需要抛异常。
package com.yxc; // 自定义GroupID
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
public class Main {
public static void main(String[] args) throws Exception {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("Hello World\n");
// bw.write中输出 `int` 是将值输出为两字节格式且用unicode编码,因此直接输出int会出现乱码
// 可以用String.valueOf()将int转化为String
bw.flush(); // 需要手动刷新缓冲区才能把write的内容刷新到输出流中
}
}
判断语句
if-else
语句
与 C++
、Python
中类似。
例如:
package com.yxc; // 自定义GroupID
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int year = sc.nextInt();
if (year % 100 == 0) {
if (year % 400 == 0)
System.out.printf("%d是闰年\n", year);
else
System.out.printf("%d不是闰年\n", year);
} else {
if (year % 4 == 0)
System.out.printf("%d是闰年\n", year);
else
System.out.printf("%d不是闰年\n", year);
}
}
}
switch
语句
与 C++
中类似。
package com.yxc; // 自定义GroupID
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int day = sc.nextInt();
String name;
switch (day) {
case 1:
name = "Monday";
break; // 一定要break,不然本条满足后程序会忽略case,把下面的语句全部执行
case 2:
name = "Tuesday";
break;
case 3:
name = "Wednesday";
break;
case 4:
name = "Thursday";
break;
case 5:
name = "Friday";
break;
case 6:
name = "Saturday";
break;
case 7:
name = "Sunday";
break;
default:
name = "not valid";
}
System.out.println(name);
}
}
逻辑运算符与条件表达式
与 C++
、Python
类似。但 Java
中的整数不用充当布尔值,因此不能省略 != ==
例如:
package com.yxc; // 自定义GroupID
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int year = sc.nextInt();
if (year % 100 != 0 && year % 4 == 0 || year % 400 == 0)
System.out.printf("%d是闰年\n", year);
else
System.out.printf("%d不是闰年\n", year);
}
}
循环语句
while
循环
与 C++
、Python
类似,例如:
int i = 0;
while (i < 5) {
System.out.println(i);
i ++ ;
}
do while循环
与 C++
、Python
类似,例如:
int i = 0;
do {
System.out.println(i);
i ++ ;
} while (i < 5);
do while
语句与 while
语句非常相似。唯一的区别是,do while
语句限制性循环体后检查条件。不管条件的值如何,我们都要至少执行一次循环。
for
循环
与 C++
、Python
类似,例如:
for (int i = 0; i < 5; i ++ ) { // 普通循环
System.out.println(i);
}
int[] a = {0, 1, 2, 3, 4};
for (int x: a) { // forEach循环
System.out.println(x);
}
数组
Java
中的数组与 C++
中的数组类似。
初始化
与 C++
类似。
初始化定长数组,长度可以是变量,可以在初始化时赋值。
int[] a = new int[5]; // 初始化长度为5的int数组,初始值为0
int n = 10;
float[] b = new float[n]; // 初始化长度为n的float数组,初始值为0.0F
char[] c = {'a', 'b', 'c'}; // 初始化长度为3的char数组,初始值为:'a', 'b', 'c'
// Java里字符串和字符数组是独立的类型,字符数组不能用字符串赋值,不能当作字符串使用
char[] d = c; // d与c地址相同,更改c中的元素,d中的元素也会改变
/*
在C语言和C++中,数组的名称也是一个变量,叫做指针,
存储的值是数组第一个元素(或数组头)在内存中存储的地址。
而在Java中取消了指针这种数据类型,把C中的指针包装成了引用,
也就是C++中的&。
引用可以理解为取别名,如上面的d和c,除了名字不一样外,这两个变量
指向的数据都是内存中同一个地址的数据,因此更改c中的元素,d中的
元素也会改变
Java里所有对象的赋值都是传引用
*/
//可以查看c和d的内存地址的哈希值
System.out.println(c.hashCode());
System.out.println(d.hashCode());
数组元素的读取与写入
与 C++
类似。
int[] a = new int[5];
for (int i = 0; i < 5; i++) {
a[i] = i;
}
for (int i = 0; i < 5; i ++ ) {
System.out.println(a[i] * a[i]);
}
多维数组
与 C++
类似。
int[][] a = new int[2][3];
a[1][2] = 1;
int[][] b = {
{1, 2, 3},
{4, 5, 6},
};
System.out.println(a[1][2]);
System.out.println(b[0][1]);
常用API
- 属性
length
:返回数组长度,注意不加小括号 Arrays.sort()
:数组排序Arrays.fill(int[] a, int val)
:填充数组Arrays.toString()
:将数组转化为字符串(只能转化一维)Arrays.deepToString()
:将多维数组转化为字符串- 数组不可变长
字符串
String类
初始化:
String a = "Hello World";
String b = "My name is";
String x = b; // 存储到了相同地址
String c = b + "yxc"; // String可以通过加号拼接
String d = "My age is " + 18; // int会被隐式转化成字符串"18"
String str = String.format("My age is %d", 18); // 格式化字符串,类似于C++中的sprintf
String money_str = "123.45";
double money = Double.parseDouble(money_str); // String转double
// String转int就是Integer.parseInt();
// Double是浮点数类,Integer是整数类
只读变量,不能修改,例如:
String a = "Hello ";
a += "World"; // 会构造一个新的字符串,表现出来就是a存储的内存地址会变
访问 String
中的字符:
String str = "Hello World";
for (int i = 0; i < str.length(); i ++ ) {
System.out.print(str.charAt(i)); // 只能读取,不能写入
}
常用API:
length()
:返回长度,要加小括号split(String regex)
:分割字符串indexOf(char c)、indexOf(String str)
:查找,找不到返回 $-1$equals()
:判断两个字符串是否相等,注意不能直接用==
,否则比较的是两个变量存储的内存地址值- 调用
str.equals()
时要保证str
不为空 compareTo()
:判断两个字符串的字典序大小,负数表示小于,$0$ 表示相等,正数表示大于startsWith()
:判断是否以某个前缀开头endsWith()
:判断是否以某个后缀结尾trim()
:去掉首尾的空白字符toLowerCase()
:全部用小写字符toUpperCase()
:全部用大写字符replace(char oldChar, char newChar)
:替换字符replace(String oldRegex, String newRegex)
:替换字符串substring(int beginIndex, int endIndex)
:返回[beginIndex, endIndex)
中的子串hashcode()
:返回字符串的哈希值,不是物理地址的哈希值
StringBuilder、StringBuffer
String
不能被修改,如果打算修改字符串,可以使用StringBuilder
和 StringBuffer
。
StringBuffer
线程安全,速度较慢;StringBuilder
线程不安全,速度较快。
StringBuilder sb = new StringBuilder("Hello "); // 初始化
sb.append("World"); // 拼接字符串
System.out.println(sb);
for (int i = 0; i < sb.length(); i ++ ) {
sb.setCharAt(i, (char)(sb.charAt(i) + 1)); // 读取和写入字符
}
System.out.println(sb);
常用API:
reverse()
:翻转字符串hashcode()
:输出物理地址的哈希值
函数
Java
的所有变量和函数都要定义在类中。
函数或变量前加 static
表示静态对象,类似于全局变量。
静态对象属于 class
,而不属于 class
的具体实例,因此调用时通过 class
来调用,最好不要通过实例来调用。非静态函数或变量只能通过实例来调用
静态函数中只能调用静态函数和静态变量。
示例:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
System.out.println(max(3, 4));
int[][] a = new int[3][4];
fill(a, 3);
System.out.println(Arrays.deepToString(a));
int[][] b = getArray2d(2, 3, 5);
System.out.println(Arrays.deepToString(b));
}
private static int max(int a, int b) {
if (a > b) return a;
return b;
}
private static void fill(int[][] a, int val) {
for (int i = 0; i < a.length; i ++ )
for (int j = 0; j < a[i].length; j ++ )
a[i][j] = val;
}
private static int[][] getArray2d(int row, int col, int val) {
int[][] a = new int[row][col];
for (int i = 0; i < row; i ++ )
for (int j = 0; j < col; j ++ )
a[i][j] = val;
return a;
}
}
类与接口
类
class
与 C++
、 Python
类似。
源文件声明规则
- 一个源文件中只能有一个
public
类。 - 一个源文件可以有多个非
public
类。 - 源文件的名称应该和
public
类的类名保持一致。 - 每个源文件中,先写
package
语句,再写import
语句,最后定义类。
类的定义
public
: 所有对象均可以访问private
: 只有自己可以访问(子类也不能访问)
class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
// x, y重名,编译器采用就近原则,优先考虑局部变量
// 因此类里的x, y要用this引用
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public String toString() {
return String.format("(%d, %d)", x, y);
}
}
类的继承
每个类只能继承一个类。
继承可以把一些类的公共功能写到一个公共的类中,减少重复编写
class ColorPoint extends Point {
private String color;
public ColorPoint(int x, int y, String color) {
super(x, y);
this.color = color;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return String.format("(%d, %d, %s)", super.getX(), super.getY(), this.color);
}
// toString函数,子类的会覆盖掉父类的同名函数
}
类的多态
public class Main {
public static void main(String[] args) {
Point point = new Point(3, 4);
Point colorPoint = new ColorPoint(1, 2, "red");
// ColorPoint继承自Point,因此colorPoint实例既是Point类,也是ColorPoint类
// 但是Point类的colorPoint不能调用Point没有的函数
// 多态,同一个类的实例,调用相同的函数,运行结果不同
System.out.println(point.toString());
System.out.println(colorPoint.toString());
}
}
每个类尽量都单独写在一个 .java
文件里,相同方面的类文件放在一个文件夹里。调用其他的类可以通过 import
来调用
给类编写比较函数
class Data implements Comparable<Data> {
int x;
double y;
String z;
public Data(int x, double y, String z) {
this.x = x;
this.y = y;
this.z = z;
}
public int compareTo(Data t) {
// 按x从小到大排序
return x - t.x; // 返回负数相当于true
}
public String toString() {
return String.format("%d %.2f %s", x, y, z);
}
}
接口
interface
用来定义一种实例类型,内容包含这种实例类型应该包含的函数
类在编写时可以实现接口,这样这个类的实例的类型就可以是接口定义的实例类型。类在实现接口时必须把接口中的所有函数都实现
接口也可以继承其他接口,可以继承多个接口。一个类可以实现多个接口。
接口的定义
interface Role {
public void greet();
public void move();
public int getSpeed();
}
接口的继承
每个接口可以继承多个接口
interface Hero extends Role {
public void attack();
}
接口的实现
每个类可以实现多个接口
Alt + Enter
直接实现所有接口的方法
class Zeus implements Hero {
private final String name = "Zeus";
public void attack() {
System.out.println(name + ": attack!");
}
public void greet() {
System.out.println(name + ": Hi!");
}
public void move() {
System.out.println(name + ": Move!");
}
public int getSpeed() {
return 10;
}
}
接口的多态
和类的多态是一样的。一个类实现了哪些接口,就可以把类的对象放到哪些接口的引用上
class Athena implements Hero {
private final String name = "Athena";
public void attack() {
System.out.println(name + ": attack!");
}
public void greet() {
System.out.println(name + ": Hi!");
}
public void move() {
System.out.println(name + ": Move!");
}
public int getSpeed() {
return 10;
}
}
public class Main {
public static void main(String[] args) {
Hero[] heros = {new Zeus(), new Athena()};
for (Hero hero: heros) {
hero.greet();
}
}
}
泛型
类似于 C++
的 template
, Java
的类和接口也可以定义泛型,即同一套函数可以作用于不同的对象类型。
泛型只能使用对象类型,不能使用基本变量类型。
常用容器
List
接口: java.util.List<>
。
实现:
java.util.ArrayList<>
:变长数组- 下标访问为 $O(1)$ ,添加删除为 $O(n)$
java.util.LinkedList<>
:双链表- 下标访问为 $O(n)$ ,添加删除为 $O(1)$
函数:
add()
:在末尾添加一个元素clear()
:清空size()
:返回长度isEmpty()
:是否为空get(i)
:获取第i
个元素set(i, val)
:将第i
个元素设置为val
栈
类: java.util.Stack<>
函数:
push()
:压入元素pop()
:弹出栈顶元素,并返回栈顶元素peek()
:返回栈顶元素size()
:返回长度empty()
:栈是否为空clear()
:清空
队列
接口: java.util.Queue<>
实现:
java.util.LinkedList<>
:双链表java.util.PriorityQueue<>
:优先队列- 默认是小根堆,大根堆写法:
new PriorityQueue<>(Collections.reverseOrder())
函数:
add()
:在队尾添加元素remove()
:删除并返回队头isEmpty()
:是否为空size()
:返回长度peek()
:返回队头clear()
:清空
Set
接口: java.util.Set<K>
实现:
java.util.HashSet<K>
:哈希表- 无序,每个操作都是 $O(1)$
java.util.TreeSet<K>
:平衡树- 有序,每个操作都是 $O(\log n)$
函数:
add()
:添加元素contains()
:是否包含某个元素remove()
:删除元素size()
:返回元素数isEmpty()
:是否为空clear()
:清空
java.util.TreeSet
多的函数:
ceiling(key)
:返回大于等于key的最小元素,不存在则返回null
floor(key)
:返回小于等于key的最大元素,不存在则返回null
Map
接口: java.util.Map<K, V>
实现:
java.util.HashMap<K, V>
:哈希表java.util.TreeMap<K, V>
:平衡树
函数:
put(key, value)
:添加关键字和其对应的值get(key)
:返回关键字对应的值containsKey(key)
:是否包含关键字remove(key)
:删除关键字size()
:返回元素数isEmpty()
:是否为空clear()
:清空entrySet()
:获取Map
中的所有对象的集合System.out.println(map.entrySet());
Map.Entry<K, V>
:Map
中的对象类型getKey()
:获取关键字getValue()
:获取值- 遍历Map:
for (Map.Entry<Integer, String> p : map.entrySet()) {
// 执行操作
System.out.println(p.getKey());
System.out.println(p.getValue());
System.out.println(p.getClass());
}
java.util.TreeMap<K, V>
多的函数:
ceilingEntry(key)
:返回大于等于key
的最小元素,不存在则返回null
floorEntry(key)
:返回小于等于key
的最大元素,不存在则返回null
著作权信息
- 原作者:yxc
- 链接:https://www.acwing.com/file_system/file/content/whole/index/content/5914056/
- 来源:AcWing
- 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。