这就是又能写安卓,又能写mc mod的函数名一长串的语言啊!

简介

分类

java分为三个版本Java SEJava EE(现更名为Jakarta EE)、Java ME,他们之间关系就好像Python 和 MicroPython。

特性 JSE JRE JEE JRE JME JRE
定位 标准版 企业版 微型版
库和 API 核心 Java 库 核心库 + 企业级 API 精简库 + 设备特定 API
JVM 标准 JVM 标准 JVM 定制化 JVM(如 CLDC HotSpot)
适用场景 桌面应用、小型服务器 企业应用、Web 服务 移动设备、嵌入式系统
  • JSE JRE 只能运行基于 JSE 开发的程序。
  • JEE JRE 可以运行 JSE 和 JEE 程序,因为它包含了 JSE 的所有功能。
  • JME JRE 只能运行基于 JME 开发的程序,因为它的库和 JVM 是专门为资源受限设备设计的。

官网地址显示的就是**JDK 21 is the latest Long-Term Support (LTS) release of the Java SE Platform.**正常我们用的就是java se。上述内容不重要。

我学的好像是java8的老版本,但是为了fabric 1.21.4,我使用jdk21来进行实操。

C++的过渡

  1. Java没有指针,有引用,但是它的引用和C++的引用不是相同的概念
  2. Java能够自动管理内存
  3. Java多平台数据类型是统一大小的
  4. Java类似python,声明和实现在一起,不需要头文件h和实现cpp这种形式
  5. 没有define这种宏
  6. 不支持多重继承,即不能有多个爹,为了解决钻石继承,引入了接口这个性质
  7. 没有全局变量,作用域最大就是类里面
  8. 没有struct和union,有class就足够了
  9. 没有goto

运行

xxx.java编译成字节码xxx.class,在不同平台上使用不同的解释器来运行

JRE = JVE + API JDK = JRE + Tools

基本工具如通过javac.exe编译,java.exe运行。

引入

hello

先写一个经典的hello world

1
2
3
4
5
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World"); // 输出 Hello World
    }
}

注意:类名和文件名要相同

对于一个**.java文件**我们要知道其组成部分:

  1. package (0 or 1)
  2. import (>= 0)
  3. class (>= 1) 但是 public class (= 1 并且与文件同名)

注:

  1. package 的作用就是 c++ 的 namespace 的作用,防止名字相同的类产生冲突。
  2. java因强制要求类名(唯一的public类)和文件名统一,因此在引用其它类时无需显式声明。在编译时,编译器会根据类名去寻找同名文件。

from runoob

注释

和c差不多,Javadoc提供根据文档注释生成文档的功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 单行注释

/*
多行注释
*/

/**
* 文档注释
* @author cbksb
* @version 1.2
*/

类和对象

  1. 类 Class :含有 方法method (成员函数), 字段 field (成员变量)
  2. 对象 Object
  3. 继承 通过 class A extends B {…}
  4. 封装:通过public方法访问私有字段
  5. 多态
  6. 抽象:类似纯虚函数
  7. 接口:定义类必须实现的方法,支持多重继承。
  8. 重载:同c++,同名不同参

数据类型

Java由于没有指针,所以全是引用,除了基本的数据类型,全是引用数据类型。

内置

1. 整数类型

用于存储整数值,包括正数、负数和零。

数据类型 大小(字节) 取值范围 默认值
byte 1 -128 到 127 0
short 2 -32,768 到 32,767 0
int 4 -2³¹ 到 2³¹-1(约 -2.1亿 到 2.1亿) 0
long 8 -2⁶³ 到 2⁶³-1 0L

long在用数字表示的时候后面一定要加L,如123456789123L,否则视作int

2. 浮点类型

用于存储带小数部分的数值。

数据类型 大小(字节) 取值范围 默认值
float 4 约 ±3.4e-38 到 ±3.4e38 0.0f
double 8 约 ±1.7e-308 到 ±1.7e308 0.0d

3. 字符类型

用于存储单个字符。

数据类型 大小(字节) 取值范围 默认值
char 2 0 到 65,535(Unicode 字符) ‘\u0000’

4. 布尔类型

用于存储逻辑值,只有两个可能的值:truefalse

不可以0或非0的整数替代true和false ,boolean不能与整数类型相互转换。

数据类型 大小(字节) 取值范围 默认值
boolean 1(实际大小取决于 JVM 实现,可能被优化为 1 位) truefalse false

引用

类,接口,数组都是引用类型。

1
2
Person p = new Person(); // Person的实例在堆区,p在栈区,p指向它
Person p2 = p; // 复制的是引用

数组

支持两种写法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
数据类型[] 数组名;  // 推荐写法
数据类型 数组名[];  // 不推荐,兼容 C/C++ 风格

int[] numbers;  // 声明一个整型数组
String[] names; // 声明一个字符串数组

int[] numbers = {1, 2, 3, 4, 5}; // 静态初始化
String[] names = {"Alice", "Bob", "Charlie"};

int[] numbers = new int[5]; // 动态初始化,长度为 5
String[] names = new String[3]; // 动态初始化,长度为 3
//动态初始化时,数组的元素会被赋予默认值

可以通过 数组名.length 获取数组的长度。

遍历数组,一种是下标,一种是类似python的for i in xxx,和c++11之后的for auto i : xxx那样的,但是是只读的。

1
2
3
4
5
6
7
8
int[] numbers = {10, 20, 30, 40, 50};
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

for (int num : numbers) {
    System.out.println(num);
}

二维数组

1
2
3
4
5
6
int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; // 静态初始化
System.out.println(matrix[1][2]); // 输出: 6

int[][] matrix2 = new int[3][3]; // 动态初始化
matrix2[0][0] = 1;
matrix2[1][1] = 5;

以下是和C++不同的不规则数组(锯齿数组)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
int[][] t = new int[3][]; // 声明一个二维数组,第一维长度为 3,第二维长度未定
t[0] = new int[2]; // 第一行长度为 2
t[1] = new int[3]; // 第二行长度为 3
t[2] = new int[4]; // 第三行长度为 4

// 访问
for (int i = 0; i < t.length; i++) {
    for (int j = 0; j < t[i].length; j++) {
        System.out.print(t[i][j] + " ");
    }
    System.out.println();
}

// 所以下面的C++写法在Java是不行的
// int[][] t = new int[][3]; 

数组拷贝

1
2
3
4
5
6
7
public static void arraycopy(
    Object src,  // 源数组
    int srcPos,  // 源数组的起始位置
    Object dest, // 目标数组
    int destPos, // 目标数组的起始位置
    int length   // 要复制的元素个数
)

System.arraycopy是最高效的!

流程控制

同c++

switch也需要break

还有我的大括号不换行的码风是Java毋庸置疑的正统!

变量常量

1
type identifier [ = value][, identifier [= value] ...] ;
  • type – 数据类型。
  • identifier – 是变量名,可以使用逗号 , 隔开来声明多个同类型变量。

按Java惯例, 类名首字母用大写( Pascal) 其余的(包名、方法名、变量名) 首字母都小写(camel)

静态 static 常量 final 还可以组合 static final 数据类型 常量名 = 值;

类和接口

虽然说你要是用C++那一套也都能听懂,但是吧人家都有翻译规范了就规范点吧。

以下是Java类相关的术语中英文对照:

对照

  1. 类 (Class)

  2. 对象 (Object)

  3. 实例 (Instance)

  4. 属性/域 (Attribute/Field)

  5. 方法 (Method)

  6. 构造函数 (Constructor)

  7. 继承 (Inheritance)

  8. 父类/超类 (Superclass/Parent Class)

  9. 子类 (Subclass/Child Class)

  10. 封装 (Encapsulation)

  11. 多态 (Polymorphism)

  12. 抽象类 (Abstract Class)

  13. 接口 (Interface)

  14. 重载 (Overloading)

  15. 重写/覆盖 (Overriding)

  16. 访问修饰符 (Access Modifier)

    • public
    • private
    • protected
    • default (package-private)
  17. 静态 (Static)

  18. final

  19. this

  20. super

  21. 包 (Package)

  22. 导入 (Import)

  23. 成员变量 (Member Variable)

  24. 局部变量 (Local Variable)

  25. 实例变量 (Instance Variable)

  26. 类变量 (Class Variable)

  27. 方法签名 (Method Signature)

  28. 参数 (Parameter)

  29. 返回值 (Return Value)

  30. 异常 (Exception)

  31. 泛型 (Generics)

  32. 注解 (Annotation)

  33. 枚举 (Enum)

  34. 内部类 (Inner Class)

  35. 匿名类 (Anonymous Class)

  36. Lambda表达式 (Lambda Expression)

  37. 流 (Stream)

  38. 集合 (Collection)

  39. 数组 (Array)

  40. 反射 (Reflection)

  41. 序列化 (Serialization)

  42. 反序列化 (Deserialization)

  43. 线程 (Thread)

  44. 同步 (Synchronization)

  45. 异步 (Asynchronous)

类样例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[public] [abstract|final] class className [extends superClass] 
[implements InterfaceNameList] { // 类声明
    [public | protected | private] [static] [final] [transient] [volatile] type variableName;
    // 成员变量声明,可为多个

    [public | protected | private] [static] [final | abstract] [native] [synchronized] returnType methodName ( [paramList] ) 
    // 方法定义及实现,可为多个
        
    [throws exceptionList] {
        statements
    }
}

1. 访问修饰符和类修饰符

  • public:表示该类可以被任何其他类访问。如果省略,则默认为包级私有(仅在同一个包内可见)。

  • abstract:表示这是一个抽象类,不能被实例化,只能被继承。抽象类可以包含抽象方法和具体方法。

  • final:表示这是一个最终类,不能被继承。所有方法默认也是final的,不能被子类重写。

2. 类声明

  • class className:定义一个名为className的类。

  • extends superClass:表示当前类继承自superClass。一个类只能继承一个父类。

  • implements InterfaceNameList:表示当前类实现了InterfaceNameList中列出的一个或多个接口。一个类可以实现多个接口,接口之间用逗号分隔。

3. 成员变量声明

1
[type] variableName;
  • 修饰符

    • publicprotectedprivate:控制变量的访问权限。
    • static:表示该变量属于类本身,而不是某个实例。
    • final:表示该变量的值一旦赋值后不可更改。
    • transient:表示该变量不会被序列化。
    • volatile:确保多线程环境下对该变量的修改对所有线程可见。
  • type:变量的数据类型,如intString等。

  • variableName:变量的名称。

4. 方法声明与定义

1
2
3
[returnType] methodName ( [paramList] ) [throws exceptionList] {
    statements
}
  • 修饰符

    • publicprotectedprivate:控制方法的访问权限。
    • static:表示该方法属于类本身,可以通过类名直接调用。
    • final:表示该方法不能被子类重写。
    • abstract:表示该方法没有实现,必须在子类中被实现(仅适用于抽象类)。
    • native:表示该方法的实现由本地代码(如C/C++)提供。
    • synchronized:表示该方法在同一时间只能被一个线程访问,用于线程同步。
  • returnType:方法的返回类型,如voidintString等。如果是void,表示方法不返回任何值。

  • methodName:方法的名称。

  • paramList:方法的参数列表,多个参数用逗号分隔。例如:(int a, String b)

  • throws exceptionList:声明方法可能抛出的异常类型,多个异常用逗号分隔。例如:throws IOException, SQLException

  • statements:方法体内的代码块,包含具体的操作和逻辑。

示例

以下是一个结合上述元素的完整示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public final class Person implements Serializable, Cloneable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public synchronized void setAge(int age) {
        if(age >= 0){
            this.age = age;
        }
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

接口样例

接口其实就是某种程度上的抽象类,抽象的不能再抽象,但是它不再是具有相同特征的一类事物,而是类似一些事物具有相同的特点,我们再在子类中实现这些它的特性。侧重于行为的定义

1
2
3
4
5
6
7
[public] interface InterfaceName [extends superInterfaceList] {
    // 常量声明,可为多个
    type CONSTANT_NAME = value;
    
    // 方法声明,可为多个
    returnType methodName([paramList]);
}

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public interface Animal {
    String NAME = "Animal"; // 常量声明
    
    void eat(); // 抽象方法声明
    
    void sleep(int hours); // 另一个抽象方法声明
}

public interface Mammal extends Animal {
    void giveBirth(); // 继承自Animal接口,并添加新的抽象方法
}

注意,在Java 8之前,接口中的所有方法都是抽象的(即没有方法体)。Java 8之后,可以有默认方法和静态方法。

三个默认方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// (1) 构造方法,声明为:
className( [paramlist] ){

}
// (2) main( )方法,声明为:
public static void main ( String args[ ] ){

}
//3. finalize( )方法,声明为:
protected void finalize( ) throws throwable{

}

注意从Java9开始finalize就被标记成弃用,Java18正式干掉它。

继承

子类:subclass

父类/超类:superclass

Java只支持单继承: 一个类只能有一个直接父类

在面向对象编程中,继承(Inheritance) 是代码复用和逻辑分层的重要机制。Java 的继承机制遵循以下核心要点:


🧬 继承的本质

  • 子类(subclass) 继承 父类/超类(superclass) 的属性和方法(private 成员除外)
  • 子类可以 扩展(extend) 父类的功能,添加新属性和方法
  • 子类可以 重写(override) 父类的方法(非 private 和非 final 的方法)

⚠️ Java 单继承限制

  • 一个类只能有一个直接父类(通过 extends 关键字)
  • 但可以通过接口(implements)实现多重继承的效果
  • 继承关系形成树状层次结构(非网状结构)
1
2
3
4
5
6
// 单继承示例
class Animal { /* 父类 */ }
class Dog extends Animal { /* 子类 */ }

// 错误示例:尝试多继承
// class Cat extends Animal, Mammal {} // 编译错误

🛠️ 继承的实际应用

典型场景:

  1. 代码复用:公共功能提取到父类
  2. 多态实现:通过父类引用操作子类对象
  3. 层次建模:表达 “is-a” 关系(如:狗是动物)

继承链示例:

      Animal
        ↑
      Mammal
        ↑
      Dog
        ↑
GoldenRetriever

🔑 关键语法要素

  1. extends 关键字

    1
    
    class Subclass extends Superclass { ... }
    
  2. super 关键字

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    public class Dog extends Animal {
        public Dog(String name) {
            super(name); // 调用父类构造器
        }
    
        @Override
        public void eat() {
            super.eat(); // 调用父类方法
            System.out.println("狗狗在啃骨头");
        }
    }
    
  3. 构造器调用规则

    • 子类构造器必须首先调用父类构造器
    • 默认调用父类无参构造器(若父类没有无参构造器,必须显式调用)

❓ 为什么 Java 选择单继承?

  • 避免多继承的复杂性(如著名的菱形继承问题)
  • 简化语言设计,提高代码可维护性
  • 通过接口机制弥补功能缺失,实现更灵活的设计
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 通过接口实现多继承效果
interface Swimmer {
    void swim();
}

interface Flyer {
    void fly();
}

class Duck extends Animal implements Swimmer, Flyer {
    // 实现多个接口
}

📜 访问权限

Java的继承只有extends这一种方式,和cpp不同,访问权限只由父类的修饰符决定。

修饰符 同类访问 同包访问 子类访问(不同包) 不同包非子类
public
protected
默认(无修饰符)
private

🧩 继承中的访问规则

1️⃣ public 成员

  • 完全开放访问

  • 子类可以直接访问父类的 public 成员

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    class Parent {
        public String publicField = "Public";
    }
    
    class Child extends Parent {
        void print() {
            System.out.println(publicField); // 直接访问
        }
    }
    

2️⃣ protected 成员

  • 子类特权访问

  • 子类可以直接访问父类的 protected 成员(即使位于不同包)

  • 示例:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    // Parent.java(包 com.example)
    package com.example;
    public class Parent {
        protected String protectedField = "Protected";
    }
    
    // Child.java(包 com.test)
    package com.test;
    import com.example.Parent;
    
    public class Child extends Parent {
        void print() {
            System.out.println(protectedField); // 不同包子类可直接访问
        }
    }
    

3️⃣ 默认(包私有)成员

  • 同包访问限制

  • 只有相同包内的子类可以访问

  • 示例:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    // Parent.java(包 com.example)
    class Parent {
        String packageField = "Package-private";
    }
    
    // ChildSamePackage.java(包 com.example)
    public class ChildSamePackage extends Parent {
        void print() {
            System.out.println(packageField); // 同包子类可访问
        }
    }
    
    // ChildDiffPackage.java(包 com.test)→ 无法访问
    

4️⃣ private 成员

  • 完全继承隔离

  • 子类无法直接访问父类的 private 成员

  • 只能通过父类提供的 public/protected 方法间接访问

  • 示例:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    class Parent {
        private String secret = "Confidential";
    
        public String getSecret() {
            return secret;
        }
    }
    
    class Child extends Parent {
        void printSecret() {
            // System.out.println(secret); // 编译错误
            System.out.println(getSecret()); // 通过方法访问
        }
    }
    

🔧 继承中的构造方法

  • 构造方法不被继承(即使父类构造方法是 public

  • 子类构造器必须调用父类构造器(通过 super()

  • 示例:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    class Parent {
        protected String name;
    
        public Parent(String name) {
            this.name = name;
        }
    }
    
    class Child extends Parent {
        public Child(String name) {
            super(name); // 必须显式调用父类构造器
        }
    }
    

💡 关键注意事项

  1. 跨包继承时

    • protected 成员在不同包子类中可以直接访问
    • 但不同包的非子类不能访问 protected 成员
  2. 方法重写规则

    • 重写方法的访问权限不能 缩小(如父类方法是 public,子类重写方法不能改为 protected
  3. 成员变量访问

    • 子类可以定义与父类同名的成员变量(但不推荐,会产生隐藏现象)
  4. Java 没有 C++ 式的访问继承

    // Java 不支持这种写法!
    // class Child extends private Parent {} // 非法语法
    
  5. **构造方法是不能继承的 **

    • 但是可以调用
  6. **super访问父类的域 **

    • 通过super.{field_name}来访问
    • 使用super可以访问被子类所隐藏了的同名变量。
    • 当覆盖父类的同名方法的同时,又要调用父类的方法,就必须使用super。