Java基础知识一

一、Java 概述

1.1 Java 简介

Java 是由 Sun Microsystems 公司(2010 年被 Oracle 收购)于 1995 年推出的高级编程语言。它具有以下显著特点:

  • 面向对象:Java 采用面向对象的编程范式,支持封装、继承和多态等特性,使得代码更具可维护性、可扩展性和可复用性。
  • 跨平台:Java 程序通过编译器编译成字节码(.class 文件),字节码可以在不同操作系统上的 Java 虚拟机(JVM)中运行,实现了 “一次编写,到处运行”。
  • 安全性:Java 提供了安全机制,如字节码验证、安全管理器等,能够有效防止恶意代码的攻击。
  • 多线程:Java 内置了对多线程的支持,使得程序可以同时执行多个任务,提高了程序的并发性能。
  • 自动内存管理:Java 采用垃圾回收机制(GC),自动管理内存的分配和回收,减少了程序员手动管理内存的负担。

1.2 Java 平台

  • Java SE(Standard Edition):Java 标准版,是 Java 技术的基础,提供了核心的类库和开发工具,用于开发桌面和简单服务器应用。
  • Java EE(Enterprise Edition):Java 企业版,在 Java SE 的基础上提供了一系列的企业级服务和 API,如 Servlet、JSP、EJB 等,用于开发大型企业级应用。
  • Java ME(Micro Edition):Java 微型版,主要用于嵌入式系统和移动设备开发,提供了精简的类库和运行环境。

1.3 Java 跨平台原理

Java 程序的执行过程分为编译和运行两个阶段:

  • 编译阶段:Java 源文件(.java)通过 Java 编译器(javac)编译成字节码文件(.class)。字节码是一种中间代码,不针对特定的操作系统和硬件平台。
  • 运行阶段:字节码文件在 Java 虚拟机(JVM)中运行。JVM 是 Java 实现跨平台的关键,它负责加载字节码文件,并将字节码或编译成具体操作系统和硬件平台能够执行的机器码。不同的操作系统需要安装相应版本的 JVM,从而保证 Java 程序可以在不同的平台上运行。

二、数据类型

2.1 基本数据类型

Java 提供了 8 种基本数据类型,分为 4 类:整数类型、浮点类型、字符类型和布尔类型。

数据类型 位数 取值范围 默认值 示例
byte 8 -128 ~ 127 0 byte b = 10;
short 16 -32768 ~ 32767 0 short s = 100;
int 32 -2147483648 ~ 2147483647 0 int i = 1000;
long 64 -2^63 ~ 2^63 - 1 0L long l = 10000L;
float 32 单精度浮点数,有效位数约 7 位 0.0f float f = 3.14f;
double 64 双精度浮点数,有效位数约 15 位 0.0d double d = 3.14159;
char 16 0 ~ 65535 '\u0000' char c = 'A';
boolean 1 true 或 false false boolean flag = true;

2.2 引用数据类型

引用数据类型是对对象的引用,而不是对象本身。常见的引用数据类型包括:

  • 类(Class):类是对象的抽象,定义了对象的属性和方法。例如,String 类是 Java 中用于处理字符串的类。
String str = "Hello, World!";
  • 接口(Interface):接口是一种抽象类型,定义了一组方法的签名,但不包含方法的实现。类可以实现接口,从而实现接口中定义的方法。
interface Animal {
    void eat();
}

class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating.");
    }
}
  • 数组(Array):数组是一种存储相同类型数据的容器,数组的长度在创建时确定,并且不能改变。
int[] arr = new int[5];
arr[0] = 1;

三、变量和常量

3.1 变量

变量是程序中存储数据的基本单元,使用前需要先声明和初始化。变量的声明格式为:数据类型 变量名;,初始化格式为:变量名 = 值;。也可以在声明变量的同时进行初始化。

// 声明变量
int num;
// 初始化变量
num = 10;

// 声明并初始化变量
int age = 20;

3.2 常量

常量是在程序运行过程中值不能被改变的量,使用 final 关键字修饰。常量在声明时必须进行初始化,并且不能再被赋值。

final double PI = 3.14159;
// 以下代码会编译错误,因为常量不能再被赋值
// PI = 3.14;

3.3 变量的作用域

变量的作用域是指变量在程序中可以被访问的范围。Java 中变量的作用域分为以下几种:

  • 类变量(静态变量):使用 static 关键字修饰,属于类,而不是类的实例。类变量在类加载时被初始化,在整个程序运行期间都存在。
class MyClass {
    static int classVar = 10;
}
  • 实例变量:属于类的实例,每个实例都有自己的一份实例变量。实例变量在创建对象时被初始化。
class MyClass {
    int instanceVar = 20;
}
  • 局部变量:定义在方法、构造方法或代码块中的变量,局部变量必须在使用前进行初始化,其作用域仅限于定义它的方法、构造方法或代码块。
public void method() {
    int localVar = 30;
    // 局部变量的作用域仅限于该方法
}

四、运算符

4.1 算术运算符

算术运算符用于执行基本的数学运算,包括加法(+)、减法(-)、乘法(*)、除法(/)、取模(%)、自增(++)和自减(--)。

int a = 10;
int b = 3;
int sum = a + b; // 加法
int diff = a - b; // 减法
int prod = a * b; // 乘法
int quot = a / b; // 除法
int rem = a % b; // 取模

a++; // 自增
b--; // 自减

4.2 赋值运算符

赋值运算符用于将一个值赋给变量,常见的赋值运算符包括 =+=-=*=/=%=

int x = 5;
x += 3; // 相当于 x = x + 3;
x -= 2; // 相当于 x = x - 2;
x *= 4; // 相当于 x = x * 4;
x /= 2; // 相当于 x = x / 2;
x %= 3; // 相当于 x = x % 3;

4.3 比较运算符

比较运算符用于比较两个值的大小关系,返回一个布尔值(truefalse)。常见的比较运算符包括 ==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)。

int m = 10;
int n = 20;
boolean result1 = m == n; // false
boolean result2 = m != n; // true
boolean result3 = m > n;  // false
boolean result4 = m < n;  // true
boolean result5 = m >= n; // false
boolean result6 = m <= n; // true

4.4 逻辑运算符

逻辑运算符用于对布尔值进行逻辑运算,常见的逻辑运算符包括 &&(逻辑与)、||(逻辑或)、!(逻辑非)。

boolean p = true;
boolean q = false;
boolean andResult = p && q; // false
boolean orResult = p || q;  // true
boolean notResult = !p;     // false

4.5 位运算符

位运算符用于对整数类型的数据进行二进制位操作,常见的位运算符包括 &(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(左移)、>>(右移)、>>>(无符号右移)。

int num1 = 5; // 二进制: 0101
int num2 = 3; // 二进制: 0011
int andResult = num1 & num2; // 二进制: 0001,十进制: 1
int orResult = num1 | num2;  // 二进制: 0111,十进制: 7
int xorResult = num1 ^ num2; // 二进制: 0110,十进制: 6
int notResult = ~num1;       // 二进制: 1010,十进制: -6
int leftShift = num1 << 1;   // 二进制: 1010,十进制: 10
int rightShift = num1 >> 1;  // 二进制: 0010,十进制: 2

五、控制语句

5.1 条件语句

if - else 语句

if - else 语句用于根据条件的真假来执行不同的代码块。

int score = 80;
if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

if - else if - else 语句

当有多个条件需要判断时,可以使用 if - else if - else 语句。

int score = 80;
if (score >= 90) {
    System.out.println("优秀");
} else if (score >= 80) {
    System.out.println("良好");
} else if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

switch 语句

switch 语句用于根据一个表达式的值来选择执行不同的代码块。

int day = 3;
switch (day) {
    case 1:
        System.out.println("星期一");
        break;
    case 2:
        System.out.println("星期二");
        break;
    case 3:
        System.out.println("星期三");
        break;
    default:
        System.out.println("其他");
}

5.2 循环语句

for 循环

for 循环用于执行指定次数的循环。

for (int i = 0; i < 5; i++) {
    System.out.println(i);
}

while 循环

while 循环在条件为真时重复执行代码块。

int j = 0;
while (j < 5) {
    System.out.println(j);
    j++;
}

do - while 循环

do - while 循环先执行一次代码块,然后再判断条件是否为真,如果为真则继续执行循环。

int k = 0;
do {
    System.out.println(k);
    k++;
} while (k < 5);

5.3 跳转语句

break 语句

break 语句用于跳出当前所在的循环或 switch 语句。

for (int i = 0; i < 10; i++) {
    if (i == 5) {
        break;
    }
    System.out.println(i);
}

continue 语句

continue 语句用于跳过本次循环的剩余代码,直接进入下一次循环。

for (int i = 0; i < 10; i++) {
    if (i == 5) {
        continue;
    }
    System.out.println(i);
}

return 语句

return 语句用于从方法中返回值并结束方法的执行。

public int add(int a, int b) {
    return a + b;
}

六、面向对象编程

6.1 类和对象

类是对象的抽象,定义了对象的属性和方法。类的定义格式为:

class 类名 {
    // 成员变量(属性)
    数据类型 变量名;

    // 构造方法
    类名() {
        // 构造方法体
    }

    // 成员方法(行为)
    返回类型 方法名(参数列表) {
        // 方法体
        return 返回值;
    }
}

对象

对象是类的实例,通过 new 关键字创建对象。

class Person {
    String name;
    int age;

    void speak() {
        System.out.println("My name is " + name + ", I'm " + age + " years old.");
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建对象
        Person p = new Person();
        // 给对象的属性赋值
        p.name = "John";
        p.age = 20;
        // 调用对象的方法
        p.speak();
    }
}

6.2 封装

封装是将对象的属性和方法隐藏起来,只对外提供公共的访问方式,提高了代码的安全性和可维护性。在 Java 中,可以使用访问修饰符来实现封装。

访问修饰符 本类 同一个包中的类 不同包中的子类 不同包中的非子类
private 可以访问 不可以访问 不可以访问 不可以访问
default 可以访问 可以访问 不可以访问 不可以访问
protected 可以访问 可以访问 可以访问 不可以访问
public 可以访问 可以访问 可以访问 可以访问
class Student {
    private String name;
    private int age;

    // 公共的访问方法
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

6.3 继承

继承是指一个类可以继承另一个类的属性和方法,提高了代码的复用性。被继承的类称为父类(基类),继承的类称为子类(派生类)。Java 中使用 extends 关键字实现继承。

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

class Dog extends Animal {
    void bark() {
        System.out.println("Dog is barking.");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat(); // 调用父类的方法
        dog.bark(); // 调用子类的方法
    }
}

6.4 多态

多态是指同一个方法可以根据对象的不同类型表现出不同的行为,包括方法重载和方法重写。

方法重载

方法重载是指在同一个类中,方法名相同但参数列表不同的多个方法。方法重载的判断依据是方法的参数类型、参数个数或参数顺序不同。

class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    int add(int a, int b, int c) {
        return a + b + c;
    }

    double add(double a, double b) {
        return a + b;
    }
}

方法重写

方法重写是指在子类中重写父类的方法,方法名、参数

继续生成未生成的内容

列表和返回值类型都必须与父类方法相同,并且可以使用@Override注解来确保重写的正确性。方法重写体现了运行时多态。

class Animal {
    void sound() {
        System.out.println("Animal makes a sound.");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows.");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal cat = new Cat();
        Animal dog = new Dog();
        cat.sound(); // 输出 "Cat meows."
        dog.sound(); // 输出 "Dog barks."
    }
}

6.5 抽象类和接口

抽象类

抽象类是使用abstract关键字修饰的类,抽象类不能被实例化,主要用于作为其他类的基类。抽象类中可以包含抽象方法和具体方法。抽象方法是使用abstract关键字修饰的方法,只有方法声明,没有方法体,子类必须实现抽象类中的所有抽象方法。

abstract class Shape {
    abstract double area(); // 抽象方法

    void printInfo() {
        System.out.println("This is a shape.");
    }
}

class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double area() {
        return Math.PI * radius * radius;
    }
}

接口

接口是一种特殊的抽象类型,使用interface关键字定义。接口中只能包含抽象方法和常量(默认使用public static final修饰)。类通过implements关键字实现接口,一个类可以实现多个接口。

interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("Duck is flying.");
    }

    @Override
    public void swim() {
        System.out.println("Duck is swimming.");
    }
}

6.6 内部类

内部类是定义在另一个类内部的类,根据定义位置和修饰符的不同,内部类可以分为成员内部类、局部内部类、静态内部类和匿名内部类。

成员内部类

成员内部类是定义在类的成员位置的类,它可以访问外部类的所有成员。

class Outer {
    private int outerField = 10;

    class Inner {
        void printOuterField() {
            System.out.println(outerField);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.printOuterField();
    }
}

局部内部类

局部内部类是定义在方法或代码块内部的类,它只能在定义它的方法或代码块内部使用。

class Outer {
    void outerMethod() {
        class LocalInner {
            void printMessage() {
                System.out.println("This is a local inner class.");
            }
        }
        LocalInner localInner = new LocalInner();
        localInner.printMessage();
    }
}

静态内部类

静态内部类是使用static关键字修饰的内部类,它只能访问外部类的静态成员。

class Outer {
    private static int outerStaticField = 20;

    static class StaticInner {
        void printOuterStaticField() {
            System.out.println(outerStaticField);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.StaticInner staticInner = new Outer.StaticInner();
        staticInner.printOuterStaticField();
    }
}

匿名内部类

匿名内部类是一种没有显式类名的内部类,通常用于创建一次性的对象。

interface MyInterface {
    void doSomething();
}

public class Main {
    public static void main(String[] args) {
        MyInterface obj = new MyInterface() {
            @Override
            public void doSomething() {
                System.out.println("Doing something...");
            }
        };
        obj.doSomething();
    }
}

七、异常处理

7.1 异常概述

异常是指程序在运行过程中出现的错误或意外情况,Java 提供了异常处理机制来捕获和处理这些异常,保证程序的健壮性。异常类都继承自Throwable类,Throwable类有两个子类:ErrorException

  • Error:表示系统级的错误,通常是由 JVM 或硬件问题引起的,程序无法处理,如OutOfMemoryErrorStackOverflowError等。
  • Exception:表示程序可以处理的异常,又分为受检查异常(Checked Exception)和运行时异常(Runtime Exception)。

7.2 异常分类

受检查异常(Checked Exception)

受检查异常是指在编译时必须进行捕获或声明抛出的异常,如IOExceptionSQLException等。如果不处理受检查异常,程序将无法通过编译。

运行时异常(Runtime Exception)

运行时异常是指在编译时不需要进行捕获或声明抛出的异常,通常是由程序逻辑错误引起的,如NullPointerExceptionArrayIndexOutOfBoundsException等。

7.3 异常处理语句

try - catch 语句

try - catch语句用于捕获和处理异常。try块中包含可能会抛出异常的代码,catch块用于捕获并处理相应类型的异常。

try {
    int[] arr = new int[5];
    System.out.println(arr[10]); // 可能会抛出 ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("数组越界异常: " + e.getMessage());
}

try - catch - finally 语句

finally块中的代码无论是否发生异常都会执行,通常用于释放资源,如关闭文件、数据库连接等。

try {
    int[] arr = new int[5];
    System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("数组越界异常: " + e.getMessage());
} finally {
    System.out.println("无论是否发生异常,finally 块都会执行。");
}

throws 关键字

throws关键字用于声明方法可能会抛出的异常,调用该方法的代码必须处理这些异常。

public void readFile() throws java.io.IOException {
    java.io.FileReader fr = new java.io.FileReader("test.txt");
    // 读取文件的代码
    fr.close();
}

throw 关键字

throw关键字用于手动抛出异常,通常在方法内部使用。

public void checkAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("年龄不能为负数。");
    }
}

7.4 自定义异常

在 Java 中,我们可以通过继承ExceptionRuntimeException类来创建自定义异常。

class MyException extends Exception {
    public MyException(String message) {
        super(message);
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            throw new MyException("这是一个自定义异常。");
        } catch (MyException e) {
            System.out.println(e.getMessage());
        }
    }
}

八、数组

8.1 数组定义

数组是一种存储相同类型数据的容器,数组的长度在创建时确定,并且不能改变。数组的定义方式有以下几种:

// 声明数组
int[] arr1;
// 创建数组
arr1 = new int[5];

// 声明并创建数组
int[] arr2 = new int[5];

// 声明、创建并初始化数组
int[] arr3 = {1, 2, 3, 4, 5};

8.2 数组访问

通过下标访问数组元素,下标从 0 开始,最大下标为数组长度减 1。

int[] arr = {1, 2, 3, 4, 5};
// 访问数组元素
int num = arr[2]; // 访问下标为 2 的元素,值为 3
// 修改数组元素
arr[3] = 10;

8.3 多维数组

多维数组是数组的数组,常见的有二维数组。

// 声明并创建二维数组
int[][] matrix = new int[3][4];

// 声明、创建并初始化二维数组
int[][] matrix2 = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// 访问二维数组元素
int element = matrix2[1][2]; // 访问第 2 行第 3 列的元素,值为 7

8.4 数组常用操作

遍历数组

int[] arr = {1, 2, 3, 4, 5};
// 使用 for 循环遍历数组
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

// 使用增强 for 循环遍历数组
for (int num : arr) {
    System.out.println(num);
}

数组排序

import java.util.Arrays;

int[] arr = {5, 3, 1, 4, 2};
// 使用 Arrays 类的 sort 方法对数组进行排序
Arrays.sort(arr);
for (int num : arr) {
    System.out.println(num);
}

九、集合框架

9.1 集合概述

集合是 Java 中用于存储和操作一组对象的容器,与数组相比,集合的长度可以动态变化。Java 集合框架主要分为CollectionMap两大接口。

9.2 Collection 接口

Collection接口是所有集合类的根接口,它有两个重要的子接口:ListSet

List 接口

List接口表示有序、可重复的集合,常见的实现类有ArrayListLinkedList

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

// 创建 ArrayList 对象
List<String> list = new ArrayList<>();
// 添加元素
list.add("apple");
list.add("banana");
// 访问元素
String element = list.get(0);
// 遍历列表
for (String item : list) {
    System.out.println(item);
}

Set 接口

Set接口表示无序、不可重复的集合,常见的实现类有HashSetTreeSet

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

// 创建 HashSet 对象
Set<String> set = new HashSet<>();
// 添加元素
set.add("apple");
set.add("banana");
// 遍历集合
for (String item : set) {
    System.out.println(item);
}

9.3 Map 接口

Map接口用于存储键值对,键不可重复,常见的实现类有HashMapTreeMap

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

// 创建 HashMap 对象
Map<String, Integer> map = new HashMap<>();
// 添加键值对
map.put("one", 1);
map.put("two", 2);
// 获取值
int value = map.get("one");
// 遍历 Map
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

9.4 集合的常用方法

  • 添加元素add()put()
  • 删除元素remove()
  • 判断元素是否存在contains()
  • 获取集合大小size()
  • 清空集合clear()

十、输入输出流

10.1 流的分类

  • 按方向分:输入流和输出流。输入流用于从数据源读取数据,输出流用于向目的地写入数据。
  • 按处理数据单位分:字节流和字符流。字节流以字节为单位处理数据,字符流以字符为单位处理数据。

10.2 字节流

字节流的抽象基类是InputStreamOutputStream。常见的字节流类有FileInputStreamFileOutputStream等。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("input.txt");
             FileOutputStream fos = new FileOutputStream("output.txt")) {
            int c;
            while ((c = fis.read()) != -1) {
                fos.write(c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

10.3 字符流

字符流的抽象基类是ReaderWriter。常见的字符流类有FileReaderFileWriter等。

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharacterStreamExample {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("input.txt");
             FileWriter fw = new FileWriter("output.txt")) {
            int c;
            while ((c = fr.read()) != -1) {
                fw.write(c);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

10.4 缓冲流

缓冲流是对字节流和字符流的包装,提供了缓冲功能,提高了读写效率。常见的缓冲流类有BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter等。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedStreamExample {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));
             BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                bw.write(line);
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

10.5 对象序列化和反序列化

对象序列化是指将对象转换为字节序列的过程,反序列化是指将字节序列转换为对象的过程。要实现对象序列化,类必须实现Serializable接口。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            Person person = new Person("John", 20);
            oos.writeObject(person);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject();
            System.out.println("Name: " + person.getName() + ", Age: " + person.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

十一、多线程

11.1 线程概述

线程是程序执行的最小单位,一个进程可以包含多个线程。多线程可以提高程序的并发性能,使得程序可以同时执行多个任务。

11.2 创建线程的方式

继承 Thread 类

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

实现 Runnable 接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for

继续生成未生成的内容

11.2 创建线程的方式(续)

实现 Callable 接口

Callable 接口与 Runnable 接口类似,但 Callablecall() 方法可以有返回值,并且可以抛出异常。通常需要结合 FutureTask 类来获取返回值。

import java.util.concurrent.*;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += i;
        }
        return sum;
    }
}

public class CallableExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();

        // 获取线程执行结果
        Integer result = futureTask.get();
        System.out.println("线程执行结果: " + result);
    }
}

11.3 线程的生命周期

线程的生命周期包含以下几个状态:

  • 新建(New):当创建一个 Thread 对象时,线程处于新建状态。
Thread thread = new Thread(() -> System.out.println("Hello"));
  • 就绪(Runnable):调用 start() 方法后,线程进入就绪状态,等待 CPU 调度执行。
thread.start();
  • 运行(Running):当 CPU 调度该线程时,线程进入运行状态,执行 run() 方法中的代码。
  • 阻塞(Blocked):线程在某些情况下会进入阻塞状态,如等待 I/O 操作、获取锁失败等。当阻塞条件解除后,线程会重新进入就绪状态。
  • 等待(Waiting):线程调用 wait()join()LockSupport.park() 方法后会进入等待状态,需要其他线程进行相应的唤醒操作。
  • 计时等待(Timed Waiting):线程调用 sleep(long millis)wait(long timeout)join(long millis) 等带有时间参数的方法后会进入计时等待状态,当时间到期或被其他线程唤醒时,线程会重新进入就绪状态。
  • 终止(Terminated):线程的 run() 方法执行完毕或因异常退出时,线程进入终止状态。

11.4 线程同步

多个线程同时访问共享资源时可能会出现数据不一致的问题,需要进行线程同步来保证数据的一致性和完整性。

synchronized 关键字

  • 同步方法:使用 synchronized 修饰方法,同一时间只有一个线程可以访问该方法。
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}
  • 同步代码块:使用 synchronized 关键字指定同步的对象,同一时间只有一个线程可以访问该同步代码块。
class Counter {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

Lock 接口

Lock 接口提供了比 synchronized 更灵活的锁机制,常见的实现类有 ReentrantLock

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Counter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

11.5 线程通信

线程通信是指多个线程之间通过某种机制进行信息交换和协调工作,常见的方法有 wait()notify()notifyAll()

class Message {
    private String content;
    private boolean available = false;

    public synchronized String read() {
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        available = false;
        notifyAll();
        return content;
    }

    public synchronized void write(String content) {
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        this.content = content;
        available = true;
        notifyAll();
    }
}

public class ThreadCommunicationExample {
    public static void main(String[] args) {
        Message message = new Message();
        new Thread(() -> {
            message.write("Hello, World!");
        }).start();
        new Thread(() -> {
            System.out.println(message.read());
        }).start();
    }
}

十二、反射

12.1 反射概述

反射是指在运行时动态获取类的信息并操作类的属性和方法的机制。通过反射,我们可以在运行时创建对象、调用方法、访问属性等,提高了程序的灵活性和可扩展性。

12.2 反射的基本操作

获取 Class 对象

  • 通过类名的 class 属性
Class<?> clazz1 = String.class;
  • 通过对象的 getClass() 方法
String str = "Hello";
Class<?> clazz2 = str.getClass();
  • 通过 Class.forName() 方法
try {
    Class<?> clazz3 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

创建对象

try {
    Class<?> clazz = Class.forName("java.util.ArrayList");
    Object obj = clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
    e.printStackTrace();
}

调用方法

import java.lang.reflect.Method;

class MyClass {
    public void sayHello() {
        System.out.println("Hello!");
    }
}

public class ReflectionMethodExample {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = MyClass.class;
        Object obj = clazz.getDeclaredConstructor().newInstance();
        Method method = clazz.getMethod("sayHello");
        method.invoke(obj);
    }
}

访问属性

import java.lang.reflect.Field;

class MyClass {
    private String name = "John";
}

public class ReflectionFieldExample {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = MyClass.class;
        Object obj = clazz.getDeclaredConstructor().newInstance();
        Field field = clazz.getDeclaredField("name");
        field.setAccessible(true);
        String value = (String) field.get(obj);
        System.out.println(value);
    }
}

12.3 反射的优缺点

优点

  • 灵活性高:可以在运行时动态地创建对象、调用方法和访问属性,提高了程序的灵活性。
  • 可扩展性强:可以通过反射实现插件化开发,方便地扩展程序的功能。

缺点

  • 性能开销大:反射涉及到动态解析和调用,会比直接调用方法和访问属性的性能低。
  • 安全性问题:反射可以访问和修改类的私有成员,可能会破坏类的封装性和安全性。

十三、注解

13.1 注解概述

注解(Annotation)是 Java 5 引入的一种元数据机制,它可以为程序元素(类、方法、字段等)添加额外的信息,这些信息可以在编译时、运行时被读取和处理。

13.2 内置注解

Java 提供了一些内置注解,常见的有:

  • @Override:用于标记方法是重写父类的方法,如果不符合重写规则,编译器会报错。
class Parent {
    public void print() {
        System.out.println("Parent");
    }
}

class Child extends Parent {
    @Override
    public void print() {
        System.out.println("Child");
    }
}
  • @Deprecated:用于标记某个元素(类、方法等)已经过时,不建议使用。
@Deprecated
public void oldMethod() {
    // 旧方法的实现
}
  • @SuppressWarnings:用于抑制编译器的警告信息。
@SuppressWarnings("unchecked")
List list = new ArrayList();

13.3 自定义注解

自定义注解使用 @interface 关键字定义,注解中可以定义成员变量。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyAnnotation {
    String value() default "";
}

// 使用注解
class MyClass {
    @MyAnnotation("Hello")
    public void myMethod() {
        // 方法实现
    }
}

13.4 注解处理器

注解处理器可以在编译时或运行时读取和处理注解信息。以下是一个简单的运行时注解处理器示例:

import java.lang.reflect.Method;

public class AnnotationProcessor {
    public static void processAnnotations(Object obj) throws Exception {
        Class<?> clazz = obj.getClass();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
                System.out.println("Annotation value: " + annotation.value());
                method.invoke(obj);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        MyClass myClass = new MyClass();
        processAnnotations(myClass);
    }
}

十四、泛型

14.1 泛型概述

泛型是 Java 5 引入的一种类型参数化机制,它允许在定义类、接口和方法时使用类型参数,使得代码可以处理不同类型的数据,提高了代码的复用性和类型安全性。

14.2 泛型类

class Box<T> {
    private T content;

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content;
    }
}

public class GenericClassExample {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.setContent("Hello");
        String str = stringBox.getContent();
        System.out.println(str);
    }
}

14.3 泛型接口

interface Generator<T> {
    T generate();
}

class IntegerGenerator implements Generator<Integer> {
    @Override
    public Integer generate() {
        return (int) (Math.random() * 100);
    }
}

public class GenericInterfaceExample {
    public static void main(String[] args) {
        Generator<Integer> generator = new IntegerGenerator();
        Integer num = generator.generate();
        System.out.println(num);
    }
}

14.4 泛型方法

class GenericMethods {
    public <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

public class GenericMethodExample {
    public static void main(String[] args) {
        GenericMethods gm = new GenericMethods();
        Integer[] intArray = {1, 2, 3};
        String[] strArray = {"Hello", "World"};
        gm.printArray(intArray);
        gm.printArray(strArray);
    }
}

14.5 泛型的通配符

  • ? extends T:表示上界通配符,即泛型类型必须是 TT 的子类。
  • ? super T:表示下界通配符,即泛型类型必须是 TT 的父类。
import java.util.ArrayList;
import java.util.List;

class Animal {}
class Dog extends Animal {}

public class WildcardExample {
    public static void printAnimals(List<? extends Animal> animals) {
        for (Animal animal : animals) {
            System.out.println(animal);
        }
    }

    public static void addDog(List<? super Dog> dogs) {
        dogs.add(new Dog());
    }

    public static void main(String[] args) {
        List<Dog> dogList = new ArrayList<>();
        printAnimals(dogList);
        addDog(dogList);
    }
}

继续生成未生成的内容

十五、枚举类型

15.1 枚举概述

枚举类型(enum)是 Java 5 引入的一种特殊类,用于定义一组固定的常量。枚举类型可以使代码更具可读性和可维护性,同时避免了使用普通常量可能带来的错误。

15.2 枚举的基本定义和使用

// 定义一个表示星期的枚举类型
enum Weekday {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

public class EnumExample {
    public static void main(String[] args) {
        // 使用枚举常量
        Weekday today = Weekday.MONDAY;
        System.out.println("Today is " + today);

        // 遍历枚举常量
        for (Weekday day : Weekday.values()) {
            System.out.println(day);
        }
    }
}

15.3 枚举的属性和方法

枚举类型可以有自己的属性和方法,类似于普通类。

enum Color {
    RED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255);

    private int red;
    private int green;
    private int blue;

    // 构造方法
    Color(int red, int green, int blue) {
        this.red = red;
        this.green = green;
        this.blue = blue;
    }

    // 获取 RGB 值的方法
    public String getRGB() {
        return "RGB: (" + red + ", " + green + ", " + blue + ")";
    }
}

public class EnumWithPropertiesExample {
    public static void main(String[] args) {
        Color color = Color.RED;
        System.out.println(color.getRGB());
    }
}

15.4 枚举的 switch 语句

枚举类型非常适合在 switch 语句中使用,能使代码更加清晰。

enum Season {
    SPRING, SUMMER, AUTUMN, WINTER
}

public class EnumSwitchExample {
    public static void main(String[] args) {
        Season currentSeason = Season.SPRING;
        switch (currentSeason) {
            case SPRING:
                System.out.println("It's spring, flowers are blooming.");
                break;
            case SUMMER:
                System.out.println("It's summer, it's very hot.");
                break;
            case AUTUMN:
                System.out.println("It's autumn, leaves are falling.");
                break;
            case WINTER:
                System.out.println("It's winter, it's cold outside.");
                break;
        }
    }
}

十六、字符串处理

16.1 String

String 类是 Java 中用于处理字符串的常用类,它是不可变的,即一旦创建,其值不能被改变。

// 创建字符串对象
String str1 = "Hello";
String str2 = new String("World");

// 字符串拼接
String str3 = str1 + " " + str2;
System.out.println(str3);

// 获取字符串长度
int length = str3.length();
System.out.println("Length: " + length);

// 字符串比较
boolean isEqual = str1.equals("Hello");
System.out.println("Is equal: " + isEqual);

// 字符串查找
int index = str3.indexOf("World");
System.out.println("Index of 'World': " + index);

16.2 StringBuilderStringBuffer

由于 String 类是不可变的,频繁拼接字符串会产生大量的临时对象,影响性能。StringBuilderStringBuffer 是可变的字符串序列,用于高效地拼接字符串。

StringBuilder

StringBuilder 是非线程安全的,性能较高,适用于单线程环境。

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World");
String result = sb.toString();
System.out.println(result);

StringBuffer

StringBuffer 是线程安全的,性能相对较低,适用于多线程环境。

StringBuffer sbf = new StringBuffer();
sbf.append("Hello");
sbf.append(" ");
sbf.append("World");
String result2 = sbf.toString();
System.out.println(result2);

16.3 字符串格式化

Java 提供了 String.format() 方法和 Formatter 类用于字符串格式化。

// 使用 String.format() 方法
String name = "John";
int age = 20;
String formatted = String.format("My name is %s and I'm %d years old.", name, age);
System.out.println(formatted);

// 使用 Formatter 类
import java.util.Formatter;
Formatter formatter = new Formatter();
formatter.format("The price is %.2f dollars.", 9.99);
System.out.println(formatter.toString());
formatter.close();

16.4 字符串分割和替换

// 字符串分割
String str = "apple,banana,orange";
String[] fruits = str.split(",");
for (String fruit : fruits) {
    System.out.println(fruit);
}

// 字符串替换
String replaced = str.replace("apple", "grape");
System.out.println(replaced);

十七、日期和时间处理

17.1 旧的日期和时间 API(Java 8 之前)

Date

Date 类用于表示特定的瞬间,精确到毫秒。

import java.util.Date;

Date now = new Date();
System.out.println("Current date: " + now);

SimpleDateFormat

SimpleDateFormat 类用于格式化和解析日期字符串。

import java.text.SimpleDateFormat;
import java.util.Date;

Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = sdf.format(date);
System.out.println("Formatted date: " + formattedDate);

try {
    Date parsedDate = sdf.parse("2023-10-01 12:00:00");
    System.out.println("Parsed date: " + parsedDate);
} catch (Exception e) {
    e.printStackTrace();
}

17.2 新的日期和时间 API(Java 8 及以后)

Java 8 引入了新的日期和时间 API,位于 java.time 包下,提供了更丰富、更易用的日期和时间处理功能。

LocalDateLocalTimeLocalDateTime

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;

// 获取当前日期
LocalDate currentDate = LocalDate.now();
System.out.println("Current date: " + currentDate);

// 获取当前时间
LocalTime currentTime = LocalTime.now();
System.out.println("Current time: " + currentTime);

// 获取当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current date and time: " + currentDateTime);

DateTimeFormatter

DateTimeFormatter 类用于格式化和解析日期时间对象。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(formatter);
System.out.println("Formatted date and time: " + formatted);

LocalDateTime parsedDateTime = LocalDateTime.parse("2023-10-01 12:00:00", formatter);
System.out.println("Parsed date and time: " + parsedDateTime);

DurationPeriod

Duration 用于表示两个时间点之间的时长,Period 用于表示两个日期之间的时间段。

import java.time.Duration;
import java.time.LocalTime;
import java.time.Period;
import java.time.LocalDate;

// 使用 Duration
LocalTime start = LocalTime.of(10, 0);
LocalTime end = LocalTime.of(12, 30);
Duration duration = Duration.between(start, end);
System.out.println("Duration: " + duration.toMinutes() + " minutes");

// 使用 Period
LocalDate startDate = LocalDate.of(2023, 1, 1);
LocalDate endDate = LocalDate.of(2023, 12, 31);
Period period = Period.between(startDate, endDate);
System.out.println("Period: " + period.getMonths() + " months and " + period.getDays() + " days");

十八、正则表达式

18.1 正则表达式概述

正则表达式是一种用于描述字符串模式的工具,通过特定的字符和语法规则,可以方便地进行字符串的匹配、查找、替换等操作。

18.2 正则表达式的基本语法

  • 字符类[abc] 表示匹配 abc 中的任意一个字符;[^abc] 表示匹配除 abc 之外的任意一个字符。
  • 预定义字符类. 表示匹配任意一个字符;\d 表示匹配一个数字字符,等价于 [0-9]\w 表示匹配一个单词字符,等价于 [a-zA-Z_0-9]
  • 数量词* 表示匹配前面的元素零次或多次;+ 表示匹配前面的元素一次或多次;? 表示匹配前面的元素零次或一次。

18.3 Java 中使用正则表达式

import java.util.regex.Matcher;
import java.util.regex.Pattern;

// 定义正则表达式
String regex = "\\d+";
// 编译正则表达式
Pattern pattern = Pattern.compile(regex);
// 创建 Matcher 对象
Matcher matcher = pattern.matcher("There are 123 apples.");

// 查找匹配的字符串
while (matcher.find()) {
    System.out.println("Found: " + matcher.group());
}

// 替换匹配的字符串
String replaced = pattern.matcher("There are 123 apples.").replaceAll("XXX");
System.out.println("Replaced: " + replaced);

十九、文件和目录操作

19.1 File

File 类用于表示文件和目录的抽象路径名,可以进行文件和目录的创建、删除、重命名等操作。

import java.io.File;

// 创建 File 对象
File file = new File("test.txt");

// 判断文件是否存在
if (file.exists()) {
    System.out.println("File exists.");
} else {
    try {
        // 创建新文件
        boolean created = file.createNewFile();
        if (created) {
            System.out.println("File created.");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

// 删除文件
boolean deleted = file.delete();
if (deleted) {
    System.out.println("File deleted.");
}

// 操作目录
File dir = new File("mydir");
if (!dir.exists()) {
    // 创建目录
    boolean dirCreated = dir.mkdir();
    if (dirCreated) {
        System.out.println("Directory created.");
    }
}

// 列出目录下的文件和子目录
File[] files = dir.listFiles();
if (files != null) {
    for (File f : files) {
        System.out.println(f.getName());
    }
}

19.2 PathPaths 类(Java 7 及以后)

Java 7 引入了 java.nio.file 包,提供了更强大的文件和目录操作功能,PathPaths 是其中的重要类。

import java.nio.file.Path;
import java.nio.file.Paths;

// 创建 Path 对象
Path path = Paths.get("test.txt");
System.out.println("Path: " + path);

// 获取文件名
String fileName = path.getFileName().toString();
System.out.println("File name: " + fileName);

// 获取父目录
Path parent = path.getParent();
if (parent != null) {
    System.out.println("Parent directory: " + parent);
}

19.3 文件复制和移动

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileCopyMoveExample {
    public static void main(String[] args) {
        Path source = Paths.get("source.txt");
        Path destination = Paths.get("destination.txt");

        try {
            // 复制文件
            Files.copy(source, destination);
            System.out.println("File copied.");

            // 移动文件
            Files.move(destination, Paths.get("newdestination.txt"));
            System.out.println("File moved.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以上涵盖了 Java 基础知识的主要方面,在实际开发中,这些知识是进一步学习和应用 Java 高级特性的基础。

posted @ 2025-02-12 01:36  练剑哥  阅读(47)  评论(0)    收藏  举报