Featured image of post java笔记

java笔记

Java历史

之所以把Java历史摆在最前面,是因为我们需要知道Java的生态为何如此混乱.

  • 1991年06月:项目启动 James Gosling、Mike Sheridan 与 Patrick Naughton 发起 Java 语言项目,最初命名为 Oak,随后曾更名为 Green,最终定名为 Java
  • 1996年:正式发布 Sun Microsystems 发布 Java 1.0 实现。该版本确立了“一次编写,到处运行”(WORA)的核心理念,并因其安全特性和浏览器对 Java Applet 的支持迅速普及。
  • 1997年:标准化博弈 Sun 曾尝试通过 ISO/IEC JTC 1 和 Ecma International 推动 Java 标准化,但随后撤出,转而通过 Java Community Process (JCP) 维持事实上的标准控制。
  • 1998年12月:架构分化 Java 2 (J2SE 1.2) 发布。Java 体系被正式划分为三个方向:面向企业级的 J2EE、面向桌面的 J2SE 以及面向移动设备的 J2ME
  • 2006年:更名与开源启动 出于市场营销考虑,Sun 将版本重新命名为 Java EEJava SEJava ME。同年 11 月,Sun 开始基于 GPL-2.0 协议发布 JVM 开源软件。
  • 2007年:完成开源 除了极少数不持有版权的代码外,Sun 完成了 JVM 核心代码的开源分发。
  • 2009年-2010年:所有权更迭 Oracle 收购 Sun Microsystems。随后不久,Oracle 就 Android SDK 中使用 Java 的问题对 Google 发起诉讼。
  • 2010年04月:核心人物离职 Java 之父 James Gosling 从 Oracle 辞职。
  • 2016年01月:淘汰过时技术 Oracle 宣布从 JDK 9 开始,Java 运行时环境将停止支持浏览器插件。

环境配置

编译器下载

与Cpp运行需要MSVC等编译器,python运行需要python虚拟环境一样,Java运行需要的是JDK(Java Development Kit). 如前面所说,尽管Java版权归Oracle公司所有,但也有开源的社区版本和基于开源版本制作的第三方JDK,性能上的差别非常小. 为了省事,我们直接上微软官网下载JDK21即可,这样可以跳过烦人的环境变量配置环节.

  • 至于为什么不选最新的25版本是因为体积更大也没必要.

配置环境变量(可跳过)

与cpp类似,如果不配置编译器的环境变量的话,系统是无法识别你的命令的:

1
2
3
4
5
6
7
8
java --version
java : 无法将“java”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确
,然后再试一次。
所在位置 行:1 字符: 1
+ java --version
+ ~~~~
    + CategoryInfo          : ObjectNotFound: (java:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

但是,如果你用的是微软的OpenJDK,就可以在安装的时候勾选配置环境变量直接跳过这一步:

1
2
3
4
java --version
openjdk 21.0.10 2026-01-20 LTS
OpenJDK Runtime Environment Microsoft-13106404 (build 21.0.10+7-LTS)
OpenJDK 64-Bit Server VM Microsoft-13106404 (build 21.0.10+7-LTS, mixed mode, sharing)

但如果你铁着头选择了Oracle官方版本的话,那么还是需要自己配置的…预先警告这很麻烦

VScode配置Java环境

当你第一次创建.java文件时,VScode会自动为你推荐所需的扩展并配置好环境,所以无需额外操作.

Java基础学习

  • W3schools
    • 不要看廖雪峰教程…写的并不是很好,而且很枯燥,搞不懂为什么流量这么大.
  • 建议先学好cpp后再学Java,本部分经常会拿cpp来跟Java做比较

基础语法

学习前的要点

我们首先需要知道JAVA的面向对象特性:

  1. 所有函数和变量都写在类里面,因此函数都变成了方法,变量都变成了属性
  2. 如果源文件中包含public类,则文件名必须与该类名完全一致
  3. 所有的类名都需要大写,仅是一种规范,但最好大写.

例如:

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

包含了这个代码的文件必须叫做Main.java,区分大小写.

主函数

上述代码中的main函数是Java程序的入口,其作用与c/cpp中的main函数别无二致:

1
2
3
public static void main(String[] args) {
    System.out.println("Hello World");
  }

如果你不觉得main函数里的String[] args参数很奇怪的话,那我就觉得你很奇怪了.

这个参数作为命令行交互的接收器,尽管你未必会显式用到这个参数,但是必须要写上. 尽管从Java21开始允许不写这个参数了,但是很多企业内部的数据库交互用的依然是Java8…所以还是写上比较好

  • 很难想象这么一个简单的修改需要经过将近三十年的考量…Java生态的陈旧性可见一斑

变量类型

1
2
3
4
5
6
7
// 常用变量类型
int myNum = 5;
float myFloatNum = 5.99f;
double myDoubleNum = 3.14159;
char myLetter = 'D';
boolean myBool = true;
String myText = "Hello";

值得注意的是唯独String这个变量类型是大写的,因为它是一个类,如前面所说,Java中的类名都需要大写,所以这里就大写了.

Java 10(2018年发布)引入了类似于cpp中auto的变量类型var:

1
2
var x = 5;  // x is an int
System.out.println(x);

与auto一样,var不允许只声明不赋值:

1
2
var x; // Error
var x = 5;  // OK

Java数组

由于cpp反直觉的类似int a[10]这样的语法,所以Java将声明时的[]优化到了变量名前面,变成了这样:

1
2
3
String[] cars = {"Volvo", "BMW", "Ford", "Mazda"};
System.out.println(cars[0]);
// Outputs Volvo
  • 尽管其他特性没什么改变,但看上去顺眼多了,毕竟我们都说int 数组a,而不会说int a数组

二维数组

自然,如果是二维数组,就要写两个[]了:

1
2
3
4
5
6
7
int[][] myNumbers = { {1, 4, 2}, {3, 6, 8, 5, 2} };

for (int row = 0; row < myNumbers.length; row++) {
  for (int col = 0; col < myNumbers[row].length; col++) {
    System.out.println("myNumbers[" + row + "][" + col + "] = " + myNumbers[row][col]);
  }
}

Java的for-each循环

由于Java的大多数逻辑语句和cpp别无二致,因此全都略过,但值得一提的是下面这个语法:

1
2
3
4
5
String[] cars = {"Volvo", "BMW", "Ford", "Mazda"};

for (String car : cars) {
  System.out.println(car);
}

这被称为for-each循环,于04年的Java5.0引入,而cpp直到c++11才正式引入:

1
2
3
4
5
std::vector<std::string> cars = {"Volvo", "BMW", "Ford", "Mazda"};

for (const std::string& car : cars) {
    std::cout << car << std::endl;
}

OOP

修饰符

先概览一下Java中的常用修饰符,之后会逐渐根据代码理解的

类修饰符 (Class Modifiers)

在 Java 中,类修饰符决定了类的访问权限、继承特性以及实例化规则。

修饰符描述适用范围
public最宽泛的访问级别。该类对所有类可见。顶级类、内部类
protected对同一包内的类及所有子类可见。仅内部类
default(不写关键字) 仅对同一包内的类可见。顶级类、内部类
private仅对定义它的外部类可见。仅内部类
abstract抽象类。不能被实例化,必须由子类继承并实现其抽象方法。顶级类、内部类
final最终类。不能被继承(例如 java.lang.String)。顶级类、内部类
static静态内部类。不需要依赖外部类实例即可创建。仅内部类
sealed密封类(Java 17+)。限制哪些类可以继承它。顶级类

方法修饰符 (Method Modifiers)

方法修饰符控制方法的访问权限、执行逻辑、以及子类覆盖规则。

访问控制修饰符
  • public: 方法对所有类可见。
  • protected: 方法对同一包内的类及所有子类可见。
  • default: (不写关键字) 仅对同一包内的类可见。
  • private: 仅在当前类内部可见。
非访问控制修饰符
  • static: 静态方法。属于类而非实例,通过类名直接调用,不能访问非静态成员。
  • final: 最终方法。子类可以继承但不能覆盖(Override)此方法。
  • abstract: 抽象方法。没有方法体,必须由非抽象子类实现。

static修饰符详解

首先我们需要知道一件事:尽管Java强制要求所有方法都写在类中,但是有一些方法我们并不想让它与某个类的实例有任何关系,也就是说,我们想要像cpp定义全局函数那样,直接在类内方法中调用该函数,而不需要带上类访问符.,那么就可以用static关键字来修饰某个方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class Main {
  static void myMethod() {
    System.out.println("Hello World!");
  }

  public static void main(String[] args) {
    myMethod();
  }
}

// Outputs "Hello World!"
  • 这里我们直接调用了myMethod方法而不需要通过this指针或者类访问符来操作它

总结一下就是说,Java的static方法属于这个类,类外访问时通过类名.方法调用,类内访问可以直呼其名,而非static方法属于类实例,需要先实例化一个类后再通过实例调用.

创建类实例

Java创建类实例的方法有很多,但最常用的还是通过new操作符:

1
2
3
4
5
6
7
8
public class Main {
  int x = 5;

  public static void main(String[] args) {
    Main myObj = new Main();
    System.out.println(myObj.x);
  }
}

由于Java的垃圾回收机制(很后面会提到),我们不用像在cpp中一样,new一个对象后就要使用delete操作符删除它,还是很便利的.

构造函数

与Java有new没有delete同理,Java有构造函数但没有析构函数,构造函数的写法与cpp的写法相同:与类同名

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Main {
  int x;

  public Main(int y) {
    x = y;
  }

  public static void main(String[] args) {
    Main myObj = new Main(5);
    System.out.println(myObj.x);
  }
}

// Outputs 5

this引用符

由于Java不再显式使用指针,但又需要像cpp一样用某个符号代指类实例,否则无法通过类内方法访问私有属性.

因此,Java引入了this引用符,用来指向当前的类实例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Main {
  int x;  // Class variable x

  // Constructor with one parameter x
  public Main(int x) {
    this.x = x; // refers to the class variable x
  }

  public static void main(String[] args) {
    // Create an object of Main and pass the value 5 to the constructor
    Main myObj = new Main(5);
    System.out.println("Value of x = " + myObj.x);
  }
}
  • 这与Python中的self有异曲同工之处,但不同的是self需要显示声明,而Java的this与cpp的this一样都是隐式存在

构造函数的重载

 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 class Main {
  int modelYear;
  String modelName;

  // Constructor with one parameter
  public Main(String modelName) {
    // Call the two-parameter constructor to reuse code and set a default year    
    this(2020, modelName);
  }

  // Constructor with two parameters
  public Main(int modelYear, String modelName) {
    // Use 'this' to assign values to the class variables
    this.modelYear = modelYear;
    this.modelName = modelName;
  }

  // Method to print car information
  public void printInfo() {
    System.out.println(modelYear + " " + modelName);
  }

  public static void main(String[] args) {
    // Create a car with only model name (uses default year)
    Main car1 = new Main("Corvette");

    // Create a car with both model year and name
    Main car2 = new Main(1969, "Mustang");

    car1.printInfo();
    car2.printInfo();
  }
}

通过构造函数的重载可以实现类的不同初始化数值

非常值得注意

上述代码的this(2020, modelName)并非是简单的语法糖,它指向的是完整版本的构造函数,从而实现代码的复用,换句话说,如果只写这一句而不写完整构造函数就会报错.

  • 这么来看的话,它与cpp中的初始化列表完全不同
1
2
3
4
5
6
7
8
9
class Main {
public:
    int modelYear;
    string modelName;

    // 使用初始化列表:变量(值)
    Main(string name) : modelYear(2020), modelName(name) {
    }
}

继承与多态

由于cpp中的继承符号:过于简单和抽象,因此Java将继承符号改为了继承关键字extends:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class Vehicle {
  protected String brand = "Ford";        // Vehicle attribute
  public void honk() {                    // Vehicle method
    System.out.println("Tuut, tuut!");
  }
}

class Car extends Vehicle {
  private String modelName = "Mustang";    // Car attribute
  public static void main(String[] args) {

    // Create a myCar object
    Car myCar = new Car();

    // Call the honk() method (from the Vehicle class) on the myCar object
    myCar.honk();

    // Display the value of the brand attribute (from the Vehicle class) and the value of the modelName from the Car class
    System.out.println(myCar.brand + " " + myCar.modelName);
  }
}

final修饰符详解

Java将cpp中的常量修饰符const改成了final,这可不是脱裤子放屁,而是因为Java中的final还可以限制某些类不可被继承:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
final class Vehicle {
  ...
}

class Car extends Vehicle {
  ...
}
// Main.java:9: error: cannot inherit from final Vehicle
// class Main extends Vehicle {
//                   ^
// 1 error)

将类也看做常量的想法确实挺奇妙的,但一般来说根本用不到吧.

多态

Java中的方法默认是可以被重载的,这比起cpp要便利很多,同样,被final修饰的方法只能被继承但不能被重载:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Animal {
  public void animalSound() {
    System.out.println("The animal makes a sound");
  }
}

class Pig extends Animal {
  public void animalSound() {
    System.out.println("The pig says: wee wee");
  }
}

class Dog extends Animal {
  public void animalSound() {
    System.out.println("The dog says: bow wow");
  }
}

super引用符

Java设计了super引用符来指向父类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Animal {
  public void animalSound() {
    System.out.println("The animal makes a sound");
  }
}

class Dog extends Animal {
  public void animalSound() {
    super.animalSound(); // Call the parent method
    System.out.println("The dog says: bow wow");
  }
}

public class Main {
  public static void main(String[] args) {
    Dog myDog = new Dog();
    myDog.animalSound();
  }
}

当然,super更强大的地方在于能够调用父类的构造函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
  Animal() {
    System.out.println("Animal is created");
  }
}

class Dog extends Animal {
  Dog() {
    super(); // Call parent constructor
    System.out.println("Dog is created");
  }
}

public class Main {
  public static void main(String[] args) {
    Dog myDog = new Dog();
  }
}
// Animal is created
// Dog is created

这其实很好理解,把super换成父类的类名即可看懂了.

抽象与接口

Java将Cpp中的虚函数与重载机制进一步发扬光大,发明了抽象方法和接口,简单来说的话,抽象类内的抽象方法没有函数内容,而且必须被子类重载实现;而接口就是抽象类的简写版,里面的所有函数和属性默认都要被重载实现. 抽象类和抽象方法

 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
// Abstract class
abstract class Animal {
  // Abstract method (does not have a body)
  public abstract void animalSound();
  // Regular method
  public void sleep() {
    System.out.println("Zzz");
  }
}

// Subclass (inherit from Animal)
class Pig extends Animal {
  public void animalSound() {
    // The body of animalSound() is provided here
    System.out.println("The pig says: wee wee");
  }
}

class Main {
  public static void main(String[] args) {
    Pig myPig = new Pig(); // Create a Pig object
    myPig.animalSound();
    myPig.sleep();
  }
}

为了区分抽象类和接口,Java特定设置了接口的继承关键字implements,从而与extends区分开来:

 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
// Interface
interface Animal {
  public void animalSound(); // interface method (does not have a body)
  public void sleep(); // interface method (does not have a body)
}

// Pig "implements" the Animal interface
class Pig implements Animal {
  public void animalSound() {
    // The body of animalSound() is provided here
    System.out.println("The pig says: wee wee");
  }
  public void sleep() {
    // The body of sleep() is provided here
    System.out.println("Zzz");
  }
}

class Main {
  public static void main(String[] args) {
    Pig myPig = new Pig();  // Create a Pig object
    myPig.animalSound();
    myPig.sleep();
  }
}

至于为什么Java要单独设置implements关键字,是因为它确实与extends有些许不同,implements后可以跟多个接口,而extends后只可以跟一个父类.

  • 为什么这么设计?那是设计者的问题了
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface FirstInterface {
  public void myMethod(); // interface method
}

interface SecondInterface {
  public void myOtherMethod(); // interface method
}

class DemoClass implements FirstInterface, SecondInterface {
  public void myMethod() {
    System.out.println("Some text..");
  }
  public void myOtherMethod() {
    System.out.println("Some other text...");
  }
}

class Main {
  public static void main(String[] args) {
    DemoClass myObj = new DemoClass();
    myObj.myMethod();
    myObj.myOtherMethod();
  }
}

Java高级特性

说是高级,其实都很简单,都怪营销号和垃圾博客把Java渲染的多么复杂高深,比起Cpp来说,Java简直不能再简单了.

Wrapper Classes(封装类)

Primitive Data TypeWrapper Class
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter

由于Java内置的数据结构如ArrayList只能存储对象,所以我们需要将数据类型包装成一个数据类来处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import java.util.ArrayList;

public class Main {
  public static void main(String[] args) {
    ArrayList<String> cars = new ArrayList<String>();
    cars.add("Volvo");
    cars.add("BMW");
    cars.add("Ford");
    cars.add("Mazda");
    System.out.println(cars);
  }
}
  • 自然,这种脱裤子放屁的事情在Java中还有很多… alt text

Java 泛型(generic)

将不同类型的数据统一处理

 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
class Box<T> {
  T value; // T is a placeholder for any data type

  void set(T value) {
    this.value = value;
  }

  T get() {
    return value;
  }
}

public class Main {
  public static void main(String[] args) {
    // Create a Box to hold a String
    Box<String> stringBox = new Box<>();
    stringBox.set("Hello");
    System.out.println("Value: " + stringBox.get());

    // Create a Box to hold an Integer
    Box<Integer> intBox = new Box<>();
    intBox.set(50);
    System.out.println("Value: " + intBox.get());
  }
}

处理不同的数据输入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Main {
  // Generic method: works with any type T
  public static <T> void printArray(T[] array) {
    for (T item : array) {
      System.out.println(item);
    }
  }

  public static void main(String[] args) {
    // Array of Strings
    String[] names = {"Jenny", "Liam"};

    // Array of Integers
    Integer[] numbers = {1, 2, 3};

    // Call the generic method with both arrays
    printArray(names);
    printArray(numbers);
  }
}

Java Annotations(Java注解)

Annotations are special notes you add to your Java code. They start with the @ symbol.

注解并不会改变程序的运行方式,但是会为编译器和构建工具提供额外的信息,这与Python中的语法糖完全不同.

最常用的注解有三个:

  1. @Override: Indicates that a method overrides a method in a superclass
  2. @Deprecated: Marks a method or class as outdated or discouraged from use
  3. @SuppressWarnings: Tells the compiler to ignore certain warnings
  • 尽管有点用吧,但依然是累赘设计…

Java多线程

Java中有两种方法可以创建进程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 1.继承Thread系统类
public class Main extends Thread {
  public void run() {
    System.out.println("This code is running in a thread");
  }
}
// 2.实现Runnable接口,更推荐
public class Main implements Runnable {
  public void run() {
    System.out.println("This code is running in a thread");
  }
}

真正要详细了解多线程需要在后面的Java线程池中学习

Java Lambda函数

基本格式

1
(parameters) -> { body }
  1. (parameters):类似于方法的参数列表。如果没有参数,直接写 ();如果只有一个参数且类型可推导,可以省略圆括号。
  2. ->:Lambda 运算符,固定写法,代表“传递”或“应用”。
  3. { body }:函数体。如果逻辑只有一行代码,可以省略花括号和 return 关键字。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.util.ArrayList;
import java.util.function.Consumer;

public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    numbers.add(5);
    numbers.add(9);
    numbers.add(8);
    numbers.add(1);
    
    Consumer<Integer> method = (n) -> { System.out.println(n); };
    numbers.forEach(method);
  }
}

如果你详细看上述代码的话,你会看到一个神奇的地方, method类竟然和一个匿名函数用等号连接起来了:

1
Consumer<Integer> method = (n) -> { ... };

这个特性就引出了Java的函数式接口特性,该特性由Java 8引入,属于比较高级的特性,故放在后面讲

Java系统库

随便看看即可,用上的时候再去详细了解

Java文件读写

The File class from the java.io package, allows us to work with files:

1
2
3
import java.io.File;  // Import the File class

File myObj = new File("filename.txt"); // Specify the filename

创建文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import java.io.File;       // Import the File class
import java.io.IOException; // Import IOException to handle errors

public class CreateFile {
  public static void main(String[] args) {
    try {
      File myObj = new File("filename.txt"); // Create File object
      if (myObj.createNewFile()) {           // Try to create the file
        System.out.println("File created: " + myObj.getName());
      } else {
        System.out.println("File already exists.");
      }
    } catch (IOException e) {
      System.out.println("An error occurred.");
      e.printStackTrace(); // Print error details
    }
  }
}

写入文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import java.io.FileWriter;   // Import the FileWriter class
import java.io.IOException;  // Import the IOException class

public class WriteToFile {
  public static void main(String[] args) {
    try {
      FileWriter myWriter = new FileWriter("filename.txt");
      myWriter.write("Files in Java might be tricky, but it is fun enough!");
      myWriter.close();  // must close manually
      System.out.println("Successfully wrote to the file.");
    } catch (IOException e) {
      System.out.println("An error occurred.");
      e.printStackTrace();
    }
  }
}

读取文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import java.io.File;                  // Import the File class
import java.io.FileNotFoundException; // Import this class to handle errors
import java.util.Scanner;             // Import the Scanner class to read text files

public class ReadFile {
  public static void main(String[] args) {
    File myObj = new File("filename.txt");

    // try-with-resources: Scanner will be closed automatically
    try (Scanner myReader = new Scanner(myObj)) {
      while (myReader.hasNextLine()) {
        String data = myReader.nextLine();
        System.out.println(data);
      }
    } catch (FileNotFoundException e) {
      System.out.println("An error occurred.");
      e.printStackTrace();
    }
  }
}

Java I/O

读缓冲区

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    try (BufferedReader br = new BufferedReader(new FileReader("filename.txt"))) {
      String line;
      while ((line = br.readLine()) != null) {
        System.out.println(line);
      }
    } catch (IOException e) {
      System.out.println("Error reading file.");
    }
  }
}

写缓冲区

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    try (BufferedWriter bw = new BufferedWriter(new FileWriter("filename.txt"))) {
      bw.write("First line");
      bw.newLine();  // add line break
      bw.write("Second line");
      System.out.println("Successfully wrote to the file.");
    } catch (IOException e) {
      System.out.println("Error writing file.");
    }
  }
}

Java数据结构

最常用的数据结构都被包装到java.util包中了:

  • ArrayList
  • HashSet
  • HashMap

这三个数据结构同样也是Java岗面试的必考点,尽管不太看得出来考的必要

快速对照表

Java 数据结构C++ (STL) 对照底层实现排序性
ArrayListstd::vector动态数组插入顺序
HashSetstd::unordered_set哈希表无序
HashMapstd::unordered_map哈希表无序
TreeSetstd::set红黑树自动排序
TreeMapstd::map红黑树自动排序

Java进阶学习

Java编译与构建

学习完OOP后,我们很自然的会将不同功能的类拆分到不同java文件中,那么接下来就来看看Java是如何编译和构建不同文件的

JVM

Java8新特性

函数式接口

Java构建工具

与Cpp有Make,ninja,CMake类似,Java也有自己的构建工具,早期的构建工具为Ant,目前由Maven和Gradle两款工具统治,它俩也同时承担了包管理器的责任.

Maven

Maven历史

1. 概念起源与孵化 (2002 - 2003)

  • 2002年:由 Jason van Zyl 创建,最初作为 Apache Turbine 的子项目,旨在解决该项目极其复杂的构建过程。
  • 2003年:正式被接纳为 Apache 软件基金会的顶级项目。

2. 标准确立与快速普及 (2004 - 2005)

  • 2004年7月 (Version 1.0):第一个里程碑版本发布。确立了“约定优于配置”的核心理念。
  • 2005年10月 (Version 2.0):重大架构升级版本。
    • 核心贡献:确立了 Maven 中央仓库 (Central Repository) 机制,支持动态下载依赖。
    • 插件架构:采用基于插件的架构,使其具备了跨语言构建(如 C/C++)的潜力。

3. 架构优化与并行化 (2010)

  • 2010年10月 (Version 3.0)
    • 解耦与兼容:在保持与 2.x 项目兼容的同时,重构了核心项目构建器。
    • 性能提升:引入并行构建特性,能够利用多核 CPU 处理大型多模块项目。
    • 非 XML 尝试:开始支持非 XML 的项目定义文件(如 Ruby、YAML、Groovy),解耦了内存表示与文件格式。

使用方法

Gradle

Gradle历史

1. 概念孵化与 Groovy 基因 (2007 - 2008)

  • 2007年:Hans Dockter 开始构思一种结合 Ant 的灵活性与 Maven 的依赖管理能力的新工具。
  • 2008年4月 (Version 0.1):首个版本发布。创始人最初想命名为“Cradle”,但为了使其更具独特性,取 Groovy 语言的首字母 G,更名为 Gradle。其核心特征是使用基于 Groovy 的 DSL 取代繁琐的 XML 配置。

2. 核心架构确立 (2012 - 2014)

  • 2012年6月 (Version 1.0):历经四年迭代,首个正式稳定版发布。确立了有向无环图 (DAG) 任务模型,解决了复杂构建流程的编排问题。
  • 2013年Google 宣布 Android Studio 采用 Gradle 作为官方构建系统。这一决策直接将 Gradle 推向了顶级构建工具的地位。
  • 2014年7月 (Version 2.0):提升了构建性能,并正式引入了对原生语言(C/C++)构建的初步支持,展示了其跨语言构建的野心。

3. 性能突破与 Kotlin 引入 (2016 - 2018)

  • 2016年8月 (Version 3.0):引入 Gradle Daemon(守护进程)并默认开启,极大地缩短了短周期任务的启动时间。
  • 2017年6月 (Version 4.0):引入 Build Cache(构建缓存)技术,允许跨机器共享编译产物,大幅减少了大型项目的重复编译时间。
  • 2018年11月 (Version 5.0):正式支持 Kotlin DSL。开发者可以使用类型安全的 Kotlin 编写构建脚本,解决了 Groovy 脚本缺乏 IDE 智能补全和编译时检查的痛点。

4. 现代化与大规模构建优化 (2019 - 2025)

  • 2021年4月 (Version 7.0):引入 Version Catalogs(版本目录),实现了多项目之间依赖版本的集中管理,正式统一了大型项目的依赖规范。
  • 2023年2月 (Version 8.0):针对配置阶段进行了深度优化,支持 Configuration Cache,使得复杂项目的构建配置速度提升数倍。
  • 2025年7月 (Version 9.0):最新里程碑版本。进一步强化了云端构建能力和对现代 JDK 特性的深度适配,确立了其在高复杂度、高性能要求构建场景下的统治地位。

使用Docker开发Java

Licensed under CC BY-NC-SA 4.0
网站总访客数:Loading
网站总访问量:Loading
使用 Hugo 构建
主题 StackJimmy 设计