Java的基础语法

叠甲:以下文章主要是依靠我的实际编码学习中总结出来的经验之谈,求逻辑自洽,不能百分百保证正确,有错误、未定义、不合适的内容请尽情指出!

文章目录

  • 1.第一份程序
    • 1.1.代码编写
    • 1.2.代码运行
      • 1.2.1.命令行编译
      • 1.2.2.IEDA 编译
    • 1.3.代码文档
  • 2.运行过程
  • 3.关键字
  • 4.标识符
  • 5.常量与变量
    • 5.1.数据类型
      • 5.1.1.基本类型
      • 5.1.2.引用类型
    • 5.2.数据的量
      • 5.2.1.字面常量
      • 5.2.2.数据变量
  • 6.类型转化
    • 6.1.隐式类型转换
    • 6.2.显式类型转换
    • 6.3.数据类型提升
  • 7.运算符
  • 8.控制语句
    • 8.1.分支语句
      • 8.1.1.if 语句
      • 8.1.2.switch 语句
    • 8.2.循环语句
      • 8.2.1.for 语句
      • 8.2.2.while 语句
      • 8.2.3.do-while 语句
  • 9.输入输出
    • 9.1.输出数据
    • 9.2.输入数据
  • 10.随机数
  • 11.方法
    • 11.1.方法的定义
    • 11.2.方法的参数
    • 11.3.方法的递归
    • 11.4.方法的重载
  • 12.数组
    • 12.1.数组的使用
    • 12.2.数组的引用
    • 12.3.数组的传递
    • 12.4.数组的拷贝
  • 13.字符串
  • 14.IDEA 快捷键

概要:…

资料:…


1.第一份程序

1.1.代码编写

/* (块注释)
HelloWord.java 内部
*/

/** (文档注释)
* 作者:limou3434
*/

public class HelloWord {
    public static void main(String[] args){
        System.out.println("Hello Word!"); // (普通注释)打印“Hello Word!”
    }
}

直接上代码,上面就是一段 Java 代码,我们先来观察一下这段代码的细节:

  1. 首先看到的是一个 main() 函数(每一个类只能有一个 main()),主函数没有所谓的返回值,因此返回类型是 void,在 main() 的内部有一个字符数组类型的 args 变量,用来接收命令行参数

  2. 在一个 Java 文件中只能有一个 public 类,并且 pubilc 类的名字必须和 .java 文件名字相同。而 main() 函数被包含在这个类中,不允许被写在类外

  3. main() 内部,有一个调用,调用了打印函数 println(),这个函数后面的 ln 指的是换行,打印函数内部包含了需要被打印的内容

  4. Java0 中有三种注释:行注释、段注释、文档注释,这里故意都使用了一遍

1.2.代码运行

那么如何编译这一份代码呢?我将带您用两种方式进行运行,一种是脱离 IDE 环境的命令行运行,另一种就是在 IDEA 中运行。这两种方法您都需要掌握…

1.2.1.命令行编译

  1. 在最原始的情况下只需要将上述代码写入 HelloWord.java 文件中(注意文件名一定要使用大驼峰,实际上在 Java 中一个文件就对应一个类,一般直接使用类名来命名文件)

  2. 然后通过使用 Java 的编译器 javac.exe 这个可执行程序,使用命令 javac ./HelloWord.java(注意需要正确写好 Hellow.java 的相对路径或者绝对路劲)

  3. 此时在 HelloWord.java 的同级目录中就存在一个经过编译的字节码文件 HelloWord.class

  4. 运行 Java 代码代码直接使用 Java HelloWord 即可

  5. 需要注意的是,Java 不是单个文件就生成单个字节码的文件,而是有多少个类就有多少个字节码文件,并且字节码文件名和类相同,这点和很多编程语言有很大的不同。

注意:这里如果因为添加了中文注释导致无法通过编译,则可以尝试在编译的时候设置某个编码运行,例如 javac -encoding utf-8 HellowWord.java 就可以使用 utf-8 来进行编码。而关于命令行中的其他操作,可以查阅其他资料…

1.2.2.IEDA 编译

在这里插入图片描述

1.3.代码文档

Java 有一个非常独特的功能,给代码生成文档,使用 javadoc -d [存放文档的目录路径] -sourcepath [存放源代码的目录路径] [-docencoding UTF-8 -charset UTF-8] [指定需要生成文档的 .java 文件]> 即可生成一个 html 帮助文档。

不过这种注释需要搭配一些特殊的注释,这些注释的使用和意义有点类似 C/CppDoxygen 注释,主要有以下特殊注释(这里只举例常用的)。

  1. 类和接口文档:使用 /** ... */ 放在类或接口声明之前,描述类或接口的用途和功能
  2. 成员变量文档:使用 /** ... */ 放在成员变量声明之前,描述变量的用途
  3. 成员方法文档:使用 /** ... */ 放在方法声明之前,描述方法的功能、参数、返回值和可能抛出的异常
  4. 参数文档:使用 @param 标记在方法文档中,为每个参数提供描述
  5. 返回值文档:使用 @return 标记在方法文档中,描述方法的返回值
  6. 异常文档:使用 @throws 标记在方法文档中,描述方法可能抛出的异常及其条件
  7. 版本和作者文档:使用 @version 标记来记录类或接口的版本信息,使用 @author 标记来记录代码的作者
  8. 自描述:使用 @see 标记来引用其他主题或 URL,在最后文档生成中会达到跳转的目的
  9. 弃用文档:使用 @deprecated 标记来标明某个 API 成员(类、方法、字段等)已过时,一般添加到整个注释文档的最后即可
  10. 序列化文档:使用 @serial 标记来描述序列化字段

以下是一些示例 javadoc 的注释,以后遇到我也会使用上其他的注释。

// 使用 javadoc 注释
/**
 * 表示一个简单的计算器类。
 */
public class Calculator {

    /**
     * 计算两个整数的和。
     *
     * @param a 第一个加数。
     * @param b 第二个加数。
     * @return 两个数的和。
     */
    public int add(int a, int b) {
        return a + b;
    }

    /**
     * 这个字段表示计算器的版本。
     * 
     * @version 1.0
     * @author John Doe
     */
    private String version;
}

这也可以根据需要使用不同的注释标记组合来生成详细的 API 文档。

2.运行过程

在我们安装 JDKJava 开发开发工具包)的时候

  • JDK 里包含 JREJava 运行时环境)
  • JRE 里包含 JVMJava 所需虚拟机)

.java 后缀的 Java 文件使用 javac 编译成 .class 后缀的字节码文件,字节码文件再通过不同操作系统实现的具体 JVM 虚拟机转化为机器码运行起来(因此 Java 是半解释半编译的语言)。

因此哪怕是其他语言,如果能被转化成字节码并且运行的操作系统上实现了对应的 JVM,也同样可以在虚拟机上运行。通过中间层来达到 一次编译到处运行 的目的,使得 Java 在跨平台能力要优于 C/Cpp

3.关键字

有些关键字被 Java 所保留,不可以给用户创建标识符来使用,这些关键字的类别有很多,例如:intclasscatch 等等,我们后面再来一一介绍。

4.标识符

Java 中可以将类名、对象名、变量名、方法名称为“标识符”。Java 的标识符可以包含:字母、数字、下划线、$ 符号等。

不过需要注意的是不可以使用数字作为标识符的起始字符,但是可以把 $ 作为标识符的开头(但是不建议)。

在命名的时候,不仅要注意命名合法,还要注意合理。在本系列文章中我统一采用:

  1. 类名:大驼峰

  2. 方法名:小驼峰

  3. 变量名:小驼峰

吐槽:从编译原理的角度来看,实际上关键字就是一种特殊的标识符。

5.常量与变量

在提及常量和变量的时候,就需要先提及数据类型,注意这里和 C/Cpp 有很大的不同之处。

5.1.数据类型

Java 的数据类型分为两种:基本类型和引用类型。

  • 其中基本数据类型有四类八种
  • 引用类型通常指类(class)、接口(interface)、数组(array)等

5.1.1.基本类型

四类即:

  1. 整型(整数)

  2. 浮点型(小数)

  3. 字符型(一个字符)

  4. 布尔类型(truefalse,对应对和错,和整型没关系)

八种即:

数据类型关键字内存占用范围
字节型byte1 字节 [ − 128 , 127 ] [-128,127] [128,127]
字符型char2 字节 [ 0 , 65535 ] [0,65535] [0,65535]
短整型short2 字节 [ − 32768 , 32767 ] [-32768,32767] [32768,32767]
整型int4 字节 [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31-1}] [231,2311]
长整型long8 字节 [ − 2 63 , 2 63 − 1 ] [-2^{63},2^{63}-1] [263,2631]
单精度浮点数float4 字节有,但是不关注
双精度浮点数double8 字节有,但是不关注
布尔型boolean无说明truefalse

Java 的数据类型是固定的不会受平台影响,因此很方便做代码移植。

吐槽:C/Cpp 在不同平台,甚至在同平台的不同编译器上,对于类型的限定都可能会不一样,尤其是在 32/64 平台上有两种解释,因为标准只规定了大致范围和限定,剩下的几乎都交给了编译器来实现。

5.1.2.引用类型

引用类型主要依赖引用这一区别于 C/Cpp 指针的机制,后面再来详细解释,您先简单看作类(class)、接口(interface)、数组(array)等创建的类型即可。

5.2.数据的量

有了数据类型,就可以创建出对应的容器来指代某些量,这些量可以被分为字面常量和数据变量。

5.2.1.字面常量

数据类型可以给字面常量(数据)做出分类。

类似 1003.14"abcdef"false 等这种一眼就能看出数据的都是字面常量,字面常量的类别也是根据数据类型来分类的。

其中 100 就是整型常量、3.14 就是浮点数常量、"abcdef" 就是字符串常量、构成字符串的每一个字符 abc…就是字符常量、falsetrue 就是布尔常量。

5.2.2.数据变量

变量可以理解为一个容器,可以用来存储一个常量,不同类别的常量需要靠不同类别的变量来存储。
而我们需要用一些关键字(也就是前面在数据类型中提到的),使得变量变成只存储某一字面常量类型的变量。

// 定义变量的语法形式
int a = 10; // int 是关键字, a 是标识符, 标记一个变量, 10 为字面常量
// 此时变量 a 存储了 10 这个字面量

补充:在数据变量上,JavaC/Cpp 有很大的不同。

  • Java 没有全局变量这个术语的,但有类似的。
  • Java 编程中如果没有对变量初始化就使用的话,很多编译器直接就会报错,编译也不会通过。
  • 如果赋值给变量过大或者过小的值,Java 也是不会通过编译的。

补充:Java 的字符串类型是包装类型,不是基本数据类型。为了方便后续一些代码的使用,这里提前讲解一下字符类的相关概念。

C 没有字符类型,但是 Java 利用类的优势,使用 String 类定义字符串类型。

// 使用 String 类
public static viod main() {
     String s1 = "Hello";
     String s2 = "I am limou3434";
       String s3 = "100";
       System.out.prinln(s1 + s2);
       //String 转 int
       int number = Inreger.parseInt(str);
       //int 转 String
       String s = String.valueOf(number);
   }

如果您学过 Cpp 其实您能很快理解什么是包装类,您可以简单理解为基本数据类型被包装为一个类类型,这么做的好处是统一参数。而由于 Java 是纯粹的面向对象语言,因此传递参数的时候,大部分使用的是类的实例化对象,将基本数据类型就被包装为包装类,供程序传递参数使用时就会更加方便。

6.类型转化

需要注意的是,Java 是强类型语言,有些不合适的类型转化将会直接报错(C/Cpp 语言是弱类型语言,在这方面会宽松很多)。

6.1.隐式类型转换

代码不需要经过处理,编译器会进行处理不同类型的转换,但是 Java 在这一方面检查得比较严格,不允许溢出和不相关类型转化。

// 使用隐式类型转化
public class Main {
    public static void main(String[] args) {
        int a = 10;
        long b = 100L;
        b = a;// 可以,发生了隐式类型转化, a 从 int 变成 long
        // a = b; // 不可以, 不够存

        float f = 3.14F;
        double d = 5.12;
        d = f; // 可以, f 从 float 转化为 double
        // f = d; // 不可以, 不够存
    }
}

6.2.显式类型转换

这方面类似 C 语言,也是使用 () 来转换,但并不总是能成功,小范围可以转化为大范围的,赋值的数值一定不能溢出,且强制转换的类型要相关且合理。

// 使用显示类型转化
public class Main {
    public static void main(String[] args) {
        int a = 10;
        long b = 100L;
        b = a; // 可以, 发生了隐式类型转化, a 从 int 变成 long
        a = (int)b; // 可以, 将 b 从 long 强制转化为 int

        float f = 3.14F;
        double d = 5.12;
        d = f; // 可以, f 从 float 转化为 double
        f = (float)d; // 可以, 将 d 从 double 前置转化为 float
    }
}

6.3.数据类型提升

在不同变量的运算的运算中,Java 也存在整型提升,和 C 基本差不多。需要注意是 Java 对溢出极其敏感(C 对待溢出十分迟钝),提升后需要赋给对应的容器,除非进行强转,否则会报错。

//查看整型提升现象
public class Main {
    public static void main(String[] args) {
        byte a = 127;
        byte b = 127;
        // byte c = (byte)(a + b); // a 和 b 提升为 int 进行计算,结果也为 int, 虽然可以强转为 byte, 但很危险
        int d = a + b; // 这样才比较好
    }
}

7.运算符

这里我们只挑出几个比较特殊的运算符,其余的运算符和 C 基本差不多(包括“结合性”和“优先级”),这里就不再赘述。

  1. Java 的除法和 C 类似,会向下取整,并且除以 0 会抛出异常 ArithmeticException(算数异常)

    // 使用除法的示例
    public class Main {
        public static void main(String[] args) {
            System.out.println(5 / 2); // 2
            System.out.println(5.0 / 2); // 2.5
            System.out.println(5 / 2.0); // 2.5
            System.out.println((float)5 / 2); // 2.5
            System.out.println(5 / (float)2); // 2.5
            System.out.println((float)(5 / 2)); // 2.0
        }
    }
    
  2. 由于除法一样,所以 % 运算也是一样的,需要注意的是,该运算符也可以对小数进行操作(就是很少用)

    // 使用取模的示例
    public class Main {
        public static void main(String[] args) {
            System.out.println(10 % 3); // 1
            System.out.println(-10 % 3); // -1
            System.out.println(10 % -3); // 1
            System.out.println(-10 % -3); // -1
    
            System.out.println(-10.3 % 3); // -1.3000000000000007
        }
    }
    
  3. 在增量运算符中有的时候会发生类型转化,等价于强转

    // 使用增量运算符发生隐式强转
    public class Main {
        public static void main(String[] args) {
            int num1 = 10;
            double num2 = 3.14;
    
            int add1 = 0;
            double add2 = 0.0;
    
            // 方式一
            // add1 = num1 + num2; // 4 字节和 8 字节相加,发生整形提升, 只用 4 个字节是放不下去的
            // System.out.println(add1);
            add2 = num1 + num2;
            System.out.println(add2);
            System.out.println();
    
            // 方式二
            add1 = (int) (num1 + num2);
            System.out.println(add1);
            add2 = (double) (num1 + num2);
            System.out.println(add2);
            System.out.println();
    
            // 方式三
            num1 += num2; // 等价于 a = (int)(a + b)
            System.out.println(num1);
        }
    }
    

    而自增、自减运算符还有一种特殊的情况需要您注意,您应该避免出现下面这样的代码…

    // 有关于加加和减减的一个很坑的点
    public class Main {
        public static void main(String[] args) {
            int a = 10;
            a = a++;
            System.out.println(a);
            // 结果为 10,而不是 11,这个原因涉及底层
            // 以后我有机会再来填坑,这里充分体现了 Java 和 C/C++ 不一样!
        }
    }
    
  4. 关系运算符、逻辑运算符(也具有短路效应)的操作表达式必须为布尔表达式,其运算结果为 trueflase,不是 非00。而 iffor 判断的环节中使用的也是布尔表达式

  5. Java 的移位操作符有三个 <<>>>>>>> 是左补符号位,>>> 是左补 0

  6. 也有一个唯一的三目操作符:条件运算符 表达式1 ? 表达式2 : 表达式3。并且该表达式必须是被使用的(其结果必须被使用),不能单独存在

  7. 对于位操作,建议还是使用括号引导表达式的逻辑,避免出现意想不到的后果(在 C 中也最好一样)

注意:如果摒弃掉 C 的“非零为真”的概念在接下来的编码中会轻松很多…

8.控制语句

和大部分编程语言类似,Java 也有自己的控制语句,和 C 也有些类似,但是在入口判断有很大的不同,不同的根源来自于:C 使用整数的非零和 0 来判断真假,而 Java 完全使用布尔类型来判断真假,而 Java 的布尔类型和整数是无法进行比较的,这就导致 Java 写出来的入口判断会更加清晰(也可以说没有 C/C++ 那样整洁,至于是清晰好还是简洁好,看您喜好而定)

8.1.分支语句

8.1.1.if 语句

Javaif 语句,几乎和 C/C++ 的使用一样,并且也有类似的悬挂 else 的问题,真要说有哪些点不同,就是 C/C++Java 的代码缩进风格不太一样。

//使用 if 语句
public class Main {
    public static void main(String[] args) {
        int a = 1;
        if (a == 1) { //这里只能是整型
            System.out.println(a);
        } else {
            System.out.println("a != 1");
        }
    }
}

8.1.2.switch 语句

同样,几乎和 C 一样,就是需要注意的是:switch 的入口只能使用 charbyteshortintCharacterByteShortIntegerStringenum 类型,其他类型一概不支持(比如 long 就不行),这点很重要。

//使用 switch 语句
public class Main {
    public static void main(String[] args) {
        int a = 1;
        switch (a) { //这里只能是整型
            case 1:
                System.out.println(a);
                break;
            case 2:
                System.out.println(a);
                break;
            default:
                System.out.println("default");
        }
    }
}

8.2.循环语句

8.2.1.for 语句

//使用 for 语句
public class Main {
    public static void main(String[] args) {
        for (int a = 1; a < 10; a++) {
            System.out.println(a);
        }
    }
}

8.2.2.while 语句

//使用 while 语句
public class Main {
    public static void main(String[] args) {
        int a = 1;
        while (a < 10) {
            System.out.println(a);
            a++;
        }
    }
}

8.2.3.do-while 语句

//使用 do-while 语句
public class Main {
    public static void main(String[] args) {
        int a = 1;
        do {
            System.out.println(a);
            a++;
        } while (a < 10);
    }
}

让我们借助循环语句,顺便来讲解一下在 IDEA 中如何调试代码:

在这里插入图片描述

9.输入输出

9.1.输出数据

Java 有三种常用的输出,均体现在下述代码中:

// 使用三种输出语句
public class Main {
    public static void main(String[] args) {
        System.out.print("limou"); //输出,但不换行(无需关注类型)
        System.out.println("limou"); //输出,但是换行(无需关注类型)
        System.out.printf("%s", "limou"); //格式化输出输出
    }
}

您可能会好奇格式化输出是否和 C 一样,实际上有些类似,也有些不同:

  • %o%d%x:整数的八进制、十进制、十六进制输出
  • %f%e%g%a:定点浮点数、指数浮点数、通用浮点数、十六进制浮点数输出
  • %s%c:字符串、字符输出
  • %b:布尔值输出
  • %h:散列码输出
  • %%:百分号输出

此外,也有一些修饰符,您稍微了解一下即可。

9.2.输入数据

// 使用输入语句
// 导入相关的包(可以将光标停在对应关键字上,使用快捷键 [alt+enter] 来快速导入)
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        // 创建一个 scan 对象, 且 System.in 代表设置为“从键盘输入”
        Scanner scan = new Scanner(System.in);

        System.out.println("请输入您的年龄"); // 提示用户输入
        int age = scan.nextInt(); // 通过方法获取输入

        System.out.println("请输入您的名字"); // 提示用户输入
        String name = scan.next(); // 通过方法获取输入(会忽略一开始的空白字符, 再次遇到空白字符就会停下)

        // 使用 nextLine() 则会获取空白字符, 但是有可能有失效的问题, 也就是多余空白字符被误输入的问题
        String ch = scan.nextLine(); // 先除去上述输入得最后产生得换行符
        System.out.println("输入您的爱好");
        String hobby = scan.nextLine(); // 忽略一开始的空白字符(除了换行字符), 获取连续的字符串, 包括空白字符(除了换行字符)

        System.out.println("请输入您的体重"); // 提示用户输入
        float weight = scan.nextFloat(); // 通过方法获取输入(会忽略空白字符)

        System.out.println("年龄:" + age); // 输出信息
        System.out.println("名字:" + name); // 输出信息
        System.out.println("爱好:" + hobby); // 输出信息
        System.out.println("体重:" + weight); // 输出信息

        scan.close(); //类似 C 语言的文件关闭
    }
}

/* 输出结果
请输入您的年龄
18
请输入您的名字
limou 3434
输入您的爱好
game and programme
请输入您的体重
51.2
年龄: 18
名字: limou
爱好: game and programme
体重: 51.2
*/

注意:关于 Scanner 还有很多的知识,我将会在 IO 详细讲解。

当然,一般建议文本输入最好放在最前面处理(尤其是多数据类型输入的时候)。还有一个循环输入的代码也值得您一看。

// 多组输入
// 导入相关的包(可以将光标停在对应关键字上,使用快捷键 [alt+enter] 来快速导入)
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.print("请输入一个数字 ");
        while (scan.hasNextInt()) {
            int number = scan.nextInt();
            System.out.println("您输入的数字是:" + number);
            System.out.print("请输入一个数字 ");
        }
        // 在终端中使用 [ctrl+d] 会终止循环
    }
}

/* 输出结果
请输入一个数字 18
您输入的数字是: 18
请输入一个数字 20
您输入的数字是: 20
请输入一个数字 35
您输入的数字是: 35
请输入一个数字 5
您输入的数字是: 5
请输入一个数字 100
您输入的数字是: 100
请输入一个数字
...
*/

10.随机数

Java 的随机数生成也比较简单,使用如下代码即可:

// 随机数生成
// 导入相关的包(可以将光标停在对应关键字上,使用快捷键 [alt+enter] 来快速导入)
import java.util.Random;

public class Main {
    public static void main(String[] args) {
        int count = 0;

        Random random1 = new Random();
        while (count < 10) { // 循环打印 10 次查看随机数
            int n = random1.nextInt();
            System.out.println(n);
            count++;
        }

        Random random2 = new Random();
        while (count > 0) { // 循环打印 10 次查看随机数
            int n = random2.nextInt(100); // 限定 [0, 100) 的随机数
            System.out.println(n);
            count--;
        }
    }
}

/* 输出结果(随机的)
1341966210
210008845
453512808
804932370
28871118
-913616368
469568144
-904536397
190689066
-546299782
69
65
21
54
6
83
0
1
81
41
*/

还有一个数学库的随机数,您也可以去了解一下…

11.方法

11.1.方法的定义

方法和 C 中的函数是否类似(因为它们的工作是差不多的,都是通过调用来达到简化代码的目的),而为什么不继续延用“函数”这个术语,而使用“方法”呢?

简单来说就是类的出现导致的,Java 使用类来创建一个又一个的对象,这些对象很类似普通的变量,而类内写入的对象可以执行的方法,这样创建出一个对象就可以使用配套的对象方法,这些内容我将会在下一节的类中重新阐述(现在您把方法简单视为函数)。

// 方法的使用
public class Main {
    public static void main(String[] args) {
        System.out.println(add(1, 2));
    }
    public static int Add(int a, int b) {
        return a + b;
    }
}

/* 输出结果
3
*/

另外,方法不能嵌套定义,一个方法的内部是不能定义另外一个方法的。

方法在类内是全局的,也就是说无论把 Add() 写到类的哪里,类内的 main() 都可以执行该方法。

11.2.方法的参数

Java 的方法也有和 C 函数类似的形参和实参的问题,但由于 Java 没有 C/C++ 的指针,如果传递一个参数过来,该怎么进行修改呢?

//形参和实参的一个问题
public class Main {
    public static void main(String[] args) {
            int num1 = 5, num2 = 10;

            System.out.println("交换方法一");
            System.out.println("交换前 num1:" + num1 + " " + "num2:" + num2);
            int tmp = num1;
            num1 = num2;
            num2 = tmp;
            System.out.println("交换后 num1:" + num1 + " " + "num2:" + num2);

            System.out.println("交换方法二");
            System.out.println("交换前 num1:" + num1 + " " + "num2:" + num2);
            Swap(num1, num2);//使用方法交换(交换失败)
            System.out.println("交换后 num1:" + num1 + " " + "num2:" + num2);
    }
    public static void Swap(int num1, int num2) {
        int tmp = num1;
        num1 = num2;
        num2 = tmp;
    }
}
/*
交换方法一
交换前 num1: 5 num2: 10
交换后 num1: 10 num2: 5
交换方法二
交换前 num1: 10 num2: 5
交换后 num1: 10 num2: 5
*/

可以采用数组的方法来规避这一问题。

// 使用数组的引用达到目的
public class Main {
    public static void main(String[] args) {
        int[] nums = {5, 10};

        System.out.println("交换前 num1:" + nums[0] + " " + "num2:" + nums[1]);
        swap(nums, 0, 1);
        System.out.println("交换后 num1:" + nums[0] + " " + "num2:" + nums[1]);
    }

    public static void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}

11.3.方法的递归

除此之外,Java 也支持递归调用方法。

//使用方法递归计算阶乘
public class Main {
    public static void main(String[] args) {
        int number = 5;
        System.out.println(Test(number));
    }
    public static int Test(int number) {
        if (number == 0 || number == 1) {
            return 1;
        } else if (number < 0) {
            return -1;
        }
        return Test(number - 1) * number;
    }
}

/* 输出结果
120
*/

11.4.方法的重载

函数重载和 C++ 的重载类似,也就是说 Java 允许类内一个方法可以有多种实现。这些方法实现的方法名字是一样的,并且都 在同一个类内,但参数列表是不一样的(体系在 参数个数参数顺序 不一样,但是不包括返回值不一样)。

// 需要使用方法重载的例子
public class Main {
    public static void main(String[] args) {
        int number1 = 5;
        System.out.println(Test(number1));
        double number2 = 5.0;
        System.out.println(Test(number2)); // 无法调用 Test()
    }

    public static int Test(int number) {
        if (number == 0 || number == 1) {
            return 1;
        } else if (number < 0) {
            return -1;
        }
        return Test(number - 1) * number;
    }
}
// 为 Test() 提供重载版本
public class Main {
    public static void main(String[] args) {
        int number1 = 5;
        System.out.println(Test(number1));

        double number2 = 5.0;
        System.out.println(Test(number2)); // 成功调用 Test() 的重载版本
    }

    public static int Test(int number) {
        if (number == 0 || number == 1) {
            return 1;
        } else if (number < 0) {
            return -1;
        }
        return Test(number - 1) * number;
    }

    public static double Test(double number) {
        if (number == 0 || number == 1) {
            return 1;
        } else if (number < 0) {
            return -1;
        }
        return Test(number - 1) * number;
    }
}

/* 输出结果
120
120.0 
*/

12.数组

12.1.数组的使用

C/C++ 诡异的数组、指针创建风格曾折磨过不少初入门的家伙们,而 Java 的数组创建在这里和 C/C++ 有很大的不同,并且有更加便捷的操作。

// 尝试使用数组
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        // 1.创建数组
        // 创建静态数组: 数组类型 数组名 = { 元素列表 };
        // 创建动态数组: 数组类型 数组名 = new 数组类型 { 元素列表 }
        // 无论是静态创建还是动态创建, 其实最终数组元素都会存储到堆上

        // (1)创建一维数组
        int[] arr1 = { 3, 2, 1 };
        int[] arr2 = new int[]{ 3, 2, 1 };
        int[] arr3 = new int[3]; /* 空数组默认元素都为 0 */

        // (2)创建二维数组
        int[][] arr4 = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} };
        int[][] arr5 = new int[][] { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} };
        int[][] arr6 = new int[4][3]; /* 空数组默认元素都为 0 */

        // 初始化的元素列表只有在定义阶段使用, 不能先定义然后另起一行进行初始化元素列表
        // 如果希望定义出一个空数组, 那么需要携带部分值供编译器识别
        // 数组类型 数组名 = new 数组类型[量][...]
        // 这里的量可以是常量也可以是变量


        // 2.遍历数组
        // (1)使用普通 for 循环
        for (int i = 0; i < arr1.length; i++) { // 属性 array.length 用来获取数组的长度
            System.out.print(arr1[i] + ", ");
        }
        System.out.println();

        // (2)使用增强 for 循环 foreach(Cpp 中的范围 for)
        for (int index : arr2) {
            System.out.print(index + ", ");
        }
        System.out.println();

        // (3)使用数组类打印
        System.out.println(Arrays.toString(arr4)); // 关于 Arrays 后续再来提及
        System.out.println(Arrays.deepToString(arr5));


        // 3.排序数组
        System.out.println("排序后");
        // (1)全局排序
        Arrays.sort(arr1);
        System.out.println(Arrays.toString(arr1));
        // (2)局部排序
        Arrays.sort(arr2, 0, 2); // 排序范围是 [0, 2)
        System.out.println(Arrays.toString(arr2));
        /* 逆向排序有些麻烦, 后面再提及 */


        // 4.填充数组
        System.out.println("填充前" + Arrays.toString(arr3)); // 填充前
        Arrays.fill(arr3, 1, 3, -1); // 从 [1, 3) 的范围中填充 -1 这个数字
        System.out.println("填充后" + Arrays.toString(arr3)); // 填充后


        // 5.查找数组
        System.out.println("在数组 arr5[3] 中, 存在元素 12, 其对应的索引为 " + Arrays.binarySearch(arr5[3], 12)); // 使用二分查找来查找


        // 6.比较数组
        if(Arrays.equals(arr1, arr1)) {
            System.out.println("arr1 自己和自己等价");
        }

        if(Arrays.deepEquals(arr4, arr5)) { // 深度等价
            System.out.println("arr4 和 arr5 是等价的");
        }

        if(!Arrays.equals(arr1, arr3)) {
            System.out.println("arr1 和 arr3 是不等价的");
        }
    }
}

警告:无论是静态创建还是动态创建,最终数组元素都会存储到堆上,而数组名作为引用变量指向/引用堆空间里的数组元素,并且使用下标/索引来进行访问。

警告:对于基本类型的数组,默认初始化为 0

补充:数组被创建时,如果类型时基本数据类型会自动进行初始化,如果时自定义类类型,也会调用构造函数进行初始化,这点在和 Cpp 是一样的。

补充:Java 的数组可以使用索引来查找和修改对应元素,并且索引也是从 0 开始的,Java 的数组越界会抛出异常,这比 C/C++ 的基本数组要安全得多。

补充:当 Java 数组发生索引越界时会发生异常。

12.2.数组的引用

首先您需要注意,数组是一个引用类型,什么是引用类型呢?首先您需要了解一下 Java 的内存分布。首先内存是连续分布的存储空间,程序中运行所需要的数据就存储在内存空间中,而 JVM 虚拟机对内存的划分大致如下:

flowchart TD
subgraph "运行时数据区"
	subgraph "所有线程共享的数据区"
        a["方法区"]
        b["堆区"]
    end
	subgraph "线程隔离的数据区"
		c["虚拟机栈"] ~~~ d["本地方法栈"] ~~~ e["程序计数器"]
    end
end
  1. 方法区(Method Area): 存储虚拟机加载的类信息、常量、静态变量等,即编译器编译后的代码数据。
  2. 堆区(Heap): JVM 所管理的最大内存区域,所有使用 new 创建的对象都是在堆上保存,堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有程序在使用,就不会被销毁。其中销毁数据的 GC,也是 Java 最重要的特征之一…
  3. 虚拟机栈(JVM stack):与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有了局部变量表、操作数栈、动态链接、返回地址…保存的都是与方法执行时相关信息(例如局部变量,在方法运行结束后,栈帧就会被销毁,栈帧中保存的数据也跟着销毁了。
  4. 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似,只不过保存的内容是 Native 方法(即原生方法)的局部变员,在有些版本的 JVM 实现中(例如 HotSpot),本地方法栈和虚拟机栈是在一起使用的,这些原生方法很大程度上就是 C/Cpp 代码实现的。
  5. 程序计数器(PC Register):只是一个很小的空间,保存下一条执行的指令的地址

补充:此外还有个 jdk 1.8 的概念,即本地内存(元空间、运行时常量池以及直接内存)

这里简单看一下即可…我们主要焦距在堆和虚拟机栈上,让我们来分析下面这个代码的变量和数组的存储情况:

// 分析内存情况
public class Main {
    public static void main(String[] args) {
        int a = 10;
        int b = 5;
        int[] arr = { 1, 2, 3, 4 };
    }
}
Stack
Heap
引用
a变量
b变量
arr的哈希地址(形式地址/引用)
存储数组元素[1][2][3][4]

可以看到 arr 这个标识符引用了数组,因此在 Stack 区域中不是直接存储数组的数据,只是存储了数组的一个“标记”。

注意:这里的形式地址不是 C/Cpp 中的地址,但概念有些类似。

因此我们在 Java 中就会遇到传递引用的情况,并且我还给出了图示:

// 分析传引用的情况
public class Main {
    public static void main(String[] args) {
        int a = 10;
        int b = 5;
        int[] arr1 = { 1, 2, 3, 4 };
        for(int e : arr1) {
            System.out.print(e + " ");
        }
        System.out.println();

        int[] arr2 = arr1;
        for(int e : arr2) {
            System.out.print(e + " ");
        }
        System.out.println();

        arr2[2] = 100;
        for(int e : arr1) {
            System.out.print(e + " ");
        }
        System.out.println();
    }
}
/* 输出结果
1 2 3 4
1 2 3 4
1 2 100 4
*/
Stack
Heap
引用
通过 arr2 引用修改数组元素
a变量
b变量
arr1的哈希地址(形式地址/引用)
arr2的哈希地址(形式地址/引用)
存储数组元素[1][2][100][4]

注意:强调一下,从这里的引用就可以看出 Java 的引用和 C++ 的引用有很大的不同,我建议直接认为是两个不同的东西,以避免混淆…

还有一些特殊并且值得注意的传递引用情况:

// 改变引用
public class Main {
    public static void main(String[] args) {
        int a = 10;
        int b = 5;
        int[] arr1 = { 1, 2, 3, 4 };
        for(int e : arr1) {
            System.out.print(e + " ");
        }
        System.out.println();

        int[] arr2 = { 4, 3, 2, 1 };
        for(int e : arr2) {
            System.out.print(e + " ");
        }
        System.out.println();

        arr1 = arr2;
        for(int e : arr1) {
            System.out.print(e + " ");
        }
        System.out.println();

        // 尝试修改并观察
        arr2[1] = 10000;
        for(int e : arr1) {
            System.out.print(e + " ");
        }
        System.out.println();
        for(int e : arr2) {
            System.out.print(e + " ");
        }
        System.out.println();
    }
}
/* 输出结果
1 2 3 4 
4 3 2 1 
4 3 2 1 
4 10000 2 1 
4 10000 2 1 
*/
Stack
Heap
取消引用
改变引用
引用
a变量
b变量
arr1的哈希地址(形式地址/引用)
arr2的哈希地址(形式地址/引用)
(由于没有引用指向该数组,此处将被自动销毁)存储数组元素[1][2][3][4]
存储数组元素[4][3][2][1]

也就是说,一个引用不能同时指向多个对象,但是一个对象能被多个引用指向。同时,如果堆中的数组没有任何“标记”存在于 Stack 中,也就是没有任何一个标识符引用这个数组,那么 Java GC 会根据自己的决策来释放该数组。

引用类型的初始化可以使用 null,代表引用不指向任何的对象(直接使用索引进行读写操作就会出现空指针异常)。因此JavanullC/C++ 不一样,它不是指内存上的 0 地址,只是代表不指向任何对象,两者没有直接关联。

12.3.数组的传递

下面这个代码您需要好好分析一下:

// 传递数组参数
import java.util.Arrays;
public class Main {
    public static void func1(int[] arr) { // 形参 arr
        arr = new int[] {0, 0, 0, 0};
    }
    public static void func2(int[] arr) { // 形参 arr 
        arr[1] = 100;
    }

    public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4 }; // 实参 arr
        func1(arr);
        func2(arr);
        System.out.println(Arrays.toString(arr));
    }
}
/* 输出结果
[1, 100, 3, 4]
*/

这份代码可能会让您吃惊,让我们来看看引用指向和内存分布的分析图,调用方法 func1() 且尚未执行 arr = new int[]{0, 0, 0, 0}; 时,发生以下事情。

Stack
Heap
引用
引用
形参 arr 的哈希地址(形式地址/引用)
实参 arr 的哈希地址(形式地址/引用)
存储数组元素[1][2][3][4]

执行 arr = new int[]{0, 0, 0, 0}; 后发生以下事情。

Stack
Heap
引用
取消引用
引用
形参 arr 的哈希地址(形式地址/引用)
实参 arr 的哈希地址(形式地址/引用)
存储数组元素[1][2][3][4]
存储数组元素[0][0][0][0]

func1() 调用结束后,形参 arr 被销毁,原本指向的对象没有被引用,就会被 Java 自动销毁。最终什么事情没有发生,实参 arr 没有发生任何改变。

Stack
Heap
引用
实参 arr 的哈希地址(形式地址/引用)
存储数组元素[1][2][3][4]

而调用 func2() 并且执行语句 arr[1] = 100; 就会导致实参指向的数组也会跟着变化。

Stack
Heap
引用(并且对数组做修改)
引用
形参 arr 的哈希地址(形式地址/引用)
实参 arr 的哈希地址(形式地址/引用)
存储数组元素[1][100][3][4]

利用数组的引用传递,我们可以使用数组引用的特性来完成两数交换的目的。

// 两数交换
import java.util.Arrays;
public class Main {
    public static int[] Swap(int[] arr) {
        int tmp = arr[0];
        arr[0] = arr[1];
        arr[1] = tmp;
        return arr;
    }

    public static void main(String[] args) {
        int[] arr = { 5, 10 };
        System.out.println(Arrays.toString(arr));

        int[] swapArr = Swap(arr);
        System.out.println(Arrays.toString(swapArr));
    }
}
/* 输出结果
[5, 10]
[10, 5]
*/

12.4.数组的拷贝

但对于方法来说,传应用有时太过于危险,在不考虑拷贝开销的情况下,可以考虑对数组进行拷贝后再进行处理(可以是拷贝后传递给方法,也可以是方法获得数组后多一步拷贝,这样就有可能会有多次拷贝,这种情况以后补充…)。

使用 Arrays 的方法 copyOf() 可以拷贝数组的内容,并且可以带有拷贝长度的参数,长度小于源 s 数组就拷贝子数组,长度大于原数组则多余的部分默认初始为 0

// 数组拷贝
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        int[] arr = new int[] {1, 2, 3, 4, 5};
        int[] copy1 = Arrays.copyOf(arr, 3); // 只拷贝三个元素
        for (int e : copy1) {
            System.out.print(e + " ");
        }

        System.out.println();

        int[] copy2 = Arrays.copyOf(arr, arr.length * 2); // 拷贝数组所有元素, 不够拷贝就填零
        for (int e : copy2) {
            System.out.print(e + " ");
        }
    }
}

/* 输出结果
1 2 3 
1 2 3 4 5 0 0 0 0 0
*/

补充:实际上 copyOf() 的底层实现使用 arraycopy() ,其底层是使用 C/C++ 实现的,如果打开实现就会出现关键字 native,代表该实现不是使用 Java 代码实现的,而是使用 C/C++(这还是要看具体的实现需要看 JVM 的源码)。

// 另一种拷贝(比较底层)
public class Main {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 2, 3, 4, 5};
        int[] copy = new int[arr.length * 2];
        System.arraycopy(arr, 2, copy, 1, arr.length - 2); //底层使用 C/C++ 实现
        for (int e : copy) {
            System.out.print(e + " ");
        }
    }
}

/* 输出结果
0 3 4 5 0 0 0 0 0 0
*/

还有另外一个方法 copyOfRange() 可以拷贝局部的子数组。

//拷贝局部子数组
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        int[] arr = new int[] {1, 2, 3, 4, 5};
        int[] copy = Arrays.copyOfRange(arr, 2, 4); // 拷贝 [2, 4) 的数组元素
        for (int e : copy) {
            System.out.print(e + " ");
        }
    }
}
/* 输出结果
3 4
*/

如果只是单纯想拷贝全部的数组,直接使用 clone() 会更加方便。

// 直接克隆数组整体
public class Main {
    public static void main(String[] args) {
        int[] arr = new int[] {1, 2, 3, 4, 5};
        int[] copy = arr.clone();
        for (int e : copy) {
            System.out.print(e + " ");
        }
    }
}

/* 输出结果
1 2 3 4 5 
*/

注意:目前我只讨论基本数据类型构成的数组,不涉及到深拷贝的问题,关于深浅拷贝我们以后再来提及。

13.字符串

在您学习了如何使用数组后,就能很快理解字符串,从另外一个角度上来说,字符串实际上是数组的一种特殊形式(虽然我不知道在 Java 内部 String 是否有进行复用,但理解终究是和数组一样的)。因此前面有关数组的使用我尽可能讲的详细,但有关字符串的部分我只提供一份代码和注释供您研究。

// 尝试使用字符串
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        // 1.获取字符串中的单字符
        String str1 = new String("Hello, World!");
        String str2 = "你好,世界";
        String str3 = "limou3434.com/blog/login";
        String str4 = "23";
        System.out.println("str1.charAt(0): " + str1.charAt(0)); // 获取索引为 0 的字符, 不允许使用 str1[0] 进行访问
        System.out.println("str2.length(): " + str2.length()); // 获取字符串的长度, 可以看到汉字也和字母一样做处理


        // 2.比较字符串是否等价
        if (str1.equals("Hello, World!")) { // 比较两个字符串是否相等
            System.out.println("两个字符串相等");
        }
        if (str1.equalsIgnoreCase("hello, world!")) { // 忽略大小写比较字符串
            System.out.println("两个字符串相等");
        }


        // 3.字符串大小写转换
        System.out.println("大写: " + str1.toUpperCase()); // 将字符串转换为大写
        System.out.println("小写: " + str1.toLowerCase()); // 将字符串转换为小写


        // 4.截取子串
        System.out.println("子串: " + str1.substring(3, 6)); // 截取 str1 中 [3, 6) 的部分


        // 5.分割子串
        System.out.println("分割结果为: " + Arrays.toString(str3.split("/"))); // 根据指定的分隔符将此字符串分割成子字符串数组


        // 6.替换和搜索
        System.out.println(str1.indexOf("World")); // 返回指定子字符串在此字符串中第一次出现处的索引
        System.out.println(str1.replace('o', '0')); // 将字符串中所有的 'o' 替换为 '0'
        if (str1.matches(".*Hello.*")) {
            System.out.println("符合给定的匹配模式");
        }

        // 7.格式化字符串
        System.out.println("格式化结果: " + String.format("Hello, %s!", "World"));


        // 8.转为目标类型
        int number = Integer.parseInt(str4);
        System.out.println("转化结果: " + number);


        // 9.字符串编码和解码
        byte[] bytes = str1.getBytes(StandardCharsets.UTF_8); // 将字符串编码为字节序列
        System.out.println("编码后: " + Arrays.toString(bytes));
        System.out.println("解码后: " + new String(bytes, StandardCharsets.UTF_8)); // 将字节序列解码为字符串
    }
}

14.IDEA 快捷键

和其他编程语言有一个很大的不同在于 IDEA 几乎是每一个 Java 程序员必须要学会使用的软件,这里简单列出几个快捷键,在后续的章节中,我还会开启关于 IDEA 的详细使用。

  • [ctrl + /] 注释和取消注释
  • ctrl + alt + l 格式化
  • [psvm + tab]/[m + tab] 生成 main 方法
  • [sout + tab] 生成 println 语句

结语:…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/782846.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Science Advances|用于肌电检测的柔性微针电极阵列(健康监测/柔性传感/柔性电子)

2024年5月1日,美国南加州大学Hangbo Zhao课题组在《Science Advances》上发布了一篇题为“Highly stretchable and customizable microneedle electrode arrays for intramuscular electromyography”的论文。论文内容如下: 一、 摘要 可伸缩的三维穿透式微电极阵列在多个领…

Linux环境安装Maven

1.下载安装包 访问Maven官网下载地址&#xff1a;Maven – Download Apache Maven进行下载对应的安装包。 本文档使用的是apache-maven-3.9.8-bin.tar.gz 2.将下载好的安装包上传到环境上&#xff0c;本处是在/usr目录下新建了一个Maven的目录&#xff0c;如下&#xff1a; …

在线白板工具大揭秘:为何它成为远程团队的必备神器?

一直觉得白板是个很好的工具&#xff0c;不管是学习还是工作&#xff0c;它都能够帮助我们更好地整理思路。 作为一名经常需要远程协作和创意脑暴的职场人&#xff0c;显然传统普通的白板工具已经不够用了。 在这个数字化时代&#xff0c;我们更需要一个电子白板&#xff0c;一…

计算机如何存储浮点数

浮点数组成 在计算机中浮点数通常由三部分组成&#xff1a;符号位、指数位、尾数位。IEEE-754中32位浮点数如下&#xff1a; 上图32bit浮点数包含1bit的符号位&#xff0c;8比特的指数位和23bit的尾数位。对于一个常规浮点数&#xff0c;我们来看看它是如何存储和计算的。这里…

FPGA的理解,个人的见解,不一定对

类似于面包板上搭建电路&#xff0c;但是使用的是逻辑单元模块&#xff1b;如加法器&#xff0c;减法器&#xff0c;寄存器等 没有模拟电路的电容&#xff0c;电阻&#xff1b;但是逻辑单元的底层实现&#xff0c;使用MOS管等电路实现电路的开关&#xff1b;从而表示0&#xf…

1002-15SF 同轴连接器

型号简介 1002-15SF是Southwest Microwave的29.2 mm (V) DC 至 67 GHz 连接器。该连接器用于连接电缆和设备的组件&#xff0c;它可以提供电气连接和机械支撑。广泛应用于通信、电子、航空航天、军事等领域。 型号特点 电缆的中心导体插入连接器后部的母插座内置应力释放装置可…

SpringMVC系列十三: SpringMVC执行流程 - 源码分析

源码分析 执行流程图实验设计前端控制器分发请求处理器映射器处理器适配器调用目标方法调用视图解析器渲染视图作业布置 执行流程图 实验设计 1.新建com.zzw.web.debug.HelloHandler Controller public class HelloHandler {//编写方法, 响应请求, 返回ModelAndViewRequestMa…

GD 32点亮流水灯

1. 0 软件架构设置 2.0 API 接口以及数据结构定义 3.0 程序代码实现 程序项目的结构如下所示&#xff1a; 第一步&#xff1a;编写LED驱动&#xff0c;初始化驱动程序 创建结构体&#xff1a;第一个参数表示GPIO使能&#xff0c;第二个参数表示单片机的IO口&#xff0c;第三个…

html+css+js贪吃蛇游戏

贪吃蛇游戏&#x1f579;四个按钮控制方向&#x1f3ae; 源代码在图片后面 点赞❤️关注&#x1f64f;收藏⭐️ 互粉必回&#x1f64f;&#x1f64f;&#x1f60d;&#x1f60d;&#x1f60d; 源代码&#x1f4df; <!DOCTYPE html> <html lang"en"&…

idea删除分支并同步到gitLab以及gitLab上的分支删除

目录 idea删除分支并同步到gitLab 方法一&#xff08;推荐&#xff09; 方法二&#xff08;命令行&#xff09; gitLab上的分支删除 前言-与正文无关 ​ 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&…

Tell Me Why:利用大型语言模型进行可解释的公共健康事实核查

Tell Me Why: Explainable Public Health Fact-Checking with Large Language Models 论文地址:https://arxiv.org/abs/2405.09454https://arxiv.org/abs/2405.09454 1.概述 最近的COVID-19大流行突显了公共健康领域事实核查的关键需求。在信息通过社交媒体平台迅速传播的时…

GPU 张量核心(Tensor Core)技术解读

一文理解 GPU 张量核心&#xff08;Tensor Core&#xff09; 引言 最新一代Nvidia GPU搭载Tensor Core技术&#xff0c;本指南深度解读其卓越性能&#xff0c;为您带来极致体验。 Nvidia最新GPU微架构中的核心技术——Tensor Core&#xff0c;自Volta起每代均获突破&#xf…

【漏洞复现】Emlog Pro 2.3.4——任意用户登入、会话持久化(CVE-2024-5044)

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现 漏洞描述 漏洞编号&#xff1a;CVE-2024-5044 漏洞成因&#xff1a; 在Emlog Pro …

加密与安全_ 解读非对称密钥解决密钥配送问题的四个方案

文章目录 Pre对称密钥的死穴 - 经典的密钥配送问题什么是非对称密钥非对称密钥解决密钥配送问题的四个方案共享密钥密钥分配中心&#xff08;KDC&#xff09;Diffie-Hellman 密钥交换体系公钥密码体系RSA算法 Pre 对称密钥的死穴 - 经典的密钥配送问题 假设 Alice 和 Bob 两个人…

阻塞赋值与非阻塞赋值

文章目录 一、何为“阻塞”&#xff1f;二、阻塞赋值与非阻塞赋值1. 阻塞式赋值“”2.非阻塞式赋值“<” 三、什么时候用阻塞赋值或非阻塞赋值&#xff1f; 一、何为“阻塞”&#xff1f; 所谓“阻塞”&#xff0c;可以理解为阻止顺序语句块中其他语句的执行。例如&#xf…

ASP.NET Core----基础学习04----Model模型的创建 服务的注入

文章目录 1. 创建Models文件夹&#xff0c;3个文件的内容如下&#xff1a;&#xff08;1&#xff09;模型的创建&#xff08;2&#xff09;服务的注入 1. 创建Models文件夹&#xff0c;3个文件的内容如下&#xff1a; &#xff08;1&#xff09;模型的创建 模型的基础类Student…

阿里云 Ubuntu 开启允许 ssh 密码方式登录

以前用的 centos&#xff0c;重置系统为 ubuntu 后在ssh 远程连接时遇到了点问题&#xff1a; 在阿里云控制台重置实例密码后无法使用密码进行 SSH 连接登录 原因&#xff1a;阿里云 Ubuntu 默认禁用密码登录方式 解决办法&#xff1a; 先使用其他用户登录到服务器 这里进来…

Java中多线程经典案例

案例一单例模式 只有一个对象,只实例化一个对象 饿汉模式 在程序开始初期的实例化一个对象 static成员初始化时机是在类加载的时候,static修饰的instance只有唯一一个,初始化也是只执行一次,static修饰的是类属性,就是在类对象上的,每个类对象在JVM中只有一份,里面的静态成员…

下载安装JavaFX及解决报错:缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序|Eclipse

目录 1.下载并解压 2.Eclipse配置 3.报错问题 解决方法1&#xff1a;将javaSE更改到9以下 解决方法2&#xff1a; 使用module-info.java配置解决 1.下载并解压 JavaFX下载地址&#xff1a;JavaFX - Gluon 选择合适自己电脑配置的sdk版本下载 打不开网页的参考这个博客&…

Demeditec Diagnostics — AMH ELISA试剂盒

抗缪勒氏管激素(AMH)&#xff0c;是一种二聚体分子量为140 KDa的糖蛋白&#xff0c;是转化生长因子-β (TGF-β)细胞因子家族&#xff0c;在生殖结构正常分化中起重要作用。AMH已被被确定为卵巢储备的可靠标志&#xff0c;有助于预测早期卵泡丢失和更年期开始。AMH水平也反映了…