专业编程基础技术教程

网站首页 > 基础教程 正文

划重点,Java入门指南

ccvgpt 2025-03-03 17:37:01 基础教程 10 ℃

Java 具有诸多特性,使其在编程领域占据重要地位。跨平台性是 Java 的一大亮点,通过 Java 虚拟机(JVM),Java 程序实现 “一次编写,到处运行”,这意味着开发者只需编写一次代码,就能在不同操作系统(如 Windows、Linux、Mac OS)上运行,大大提高开发效率。

JDK,即 Java Development Kit,是 Java 开发工具包。它是 Java 开发的核心,包含了一系列开发、调试和运行 Java 应用程序所必需的工具和资源。这就好比一个大型的建筑工具箱,里面有锤子、钉子、锯子等各种工具,开发者可以利用这些工具来构建自己的 Java 程序大厦。JDK 中不仅包含了 Java 编译器(javac),它能将我们编写的 Java 源代码(.java 文件)编译成字节码(.class 文件),就像是将建筑图纸转化为实际的建筑材料;还包含了 Java 虚拟机(JVM),以及丰富的 Java 类库,这些类库提供了大量预编译的类和接口,涵盖了文件操作、网络编程、图形界面开发等各个方面,为开发者节省了大量的时间和精力。

划重点,Java入门指南

JRE,全称 Java Runtime Environment,也就是 Java 运行时环境。它是 Java 程序能够正常运行的最小环境集合,就像是一个已经布置好基本设施的舞台,只等 Java 程序这个演员登场表演。JRE 包含了 Java 虚拟机(JVM)和 Java 核心类库,以及支持文件。JVM 是 JRE 的核心组件,负责执行 Java 字节码,而 Java 核心类库则为 Java 程序提供了运行所需的各种基础功能。如果把 JDK 比作一个大型的建筑公司,那么 JRE 就是这个公司中已经搭建好的一个小型建筑场地,虽然没有 JDK 那么全面,但足以让 Java 程序在这里运行起来。

JVM,即 Java Virtual Machine,Java 虚拟机。它是 Java 实现跨平台的关键所在,是一个虚构出来的计算机环境,就像是一个神奇的翻译官,能够将 Java 字节码翻译成不同操作系统和硬件平台能够理解的机器码。无论你是在 Windows 系统、Mac 系统还是 Linux 系统上运行 Java 程序,JVM 都能确保程序的正确执行,而无需考虑底层硬件和操作系统的差异。它提供了内存管理、垃圾回收、多线程支持等核心功能,保障了 Java 程序的稳定、高效运行。

面向对象特性是 Java 的核心,它将数据和操作封装在类中,通过类的实例(对象)来实现具体功能。这种编程方式符合人类思维习惯,使代码更易理解、维护和扩展。

(一)安装JAVA环境

下载和安装JDK

目前,主流的 JDK 发行版有 Oracle JDK 和 OpenJDK 。Oracle JDK 是由 Oracle 公司官方提供的,包含了商业许可条款和支持选项,对于企业级开发,若需要官方商业支持和额外企业功能,它是不错之选;OpenJDK 则是开源的 Java 开发工具包,由社区和多个公司共同维护,具备完全免费的优势,能满足大多数开发需求。

Oracle JDK 可在 Oracle 官方网站(
https://www.oracle.com/java/technologies/downloads/)下载 ,在这里,你能依据自身操作系统,如 Windows、macOS、Linux,以及系统的位数(32 位或 64 位),挑选合适版本的 JDK。OpenJDK 的下载地址为https://jdk.java.net/ ,你同样可以根据操作系统和架构进行版本筛选。

举个例子,若你使用的是 64 位 Windows 10 系统,想要下载最新的 JDK 17,在 Oracle 官网下载页面,找到适用于 Windows x64 的 JDK 17 安装包进行下载;要是选择 OpenJDK,在对应的下载页面,找到符合 Windows 64 位系统的 JDK 17 版本下载即可。

Windows 系统

下载完成后,找到安装文件(通常是.exe 格式),双击运行它。安装向导会弹出,首先是欢迎界面,点击 “下一步”。接着是安装路径选择界面,默认路径一般为 C:\Program Files\Java\jdk [版本号] ,但建议安装路径不要包含中文和空格,你可以自定义路径,比如将其安装到 D 盘的 Java 文件夹下,选择好路径后点击 “下一步”。之后,安装组件选择界面会列出要安装的 JDK 组件,包括开发工具、源代码、公共 JRE 等,保持默认选项,点击 “下一步”,安装程序便开始复制文件进行安装,等待安装完成。安装完成后,务必记住安装目录,后续配置环境变量会用到。

右键点击 “此电脑”,选择 “属性”,在弹出的窗口中点击 “高级系统设置”,在 “系统属性” 窗口的 “高级” 选项卡中,点击 “环境变量”。在 “系统变量” 部分,点击 “新建”,新建一个名为 “JAVA_HOME” 的变量,变量值填写 JDK 的安装路径,比如 “C:\Program Files\Java\jdk17” 。接着找到 “Path” 变量,点击 “编辑”,在弹出的窗口中点击 “新建”,添加 “% JAVA_HOME%\bin”,添加完成后点击 “确定” 关闭所有窗口。为验证配置是否成功,按下 Win+R 键,输入 “cmd” 打开命令提示符,输入:

java -version

若显示 JDK 的版本信息,则说明环境变量配置成功。

macOS 系统

下载的是.dmg 文件,下载完成后双击该文件打开安装包,在安装包中找到 JDK 的安装程序,双击它开始安装。安装过程中,系统会提示你输入管理员密码,输入正确密码后,按照提示逐步完成安装。在 macOS 系统中,JDK 默认安装路径为
/Library/Java/JavaVirtualMachines/ ,安装完成后可在该路径下找到对应的 JDK 文件夹。

打开终端,若使用的是bash shell,编辑.bashrc 文件,输入:

 vi ~/.bashrc #如果使用的是 zsh shell,则编辑.zshrc 文件,输入 vi ~/.zshrc

在文件末尾添加

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-17.jdk/Contents/Home #这里的路径需根据实际安装的 JDK 路径修改,以JDK 17为例
export PATH=\(JAVA_HOME/bin:\)PATH

添加完成后,按下 Esc 键,输入 “:wq” 保存并退出编辑器。然后在终端中输入:

source ~/.bashrc   #(或source ~/.zshrc)  #使配置生效

同样,在终端输入:

java -version #验证配置是否成功

Linux 系统

对于不同的 Linux 发行版,安装方式略有不同。以 Ubuntu 为例,可通过 apt 命令安装 OpenJDK,打开终端,输入:

sudo apt-get update #更新软件

然后输入:

sudo apt-get install openjdk-17-jdk  #这里以安装 OpenJDK 17 为例,若要安装其他版本,修改版本号即可

按照提示输入管理员密码并确认安装。

对于其他发行版,如 CentOS,若下载的是 tar.gz 格式的 JDK 安装包,下载完成后,打开终端,使用命令:

tar -zxvf jdk-17.0.1_linux-x64_bin.tar.gz -C /usr/local/java #将JDK安装到/usr/local/java目录下进行解压安装

安装完成后进入安装目录确认文件是否解压成功。

(二)集成开发环境(IDE)

IntelliJ IDEA 是一款功能强大的 Java 集成开发环境(IDE),可在 JetBrains 官方网站(
https://www.jetbrains.com/idea/download/ )下载 。进入官网后,根据操作系统选择对应的版本下载,有社区版(Community)和旗舰版(Ultimate),社区版免费且功能足以满足初学者和一般开发需求。

首次启动 IntelliJ IDEA,会出现配置界面,选择 “Java Development” 选项,它会自动安装一些必要的 Java 开发插件。创建一个新的 Java 项目时,在 “New Project” 界面,左侧选择 “Java”,在 “Project SDK” 处选择之前安装好的 JDK 路径,然后配置项目名称和项目路径,点击 “Finish” 即可创建项目。此时,你就可以在 IntelliJ IDEA 中编写和运行 Java 程序了。

当然还有其他的IDE, 比如VsCode, Eclipse等,但还是推荐使用IntelliJ,这里就不一一介绍了。


(三)基础语法学习

Java 中的运算符主要有以下几类:

  • 算术运算符:用于进行基本的算术运算,如加(+)、减(-)、乘(*)、除(/)、取余(%)等。例如:
int a = 10;
int b = 3;
int sum = a + b; // 加法运算,结果为13
int difference = a - b; // 减法运算,结果为7
int product = a * b; // 乘法运算,结果为30
int quotient = a / b; // 除法运算,结果为3,因为是整数除法,会舍去小数部分
int remainder = a % b; // 取余运算,结果为1
  • 赋值运算符:用于将一个值赋给一个变量,最常用的赋值运算符是 “=”。例如:int num = 10; 。此外,还有一些复合赋值运算符,如 “+=”、“-=”、“*=”、“/=”、“%=” 等,它们可以在进行运算的同时进行赋值操作。例如:
int a = 10;
a += 5; // 相当于 a = a + 5,结果a为15
a -= 3; // 相当于 a = a - 3,结果a为12
a *= 2; // 相当于 a = a * 2,结果a为24
a /= 4; // 相当于 a = a / 4,结果a为6
a %= 3; // 相当于 a = a % 3,结果a为0
  • 比较运算符:用于比较两个值的大小或是否相等,结果是一个 boolean 类型的值(true 或 false)。常见的比较运算符有:等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)、小于等于(<=)。例如:
int a = 10;
int b = 5;
boolean result1 = a == b; // 比较a和b是否相等,结果为false
boolean result2 = a > b; // 比较a是否大于b,结果为true
boolean result3 = a <= b; // 比较a是否小于等于b,结果为false
  • 逻辑运算符:用于对 boolean 类型的值进行逻辑运算,结果也是一个 boolean 类型的值。常见的逻辑运算符有:与(&&)、或(||)、非(!)。例如:
boolean a = true;
boolean b = false;
boolean result1 = a && b; // 逻辑与运算,只有当a和b都为true时,结果才为true,所以结果为false
boolean result2 = a || b; // 逻辑或运算,只要a或b中有一个为true,结果就为true,所以结果为true
boolean result3 =!a; // 逻辑非运算,对a取反,结果为false
  • 位运算符:用于对整数类型的数据进行按位运算,它直接对二进制位进行操作。常见的位运算符有:按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)、右移(>>)、无符号右移(>>>)。位运算符在一些底层开发和算法实现中经常会用到,不过对于初学者来说,可能使用频率相对较低。例如:
int a = 5; // 二进制表示为 00000101
int b = 3; // 二进制表示为 00000011
int result1 = a & b; // 按位与运算,结果为 00000001,即1
int result2 = a | b; // 按位或运算,结果为 00000111,即7
int result3 = a ^ b; // 按位异或运算,结果为 00000110,即6
int result4 = ~a; // 按位取反运算,结果为 11111010,即-6(在计算机中,负数以补码形式表示)
int result5 = a << 2; // 左移2位,结果为 00010100,即20
int result6 = a >> 1; // 右移1位,结果为 00000010,即2
  • if-else语句

if-else语句是最常用的选择结构之一,它的基本语法如下:

if (条件表达式) {
    // 当条件表达式为true时执行的代码块
} else {
    // 当条件表达式为false时执行的代码块
}

例如,我们要根据一个学生的成绩判断他是否及格:

public class IfElseExample {
    public static void main(String[] args) {
        int score = 85;
        if (score >= 60) {
            System.out.println("该学生成绩及格");
        } else {
            System.out.println("该学生成绩不及格");
        }
    }
}

除了基本的if-else结构,还可以使用if-else if-else结构来处理多个条件的情况。例如:

public class IfElseIfExample {
    public static void main(String[] args) {
        int score = 85;
        if (score >= 90) {
            System.out.println("成绩优秀");
        } else if (score >= 80) {
            System.out.println("成绩良好");
        } else if (score >= 60) {
            System.out.println("成绩及格");
        } else {
            System.out.println("成绩不及格");
        }
    }
}

在这个例子中,程序会依次判断score是否满足各个条件,一旦找到满足的条件,就会执行对应的代码块,然后结束整个if-else if-else结构。

  • switch-case语句

switch-case语句也是一种选择结构,它根据一个表达式的值,来选择执行不同的代码块。它的基本语法如下:

switch (表达式) {
    case 值1:
        // 当表达式的值等于值1时执行的代码块
        break;
    case 值2:
        // 当表达式的值等于值2时执行的代码块
        break;
   ...
    default:
        // 当表达式的值与所有case的值都不匹配时执行的代码块
        break;
}

例如,我们根据一个整数来判断它对应的星期几:

public class SwitchCaseExample {
    public static void main(String[] args) {
        int day = 3;
        switch (day) {
            case 1:
                System.out.println("星期一");
                break;
            case 2:
                System.out.println("星期二");
                break;
            case 3:
                System.out.println("星期三");
                break;
            case 4:
                System.out.println("星期四");
                break;
            case 5:
                System.out.println("星期五");
                break;
            case 6:
                System.out.println("星期六");
                break;
            case 7:
                System.out.println("星期日");
                break;
            default:
                System.out.println("无效的日期值");
                break;
        }
    }
}

在这个例子中,程序会根据day的值,选择执行对应的case代码块。如果day的值与所有case的值都不匹配,则执行default代码块。需要注意的是,break语句用于结束当前switch结构,如果没有break语句,程序会继续执行下一个case代码块,直到遇到break语句或者switch结构结束。

  • for循环

for循环通常用于已知循环次数的情况,它的语法结构如下:

for (初始化表达式; 条件表达式; 更新表达式) {
    // 循环体
}

例如,我们要打印 1 到 10 的数字:

public class ForLoopExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            System.out.println(i);
        }
    }
}

在这个例子中,for循环的初始化表达式int i = 1用于初始化循环变量i,条件表达式i <= 10用于判断循环是否继续,更新表达式i++用于更新循环变量i的值。每次循环时,程序会先判断条件表达式是否为true,如果是,则执行循环体,然后执行更新表达式,再判断条件表达式,直到条件表达式为false时,结束循环。

  • while循环

while循环通常用于循环次数不确定的情况,它的语法结构如下:

while (条件表达式) {
    // 循环体
}

例如,我们要计算 1 到 100 的累加和:

public class WhileLoopExample {
    public static void main(String[] args) {
        int sum = 0;
        int i = 1;
        while (i <= 100) {
            sum += i;
            i++;
        }
        System.out.println("1到100的累加和为:" + sum);
    }
}

在这个例子中,程序首先初始化变量sum和i,然后在while循环中,判断i是否小于等于 100,如果是,则执行循环体,将i累加到sum中,并将i加 1,直到i大于 100 时,结束循环。

  • do-while循环

do-while循环与while循环类似,但它会先执行一次循环体,然后再判断条件表达式。它的语法结构如下:

do {
    // 循环体
} while (条件表达式);

例如,我们要让用户输入一个数字,直到输入的数字为 0 时结束循环:

import java.util.Scanner;

public class DoWhileLoopExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int num;
        do {
            System.out.print("请输入一个数字(输入0结束):");
            num = scanner.nextInt();
            System.out.println("你输入的数字是:" + num);
        } while (num!= 0);
        scanner.close();
    }
}

在这个例子中,程序会先执行一次循环体,提示用户输入数字并输出用户输入的数字,然后判断num是否不等于 0,如果是,则继续执行循环体,直到num等于 0 时,结束循环。

在循环中,我们还可以使用break语句和continue语句来控制循环的执行。break语句用于立即终止当前循环,跳出循环体;continue语句用于跳过本次循环的剩余部分,直接开始下一次循环。例如:

public class BreakAndContinueExample {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                break; // 当i等于5时,终止循环
            }
            System.out.println(i);
        }

        System.out.println("----------------");

        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                continue; // 当i等于5时,跳过本次循环
            }
            System.out.println(i);
        }
    }
}

在第一个for循环中,当i等于 5 时,执行break语句,循环立即终止;在第二个for循环中,当i等于 5 时,执行continue语句,跳过本次循环的剩余部分,直接开始下一次循环,所以数字 5 不会被输出。

(四)进阶学习

  • 集合与泛型

Java 集合框架主要由 Collection 和 Map 两个接口派生而来。Collection 接口又派生了 List、Set 和 Queue 等子接口,它们各自有不同的实现类,如 ArrayList、LinkedList、HashSet、TreeSet 等。ArrayList 基于动态数组实现,查询效率高,但插入和删除操作相对较慢,尤其是在数组中间位置进行操作时,需要移动大量元素;LinkedList 基于双向链表实现,插入和删除操作效率高,因为只需调整链表节点的指针,但查询效率较低,需要遍历链表。

List:

import java.util.ArrayList;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        // 创建一个 ArrayList 对象
        List list = new ArrayList<>();

        // 添加元素
        list.add("apple");
        list.add("banana");
        list.add("cherry");

        // 访问元素
        System.out.println("第一个元素: " + list.get(0));

        // 遍历元素
        for (String fruit : list) {
            System.out.println(fruit);
        }

        // 删除元素
        list.remove("banana");
        System.out.println("删除 banana 后: " + list);
    }
}

Set:

import java.util.HashSet;
import java.util.Set;

public class SetExample {
    public static void main(String[] args) {
        // 创建一个 HashSet 对象
        Set set = new HashSet<>();

        // 添加元素
        set.add("dog");
        set.add("cat");
        set.add("dog"); // 重复元素不会被添加

        // 遍历元素
        for (String animal : set) {
            System.out.println(animal);
        }

        // 检查元素是否存在
        boolean containsCat = set.contains("cat");
        System.out.println("Set 中是否包含 cat: " + containsCat);
    }
}

Queue:

import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {
    public static void main(String[] args) {
        // 创建一个 LinkedList 作为队列
        Queue queue = new LinkedList<>();

        // 入队操作
        queue.offer("task1");
        queue.offer("task2");
        queue.offer("task3");

        // 出队操作
        String firstTask = queue.poll();
        System.out.println("出队的任务: " + firstTask);

        // 查看队首元素
        String peekTask = queue.peek();
        System.out.println("队首任务: " + peekTask);

        // 遍历队列
        for (String task : queue) {
            System.out.println(task);
        }
    }
} 

Map 接口用于存储键值对,常见的实现类有 HashMap、TreeMap 和 ConcurrentHashMap 等。HashMap 基于哈希表实现,通过哈希值快速定位键值对,具有较高的查找效率,但在多线程环境下是非线程安全的;TreeMap 基于红黑树实现,能够对键进行排序,适用于需要有序遍历键值对的场景;ConcurrentHashMap 是线程安全的哈希表,采用了分段锁机制,允许多个线程同时访问不同的段,提高了并发性能。

HashMap:

import java.util.HashMap;
import java.util.Map;

public class MapExample {
    public static void main(String[] args) {
        // 创建一个 HashMap 对象
        Map map = new HashMap<>();

        // 添加键值对
        map.put("one", 1);
        map.put("two", 2);
        map.put("three", 3);

        // 根据键获取值
        Integer value = map.get("two");
        System.out.println("键 two 对应的值: " + value);

        // 遍历键值对
        for (Map.Entry entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 检查键是否存在
        boolean containsKey = map.containsKey("three");
        System.out.println("Map 中是否包含键 three: " + containsKey);

        // 删除键值对
        map.remove("one");
        System.out.println("删除键 one 后的 Map: " + map);
    }
}

泛型是 Java 5.0 引入的新特性,它允许在定义类、接口和方法时使用类型参数,使得代码可以处理不同类型的数据,同时保证类型安全。在使用泛型时,需要注意类型擦除的问题,即泛型信息只在编译期存在,运行时会被擦除。此外,使用通配符 ? 可以增加代码的灵活性,? extends T 表示类型的上限,即只能是 T 或 T 的子类;? super T 表示类型的下限,即只能是 T 或 T 的父类。

泛型类:

// 定义一个泛型类
class Box {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

public class GenericClassExample {
    public static void main(String[] args) {
        // 创建一个存储 Integer 类型的 Box 对象
        Box integerBox = new Box<>();
        integerBox.setItem(10);
        Integer num = integerBox.getItem();
        System.out.println("Integer Box 中的值: " + num);

        // 创建一个存储 String 类型的 Box 对象
        Box stringBox = new Box<>();
        stringBox.setItem("Hello, World!");
        String str = stringBox.getItem();
        System.out.println("String Box 中的值: " + str);
    }
}

泛型接口:

// 定义一个泛型接口
interface Pair {
    K getKey();
    V getValue();
}

// 实现泛型接口
class OrderedPair implements Pair {
    private K key;
    private V value;

    public OrderedPair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public K getKey() {
        return key;
    }

    @Override
    public V getValue() {
        return value;
    }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        // 创建一个存储 String 和 Integer 类型的 OrderedPair 对象
        Pair pair = new OrderedPair<>("one", 1);
        System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());
    }
}

泛型方法:

class GenericMethodExample {
    // 定义一个泛型方法
    public static  void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // 创建一个 Integer 数组
        Integer[] intArray = {1, 2, 3, 4, 5};
        // 调用泛型方法打印 Integer 数组
        printArray(intArray);

        // 创建一个 String 数组
        String[] stringArray = {"Hello", "World"};
        // 调用泛型方法打印 String 数组
        printArray(stringArray);
    }
}

泛型通配符-无界通配符示例:

import java.util.ArrayList;
import java.util.List;

public class UnboundedWildcardExample {
    public static void printList(List list) {
        for (Object element : list) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        List intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);
        printList(intList);

        List stringList = new ArrayList<>();
        stringList.add("Hello");
        stringList.add("World");
        printList(stringList);
    }
}

泛型通配符-上界通配符示例:

import java.util.ArrayList;
import java.util.List;

class Shape {
    public void draw() {
        System.out.println("Drawing a shape.");
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle.");
    }
}

public class UpperBoundedWildcardExample {
    public static void drawAll(List shapes) {
        for (Shape shape : shapes) {
            shape.draw();
        }
    }

    public static void main(String[] args) {
        List circleList = new ArrayList<>();
        circleList.add(new Circle());
        drawAll(circleList);
    }
}

泛型通配符-下界通配符示例:

import java.util.ArrayList;
import java.util.List;

class Animal {
    public void eat() {
        System.out.println("Animal is eating.");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating.");
    }
}

public class LowerBoundedWildcardExample {
    public static void addDogs(List dogList) {
        dogList.add(new Dog());
    }

    public static void main(String[] args) {
        List animalList = new ArrayList<>();
        addDogs(animalList);
    }
}
  • 多线程与并发编程

在 Java 中,实现多线程主要有两种方式:继承 Thread 类和实现 Runnable 接口。继承 Thread 类时,需要重写 run 方法,在其中定义线程执行的逻辑;实现 Runnable 接口时,同样要实现 run 方法,然后将实现了 Runnable 接口的对象作为参数传递给 Thread 类的构造函数来创建线程。相比之下,实现 Runnable 接口更为灵活,因为 Java 不支持多重继承,而一个类可以实现多个接口。

Thread:

// 自定义线程类,继承自 Thread 类
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try {
                // 线程休眠 100 毫秒
                Thread.sleep(100); 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class ThreadClassExample {
    public static void main(String[] args) {
        // 创建自定义线程类的实例
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

Runnable:

 // 实现 Runnable 接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try {
                // 线程休眠 100 毫秒
                Thread.sleep(100); 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class RunnableInterfaceExample {
    public static void main(String[] args) {
        // 创建实现 Runnable 接口的类的实例
        MyRunnable myRunnable = new MyRunnable();

        // 创建 Thread 类的实例,并将 MyRunnable 实例作为参数传递
        Thread thread1 = new Thread(myRunnable, "Thread-1");
        Thread thread2 = new Thread(myRunnable, "Thread-2");

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

线程池是一种管理和复用线程的机制,它可以避免频繁创建和销毁线程带来的开销。Java 提供了多种线程池实现,如 ThreadPoolExecutor 和
ScheduledThreadPoolExecutor 等。ThreadPoolExecutor 是最常用的线程池,它通过一系列参数来控制线程池的行为,如核心线程数、最大线程数、线程存活时间、任务队列等。合理配置这些参数,可以根据不同的业务场景优化线程池的性能。例如,在处理大量短任务时,可以适当增加核心线程数,减少任务在队列中的等待时间;在处理长任务时,则需要注意控制线程数,避免资源耗尽。

固定大小线程池(FixedThreadPool):

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 定义一个实现 Runnable 接口的任务类
class Task implements Runnable {
    private final int taskId;

    public Task(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        System.out.println("Task " + taskId + " is running on thread: " + Thread.currentThread().getName());
        try {
            // 模拟任务执行耗时
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is completed.");
    }
}

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小为 2 的线程池
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // 提交 5 个任务给线程池执行
        for (int i = 1; i <= 5; i++) {
            executor.submit(new Task(i));
        }

        // 关闭线程池,不再接受新任务,但会执行完已提交的任务
        executor.shutdown();
    }
}

单线程线程池(SingleThreadExecutor):

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Task implements Runnable {
    private final int taskId;

    public Task(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        System.out.println("Task " + taskId + " is running on thread: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is completed.");
    }
}

public class SingleThreadExecutorExample {
    public static void main(String[] args) {
        // 创建一个单线程线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 提交 3 个任务给线程池执行
        for (int i = 1; i <= 3; i++) {
            executor.submit(new Task(i));
        }

        // 关闭线程池
        executor.shutdown();
    }
}

缓存线程池(CachedThreadPool):

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Task implements Runnable {
    private final int taskId;

    public Task(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        System.out.println("Task " + taskId + " is running on thread: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is completed.");
    }
}

public class CachedThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个缓存线程池
        ExecutorService executor = Executors.newCachedThreadPool();

        // 提交 5 个任务给线程池执行
        for (int i = 1; i <= 5; i++) {
            executor.submit(new Task(i));
        }

        // 关闭线程池
        executor.shutdown();
    }
}

并发控制是多线程编程中的关键问题,它用于确保多个线程在访问共享资源时不会出现数据不一致或竞态条件。常见的并发控制策略包括使用 synchronized 关键字、Lock 接口、原子类等。synchronized 关键字可以修饰方法或代码块,用于实现同步访问,保证同一时间只有一个线程能够进入被修饰的方法或代码块;Lock 接口提供了更灵活的锁机制,如可中断锁、公平锁等,能够满足不同的并发控制需求;原子类如 AtomicInteger、AtomicLong 等,利用硬件级别的原子操作,实现了线程安全的计数器和累加器等功能,避免了使用锁带来的开销。

synchronized:

class Counter {
    private int count = 0;

    // 同步方法,确保同一时间只有一个线程可以访问该方法
    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

class IncrementThread implements Runnable {
    private Counter counter;

    public IncrementThread(Counter counter) {
        this.counter = counter;
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            counter.increment();
        }
    }
}

public class ThreadSynchronizationExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        // 创建两个线程
        Thread thread1 = new Thread(new IncrementThread(counter));
        Thread thread2 = new Thread(new IncrementThread(counter));

        // 启动线程
        thread1.start();
        thread2.start();

        // 等待两个线程执行完毕
        thread1.join();
        thread2.join();

        // 输出最终的计数器值
        System.out.println("Final count: " + counter.getCount());
    }
}
  • AOP

AOP 则是一种面向切面编程的思想,它能够将一些通用的功能,如日志记录、事务管理、权限控制等,从业务逻辑中分离出来,以切面的形式进行统一管理。这样可以避免在业务代码中重复编写这些通用功能的代码,提高代码的复用性和可维护性。比如,在一个电商系统中,我们需要对用户的每一次操作进行日志记录,使用 AOP 就可以创建一个日志切面,在不修改业务代码的情况下,自动记录用户的操作日志。

业务类:

// 业务类
public class UserService {
    public void addUser(String username) {
        System.out.println("Adding user: " + username);
    }

    public void deleteUser(String username) {
        System.out.println("Deleting user: " + username);
    }
}

切面类:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

// 切面类
@Aspect
@Component
public class LoggingAspect {

    // 前置通知,在目标方法执行前执行
    @Before("execution(* com.example.demo.UserService.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }

    // 后置通知,在目标方法执行后执行
    @After("execution(* com.example.demo.UserService.*(..))")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }
}
/*
@Aspect 注解表示这是一个切面类,@Before 和 @After 注解分别定义了前置通知和后置通知。
execution(* com.example.demo.UserService.*(..))是一个切入点表达式,表示匹配 UserService 类中的所有方法。
*/

创建一个配置类来启用 Spring AOP 自动代理:

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

// 配置类
@Configuration
@ComponentScan(basePackages = "com.example.demo")
@EnableAspectJAutoProxy
public class AppConfig {
}
/*
@Configuration 注解表示这是一个配置类,@ComponentScan 注解用于扫描指定包下的组件,
@EnableAspectJAutoProxy 注解用于启用 Spring AOP 自动代理。
*/

验证AOP:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        // 创建 Spring 上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取 UserService 实例
        UserService userService = context.getBean(UserService.class);

        // 调用业务方法
        userService.addUser("John");
        userService.deleteUser("Jane");

        // 关闭 Spring 上下文
        context.close();
    }
}
  • Spring Boot

Spring Boot 是基于 Spring 框架的一个快速开发框架,它的出现极大地简化了 Spring 应用的开发过程。Spring Boot 采用了 “约定大于配置” 的理念,提供了大量的默认配置和自动配置功能,使得开发者无需编写大量繁琐的配置文件,就能够快速搭建一个 Spring 应用。同时,Spring Boot 还内置了多种 Web 服务器,如 Tomcat、Jetty 等,使得应用可以直接运行,无需进行复杂的部署操作。使用 Spring Boot 创建一个 Web 应用,我们只需要引入相关的依赖,编写少量的配置代码,就可以快速启动一个功能完备的 Web 服务器,开始进行业务开发。

  • Spring Cloud

Spring Cloud 是一套完整的微服务解决方案,它基于 Spring Boot 构建,提供了服务注册与发现、配置中心、负载均衡、断路器、分布式会话等一系列微服务治理组件。在微服务架构中,一个大型的应用被拆分成多个小型的服务,每个服务独立运行,通过网络进行通信。Spring Cloud 的这些组件能够帮助我们更好地管理和协调这些微服务,确保整个微服务架构的稳定运行。例如,使用 Eureka 作为服务注册与发现组件,各个微服务可以将自己注册到 Eureka 服务器上,其他服务可以通过 Eureka 服务器发现并调用这些服务;使用 Config Server 作为配置中心,我们可以将所有微服务的配置集中管理,实现配置的动态更新和版本控制。

  • 数据库与持久化框架

数据库是存储和管理数据的重要工具,而持久化框架则用于实现 Java 对象与数据库之间的映射和数据操作。熟练掌握常用的数据库和持久化框架,对于构建高效、稳定的数据存储和管理系统至关重要。

MyBatis 是一个优秀的 Java 持久化框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 通过 XML 或注解的方式将 SQL 语句与 Java 对象进行映射,开发者可以灵活地编写 SQL 语句,实现对数据库的各种操作。MyBatis 的优点在于它的灵活性和可定制性,开发者可以根据实际需求编写高效的 SQL 语句,同时它的学习成本较低,容易上手。例如,在一个用户管理系统中,我们可以使用 MyBatis 编写 SQL 语句来实现用户的注册、登录、查询、修改等功能,通过映射文件将 SQL 语句的参数和返回结果与 Java 对象进行绑定,实现数据的持久化操作。

(五)学习资源推荐

Stack Overflow「链接」
https://stackoverflow.com/。编程界流行的 IT 技术问答网站,可免费提交、浏览问题,有大量代码片段,能帮助解决 Java 编程中遇到的各种问题,还可通过特定标签查看高频问题。(
程序员必备

菜鸟教程(Runoob):菜鸟教程 - 学的不仅是技术,更是梦想! https://www.runoob.com/。全中文教程,通俗易懂,有在线运行代码功能,知识点划分清晰,包含大量实例代码,适合初学者入门。

掘金社区稀土掘金 https://juejin.cn/。技术大咖的集散地,有很多硬核的 Java 技术文章,大神们会分享实战经验,能让学习者了解行业动态和最新技术。

Spring官网Spring | Home https://spring.io/。Spring的学习资料,必须收藏!(程序员必备


从 Java 的基本概念、特性,到环境搭建、基础语法、面向对象编程,再到学习资源推荐,一步步走进了 Java 的编程世界。这些知识是 Java 编程的基础,为大家打开了一扇通往广阔编程天地的大门。

Java 学习是一个长期积累的过程,就像建造高楼大厦,每一块基石都至关重要。随着学习的深入,大家将接触到更多高级特性,如多线程、网络编程、数据库连接等,能够开发出更复杂、更强大的应用程序,无论是在企业级开发、安卓应用开发,还是大数据处理等领域,都能发挥 Java 的强大功能。

在学习过程中,大家要保持积极的学习态度,勇于尝试新的知识和技术,多动手实践,遇到问题及时解决。同时,要善于利用各种学习资源,与其他学习者交流经验,共同进步。相信只要坚持不懈,大家都能在 Java 编程领域取得优异的成绩,实现自己的编程梦想。

最近发表
标签列表