首页 > 编程语言 >Java从萌新小白到顶级大牛(3更新中)

Java从萌新小白到顶级大牛(3更新中)

时间:2024-03-20 13:00:04浏览次数:29  
标签:新小白 Java String java jar 模块 字符串 从萌 class

模块

从Java 9开始,JDK又引入了模块(Module)。

什么是模块?这要从Java 9之前的版本说起。

我们知道,.class文件是JVM看到的最小可执行文件,而一个大型程序需要编写很多Class,并生成一堆.class文件,很不便于管理,所以,jar文件就是class文件的容器。

在Java 9之前,一个大型Java程序会生成自己的jar文件,同时引用依赖的第三方jar文件,而JVM自带的Java标准库,实际上也是以jar文件形式存放的,这个文件叫rt.jar,一共有60多M。

如果是自己开发的程序,除了一个自己的app.jar以外,还需要一堆第三方的jar包,运行一个Java程序,一般来说,命令行写这样:

java -cp app.jar:a.jar:b.jar:c.jar com.liaoxuefeng.sample.Main

 注意:JVM自带的标准库rt.jar不要写到classpath中,写了反而会干扰JVM的正常运行。

如果漏写了某个运行时需要用到的jar,那么在运行期极有可能抛出ClassNotFoundException。

所以,jar只是用于存放class的容器,它并不关心class之间的依赖。

从Java 9开始引入的模块,主要是为了解决“依赖”这个问题。如果a.jar必须依赖另一个b.jar才能运行,那我们应该给a.jar加点说明啥的,让程序在编译和运行的时候能自动定位到b.jar,这种自带“依赖关系”的class容器就是模块。

为了表明Java模块化的决心,从Java 9开始,原有的Java标准库已经由一个单一巨大的rt.jar分拆成了几十个模块,这些模块以.jmod扩展名标识,可以在$JAVA_HOME/jmods目录下找到它们:

  • java.base.jmod
  • java.compiler.jmod
  • java.datatransfer.jmod
  • java.desktop.jmod
  • ...

这些.jmod文件每一个都是一个模块,模块名就是文件名。例如:模块java.base对应的文件就是java.base.jmod。模块之间的依赖关系已经被写入到模块内的module-info.class文件了。所有的模块都直接或间接地依赖java.base模块,只有java.base模块不依赖任何模块,它可以被看作是“根模块”,好比所有的类都是从Object直接或间接继承而来。

把一堆class封装为jar仅仅是一个打包的过程,而把一堆class封装为模块则不但需要打包,还需要写入依赖关系,并且还可以包含二进制代码(通常是JNI扩展)。此外,模块支持多版本,即在同一个模块中可以为不同的JVM提供不同的版本。

编写模块

那么,我们应该如何编写模块呢?还是以具体的例子来说。首先,创建模块和原有的创建Java项目是完全一样的,以oop-module工程为例,它的目录结构如下:

oop-module
├── bin
├── build.sh
└── src
    ├── com
    │   └── itranswarp
    │       └── sample
    │           ├── Greeting.java
    │           └── Main.java
    └── module-info.java

其中,bin目录存放编译后的class文件,src目录存放源码,按包名的目录结构存放,仅仅在src目录下多了一个module-info.java这个文件,这就是模块的描述文件。在这个模块中,它长这样:

module hello.world {

requires java.base; // 可不写,任何模块都会自动引入java.base

requires java.xml;

}

其中,module是关键字,后面的hello.world是模块的名称,它的命名规范与包一致。花括号的requires xxx;表示这个模块需要引用的其他模块名。除了java.base可以被自动引入外,这里我们引入了一个java.xml的模块。

当我们使用模块声明了依赖关系后,才能使用引入的模块。例如,Main.java代码如下:

package com.itranswarp.sample;

// 必须引入java.xml模块后才能使用其中的类:import javax.xml.XMLConstants;

public class Main {

public static void main(String[] args) {

Greeting g = new Greeting();

System.out.println(g.hello(XMLConstants.XML_NS_PREFIX));

}

}

如果把requires java.xml;从module-info.java中去掉,编译将报错。可见,模块的重要作用就是声明依赖关系。

下面,我们用JDK提供的命令行工具来编译并创建模块。

首先,我们把工作目录切换到oop-module,在当前目录下编译所有的.java文件,并存放到bin目录下,命令如下:

$ javac -d bin src/module-info.java src/com/itranswarp/sample/*.java

如果编译成功,现在项目结构如下:

oop-module
├── bin
│   ├── com
│   │   └── itranswarp
│   │       └── sample
│   │           ├── Greeting.class
│   │           └── Main.class
│   └── module-info.class
└── src
    ├── com
    │   └── itranswarp
    │       └── sample
    │           ├── Greeting.java
    │           └── Main.java
    └── module-info.java

注意到src目录下的module-info.java被编译到bin目录下的module-info.class。

下一步,我们需要把bin目录下的所有class文件先打包成jar,在打包的时候,注意传入--main-class参数,让这个jar包能自己定位main方法所在的类:

$ jar --create --file hello.jar --main-class com.itranswarp.sample.Main -C bin .

现在我们就在当前目录下得到了hello.jar这个jar包,它和普通jar包并无区别,可以直接使用命令java -jar hello.jar来运行它。但是我们的目标是创建模块,所以,继续使用JDK自带的jmod命令把一个jar包转换成模块:

$ jmod create --class-path hello.jar hello.jmod

于是,在当前目录下我们又得到了hello.jmod这个模块文件,这就是最后打包出来的传说中的模块!

运行模块

要运行一个jar,我们使用java -jar xxx.jar命令。要运行一个模块,我们只需要指定模块名。试试:

$ java --module-path hello.jmod --module hello.world

结果是一个错误:

Error occurred during initialization of boot layer

java.lang.module.FindException: JMOD format not supported at execution time: hello.jmod

原因是.jmod不能被放入--module-path中。换成.jar就没问题了:

$ java --module-path hello.jar --module hello.worldHello, xml!

那我们辛辛苦苦创建的hello.jmod有什么用?答案是我们可以用它来打包JRE。

打包JRE

前面讲了,为了支持模块化,Java 9首先带头把自己的一个巨大无比的rt.jar拆成了几十个.jmod模块,原因就是,运行Java程序的时候,实际上我们用到的JDK模块,并没有那么多。不需要的模块,完全可以删除。

过去发布一个Java应用程序,要运行它,必须下载一个完整的JRE,再运行jar包。而完整的JRE块头很大,有100多M。怎么给JRE瘦身呢?

现在,JRE自身的标准库已经分拆成了模块,只需要带上程序用到的模块,其他的模块就可以被裁剪掉。怎么裁剪JRE呢?并不是说把系统安装的JRE给删掉部分模块,而是“复制”一份JRE,但只带上用到的模块。为此,JDK提供了jlink命令来干这件事。命令如下:

$ jlink --module-path hello.jmod --add-modules java.base,java.xml,hello.world --output jre/

我们在--module-path参数指定了我们自己的模块hello.jmod,然后,在--add-modules参数中指定了我们用到的3个模块java.base、java.xml和hello.world,用,分隔。最后,在--output参数指定输出目录。

现在,在当前目录下,我们可以找到jre目录,这是一个完整的并且带有我们自己hello.jmod模块的JRE。试试直接运行这个JRE:

$ jre/bin/java --module hello.worldHello, xml!

要分发我们自己的Java应用程序,只需要把这个jre目录打个包给对方发过去,对方直接运行上述命令即可,既不用下载安装JDK,也不用知道如何配置我们自己的模块,极大地方便了分发和部署。

访问权限

前面我们讲过,Java的class访问权限分为public、protected、private和默认的包访问权限。引入模块后,这些访问权限的规则就要稍微做些调整。

确切地说,class的这些访问权限只在一个模块内有效,模块和模块之间,例如,a模块要访问b模块的某个class,必要条件是b模块明确地导出了可以访问的包。

举个例子:我们编写的模块hello.world用到了模块java.xml的一个类javax.xml.XMLConstants,我们之所以能直接使用这个类,是因为模块java.xml的module-info.java中声明了若干导出:

module java.xml {

    exports java.xml;

    exports javax.xml.catalog;

    exports javax.xml.datatype;

    ...

}

只有它声明的导出的包,外部代码才被允许访问。换句话说,如果外部代码想要访问我们的hello.world模块中的com.itranswarp.sample.Greeting类,我们必须将其导出:

module hello.world {

    exports com.itranswarp.sample;



    requires java.base;

requires java.xml;

}

因此,模块进一步隔离了代码的访问权限。

练习

请下载并练习如何打包模块和JRE。

小结

Java 9引入的模块目的是为了管理依赖;

使用模块可以按需打包JRE;

使用模块对类的访问权限有了进一步限制。

Java核心类

本节我们将介绍Java的核心类,包括:

字符串

StringBuilder

StringJoiner

包装类型

JavaBean

枚举

常用工具类

字符串和编码

String

在Java中,String是一个引用类型,它本身也是一个class。但是,Java编译器对String有特殊处理,即可以直接用"..."来表示一个字符串:

String s1 = "Hello!";

实际上字符串在String内部是通过一个char[]数组表示的,因此,按下面的写法也是可以的:

String s2 = new String(new char[] {'H', 'e', 'l', 'l', 'o', '!'});

因为String太常用了,所以Java提供了"..."这种字符串字面量表示方法。

Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的private final char[]字段,以及没有任何修改char[]的方法实现的。

我们来看一个例子:

// String

public class Main {

    public static void main(String[] args) {

        String s = "Hello";

        System.out.println(s);

        s = s.toUpperCase();

        System.out.println(s);

    }

}

根据上面代码的输出,试解释字符串内容是否改变。

字符串比较

当我们想要比较两个字符串是否相同时,要特别注意,我们实际上是想比较字符串的内容是否相同。必须使用equals()方法而不能用==。

我们看下面的例子:

// String

public class Main {

    public static void main(String[] args) {

        String s1 = "hello";

        String s2 = "hello";

        System.out.println(s1 == s2);

        System.out.println(s1.equals(s2));

    }

}

从表面上看,两个字符串用==和equals()比较都为true,但实际上那只是Java编译器在编译期,会自动把所有相同的字符串当作一个对象放入常量池,自然s1和s2的引用就是相同的。

所以,这种==比较返回true纯属巧合。换一种写法,==比较就会失败:

// String

public class Main {

    public static void main(String[] args) {

        String s1 = "hello";

        String s2 = "HELLO".toLowerCase();

        System.out.println(s1 == s2);

        System.out.println(s1.equals(s2));

    }

}

结论:两个字符串比较,必须总是使用equals()方法。

要忽略大小写比较,使用equalsIgnoreCase()方法。

String类还提供了多种方法来搜索子串、提取子串。常用的方法有:

// 是否包含子串:"Hello".contains("ll"); // true

注意到contains()方法的参数是CharSequence而不是String,因为CharSequence是String实现的一个接口。

搜索子串的更多的例子:

"Hello".indexOf("l"); // 2"Hello".lastIndexOf("l"); // 3"Hello".startsWith("He"); // true"Hello".endsWith("lo"); // true

提取子串的例子:

"Hello".substring(2); // "llo""Hello".substring(2, 4); "ll"

注意索引号是从0开始的。

去除首尾空白字符

使用trim()方法可以移除字符串首尾空白字符。空白字符包括空格,\t,\r,\n:

"  \tHello\r\n ".trim(); // "Hello"

注意:trim()并没有改变字符串的内容,而是返回了一个新字符串。

另一个strip()方法也可以移除字符串首尾空白字符。它和trim()不同的是,类似中文的空格字符\u3000也会被移除:

"\u3000Hello\u3000".strip(); // "Hello"" Hello ".stripLeading(); // "Hello "" Hello ".stripTrailing(); // " Hello"

String还提供了isEmpty()和isBlank()来判断字符串是否为空和空白字符串:

"".isEmpty(); // true,因为字符串长度为0"  ".isEmpty(); // false,因为字符串长度不为0"  \n".isBlank(); // true,因为只包含空白字符" Hello ".isBlank(); // false,因为包含非空白字符

替换子串

要在字符串中替换子串,有两种方法。一种是根据字符或字符串替换:

String s = "hello";

s.replace('l', 'w'); // "hewwo",所有字符'l'被替换为'w'

s.replace("ll", "~~"); // "he~~o",所有子串"ll"被替换为"~~"

另一种是通过正则表达式替换:

String s = "A,,B;C ,D";

s.replaceAll("[\\,\\;\\s]+", ","); // "A,B,C,D"

上面的代码通过正则表达式,把匹配的子串统一替换为","。关于正则表达式的用法我们会在后面详细讲解。

分割字符串

要分割字符串,使用split()方法,并且传入的也是正则表达式:

String s = "A,B,C,D";String[] ss = s.split("\\,"); // {"A", "B", "C", "D"}

拼接字符串

拼接字符串使用静态方法join(),它用指定的字符串连接字符串数组:

String[] arr = {"A", "B", "C"};String s = String.join("***", arr); // "A***B***C"

格式化字符串

字符串提供了formatted()方法和format()静态方法,可以传入其他参数,替换占位符,然后生成新的字符串:

// String

public class Main {

    public static void main(String[] args) {

        char[] cs = "Hello".toCharArray();

        String s = new String(cs);

        System.out.println(s);

        cs[0] = 'X';

        System.out.println(s);

    }

}

有几个占位符,后面就传入几个参数。参数类型要和占位符一致。我们经常用这个方法来格式化信息。常用的占位符有:

  • %s:显示字符串;
  • %d:显示整数;
  • %x:显示十六进制整数;
  • %f:显示浮点数。

占位符还可以带格式,例如%.2f表示显示两位小数。如果你不确定用啥占位符,那就始终用%s,因为%s可以显示任何数据类型。要查看完整的格式化语法,请参考JDK文档

类型转换

要把任意基本类型或引用类型转换为字符串,可以使用静态方法valueOf()。这是一个重载方法,编译器会根据参数自动选择合适的方法:

String.valueOf(123); // "123"String.valueOf(45.67); // "45.67"String.valueOf(true); // "true"String.valueOf(new Object()); // 类似java.lang.Object@636be97c

要把字符串转换为其他类型,就需要根据情况。例如,把字符串转换为int类型:

int n1 = Integer.parseInt("123"); // 123int n2 = Integer.parseInt("ff", 16); // 按十六进制转换,255

把字符串转换为boolean类型:

boolean b1 = Boolean.parseBoolean("true"); // true

boolean b2 = Boolean.parseBoolean("FALSE"); // false

要特别注意,Integer有个getInteger(String)方法,它不是将字符串转换为int,而是把该字符串对应的系统变量转换为Integer:

Integer.getInteger("java.version"); // 版本号,11

转换为char[]

String和char[]类型可以互相转换,方法是:

char[] cs = "Hello".toCharArray(); // String -> char[]

String s = new String(cs); // char[] -> String

如果修改了char[]数组,String并不会改变:

// String <-> char[]

public class Main {

    public static void main(String[] args) {

        char[] cs = "Hello".toCharArray();

        String s = new String(cs);

        System.out.println(s);

        cs[0] = 'X';

        System.out.println(s);

    }

}

这是因为通过new String(char[])创建新的String实例时,它并不会直接引用传入的char[]数组,而是会复制一份,所以,修改外部的char[]数组不会影响String实例内部的char[]数组,因为这是两个不同的数组。

从String的不变性设计可以看出,如果传入的对象有可能改变,我们需要复制而不是直接引用。

例如,下面的代码设计了一个Score类保存一组学生的成绩:

// int[]

import java.util.Arrays;



public class Main {

    public static void main(String[] args) {

        int[] scores = new int[] { 88, 77, 51, 66 };

        Score s = new Score(scores);

        s.printScores();

        scores[2] = 99;

        s.printScores();

    }

}



class Score {

    private int[] scores;

    public Score(int[] scores) {

        this.scores = scores;

    }



    public void printScores() {

        System.out.println(Arrays.toString(scores));

    }

}

观察两次输出,由于Score内部直接引用了外部传入的int[]数组,这会造成外部代码对int[]数组的修改,影响到Score类的字段。如果外部代码不可信,这就会造成安全隐患。

请修复Score的构造方法,使得外部代码对数组的修改不影响Score实例的int[]字段。

字符编码

在早期的计算机系统中,为了给字符编码,美国国家标准学会(American National Standard Institute:ANSI)制定了一套英文字母、数字和常用符号的编码,它占用一个字节,编码范围从0到127,最高位始终为0,称为ASCII编码。例如,字符'A'的编码是0x41,字符'1'的编码是0x31。

如果要把汉字也纳入计算机编码,很显然一个字节是不够的。GB2312标准使用两个字节表示一个汉字,其中第一个字节的最高位始终为1,以便和ASCII编码区分开。例如,汉字'中'的GB2312编码是0xd6d0。

类似的,日文有Shift_JIS编码,韩文有EUC-KR编码,这些编码因为标准不统一,同时使用,就会产生冲突。

为了统一全球所有语言的编码,全球统一码联盟发布了Unicode编码,它把世界上主要语言都纳入同一个编码,这样,中文、日文、韩文和其他语言就不会冲突。

Unicode编码需要两个或者更多字节表示,我们可以比较中英文字符在ASCII、GB2312和Unicode的编码:

英文字符'A'的ASCII编码和Unicode编码:

         ┌────┐
ASCII:   │ 41 │
         └────┘

         ┌────┬────┐
Unicode: │ 00 │ 41 │
         └────┴────┘

英文字符的Unicode编码就是简单地在前面添加一个00字节。

中文字符'中'的GB2312编码和Unicode编码:

         ┌────┬────┐
GB2312:  │ d6 │ d0 │
         └────┴────┘

         ┌────┬────┐
Unicode: │ 4e │ 2d │
         └────┴────┘

那我们经常使用的UTF-8又是什么编码呢?因为英文字符的Unicode编码高字节总是00,包含大量英文的文本会浪费空间,所以,出现了UTF-8编码,它是一种变长编码,用来把固定长度的Unicode编码变成1~4字节的变长编码。通过UTF-8编码,英文字符'A'的UTF-8编码变为0x41,正好和ASCII码一致,而中文'中'的UTF-8编码为3字节0xe4b8ad。

UTF-8编码的另一个好处是容错能力强。如果传输过程中某些字符出错,不会影响后续字符,因为UTF-8编码依靠高字节位来确定一个字符究竟是几个字节,它经常用来作为传输编码。

在Java中,char类型实际上就是两个字节的Unicode编码。如果我们要手动把字符串转换成其他编码,可以这样做:

byte[] b1 = "Hello".getBytes(); // 按系统默认编码转换,不推荐byte[] b2 = "Hello".getBytes("UTF-8"); // 按UTF-8编码转换byte[] b2 = "Hello".getBytes("GBK"); // 按GBK编码转换byte[] b3 = "Hello".getBytes(StandardCharsets.UTF_8); // 按UTF-8编码转换

注意:转换编码后,就不再是char类型,而是byte类型表示的数组。

如果要把已知编码的byte[]转换为String,可以这样做:

byte[] b = ...

String s1 = new String(b, "GBK"); // 按GBK转换

String s2 = new String(b, StandardCharsets.UTF_8); // 按UTF-8转换

始终牢记:Java的String和char在内存中总是以Unicode编码表示。

延伸阅读

对于不同版本的JDK,String类在内存中有不同的优化方式。具体来说,早期JDK版本的String总是以char[]存储,它的定义如下:

public final class String {

    private final char[] value;

    private final int offset;

    private final int count;

}

而较新的JDK版本的String则以byte[]存储:如果String仅包含ASCII字符,则每个byte存储一个字符,否则,每两个byte存储一个字符,这样做的目的是为了节省内存,因为大量的长度较短的String通常仅包含ASCII字符:

public final class String {

    private final byte[] value;

    private final byte coder; // 0 = LATIN1, 1 = UTF16

对于使用者来说,String内部的优化不影响任何已有代码,因为它的public方法签名是不变的。

小结

Java字符串String是不可变对象;

字符串操作不改变原字符串内容,而是返回新字符串;

常用的字符串操作:提取子串、查找、替换、大小写转换等;

Java使用Unicode编码表示String和char;

转换编码就是将String和byte[]转换,需要指定编码;

转换为byte[]时,始终优先考虑UTF-8编码。

StringBuilder

Java编译器对String做了特殊处理,使得我们可以直接用+拼接字符串。

考察下面的循环代码:

String s = "";

for (int i = 0; i < 1000; i++) {

    s = s + "," + i;

}

虽然可以直接拼接字符串,但是,在循环中,每次循环都会创建新的字符串对象,然后扔掉旧的字符串。这样,绝大部分字符串都是临时对象,不但浪费内存,还会影响GC效率。

为了能高效拼接字符串,Java标准库提供了StringBuilder,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder中新增字符时,不会创建新的临时对象:

StringBuilder sb = new StringBuilder(1024);

for (int i = 0; i < 1000; i++) {

    sb.append(',');

    sb.append(i);

}String s = sb.toString();

StringBuilder还可以进行链式操作:

// 链式操作

public class Main {

    public static void main(String[] args) {

        var sb = new StringBuilder(1024);

        sb.append("Mr ")

          .append("Bob")

          .append("!")

          .insert(0, "Hello, ");

        System.out.println(sb.toString());

    }

}

如果我们查看StringBuilder的源码,可以发现,进行链式操作的关键是,定义的append()方法会返回this,这样,就可以不断调用自身的其他方法。

仿照StringBuilder,我们也可以设计支持链式操作的类。例如,一个可以不断增加的计数器:

// 链式操作

public class Main {

    public static void main(String[] args) {

        Adder adder = new Adder();

        adder.add(3)

             .add(5)

             .inc()

             .add(10);

        System.out.println(adder.value());

    }

}



class Adder {

    private int sum = 0;



    public Adder add(int n) {

        sum += n;

        return this;

    }



    public Adder inc() {

        sum ++;

        return this;

    }



    public int value() {

        return sum;

    }

}

注意:对于普通的字符串+操作,并不需要我们将其改写为StringBuilder,因为Java编译器在编译时就自动把多个连续的+操作编码为StringConcatFactory的操作。在运行期,StringConcatFactory会自动把字符串连接操作优化为数组复制或者StringBuilder操作。

你可能还听说过StringBuffer,这是Java早期的一个StringBuilder的线程安全版本,它通过同步来保证多个线程操作StringBuffer也是安全的,但是同步会带来执行速度的下降。

StringBuilder和StringBuffer接口完全相同,现在完全没有必要使用StringBuffer。

练习

请使用StringBuilder构造一个INSERT语句:​​​​​​​

public class Main {

    public static void main(String[] args) {

        String[] fields = { "name", "position", "salary" };

        String table = "employee";

        String insert = buildInsertSql(table, fields);

        System.out.println(insert);

        String s = "INSERT INTO employee (name, position, salary) VALUES (?, ?, ?)";

        System.out.println(s.equals(insert) ? "测试成功" : "测试失败");

    }

    static String buildInsertSql(String table, String[] fields) {

        // TODO:

        return "";

    }



}

小结

StringBuilder是可变对象,用来高效拼接字符串;

StringBuilder可以支持链式操作,实现链式操作的关键是返回实例本身;

StringBuffer是StringBuilder的线程安全版本,现在很少使用。

标签:新小白,Java,String,java,jar,模块,字符串,从萌,class
From: https://blog.csdn.net/2301_76141427/article/details/136805806

相关文章

  • 小白逆袭大神之路:零基础Java教程,手把手教你安装JDK与配置主流IDE
    了解JDK和IDE首先,让我们来简单了解一下JDK和IDE。JDK(JavaDevelopmentKit):这是Java开发的核心工具包,包含了Java运行时环境(JRE)、Java编译器(javac)以及其他一些工具。简单来说,没有JDK,你就无法编写或运行Java程序。IDE(IntegratedDevelopmentEnvironment):这是一个软件应用......
  • JAVA实战开源项目:高校大学生创业管理系统(Vue+SpringBoot)
    目录一、摘要1.1项目介绍1.2项目录屏二、功能模块2.1系统公告模块2.2创业项目模块2.3创业社团模块2.4政府政策模块2.5创业比赛模块三、系统设计3.1用例设计3.2数据库设计3.2.1系统公告表3.2.2创业项目表3.2.3创业社团表3.2.4政策表四、系统展示五、核......
  • 计算机电子书 (二)- Java
    书名有夸克网盘超链接,点击后可免费快速下载。(内容完善中)~~~文章目录前言一、Java1.基础2.并发和多线程3.虚拟机4.设计模式二、JavaEE和Spring三、SpringBoot四、SpringCloud、微服务五、优化性能重构前言计算机电子书籍一、Java1.基础Java编程思想(第4版)......
  • JAVA基础语法和开发工具配置
    程序开发步骤程序是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合。程序的步骤:编写->编译->运行JAVA基础语法一个JAVA程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作对象:对象是类的一个实例,有状态和行为。类:类是一个模板,它描......
  • 深入解析Java中的Vector集合类!
      咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及JavaSE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~......
  • Java之FileWriter详细解读
      咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及JavaSE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~......
  • JAVA 线程池SingleThreadExecutor实践教程
    SingleThreadExecutor是一个单线程的Executor,它使用单个工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。下面是使用SingleThreadExecutor的实践教程:步骤1:创建SingleThreadExecutor你可以使用Executors类提供的newSingleThreadExecutor方法来创......
  • JAVA线程池ScheduledThreadPool实践教程
    ScheduledThreadPool用于在给定的延迟之后,或者定期执行任务。以下是如何在Java中实践使用ScheduledThreadPool的步骤:步骤1:创建ScheduledThreadPool首先,使用Executors的newScheduledThreadPool方法来创建一个ScheduledThreadPool。参数是你想要在池中保持的线程数量。i......
  • 【Java初阶(一)】初识Java
    ❣博主主页:33的博客❣▶文章专栏分类:Java从入门到精通◀......
  • 【Java初阶(二)】分支与循环
    ❣博主主页:33的博客❣▶文章专栏分类:Java从入门到精通◀......