JAVA SE快速入门
class | 类 |
---|---|
method | 方法 |
field | 域,指一种属性,可以是类变量、方法变量、参数等 |
instance | 实例 |
注意区分变量和实例
Person ming = new Person();
Person ming
是定义Person
类型的变量ming
,而new Person()
是创建Person
实例。
简介
Java代码本质是一种字节码,类似于抽象的CPU指令,针对不同平台编写虚拟机,通过虚拟机加在字节码执行,实现多平台开发。
- JAVA SE:标准版
- JAVA EE:企业版(加入大量API和库)
- JAVA ME:迷你版(针对嵌入式的瘦身版)(SE的标准库在ME上无法使用)
先学习Java SE,掌握Java语言本身、Java核心开发技术以及Java标准库的使用;
如果继续学习Java EE,那么Spring框架、数据库开发、分布式架构就是需要学习的;
特性:简单性、面向对象、可移植性、高性能、分布式(URL)、动态性(反射)、多线程、安全性、健壮性
JDK和JRE:
JDK:依赖包(JAVA Developement Kit)从源码到字节码
JRE:运行环境(Java Runtime Environment)类似虚拟机
JDK包括JRE,另外还提供了编译器和调试器等开发工具
JSR:接口规范要求(Java Specification Request),给JAVA加功能时先穿件一个JSR规范,定义好接口,一个JSR发布时应该同时发布示例代码。
JVM:JAVA虚拟机
运行过程:
源代码被Java编译器转化成字节码(Byte Code),然后通过JVM这个虚拟机,字节码就能在各个平台上运行。
hello.java
public class Hello{
//以类(class)为结构展开,大小写敏感,public表示类是公开的。一个Java源码只能定义一个Public类型的class
public static void main(String[] args){
// 定义一个main方法,其中void是返回类型,String[]是参数类型,参数名为args,public和static修饰方法(公开且静态)
System.out.println("Hello World!");
// java使用‘;’结尾,缩进一般为4个空格
}
}
注:Java程序总是从main方法开始执行
保存文件时,文件名必须为Hello.java,即要和类名保持一致
两种运行方法:
- java Hello.java直接运行源码(仅用于单个不依赖第三方库的Java源码!)
- javac Hello.java ——> 产生Hello.class; java Hello ——>执行类
小结
- 一个Java源码只能定义一个
public
类型的class,并且class名称和文件名要完全一致; - 使用
javac
可以将.java
源码编译成.class
字节码; - 使用
java
可以运行一个已编译的Java程序,参数是类名。
基础知识
程序基本结构
public class Hello{ //访问修饰符 类关键词 类名
public static void main(String[] args){ //访问修饰符 修饰符 返回类型 方法名(参数类型 参数名)
System.out.println("Hello World!");
}
}
以hello为例,JAVA是面向对象语言,一个程序的基本单位是class
Public是访问修饰符,如果没有这个修饰类也能编译,但不能从命令行执行
类中可以定义若干的方法(Method),如main
static表示静态方法,也是修饰符
JAVA入口程序规定的方法必须是静态方法,方法名必须是main,括号内的参数必须是String数组。
注释:采用“/** **/” 跨行注释时,放于类和方法定义处,可以自动创建文档
变量
基本类型变量
先定义后使用,定义是可以给初始值(默认初始0),定义阶段要指明变量类型。可以重复赋值
public class Main{
public static void main(String[] args){
int n = 100;
int x = n+100;
System.out.println("x = "+x);// 这里的+值的是输出第二个内容的意思。即括号内改成 "n="+"x",则输出"n = x"
}
}
变量类型:(4+2+1+1)
- 整数类型:byte(1字节),short(2字节),int(4字节),long(8字节)
- 浮点数类型:float(4字节),double(8字节)
- 字符类型:char(2字节)(可以表示中文,即unicode)
- 布尔类型:boolean(通常存作4字节)
不同进制表示同一个数是完全相同的 15=
0xf=
0b1111
引用类型变量
类似指针
eg : String s = “hello”
定义变量时加上final修饰符,变量成为常量 final double PI = 3.14
var关键字:
var sb = new StringBuilder();
//编译器会推断出sb是StringBuilder,使得以下两句等价
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder();
变量作用范围:
在语句块中定义的变量,它有一个作用域,就是从定义处开始,到语句块结束。
小结
- Java提供了两种变量类型:基本类型和引用类型
- 基本类型包括整型,浮点型,布尔型,字符型。
- 变量可重新赋值,等号是赋值语句,不是数学意义的等号。
- 常量在初始化后不可重新赋值,使用常量便于理解程序意图
整数运算
/ :整除
% :取余
注:数值溢出的时候不会报错,但会返回奇怪的结果(采用long类型避免溢出)
++n
表示先加1再引用n,n++
表示先引用n再加1(不建议用于常规运算)
左移处理(<<)等同于十进制2*
对负数右移(>>),最高位的1不限,即仍然为负数(但>>>则为无符号右移,可以改变正负)
与或非:&、|、 -
异或: ^
int和short运算时,输出为int(运算前short被转为int)
强制转型可能报错,int转short,则丢失2个高位字节
public class Main{
public static void main(String[] args){
int i1 = 1234567
short s1 = (short) i1; //-10617
System.out.println(s1);
int i2 = 12345678;
short s2 = (short) i2; //24910
System.out.println(s2)
}
}
小结
- 整数运算的结果永远是精确的;
- 运算结果会自动提升;
- 可以强制转型,但超出范围的强制转型会得到错误的结果;
- 应该选择合适范围的整型(
int
或long
),没有必要为了节省内存而使用byte
和short
进行整数运算。
浮点数运算
只能做数值运算,不能做位运算和移位运算。
浮点数存在无法精确表示的问题:二进制无法表示某些小数,只能趋近(如0.1)
故比较浮点数大小采用减法进行判断
// 比较x和y是否相等,先计算其差的绝对值:
double r = Math.abs(x - y);
// 再判断绝对值是否足够小:
if (r < 0.00001) {
} else {
}
参与运算整数去兼容浮点数
浮点数可以做0的除法
double d1 = 0.0 / 0; // NaN
double d2 = 1.0 / 0; // Infinity
double d3 = -1.0 / 0; // -Infinity
浮点到整形要进行四舍五入可以通过+0.5实现。
布尔运算
优先级:
!
>
,>=
,<
,<=
==
,!=
&&
||
短路运算:如果一个布尔运算的表达式能提前确定结果,则后续的计算不再执行,直接返回结果。
eg:boolean b = 5 < 3;
boolean result = b && (5 / 0 > 0); // b已经false不用计算后续,否则 5 / 0 将报错
三元计算:
b ? x : y // b为true则运行x,否则y,返回选中的操作
小结
- 与运算和或运算是短路运算;
- 三元运算
b ? x : y
后面的类型必须相同,三元运算也是“短路运算”,只计算x
或y
。
字符和字符串
中文英文都占用2字节,显示unicode编码只需要将char复制给int
转义符
可以用\u+unicode编码表示一个字符
在需要表示 “ 字符时,使用 \” 表示,\被称为转义字符(\ \ 则表示\)
\"
表示字符"
\'
表示字符'
\\
表示字符\
\n
表示换行符\r
表示回车符\t
表示Tab\u####
表示一个Unicode编码的字符
可以通过 + 连接任意字符串和其他数据类型,以便字符串处理。
可以通过char(Unicode)转为字符
多行字符串:
采用"""..."""
表示多行字符串如(共同空格将被去掉):
public class Main {
public static void main(String[] args) {
String s = """ // 开始
users
WHERE id > 100
ORDER BY name DESC
"""; // 结束
System.out.println(s); //包括5行,因为DESC后相当于还有一个\n
}
}
字符串是不可变的引用变量——变的只会是变量的指向
public class Main {
public static void main(String[] args){
String s = "hello";
System.out.println(s);
s = "world";
System.out.println(s);
}
}
public class Main {
public static void main(String[] args) {
String s = "hello";
String t = s;
s = "world";
System.out.println(t); // 输出"hello"
}
}
创建引用变量可以指向一个空值(null),注意null不等于"",后者是空字符串。
小结
- Java的字符类型
char
是基本类型,字符串类型String
是引用类型; - 基本类型的变量是“持有”某个数值,引用类型的变量是“指向”某个对象;
- 引用类型的变量可以是空值
null
; - 要区分空值
null
和空字符串""
。
数组类型
int[] ns = new int[5] //其中int为数组元素类型,5为数组大小
int[] ns = new int[] { 68, 79, 91, 85, 62 }; //直接初始化元素就不必定义数组大小
数组是引用类型,数组一旦创建后,大小就不可改变。
可以用ns.length获取数组大小(ns为数组名)
如果用ArrayList即动态数组初始化时就不用限制数组大小
字符串数组:
如果数组元素不是基本类型,而是一个引用类型如字符串,对于String[]
类型的数组变量names
,它实际上包含3个元素,但每个元素都指向某个字符串对象。对其中元素赋新值时效果如下图:
小结
- 数组是同一数据类型的集合,数组一旦创建后,大小就不可变;
- 可以通过索引访问数组元素,但索引超出范围将报错;
- 数组元素可以是值类型(如int)或引用类型(如String),但数组本身是引用类型;
流程控制
输入输出
输出
System.out.println() //换行
System.out.print() //不换行
System.out.printf("%.4f\n", d); // 显示4位小数3.1416,格式化显示
占位符 | 说明 |
---|---|
%d | 格式化输出整数 |
%x | 格式化输出十六进制整数 |
%f | 格式化输出浮点数 |
%e | 格式化输出科学计数法表示的浮点数 |
%s | 格式化字符串 |
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Formatter.html#syntax
输入
System.in 即标准输入流,通过Scanner类简化in的后续操作
scanner.nextLine()
读取用户输入的字符串
scanner.nextInt()
读取用户输入的整数
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner sanner = new Scanner(System.in);// 创建Scanner对象
System.out.print("Input your name: "); // 打印提示
String name = scanner.nextLine(); // 读取一行输入并获取字符串
System.out.print("Input your age: "); // 打印提示
int age = scanner.nextInt(); // 读取一行输入并获取整数
System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
}
}
小结
Java提供的输出包括:System.out.println()
/ print()
/ printf()
,其中printf()
可以格式化输出;
Java提供Scanner对象来方便输入,读取对应的类型可以使用:scanner.nextLine()
/ nextInt()
/ nextDouble()
课后题:
import java.util.Scanner;
// 输入两次成绩,返回保留两位小数的提升百分比
public class Main {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("Input your last score:");
int score1 = scanner.nextInt();
System.out.println("Input your this score:");
int score2 = scanner.nextInt();
double percent = 100.0*(score2-score1)/score1;
System.out.printf("improve:%.2f%%",percent);
}
}
if判断
当if
语句块只有一行语句时,可以省略花括号{};但仍要换行。
样例:
if (n >= 90) {
System.out.println("优秀");
} else if (n >= 60) {
System.out.println("及格了");
} else {
System.out.println("挂科了");
}
注意边界条件。
注:判断浮点数的 == 时,通常采用减法后的某个数 < 某个临界值来判断。
当两个引用类型的变量指向不同对象但内容相同时,用==判断的结果也为false,应当使用equals() 方法
如:s1.equals(s2)
小结
if ... else
可以做条件判断,else
是可选的;- 不推荐省略花括号
{}
; - 多个
if ... else
串联要特别注意判断顺序; - 要注意
if
的边界条件; - 要注意浮点数判断相等不能直接用
==
运算符; - 引用类型判断内容相等要使用
equals()
,注意避免NullPointerException
。
switch多重选择
switch (option) {
case 1: //case判定字符串时是判断内容,不是地址
System.out.println("Selected 1");
break;
case 2:
System.out.println("Selected 2");
break;
default:
System.out.println("Not selected");
break;
}
用break跳出选择,如果没有break将在选择到的语句后一直执行直到出现break
用default表示没有匹配到任何case时执行
新版java中采用 ->{} 连接匹配语句避免穿透执行。并且可以直接返回值
public class Main {
public static void main(String[] args) {
String fruit = "apple";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> 0;
}; // 注意赋值语句要以;结束
System.out.println("opt = " + opt);
}
}
yield
大多数时候,在switch
表达式内部,我们会返回简单的值。但如果需要复杂的语句,我们也可以写很多语句,放到{...}
里,然后,用yield
返回一个值作为switch
语句的返回值
public class Main {
public static void main(String[] args) {
String fruit = "orange";
int opt = switch (fruit) {
case "apple" -> 1;
case "pear", "mango" -> 2;
default -> {
int code = fruit.hashCode();
yield code; // switch语句返回值
}
};
System.out.println("opt = " + opt);
}
}
小结
switch
语句可以做多重选择,然后执行匹配的case
语句后续代码;switch
的计算结果必须是整型、字符串或枚举类型;- 注意千万不要漏写
break
,建议打开fall-through
警告; - 总是写上
default
,建议打开missing default
警告; - 从Java 14开始,
switch
语句正式升级为表达式,不再需要break
,并且允许使用yield
返回值。
while循环
先判断是否成立,然后循环
while( 条件 ){ 语句 }
do while
另外还有do while,区别在于,do while先进行一次循环主体再进行判定
do{语句}while(条件);
小结
while
循环先判断循环条件是否满足,再执行循环语句;while
循环可能一次都不执行;- 编写循环时要注意循环条件,并避免死循环。
do while
循环先执行循环,再判断条件;do while
循环会至少执行一次。
for 循环
与python基本一致
for (初始条件; 循环检测条件; 循环后更新计数器) {}
注,在循环条件内尽量不要修改计数器以避免逻辑错误
for each
常用于遍历数组
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) { //不需计数器,直接是数组元素
System.out.println(n);
}
小结
for
循环通过计数器可以实现复杂循环;for each
循环可以直接遍历数组的每个元素;- 最佳实践:计数器变量定义在
for
循环内部,循环体内部不修改计数器;
break和continue
break跳出当前这层循环
continue会结束本次循环,进入下次循环
小结
break
语句可以跳出当前循环;break
语句通常配合if
,在满足条件时提前结束整个循环;break
语句总是跳出最近的一层循环;continue
语句可以提前结束本次循环;continue
语句通常配合if
,在满足条件时提前结束本次循环
数组操作
遍历
通过 for循环
打印数组时需要注意,System.out.println(ns) 将打印ns数组的引用地址
采用for循环或者Arrays.toString()
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 1, 2, 3, 5, 8 };
System.out.println(Arrays.toString(ns));
}
}
小结
- 遍历数组可以使用
for
循环,for
循环可以访问数组索引,for each
循环直接迭代每个数组元素,但无法获取索引; - 使用
Arrays.toString()
可以快速获取数组内容。
排序
冒泡、插入和快速
冒泡排序
每次循环最大的数交换到末尾
功能函数:Arrays.sort(ns) ——> 在ns上直接做修改,即数组的指向内容已经变化
小结
- 常用的排序算法有冒泡排序、插入排序和快速排序等;
- 冒泡排序使用两层
for
循环实现排序; - 交换两个变量的值需要借助一个临时变量。
- 可以直接使用Java标准库提供的
Arrays.sort()
进行排序; - 对数组排序会直接修改数组本身。
多维数组
二维数组:
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
加入定义普通数组,并把ns[0]赋值给它,则它获得第一个数组
二维数组的每个数组元素的长度并不要求相同,例如,可以这么定义ns
数组:
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6 },
{ 7, 8, 9 }
};
要打印可以用两层for循环,或者使用Java标准库Arrays.deepToString()
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(Arrays.deepToString(ns));
}
}
三维数组:
小结
- 二维数组就是数组的数组,三维数组就是二维数组的数组;
- 多维数组的每个数组元素长度都不要求相同;
- 打印多维数组可以使用
Arrays.deepToString()
; - 最常见的多维数组是二维数组,访问二维数组的一个元素使用
array[row][col]
。
命令行参数
java的命令入口是main方法,而main方法可以接受一个命令行参数,是一个String[],这个命令行参数由JVM接收用户输入并传给main。
可以利用接收到的命令行参数,根据不同的参数执行不同的代码。例如,实现一个-version
参数,打印程序版本号:
public class Main {
public static void main(String[] args) {
for (String arg : args) {
if ("-version".equals(arg)) {
System.out.println("v 1.0");
break;
}
}
}
}
在命令行执行,需要再传递一个参数
javac Main.java
java Main -version
v 1.0 //输出
小结
- 命令行参数类型是
String[]
数组; - 命令行参数由JVM接收用户输入并传给
main
方法; - 如何解析命令行参数需要由程序自己实现。