Java背景
1. 官网
- oracle java 文档 https://docs.oracle.com/en/java/index.html
- oracle jdk下载 https://www.oracle.com/java/technologies/downloads/
- openjdk https://openjdk.org/
2. java和JVM
-
java是基于类的、纯粹的面向对象编程语言
-
java是解释执行类的语言
-
WORA(write once, run anywhere)
- java程序被称之为一次写入,到处运行
- 只要系统上有JVM,就能运行java程序
-
相关
-
.java - java的源码文件
-
javac - java的编译器
-
.class - 编译后的二进制数据文件
-
3. java版本
3.1 平台体系
- Java SE(Java Platform Standard Edition)。Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。
- Java EE(Java Platform Enterprise Edition)。这个版本以前称为 J2EE。 开发企业级的应用
- Java ME(Java Platform Micro Edition)。这个版本以前称为 J2ME。Java ME 为在移动设备和嵌入式设备(比如手机、PDA、电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境.
3.2 JDK和JRE
-
JDK - java development kit,java开发包,包含java开发工具和
JRE
-
JRE - java runtime environment, java运行时环境,包含java虚拟机和运行时库
-
场景
-
开发 - JDK
-
运行 - JRE
-
3.3 历史版本
至本文档编写时,JAVA已经推出JDK20
,但现在广泛使用的版本是JAVA8
,官网上所显示的LTS
版本如下
版本 | 维护时间 |
---|---|
JDK8 | 2030.09 |
JDK11 | 2026.09 |
JDK17 | 2024.09 后续每年会有LTS |
所以,没有特别的需求,JDK8
就满足需求。
3.4 Oracle JDK和OpenJDK
-
Oracle JDK - 甲骨文收购了java后,维护的java版本,不开源
-
OpenJDK
-
甲骨文开源的java版
-
各公司使用这个版本进行二次开发得到自己的JDK,比如安卓的ART
-
去除了很多JAVA的类库,保留核心功能,相当于
纯净版Java
-
3.5 环境配置
-
IDE - 本课使用jetbrains的社区版idea作为IDE。
-
JDK - OpenJDK18
4. jar包
java的执行有两种方式:
- 执行源码
- 执行编译后程序
Java编译后的程序后缀名为.jar
, 也被称为jar包
4.1 打包
-
打开
File
->Project Structur
-
选择
Artifacts
,点击+
,选择jar
->From Modules xxxx
-
选择主类,点击
Main Class
后的图标,完事后点击OK -
回到主界面,菜单
build
->build Artifacts
,确认完成打包
4.2 执行
执行默认的类
java -jar xxxx.jar
指定主类执行
java -cp xxx.jar 包名.类名
4.3 引入第三方jar包
- 打包一个jar包,不同的地方是选择
empty
- 主程序创建一个文件夹
libs
,然后将之前打包的jar包拷贝过来 - 在
Project structure
中的module
中将本地jar包导入 - 创建
artifacts
,流程和之前的一样
Java语言
1. java语法
java语法与c++语法高度相似,故本课程对于相同点只列出相关语法,对于不同点会详细讲解。
2. 基本数据类型
java并不像c++那样继承了c语言的"丰富"的数据类型,java只有有符号数据类型。
基本数据类型 | 大小 | 取值 |
---|---|---|
boolean | true,false | |
byte | 1 字节 | |
char | 2 字节 | |
short | 2 字节 | |
int | 4 字节 | |
long | 8 字节 | |
float | 4 字节 | |
double | 8 字节 | |
void |
2.1 var
- java的
auto
,用于定义局部变量
2.2 final
- 格式
[访问权限] final 数据/方法/类
final
类似于C++中的final
和const
的结合final
拥有更广泛的应用范围, 可修饰数据, 方法和类- 按
final
修饰后, 会具有不可变
性不可变
一旦初始化就不能被修改, 不会被编译器优化常量
可以被编译器优化
修饰 | 意义 | 示例 |
---|---|---|
基本数据类型 | 不可修改 | private final int n = 0; |
类类型 | 对象可以修改,不可再指向别的对象 | private Foo final foo = new Foo(); |
方法 | 不可被覆盖 | public final void Foo(){} |
类 | 不可被继承 | final class Foo{} |
静态数据 | 可当做常量 | public static final int VAL = 0; |
3. 运算符
java的运算符基本上与c++的是一样的。
java没有sizeof
。
分类 | 运算符 |
---|---|
赋值 | = |
算术 | + ,- ,* ,/ , %``+= ,-= ,*= ,%=``++ ,-- |
关系 | > ,< ,<= , >= ,!= ,== |
逻辑 | && ,` |
位操作 | & ,` |
三目 | ?: |
类型转换(强转) | (新类型)原类型 |
4. 流程控制
流程控制和c++是一样的。
分类 | 运算符 |
---|---|
分支 | if-else``if-else if-else``switch-case |
循环 | while``do-while``for``for(范围迭代)``break``continue |
goto |
4.1 switch
语句
-
java中的
switch
除了提供传统的分支流程控制功能外,还可以作为一种升级版的三目
,即提供多重选择
-
语法
-
case
标签-
类型为char、byte、short或int的常量表达式
-
枚举常量
-
字符串字面量
-
多个字符串,用逗号分隔
-
-
yield
关键字 - 用于返回赋值给变量
的值。
-
// 形式1
变量 = swith(选择){
case 值 -> 表达式;
case 值 -> 表达式;
case 值 -> 表达式;
default ->表达式;
};
// 形式2
变量 = swith(选择){
case 值 -> { 表达式; yield 值;}
case 值 -> { 表达式; yield 值;}
case 值 -> { 表达式; yield 值;}
default -> { 表达式; yield 值;}
};
5. 代码组织结构
- java程序逻辑上以包为单位
- 一个包中包含多个类
-
源码结构
-
包名作为路径名
-
类名作为文件名
-
-
图示解析
-
java的包名类似c++的命名空间,但不同的是,java的包名既是空间名,也是路径名
-
下面的程序中有两个包名,也就有两个路径
-
/com/kr/base
- 有两个文件-
Foo.java
-
Test.java
-
-
/com/kr/core
- 有两个文件-
Foo.java
-
Test.java
-
-
-
6. 类
6.1 格式
java的类与c++的类在语法上大体相同,区别在于访问权限需要写在java的类和成员前面。
-
构造与类名相同,不需要析构
-
成员函数可以重载
-
访问权限 ,可选
-
public
-
private
-
protected
-
-
成员函数同样有
this
指针 -
静态成员 - 属于类,而非对象
[访问权限] class 类名{
[访问权限] 成员;
[访问权限] 成员;
[访问权限] 成员;
[访问权限] 成员;
[访问权限] 成员;
....
}
public class Foo{
public int n = 0;
private float f = (float)0.0;
String str = "hello world";
static int n0 = 0;
public void Test(){}
private void Test(int){}
static void Test(float){}
}
6.2 访问权限控制
关键字 | 权限 |
---|---|
public |
共有 |
private |
私有 |
protected |
保护 |
无 | 包权限,包内可以访问 |
6.3 构造块和静态块
构造块
- 无名的构造,实例化对象的时候调用静态块
- 当类被解析的时候被调用- 时机 -
静态块
>>>构造块
>>>构造
class 类名{
//这是构造块
{
//正常代码
}
//这是静态块
static {
//正常代码
//只能访问静态成员
}
}
6.4 包
包是java中库级的代码组织单位,高于类,等同于c++的命名空间。
-
格式 -
package 包名;
-
package
语句必须放是文件第一行非注释代码,表明本文件所属的包 -
包名
如果有多个层级,中间使用.
隔开,同时是路径名
-
比如,建立一个包名com.jlw.base
对应的文件夹路径
import
当其它类需要使用指定包中的类时,需要使用import
语句
- 格式1 -
import 包名.类名
,使用指定包中的指定类 - 格式2 -
import 包名.*
,*
是通配符,使用指定包中的所有类
import com.jlw.base.Foo;
public class Main {
public static void main(String[] args) {
Foo foo = new Foo();
//Test test = new Test(); //报错
}
}
import com.jlw.base.*;
public class Main {
public static void main(String[] args) {
Foo foo = new Foo();
Test test = new Test();
}
}
7. 继承和多态
7.1 继承
- 语法
- 继承关键字
extends
- 不允许多继承
- 继承关键字
public class Base{
void foo(){
System.out.println("Base.foo()");
}
public int n;
}
public class Derived extends Base{
}
public static void main(String args){
Derived d = new Derived();
d.n = 100;
d.foo();
}
- 调用父类构造
public class Base{
Base(){}
Base(int n){
this.n = n;
}
void foo(){
System.out.println("Base.foo()");
}
public int n;
}
public class Derived extends Base{
Derived(){}
Derived(int n){
// 在带参构造子类时,默认调用父类的无参构造
// 需要调用父类带参构造
super(n);
n = 20;
this.n = 10;
super.n = 30;
}
}
super
可当做父类的类名, 子类通过super
关键字访问父类的成员n = 20;
修改的是传入参数的值this.n = 10;
修改的子类的值super.n = 30;
修改的父类的值
instanceof
运算符
用于判断某个对象是否是某个类的对象 ( 是否满足该继承体系 )
返回值
boolean
b = new Derived2();
boolean bIs = b instanceof Derived2; // true
bIs = b instanceof Derived; // false
bIs = b instanceof Base; // true
- 隐藏
- 子类会隐藏父类同名数据, 如需指定父类数据, 可使用
super
7.2 多态
- 语法
- 函数声明相同, 直接在子类重写, 不需要加关键字就可实现多态
public class Derived extends Base{
void foo(){
System.out.println("Derived.foo()");
}
}
public static void main(String args){
Derived d = new Derived();
d.n = 100;
d.foo();
Base b = new Derived();
b.foo(); // 调用子类foo函数,自动实现多态
}
快捷键Ctrl + O
快速查看可重写的父类方法, 重写函数上可以添加@Override
注解
当子类构造调用到父类构造时, 父类构造调用了子类重新函数foo();
此时会访问到子类的foo函数, 所以我们不能再父类构造中调用到子类重写的函数
public class Base{
Base(){}
Base(int n){
this.n = n;
//this.foo(); // 子类走父类构造时,调用到了子类重写的foo函数
}
void foo(){
System.out.println("Base.foo()");
}
public int n;
}
为了防止父类的部分函数被子类重写, 我们有种方法防止被子类重写
final
关键字
public final class Base{ // 类不能被继承
Base(){}
Base(int n){
this.n = n;
//this.foo(); // 子类走父类构造时,调用到了子类重写的foo函数
}
final void foo(){ // 函数不能被重写
System.out.println("Base.foo()");
}
public int n;
}
final
关键字类似于C++的const
, 但是比const
的使用范围广, 添加到类前, 则该类不允许被继承, 添加到方法前, 则该方法不允许被重写
7.3 祖宗类
Object
-
java所有的类都继承自
Object
-
toString
用于输出对象信息 -
equals
用于判断对象是否相同- 判断类类型需要重写, 否则只能判断基本数据类型和String类型
public boolean equals(Object obj){ Foo foo = (Foo) obj; // 需要强转为该类类型,祖宗类无法访问Foo类数据成员 return n == foo.n; }
-
clone
用于深拷贝
-
8. 抽象类和接口
8.1 抽象类
java可以定义抽象类, 关键字abstract
, 该关键字也可以用于定义抽象方法(C++的纯虚函数)
[访问权限] abstract class 类名{
[访问权限] abstract 返回值 方法名(参数列表) {
// 抽象方法
}
[访问权限] 数据类型 变量;
}
8.2 接口
java允许定义一种更纯粹的抽象类, 称为接口, class
关键字改为interface
- 定义
- 接口的成员函数不能有实现
- 接口不能定义数据成员
- 接口不能实例化对象
- 实现
- java允许接口多重继承
public Derived implements Animal
- 子类必须实现接口的所有成员函数
- java允许接口多重继承
public interface Animal{
public void Eat();
public void Sound();
}
9. 内部类
从代码组织层级来看,类是定义在包级之下。
但java还支持将类定义到更小的组织单位中,比如类中,甚至方法中,统称为内部类。
9.1 内部类
内部类实际上就是一个类内部定义一个外部类的引用, 这个引用必须有一个实例化的对象 ( 用于访问外部类的数据 )
- 内部类可以定义在其它类内,也可以定义在方法内
- 内部类可以访问类外作用域的数据和方法,并无视权限
public class Foo {
private int nFoo = 0;
private void TestFoo(){
System.out.println("Foo.Test");
}
//定义在类内
public class Inn{
public int nInn= 10;
public void Test(){
nFoo = 10; //访问外部数据
TestFoo(); //访问外部方法
System.out.println("Inn.Test");
//定义在方法内
class Inn3{
public void Test(){
nInn = 20; //访问外部数据
nFoo = 30; //访问外部数据
}
}
}
}
}
- 当出现名称隐藏时,通过
类名.this
的方式指定作用域
public class Test {
private int n = 0;
class Inn{
private int n = 0;
public void Foo(){
this.n = 8; //访问自己的
Test.this.n = 9; //访问Test的
}
}
}
- 内部类不能独立创建对象,必须使用外部类对象创建
- 创建时不能直接
new
,使用对象.new
public class Outer {
public class Inner{
}
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); //创建内部类对象
}
}
- 继承内部类没有意义, 不做讨论
9.2 匿名内部类
- 格式 -
new 类名/接口名 (){}
- 语义 - 创建一个无名类(继承自指定类,或者实现指定接口)对象
- 匿名内部类可以定义数据成员
- 一生只能定义一个对象
public class Outer {
public interface Inter {
public void Foo();
}
public void Foo(){
System.out.println("Outer.Foo");
}
public static void main(String[] args) {
//创建匿名类对象
Outer outer = new Outer(){
private int n = 0;
public int n1 = 0;
public void Foo(){
System.out.println("noname.Foo");
}
};
outer.Foo();
//创建匿名类对象
Inter inter = new Inter() {
@Override
public void Foo() {
System.out.println("Inter.Foo");
}
};
inter.Foo();
}
}
9.3 嵌套类
- 格式 -
static class 类名{}
- 说明 - 内部类使用
static
修饰 - 不能访问外部的变量,可以直接创建对象
public class Outer {
public static class Inner{
public void Test(){
System.out.println("Inner.Test");
}
}
}
public class Main {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.Test();
}
}
10. 泛型
10.1 基本语法
- 类模板格式 -
访问标号 类名<T>{}
- 函数模板格式 -
访问标号 返回值 函数名<T>{}
- 意义不大
public class Foo <T>{
private T val;
private T Get(){
return val;
}
private void Set(T val){
this.val = val;
}
public static void main(String[] args) {
Foo<String> foo = new Foo<String>();
foo.Set("hello");
System.out.println(foo.Get());
}
}
10.2 类型擦除
java模板功能比c++的要弱。
在c++中,模板实例化后,模板的类型参数T
便成为了具体的类型。
但在java中,模板实例化后,模板的类型参数T
便成为了Object
。
相当于模板内部将实例化模板的类型给擦除了。
package com.kr.testtem;
public class TestErase {
static public class Foo{
public void Test(){
System.out.println("Foo.Test");
}
}
static public class Tem<Foo>{
public T obj;
public Tem(T obj){
this.obj = obj;
}
public void Call(){
obj.Test(); //报错
}
public void Test(){
}
}
public static void main(String[] args) {
Tem<Foo> tem = new Tem<>(new Foo());
tem.Call();
}
}
10.3 边界和通配符
- 边界
针对模板内部的类型擦除,java也做出了补偿,允许模板将T
视作指定继承家族中的一员,即T
都为指定类型的子类,从而在某种程度上调用类型的成员。
这种做法称之为为模板的类型指定边界。
- 格式 -
<T externds 类名>
package com.kr.testtem;
public class TestErase {
static public class Foo{
public void Test(){
System.out.println("Foo.Test");
}
}
static public class CSuperFoo extends Foo{
public void Test(){
System.out.println("Foo.Test");
}
}
static public class Tem<T extends Foo>{
public T obj;
public Tem(T obj){
this.obj = obj;
}
public void Call(){
obj.Test();
}
public void Test(){
}
}
public static void main(String[] args) {
Tem<Foo> tem = new Tem<>(new CSuperFoo());
tem.Call();
}
}
- 通配符
10.4 容器
11. 异常
-
抛出异常 -
throw new 异常
-
接收异常 -
try{}catch(异常){}
-
finally
块 - 跟在在try
后,无论是否有异常,都会执行finally
-
所有异常的父类都是
Exception
-
throws
说明-
所有方法,如果有未处理的异常,均要说明
-
格式 -
方法 throws 异常1,异常2 。。。
-
package com.kr.testexp;
import com.kr.testtem.TestErase;
public class Test {
//自定义异常
static public class MyException extends Exception{
public MyException(String str){
super(str);
}
}
//自定义异常
static public class MyException2 extends Exception{
public MyException2(String str2){
super(str2);
}
}
//方法异常签名
public void Foo(int n) throws MyException, MyException2 {
if (n>0){
throw new MyException("test"); //抛出异常
}else {
throw new MyException2("2");//抛出异常
}
}
public static void main(String[] args) {
Test t = new Test();
//接收异常
try {
t.Foo(1);
} catch (MyException e) {
System.out.println(e.getMessage());
e.printStackTrace();
} catch (MyException2 e) {
e.printStackTrace();
} finally {
System.out.println("finally");
}
}
}
11.1 try…catch
public class Main(){
public static void main(String[] args){
try{
throw new Exception("异常");
}catch (Exception e){
//System.out.println(e.toString());
e.printStackTrace();
}
}
}
11.2 往外抛
加 throw Exception
public class Foo{
public void foo() throw Exception {
throw new Exception("异常");
}
}
public class Main(){
public static void main(String[] args) throw Exception{
Foo foo = new Foo();
foo.foo();
}
}
12. 常用库类
- 字符串类
String
- java中所有的字符串都是String类对象
- 文本块
"""\n"""
, 中间可以跨行,\
用于换行拼接
- 计算
Math
- 数组
Array