0%

JDK版本现在已经更新到20了,但是目前大部分企业和开发者仍然使用的是JDK8(你发任你发,我用JAVA8),不过随着时间的推移,切换到新的JDK版本是大势所趋。因此作为开发者,为了保证我们能够持续进步,了解和掌握新版本的功能和特性是非常重要和有价值的。在已有一个稳定版本(如JDK8)的基础上,我们可以选择再安装一个新版本(如JDK17)来供我们学习和测试,由于大部分JAVA应用是部署在Linux上的,所以这次我们通过shell脚本的方式来切换JDK版本。

思路分析

  1. 安装JDK后需要在/etc/profile中配置环境变量,因此我们的目标就是更改配置文件
  2. 想要更改配置文件,首先需要知道要修改的内容,我的配置是export JAVA_HOME=/usr/java/jdk,这里=后面的路径就是我们要修改的内容
  3. 由于每个人的/etc/profile文件各不相同,因此上述配置项可能在x行,也可能在y行,所以我们需要找到该配置项所在的位置
  4. 找到配置项位置后,我们将原JDK路径替换为目标JDK路径
  5. 再使用source命令更新配置信息即可完成JDK版本的切换

开始动手

  1. 创建脚本文件

    1
    touch switch_jdk.sh
  2. 编写脚本文件

    简要说一下思路

    • 定义一个数组来存放JDK版本和路径信息,使用==关联数组==,有点类似hashmap,key为JDK版本号,value为JDK安装路径
    • 执行脚本格式为. switch_jdk.sh version,其中version即为要切换的JDK版本号,在shell脚本中使用$1(脚本第一个参数)可以获取version的值
    • 获取到目标version后,在关联数组中获取其对应的JDK安装路径,如果获取不到,则表示目标version不存在,输出提示信息
    • 根据配置项export JAVA_HOME获取其所在的行位置信息,使用grep查找配置项所在行信息,使用awk提取行号
    • 使用sed命令替换该行配置内容,-i表示直接修改文件内容,79c表示替换第79行所在的内容
    • 使用source命令更新环境变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #!/bin/sh

    # here are jdk versions you installed
    my_versions=([8]="/usr/java/jdk" [17]="/usr/java/jdk17")

    if [ -z ${my_versions[$1]} ]
    then
    echo "jdk$1 does not exist! please check whether you installed"
    else
    # 79:export JAVA_HOME=/usr/java/jdk
    line_number=`grep "export JAVA_HOME" -n profile | awk -F ":" '{print $1}'`

    sed -i "79c export JAVA_HOME=${my_versions[$1]}" /etc/profile

    source /etc/profile

    echo "switch to jdk$1 successfully!"

    java -version
    fi
  3. 执行脚本并测试

    • 先查看当前JDK版本,我这里是JDK8

      1
      java -version
    • 执行脚本,切换到JDK17

      1
      . switch_jdk.sh 17
    • 执行脚本,切换到不存在的版本

      1
      . switch_jdk.sh 10

==注意==

一开始,我使用./switch_jdk.sh来执行的脚本(已添加执行权限),控制台输出的JDK版本信息并没有更新,检查了下脚本,觉得思路没有什么问题,于是新开一个terminal测试,但是使用java -version查看JDK版本时,发现已经是新版本了。

这时候我猜测可能是脚本在当前session没有生效,在开启的新session中生效了,那只有可能是source命令的问题了,其他命令和环境变量没有关系。

查看了下相关信息,发现执行shell脚本的方式不同,其执行效果也是不同的,下面给出相关的信息

  1. ./xxx.sh,用此种方式执行脚本,会开启子shell来执行脚本,继承父shell环境变量,但是不会更改父shell环境变量,即更改只对子shell生效
  2. sh xxx.sh,此种方式效果同上
  3. source xxx.sh,等价于. xxx.sh,在当前shell执行脚本,环境变量更改对当前shell生效

对于脚本中存在source或其他会影响到环境变量的命令,最好使用source.来执行脚本

在网上看到有关Java中代码执行顺序的题目,第一次回答发现和答案不一致,于是想要亲自做个实验验证一下,解答心中的疑惑。

代码准备

  1. 创建父类Dog

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class Dog {

    private String name = getName();

    private static String gender = getGender();

    public String getName() {
    System.out.println("==>父类普通成员变量初始化");
    return "Dog";
    }

    public static String getGender() {
    System.out.println("==>父类静态成员变量初始化");
    return "male";
    }

    static {
    System.out.println("==>父类静态代码块1");
    }

    {
    System.out.println("==>父类普通代码块1");
    }

    public Dog() {
    System.out.println("==>父类构造方法");
    }

    static {
    System.out.println("==>父类静态代码块2");
    }

    {
    System.out.println("==>父类普通代码块2");
    }

    }
  2. 创建子类Husky

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    public class Husky extends Dog {

    private int age = getAge();

    private static String color = getColor();

    public int getAge() {
    System.out.println("==>子类普通成员变量初始化");
    return 2;
    }

    public static String getColor() {
    System.out.println("==>子类静态成员变量初始化");
    return "BlackAndWhite";
    }

    static {
    System.out.println("==>子类静态代码块1");
    }

    {
    System.out.println("==>子类普通代码块1");
    }

    public Husky() {
    System.out.println("==>子类构造方法");
    }

    static {
    System.out.println("==>子类静态代码块2");
    }

    {
    System.out.println("==>子类普通代码块2");
    }

    }
  3. 创建测试主类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class TestRunOrder {

    public static void main(String[] args) {

    new Husky();
    System.out.println("");
    new Husky();

    }

    }

测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
==>父类静态成员变量初始化
==>父类静态代码块1
==>父类静态代码块2
==>子类静态成员变量初始化
==>子类静态代码块1
==>子类静态代码块2
==>父类普通成员变量初始化
==>父类普通代码块1
==>父类普通代码块2
==>父类构造方法
==>子类普通成员变量初始化
==>子类普通代码块1
==>子类普通代码块2
==>子类构造方法

==>父类普通成员变量初始化
==>父类普通代码块1
==>父类普通代码块2
==>父类构造方法
==>子类普通成员变量初始化
==>子类普通代码块1
==>子类普通代码块2
==>子类构造方法

结论

  • 父类静态成员变量/代码块>子类静态成员变量/代码块
  • 父类普通成员变量/代码块>子类普通成员变量/代码块
  • 父类构造方法>子类构造方法
  • 静态成员变量/代码块、普通成员变量/代码块执行顺序和代码位置一致