当前位置: 代码网 > it编程>编程语言>Java > JDK8安装与配置实践超详细指南

JDK8安装与配置实践超详细指南

2024年10月10日 Java 我要评论
简介:本文为java开发者介绍了如何在windows 64位系统上安装和配置jdk 8,包括下载、设置环境变量及验证安装的步骤。同时,总结了jdk 8的一些核心新特性,如lambda表达式、strea

简介:

本文为java开发者介绍了如何在windows 64位系统上安装和配置jdk 8,包括下载、设置环境变量及验证安装的步骤。同时,总结了jdk 8的一些核心新特性,如lambda表达式、stream api、日期和时间api、默认方法等,旨在帮助开发者更好地利用jdk 8进行开发。 

1. jdk 8 安装步骤

1.1 确定安装环境

在安装jdk 8之前,首先要确认你的操作系统是否与jdk 8兼容。目前,jdk 8支持大多数主流操作系统,包括windows、linux和macos。确保你的操作系统版本满足jdk 8的安装需求。

1.2 下载jdk 8

访问oracle官方网站或其他jdk提供商的网站下载jdk 8。选择对应于你的操作系统的版本,根据系统是32位还是64位选择正确的安装包。

1.3 安装jdk 8

在下载完安装包后,根据操作系统类型,遵循以下步骤进行安装:

  • windows:双击安装包,遵循安装向导完成安装。需要特别注意安装路径的选择,以便后续设置环境变量时能够找到jdk的安装目录。
  • linux:对于linux系统,如果下载的是.tar.gz格式的压缩包,则需要先解压该文件。可以在终端中使用tar命令进行解压,然后设置环境变量。如果下载的是rpm包,则可以直接使用rpm命令进行安装。

  • macos:若下载的是.dmg安装包,则双击打开并拖动jdk到应用文件夹完成安装。

确保安装过程中没有错误发生,安装完成后,可以进行初步的验证,以确保jdk 8已正确安装在你的系统中。接下来,我们将讨论如何配置环境变量,确保jdk能够在系统的任意位置被调用。

2. 环境变量设置与配置

2.1 环境变量java_home配置

2.1.1 java_home的作用与重要性

java_home 环境变量是指向 jdk 安装目录的路径。它在多种场合中起到关键作用,包括但不限于使用 java 相关工具进行编译和运行 java 程序时。正确设置 java_home 有助于简化命令行中对 jdk 的引用,特别是当安装了多个版本的 jdk 时,java_home 可以确保命令行工具使用的是正确的 jdk 版本。

在不同环境和工具中,java_home 的重要性体现在以下方面:

  • ide集成开发环境 :大多数 ide 在配置时会使用 java_home 环境变量来确定 jdk 的位置。
  • 构建工具 :如 maven 和 gradle,依赖 java_home 环境变量来定位 jdk,以便执行编译、打包等任务。
  • 运行时环境 :对于在服务器端运行 java 程序的应用服务器(如tomcat、weblogic等),它们依赖 java_home 来启动 java 虚拟机(jvm)。

2.1.2 如何正确设置java_home

设置 java_home 环境变量的步骤依操作系统不同而有所区别。以windows和unix-like系统为例:

在 windows系统 中,可以通过如下步骤设置:

  • 右键点击“我的电脑”或“此电脑”,选择“属性”。
  • 在弹出的系统窗口中选择“高级系统设置”。
  • 在系统属性窗口中,点击“环境变量”按钮。
  • 在“系统变量”区域点击“新建”,变量名填写 java_home ,变量值填写jdk的安装路径(例如: c:\program files\java\jdk1.8.0_221 )。
  • 点击“确定”保存设置,并在“系统变量”区域找到 path 变量,选择编辑,在变量值的末尾添加 ;%java_home%\bin (注意开头的分号表示路径分隔)。

在 unix-like系统 中,可以通过在用户的家目录下的 .bashrc 或 .zshrc 文件中添加以下行:

export java_home=/path/to/jdk
export path=$java_home/bin:$path

这里 /path/to/jdk 是 jdk 的安装路径。添加完毕后,使用 source .bashrc 或 source .zshrc 命令使改动生效。

2.2 path环境变量配置

2.2.1 path环境变量的意义

path 环境变量在操作系统中用于指定可执行程序的搜索路径。当用户在命令行中输入可执行命令时,操作系统会在 path 环境变量指定的目录中查找该命令的可执行文件。因此,正确配置 path 环境变量对于能够在命令行中直接运行 java 命令至关重要。

2.2.2 如何在不同操作系统中配置path

在 windows系统 中,我们已经在设置 java_home 时,添加了 %java_home%\bin 到 path 环境变量中。这一步骤确保了无论在命令行的哪个位置,只要系统环境变量配置正确,就可以使用 java javac 等命令。

对于 unix-like系统 ,同样在 .bashrc 或 .zshrc 文件中,我们添加了 $java_home/bin 到 path 环境变量中。这是因为大多数 unix-like 系统使用 bash 或 zsh 作为默认的 shell,配置文件中的改动会使得每次打开新的终端窗口时,环境变量得到更新。

2.2.3 验证java_home和path配置

设置完环境变量后,验证操作至关重要。可以通过执行以下命令来确认配置是否成功:

java -version

如果配置成功,该命令会输出当前设置的 jdk 版本信息。如果命令无法识别,这表明环境变量可能设置不正确或未生效。

另外,可以检查 java_home 和 path 环境变量是否设置:

在 windows系统 中,可以在命令行中输入 echo %java_home% 和 echo %path% 来查看变量值。

在 unix-like系统 中,使用 echo $java_home 和 echo $path 来查看。

通过这些检查,可以确保环境变量的设置符合预期,为使用 jdk 8 奠定坚实的基础。

3. jdk 8 安装验证与新特性概览

3.1 jdk 8 安装验证

3.1.1 使用java -version命令进行版本检查

安装完jdk后,最直接的验证方法是通过命令行检查java的版本信息。打开命令提示符或终端窗口,并输入命令 java -version 。根据java的安装,系统会显示当前安装的java版本信息。如果输出的信息包含版本号 1.8.0_xxx ,这表明jdk 8已经正确安装在系统上。

例如,windows系统下打开命令提示符:

java -version

输出:

java version "1.8.0_xxx"
java(tm) se runtime environment (build 1.8.0_xxx-bxx)
java hotspot(tm) 64-bit server vm (build 25.xxxx, mixed mode)

在linux系统下,打开终端:

java -version

输出类似上述内容,但路径和版本号可能略有不同。

3.1.2 配置ide以使用jdk 8

大多数现代集成开发环境(ide)如intellij idea、eclipse等都支持jdk 8。为了在这些ide中使用jdk 8,开发者需要进行环境配置。

以intellij idea为例,开发者可以通过以下步骤来配置jdk 8:

  • 打开intellij idea,选择 "file" > "project structure..."。
  • 在弹出的窗口中选择 "project",然后在 "project sdk" 下拉菜单中选择 "add sdk..."。
  • 在 "add an existing sdk" 对话框中,选择 "jdk" 并浏览到jdk 8的安装路径。
  • 选择相应的jdk版本,然后点击 "ok"。
  • 点击 "apply",然后 "ok" 应用更改并关闭窗口。

完成这些步骤后,ide将配置使用jdk 8来编译和运行java应用程序。

3.2 jdk 8 新特性概述

3.2.1 java语言的改进

java 8引入了若干重要的语言特性改进,其中最显著的是lambda表达式和方法引用。它们使得在java中处理集合和使用事件监听变得更加简洁和直观。lambda表达式提供了一种简洁的方式来表示只包含单一方法的接口实例。它们特别适合用于那些将行为作为参数传递给方法的场景。

例如,lambda表达式允许你在集合的 foreach 方法中直接传递一个代码块:

list<string> names = arrays.aslist("alice", "bob", "charlie");
names.foreach(name -> system.out.println(name));

在上述代码中,lambda表达式 name -> system.out.println(name) 代替了一个匿名类的实例。这一改进大幅减少了编写和维护代码的复杂性。

3.2.2 新增的api和工具

java 8不仅改进了语言,还增加了一些新的api和工具。stream api是新增的最强大的api之一,它允许在集合上进行声明式操作,如过滤、映射和归约,让集合操作更加高效和易于读写。

例如,使用stream api来过滤并打印出长度大于5的字符串:

list<string> names = arrays.aslist("a", "bb", "ccc", "dddd", "eeeee");
names.stream()
     .filter(name -> name.length() > 5)
     .foreach(system.out::println);

在上述代码中, filter 方法接受一个lambda表达式作为参数,用于检查每个字符串的长度是否大于5。如果条件满足, foreach 方法则会打印出该字符串。这一系列操作通过链式调用流畅地表达出意图,且效率高。

java 8还包括了新的日期和时间api,如 java.time 包中的类,这些类提供了更加丰富的api以处理日期和时间。例如,使用 localdate 类来获取当前日期:

localdate today = localdate.now();
system.out.println("today's date is: " + today);

以上只是jdk 8新特性的一个简单概览。新版本的java引入了许多其他强大的功能,如接口的默认方法和静态方法、新引入的optional类以减少空指针异常、以及jsr 310对日期时间api的改进等等。这些新特性为java开发者带来了更多的工具和方法来解决复杂的问题,提高了开发效率和代码质量。

4. lambda表达式与stream api的实践应用

lambda表达式和stream api是java 8引入的两个重量级特性,极大地改善了集合的处理方式和代码的简洁性。lambda表达式为java带来了函数式编程的特性,而stream api则是一种高级的数据处理方式,它们使得代码更加直观和易于管理。

4.1 lambda表达式应用

4.1.1 lambda表达式的定义和语法

lambda表达式是一种简洁的表示可以传递的匿名函数的方式。lambda可以理解为简洁的实现了单方法接口(sam,single abstract method)的实例。lambda表达式的基本语法如下:

参数 -> 表达式或代码块
  • 参数:可以是零个或多个参数,参数类型可以显式声明,也可以由编译器推断。
  • 箭头( -> ):将参数与方法体分开。
  • 表达式或代码块:表达式应该返回一个值,而代码块则可以执行多条语句,但需要显式返回一个值。

4.1.2 lambda与匿名类的对比

在lambda表达式之前,匿名类是java中实现函数式接口的常用方法。例如,要实现 comparator 接口比较两个字符串,可以这样写:

comparator<string> comparator = new comparator<string>() {
    @override
    public int compare(string s1, string s2) {
        return s1.length() - s2.length();
    }
};

使用lambda表达式,同样的功能可以缩减为一行:

comparator<string> comparator = (s1, s2) -> s1.length() - s2.length();

lambda表达式不仅减少了代码量,提高了代码的可读性,而且使得函数式编程在java中的应用更加便捷。

4.1.3 在集合框架中的应用示例

lambda表达式在集合框架中应用广泛,特别是在集合的遍历和数据的处理上。例如,对一个列表进行排序可以使用lambda表达式:

list<string> names = arrays.aslist("alice", "bob", "charlie");
names.sort((s1, s2) -> ***pareto(s2));

这种方式比使用传统的迭代器或者增强for循环更加直观和简洁。lambda表达式让集合的处理更加函数式和声明式。

4.2 stream api 使用

4.2.1 stream api的基本概念

stream api提供了一种高效且易于理解的方式来处理集合的元素。stream可以理解为一系列元素的数据流,它可以被串行或并行处理。stream api有以下几个核心概念:

  • 流(stream) :一个来自数据源的元素序列。
  • 中间操作(intermediate operations) :如 filter 、 map 、 sorted 等,这些操作总是返回一个新的stream,并且可以链接起来形成流的处理管道。
  • 终端操作(terminal operations) :如 collect 、 reduce 、 foreach 等,这些操作会触发实际的计算过程,并且可以产生结果。

4.2.2 创建流的方法和操作类型

创建流的方法多种多样,可以使用集合的 .stream() 方法,也可以使用 stream.of() 、 arrays.stream() 等方法。创建流之后,中间操作可以顺序组合,然后通过一个终端操作来触发所有之前的操作。

list<integer> numbers = arrays.aslist(1, 2, 3, 4, 5);
list<integer> result = numbers.stream()
    .filter(n -> n % 2 != 0) // 中间操作:过滤出奇数
    .map(n -> n * n)         // 中间操作:映射成平方数
    .collect(collectors.tolist()); // 终端操作:收集结果到列表

4.2.3 集合操作的流式处理示例

下面是一个使用流api对员工列表按年龄进行排序,并选择年龄大于30岁的员工的示例:

list<employee> employees = getemployeelist();
list<employee> sortedemployees = employees.stream()
    .filter(e -> e.getage() > 30) // 过滤出年龄大于30的员工
    .sorted(***paring(employee::getage)) // 按年龄排序
    .collect(collectors.tolist()); // 收集结果到列表中

通过流式处理,代码更符合函数式编程的范式,同时提高了代码的可读性和效率。注意,在实际使用时,需要导入必要的包,例如 java.util.stream.collectors 

在本节中,我们深入探讨了lambda表达式和stream api的概念、语法和实践应用。通过比较与传统匿名类的写法,我们了解了lambda表达式的简洁性。接着,我们通过实例演示了stream api如何高效地处理集合数据。这些功能极大地丰富了java的函数式编程能力,并对集合操作进行了现代化的改进。

5. 日期和时间api的更新与应用

5.1 日期和时间api 更新

5.1.1 新旧日期时间api的比较

在java 8之前,日期和时间的处理是相当繁琐的。旧的java.util.date类存在设计上的缺陷,它既是一个时间戳,也是日期和时间的表示,这导致了代码的可读性和易用性都不理想。另外,calendar类虽然弥补了一部分date的不足,但由于其api设计上的问题,仍然不够直观和方便使用。

java 8 引入了全新的日期和时间api,这个全新的api位于java.time包中,其设计灵感来自于joda-time库。新的api更加清晰,避免了旧api中的常见问题,例如: - 不可变性:新的api中的日期时间类都是不可变的,这有助于创建线程安全的代码。 - 清晰的线程安全:新的api设计考虑到了线程安全,不必担心并发修改的问题。 - 时区支持:新的日期时间api自带时区支持,可以清晰地表达不同时区下的时间。

5.1.2 java 8中的日期时间类

java 8引入的新的日期时间类主要包括: - localdate : 只包含日期(年、月、日)。 - localtime : 只包含时间(时、分、秒)。 - localdatetime : 同时包含日期和时间。 - zoneddatetime : 带时区的日期时间。 - instant : 表示一个瞬时点。 - period : 表示日期间隔。 - duration : 表示时间间隔。

这些类都不可变,并且大部分操作都会产生新的对象,例如加一秒操作会返回一个新的localdatetime对象。

5.1.3 使用新的日期时间api进行日期计算

以下代码展示了如何使用新的日期时间api来执行一些基本的日期计算:

import java.time.localdate;
import java.time.localdatetime;
import java.time.temporal.chronounit;

public class datetimeexample {
    public static void main(string[] args) {
        // 创建一个localdate实例表示2023年3月15日
        localdate date = localdate.of(2023, 3, 15);

        // 获取当前日期
        localdate now = localdate.now();

        // 计算两个日期之间的天数差
        long daysbetween = chronounit.days.between(date, now);
        system.out.println("days between " + date + " and " + now + ": " + daysbetween);

        // 创建一个localdatetime实例表示2023年3月15日20:30
        localdatetime datetime = localdatetime.of(2023, 3, 15, 20, 30);

        // 在当前日期时间基础上增加1小时30分钟
        localdatetime future = datetime.plushours(1).plusminutes(30);
        system.out.println("new date time after adding 1 hour and 30 minutes: " + future);
    }
}

上述代码演示了如何创建日期和日期时间对象,如何使用 chronounit 来计算两个日期之间的天数差,以及如何在给定的日期时间上增加时间。

5.2 接口的默认方法

5.2.1 默认方法的定义和作用

java 8为接口引入了默认方法(default methods),允许在接口中提供方法实现。这样做的原因主要是为了向后兼容,允许在不破坏现有实现的情况下扩展接口。默认方法对于库的设计者来说,尤其有用,因为他们可以向接口添加新的方法,而不会破坏现有的实现。

例如,java 8为collection接口添加了 stream() 和 parallelstream() 方法,这样所有的集合类不需要修改代码就可以使用这两个新方法。

5.2.2 如何在接口中定义默认方法

在接口中定义默认方法非常简单,只需要在方法声明前加上 default 关键字即可。以下是一个示例:

public interface greetingservice {
    void sayhello(string name);
    // 默认方法
    default void saygoodbye(string name) {
        system.out.println("goodbye, " + name + "!");
    }
}

5.2.3 默认方法的使用场景和注意事项

默认方法的使用场景非常广泛,它们可以作为回调函数,也可以为接口提供一种约定好的“模板方法”行为。然而,使用默认方法时需要注意以下几点:

  • 默认方法不能覆盖object类的方法。
  • 如果一个类实现的两个接口定义了具有相同签名的默认方法,那么这个类必须提供一个显式的实现,以解决冲突。
  • 默认方法应该谨慎使用,因为它们可能使得api变得复杂,增加调用者的学习成本。

下面是一个实现类使用默认方法的示例:

public class defaultmethodexample implements greetingservice {
    public void sayhello(string name) {
        system.out.println("hello, " + name + "!");
    }
    // 显式覆盖默认方法
    public void saygoodbye(string name) {
        system.out.println("bye, " + name + "!");
    }
    public static void main(string[] args) {
        defaultmethodexample example = new defaultmethodexample();
        example.sayhello("alice");
        example.saygoodbye("alice");
    }
}

通过上述内容,我们可以看到java 8在日期时间api和接口设计上所做的重大改进。这些新特性的加入,使得java在处理日期时间操作和接口扩展时更加方便和强大。

6. 类型推断和optional类的深入探索

6.1 类型推断和optional类

类型推断和optional类是java 8引入的重要特性之一,它们分别解决了不同的编程问题:类型推断让开发者编写代码时更加简洁而无需显式地声明类型,而optional类则帮助避免 nullpointerexception ,让代码更易于维护和理解。

6.1.1 类型推断的机制和好处

类型推断是指编译器在编译阶段自动推断出变量或表达式的类型,使得开发者可以减少显式类型的声明。java中的类型推断主要体现在泛型和lambda表达式中。

泛型类型推断

在java 7之前,我们需要在每次使用泛型类或方法时都指定具体类型,如:

list<string> stringlist = new arraylist<string>();

从java 7开始,可以利用“菱形”语法来简化:

list<string> stringlist = new arraylist<>();

java 8进一步增强了类型推断,使得在使用方法引用和构造器引用时,类型可以被自动推断。

lambda表达式的类型推断

lambda表达式的类型推断让代码更加简洁,因为编译器可以根据上下文推断出目标类型。lambda表达式允许我们传递行为,而不是对象,其语法简化为:

collections.sort(list, (a, b) -> ***pareto(b));

这里的 (a, b) -> ***pareto(b) 就是lambda表达式,编译器能够根据 collections.sort 方法的定义推断出参数类型。

6.1.2 optional类的引入和意义

nullpointerexception 是java语言中臭名昭著的问题,它会在程序尝试访问未初始化的对象时发生。在java 8之前,开发者需要通过大量的null检查来避免这种错误,这不仅代码繁琐,还难以阅读和维护。

为了改善这种状况,java 8引入了 optional<t> 类。 optional<t> 是一个容器类,它可以包含也可以不包含非null的值。如果值存在, ispresent() 方法返回 true , get() 方法则返回值,如果值不存在,则 ispresent() 返回 false ,并且不会抛出异常。

6.1.3 optional类的常用方法和实践

optional 类的主要方法有 of() ofnullable() ispresent() orelse() orelseget() orelsethrow() 等。

  • of(t value) :将值包装在一个 optional 对象中,但要求值不能为null,否则会抛出 nullpointerexception 
  • ofnullable(t value) :类似于 of() ,但可以接收null值。
  • ispresent() :检查 optional 对象是否包含值。
  • orelse(t other) :如果 optional 对象包含值,则返回该值,否则返回 other 参数指定的值。
  • orelseget(supplier<? extends t> other) :如果 optional 对象包含值,则返回该值,否则通过 supplier 函数接口生成值。
  • orelsethrow(supplier<? extends x> exceptionsupplier) :如果 optional 对象包含值,则返回该值,否则抛出由 exceptionsupplier 提供的异常。

示例代码:

optional<string> optionalname = optional.ofnullable("john");
string name = optionalname.orelse("default name");

. . . 实践中的optional使用

为了更好地理解 optional 的使用,我们来看一个常见的实际应用场景。

假设有一个用户类 user 和订单类 order ,我们想要获取一个订单的用户的名字,但用户对象可能是null:

public class user {
    private string name;
    // ...
}

public class order {
    private user user;
    // ...
    public string getusername() {
        if (user != null) {
            return user.getname();
        }
        return "unknown";
    }
}

使用 optional 可以简化为:

public string getusername() {
    return optional.ofnullable(user).map(user::getname).orelse("unknown");
}

这里, map(user::getname) 尝试将用户的名字映射 出来,如果 user 是null,则返回一个空的 optional 对象。 orelse("unknown") 确保如果结果是空的,将返回"unknown"。

. . . 注意事项和最佳实践

在使用 optional 时,有几点需要注意: - 避免使用 optional 作为类的字段类型或方法参数类型,因为这会增加api的复杂性。 - 不应该在任何地方都使用 optional ,仅在可能为空且可能需要空值处理的场景使用。 - 不要滥用 optional ,避免深层的嵌套,这会使代码变得难以阅读。

通过上述方法,可以充分地利用java 8引入的类型推断和optional类来简化代码,并提升代码的健壮性。随着java编程模式的进化,这些特性使得代码更加符合现代编程的最佳实践。

7. 重复注解与双括号初始化的高级特性

7.1 重复注解

7.1.1 注解的回顾与限制

注解(annotation)是java语言中一种用于提供元数据信息的工具,它能够对代码中的元素进行说明,不影响代码的执行逻辑。自从java 5引入注解以来,它们就成为了java开发者不可或缺的一部分。

@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface myannotation {
    string value();
}

然而,早期的注解有一个重要的限制:同一个元素上只能使用一次特定类型的注解。这在某些情况下显得过于严格,比如在需要标记一个方法为“废弃”的同时,还想标记它为“线程安全”。

@myannotation("废弃")
@threadsafe
public void mymethod() {
    // method body
}

7.1.2 重复注解的声明和使用

java 8为了解决这个问题,引入了重复注解的概念。允许开发者在同一个元素上多次使用相同的注解类型。通过引入一个新的注解 @repeatable ,我们就可以标记一个注解类型为可重复的。

@retention(retentionpolicy.runtime)
@target(elementtype.method)
@repeatable(myannotations.class)
public @interface myannotation {
    string value();
}

@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface myannotations {
    myannotation[] value();
}

现在, myannotation 注解就可以被重复使用了:

@myannotation("废弃")
@myannotation("线程安全")
public void mymethod() {
    // method body
}

7.2 双括号初始化

7.2.1 双括号初始化的原理

双括号初始化是一种创建匿名内部类实例同时初始化其集合类型成员的便捷方法。在java中,这被认为是一种快速但不常见的编程技巧。

list<string> list = new arraylist<string>() {{
    add("one");
    add("two");
    add("three");
}};

在上述代码中,我们创建了一个 arraylist 的匿名子类,并在实例化的同时在构造块中对其成员 add 方法进行了调用。

7.2.2 双括号初始化的应用场景

双括号初始化通常用于临时创建小型集合,并直接在声明处进行初始化。它避免了单独声明、实例化和初始化集合的繁琐过程。

7.2.3 使用双括号初始化的注意事项

尽管双括号初始化很有用,但它也有一些缺点。由于创建了一个匿名内部类,这将导致额外的对象创建,这在性能敏感的应用中可能会成为问题。同时,它也难以调试,因为堆栈跟踪中会出现额外的类名。

此外,使用双括号初始化意味着无法访问集合实例的原始类型,它实际上是包级别的访问权限,因此不推荐在公共api中使用。在多线程环境中,由于匿名类可能会导致非预期的内存泄漏,因此需要特别小心使用双括号初始化。

总结

到此这篇关于jdk8安装与配置实践的文章就介绍到这了,更多相关jdk8安装与配置内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com