HarmonyOS 鸿蒙应用开发 (七、HTTP网络组件 axios 介绍及封装使用)

news/2024/7/21 8:45:49 标签: harmonyos, 华为, OpenHarmony, 鸿蒙, axios

在HarmonyOS应用开发中,通过HTTP访问网络,可以使用官方提供的@ohos.net.http模块。但是官方提供的直接使用不太好使用,需要封装下才好。推荐使用前端开发中流行的axios网络客户端库,如果是前端开发者,用 axios也会更加顺手。

目录

axios%E4%BB%8B%E7%BB%8D-toc" style="margin-left:0px;">axios介绍

在HarmonyOS也能用Axios?

axios%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E5%BA%93%E7%9A%84%E4%BD%BF%E7%94%A8-toc" style="margin-left:0px;">axios网络请求库的使用

下载安装

开通权限

简单使用

axios%E6%A8%A1%E5%9D%97%E5%B0%81%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8-toc" style="margin-left:0px;">axios模块封装及使用

客户端封装

封装后使用

官方@ohos/net.http 介绍

官方简易封装

官方http模块封装使用

写在最后

其他资源

axios%E4%BB%8B%E7%BB%8D">axios介绍

Axios 是一个著名的基于 JavaScript 的开源库,用于浏览器和 Node.js 等环境中发送 HTTP 请求。它支持 Promise API,并且可以处理 XMLHttpRequests 和 Fetch API 背后的复杂性,为开发者提供了一种简洁易用的方式来实现 AJAX(Asynchronous JavaScript and XML)请求。

最早浏览器页面在向服务器请求数据,返回的是整个页面的数据,页面会强制刷新一下,这对于用户来讲并不是很友好。我们只想修改页面的部分数据,但是从服务器端返回的却是整个页面。于是出现一种新的技术,异步网络请求Ajax(Asynchronous JavaScript and XML),它能与后台服务器进行少量数据交换,使网页实现异步局部更新。

由于浏览器中原生的XMLHttpRequest API较难使用,于是又有了更多用于实现ajax的javascript框架出现,比如我们熟悉的jQuery、Dojo、YUI等等。而如今一个叫axios的轻量框架逐步脱颖而出,它本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范。

主要特性:

  1. 跨平台支持: Axios 可以在浏览器端通过 XMLHttpRequests 发送请求,在 Node.js 中则使用 http/https 模块发送请求。
  2. Promise API: Axios 的所有网络请求方法都返回 Promise 对象,使得异步编程更加简洁和易于处理。
  3. 拦截请求与响应: 提供了请求和响应的全局和实例级别的拦截器,可以在请求发送前或响应返回后进行预处理、错误处理或数据转换等操作。
  4. 取消请求: 支持主动取消已经发出但还未完成的HTTP请求。
  5. 自动转换JSON数据: Axios 自动将来自服务器的 JSON 数据转换为 JavaScript 对象,并且对于POST、PUT等请求体中的JSON数据也会自动序列化成字符串发送。
  6. 配置灵活性: Axios 允许自定义请求头、URL参数、超时时间等多种配置项,适用于不同场景下的API调用需求。
  7. 请求方法多样: 支持所有标准的HTTP方法(GET、POST、PUT、DELETE等),以及对PATCH等非标准方法的良好支持。
  8. 上传下载进度监控: Axios 还支持监听文件上传和下载的进度事件。

在HarmonyOS也能用Axios?

在HarmonyOS中,官方提供了@ohos/net.http 模块进行网络访问。它是官方提供的基础HTTP数据请求能力库,直接提供了对HTTP协议的底层支持,开发者可以通过这个模块发送GET、POST等HTTP请求,并处理响应结果。由于它是系统级别的API,其优点在于性能和兼容性得到保证,适用于基本的HTTP通信需求。

虽然官方提供了@ohos/net.http 模块进行网络访问,但是Axios库可以看作是一种功能更强大和易用的封装,且接口使用上更符合前端开发者的惯用习惯。Axios库 以其强大的功能性和易用性成为现代JavaScript应用中非常流行的HTTP客户端库。

直接使用原始的axios库肯定是不行,在HarmonyOS中的Axios库,模块名字是@ohos/axios

@ohos/axios第三方库是基于axios库进行适配,使其可以运行在OpenHarmony中的一个发送网络请求库,并且本库沿用axios库现有用法和特性,使其更加适合于鸿蒙项目的开发。

@ohos/axios 模块可以理解为是对官方HTTP API的一个封装或者扩展,它提供了一种更高级别的抽象和便利性,可能包含了更多的功能特性,比如自动转换数据格式、错误处理、拦截器机制以及对于Promise的良好支持等,这些都是为了简化开发流程,提高开发效率。在实际开发应用时,如果需要更丰富和灵活的网络请求管理功能,通常推荐使用 @ohos/axios 这样的封装库。

通过对@ohos/axis源码的查看,发现也确实是使用ohos.net.http模块,对原库v1.3.4版本进行适配,使其可以应用在HarmonyOS上,并沿用其现有用法和特性。

  • http 请求
  • Promise API
  • request 和 response 拦截器
  • 转换 request 和 response 的 data 数据
  • 自动转换 JSON data 数据

ohos/axios 模块源码:

OpenHarmony-SIG/ohos_axios

axios%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E5%BA%93%E7%9A%84%E4%BD%BF%E7%94%A8">axios网络请求库的使用

接口列表

接口参数功能
axios(config)config:请求配置发送请求
axios.create(config)config:请求配置创建实例
axios.request(config)config:请求配置发送请求
axios.get(url[, config])url:请求地址
config:请求配置
发送get请求
axios.delete(url[, config])url:请求地址
config:请求配置
发送delete请求
axios.post(url[, data[, config]])url:请求地址
data:发送请求体数据
config:请求配置
发送post请求
axios.put(url[, data[, config]])url:请求地址
data:发送请求体数据
config:请求配置
发送put请求

属性列表

属性描述
axios.defaults['xxx']默认设置 。值为请求配置 config 中的配置项
例如 axios.defaults.headers 获取头部信息
axios.interceptors拦截器。参考 拦截器 的使用

下载安装

  • 方式一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。
ohpm install @ohos/axis

如果提示无ohpm命令,则是环境变量没有配置。 点击配置ohpm命令查看详细配置。

  • 方式二:在工程的oh-package.json5中设置三方包依赖,配置示例如下:
"dependencies": { "@ohos/axis": "^2.1.0"}

开通权限

 需要配置 ohos.permission.INTERNET权限。在工程目录entry\src\main中找到module.json5文件,配置网络请求权限。

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone"
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

简单使用

import axios from '@ohos/axios'
//创建axios的实例
const instance = axios.create({
  baseURL: "http://xx.xx.xx.xx", //基路径,要看API帮助文档的特征来确定基路径
  timeout: 5000, //请求超时的时间
  headers: {
    "Content-Type": "application/json"
  }
})
 
//响应拦截器,通过响应拦截器进一步对返回的数据做处理
instance.interceptors.response.use((response) => {
  //只返回接口有数据的结果
  if (200 === response.status) {
    return response.data; //接口返回的数据
  }
  return Promise.reject(response); //表示请求有错,交给catch来处理结构
}, err => {
  return Promise.reject(err)
})
 
/**
 * get请求
 * @param params = {} 查询参数
 * @returns
 */
export function httpGet(url:string, params = {}) {
  return instance.get<any>(url, {
    params
  })
}
 
/**
 * post请求
 * @param data = {} 请求体数据
 * @returns
 */
export function httpPost(url:string, data = {}) {
  return instance.post<any>(url, {
   data 
  })
}

axios%E6%A8%A1%E5%9D%97%E5%B0%81%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8">axios模块封装及使用

axios模块封装:

//AxiosHttp.ets

import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosRequestHeaders,
  AxiosResponse,
  InternalAxiosRequestConfig
} from "@ohos/axios";
import { LogUtils } from '../utils/LogUtils';


/**
 * 定义接口响应包装类
 */
export interface BaseResponse {
  //wanAndroid-API响应体
  errorCode: number
  errorMsg: string
  //拓展xxx-API响应体
}

/**
 * 接口实现类包装,例如有其他业务可以再次继承实现xxxResponse
 */
export interface ApiResponse<T = any> extends BaseResponse {
  //wanAndroid-API响应体
  data: T | any;
  //拓展xxx-API响应体
}

/**
 * 封装后,不支持传入拦截器
 * 需要自己定义接口继承 AxiosRequestConfig类型
 * 从而支持传入拦截器,但拦截器选项应为可选属性
 * 之后请求实例传入的options为继承了AxiosRequestConfig的自定义类型
 */
interface InterceptorHooks {
  requestInterceptor?: (config: HttpRequestConfig) => Promise<HttpRequestConfig>;
  requestInterceptorCatch?: (error: any) => any;
  responseInterceptor?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
  responseInterceptorCatch?: (error: any) => any;
}

// @ts-ignore
interface HttpRequestConfig extends InternalAxiosRequestConfig {
  showLoading?: boolean; //是否展示请求loading
  checkResultCode?: boolean; //是否检验响应结果码
  checkLoginState?: boolean //校验用户登陆状态
  needJumpToLogin?: boolean //是否需要跳转到登陆页面
  interceptorHooks?: InterceptorHooks;
  headers?: AxiosRequestHeaders
}


/**
 * 网络请求构造
 * 基于axios框架实现
 */
class AxiosHttpRequest {
  config: HttpRequestConfig;
  interceptorHooks?: InterceptorHooks;
  instance: AxiosInstance;

  constructor(options: HttpRequestConfig) {
    this.config = options;
    this.interceptorHooks = options.interceptorHooks;
    this.instance = axios.create(options);
    this.setupInterceptor()
  }

  setupInterceptor(): void {
    this.instance.interceptors.request.use(
      //这里主要是高版本的axios中设置拦截器的时候里面的Config属性必须是InternalAxiosRequestConfig,但是InternalAxiosRequestConfig里面的headers是必传,所以在实现的子类我设置成非必传会报错,加了个忽略注解
      // @ts-ignore
      this.interceptorHooks?.requestInterceptor,
      this.interceptorHooks?.requestInterceptorCatch,
    );
    this.instance.interceptors.response.use(
      this.interceptorHooks?.responseInterceptor,
      this.interceptorHooks?.responseInterceptorCatch,
    );
  }

  // 类型参数的作用,T决定AxiosResponse实例中data的类型
  request<T = any>(config: HttpRequestConfig): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.instance
        .request<any, T>(config)
        .then(res => {
          resolve(res);
        })
        .catch((err) => {
          LogUtils.error("网络请求Request异常:", err.message)
          errorHandler(err)
          if (err) {
            reject(err);
          }
        });
    });
  }

  get<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'GET' });
  }

  post<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'POST' });
  }

  delete<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'DELETE' });
  }

  patch<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'PATCH' });
  }
}

export function errorHandler(error: any) {
  if (error instanceof AxiosError) {
    showToast(error.message)
  } else if (error != undefined && error.response != undefined && error.response.status) {
    switch (error.response.status) {
    // 401: 未登录
    // 未登录则跳转登录页面,并携带当前页面的路径
    // 在登录成功后返回当前页面,这一步需要在登录页操作。
      case 401:

        break;
    // 403 token过期
    // 登录过期对用户进行提示
    // 清除本地token和清空vuex中token对象
    // 跳转登录页面
      case 403:
        showToast("登录过期,请重新登录")
      // 清除token
      // localStorage.removeItem('token');
        break;
    // 404请求不存在
      case 404:
        showToast("网络请求不存在")
        break;

    // 其他错误,直接抛出错误提示
      default:
        showToast(error.response.data.message)
    }

  }
}

export default AxiosHttpRequest

客户端封装

//AxiosRequest.ets
import {AxiosHttpRequest,errorHandler} from './AxiosHttp'
import { AxiosError, AxiosRequestHeaders } from '@ohos/axios';
import { LogUtils } from '../utils/LogUtils';
import showToast from '../utils/ToastUtils';
import { hideLoadingDialog, showLoadingDialog } from '../utils/DialogUtils';
import { StorageUtils } from '../utils/StorageUtils';
import { StorageKeys } from '../constants/StorageKeys';
import { JsonUtils } from '../utils/JsonUtils';
import { Router } from '../route/Router';
import { RoutePath } from '../route/RoutePath';

/**
 * axios请求客户端创建
 */
const axiosClient = new AxiosHttpRequest({
  baseURL: "/api",
  timeout: 10 * 1000,
  checkResultCode: false,
  headers: {
    'Content-Type': 'application/json'
  } as AxiosRequestHeaders,
  interceptorHooks: {
    requestInterceptor: async (config) => {
      // 在发送请求之前做一些处理,例如打印请求信息
      LogUtils.debug('网络请求Request 请求方法:', `${config.method}`);
      LogUtils.debug('网络请求Request 请求链接:', `${config.url}`);
      LogUtils.debug('网络请求Request Params:', `\n${JsonUtils.stringify(config.params)}`);
      LogUtils.debug('网络请求Request Data:', `${JsonUtils.stringify(config.data)}`);
      axiosClient.config.showLoading = config.showLoading
      if (config.showLoading) {
        showLoadingDialog("加载中...")
      }
      if (config.checkLoginState) {
        let hasLogin = await StorageUtils.get(StorageKeys.USER_LOGIN, false)
        LogUtils.debug('网络请求Request 登录状态校验>>>', `${hasLogin.toString()}`);
        if (hasLogin) {
          return config
        } else {
          if (config.needJumpToLogin) {
            Router.push(RoutePath.TestPage)
          }
          throw new AxiosError("请登录")
        }
      }
      return config;
    },
    requestInterceptorCatch: (err) => {
      LogUtils.error("网络请求RequestError", err.toString())
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      return err;
    },
    responseInterceptor: (response) => {
      //优先执行自己的请求响应拦截器,在执行通用请求request的
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      LogUtils.debug('网络请求响应Response:', `\n${JsonUtils.stringify(response.data)}`);
      if (response.status === 200) {
        // @ts-ignore
        const checkResultCode = response.config.checkResultCode
        if (checkResultCode && response.data.errorCode != 0) {
          showToast(response.data.errorMsg)
          return Promise.reject(response)
        }
        return Promise.resolve(response.data);
      } else {
        return Promise.reject(response);
      }
    },
    responseInterceptorCatch: (error) => {
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      LogUtils.error("网络请求响应异常", error.toString())
      errorHandler(error);
      return Promise.reject(error);
    },
  }
});

export default axiosClient;

封装后使用

经过封装后,使用变得很简单了。示例如下:


import axiosClient from './AxiosRequest'

let baseUrl = "https://www.wanandroid.com/"

//返回数据结构定义
interface HomeModelIssueList {
  releaseTime: number;
  type: string;
  date: number;
  publishTime: number;
  count: number;
}

interface HomeModel {
  issueList: HomeModelIssueList[];
  itemList: HomeModelIssueList[];
  nextPageUrl: string;
  nextPublishTime: number;
  newestIssueType: string;
}

interface BannerDataModelData {
  desc: string;
  id: number;
  imagePath: string;
  isVisible: number;
  order: number;
  title: string;
  type: number;
  url: string;
}

/**
 * 请求首页数据-axios客户端请求
 * @param date
 * @returns
 */
export function getHomeListAxios(date: string = "") {
  return axiosClient.get<HomeModel>({
    url: baseUrl + "api/v2/feed",
    params: { "date": date },
    showLoading: true
    // headers: { "Accept": "application/json" } as AxiosRequestHeaders
  })
}

/**
 * 获取分类详情接口
 * @param id
 * @param start
 */
export function getCategoryDetailList(id: number, start: number) {
  return axiosClient.get<HomeModel>(
    {
      url: baseUrl + "api/v4/categories/videoList",
      params: {
        "id": id,
        "start": start,
        "udid": CommonConstants.UUID,
        deviceModel: CommonConstants.DEVICE_NUM
      }
    }
  )
}

/**
 * 获取wanAndroid首页Banner数据,测试校验API data:T泛型数据
 * @returns
 */
export function getWanAndroidBanner() {
  return axiosClient.get<ApiResponse<BannerDataModelData[]>>(
    {
      url: wanAndroidUrl + "/banner/json",
      checkResultCode: true,
      showLoading: true
    }
  )
}

官方@ohos/net.http 介绍

在HarmonyOS(OpenHarmony)中,@ohos/net.http 是官方提供的一个用于进行HTTP通信的基础模块。开发者可以利用这个模块发送和接收HTTP请求与响应,实现应用程序与服务器之间的数据交互。

文档中心--HTTP数据请求

官方简易封装

@ohos/net.http模块直接使用起来不太友好,简易封装如下:

//http.ets
/**
 * 定义接口响应包装类
 */
import http from '@ohos.net.http';

export interface BaseResponse {
  //wanAndroid-API响应体
  errorCode: number
  errorMsg: string
  //拓展xxx-API响应体
}

/**
 * 接口实现类包装,例如有其他业务可以再次继承实现xxxResponse
 */
export interface ApiResponse<T = any> extends BaseResponse {
  //wanAndroid-API响应体
  data: T | any;
  //拓展xxx-API响应体
}

interface HttpRequestConfig extends http.HttpRequestOptions {
  showLoading?: boolean; //是否展示请求loading
  checkResultCode?: boolean; //是否检验响应结果码
  checkLoginState?: boolean //校验用户登陆状态
  needJumpToLogin?: boolean //是否需要跳转到登陆页面
  url?: string, //请求网络链接
}


/**
 * 网络请求构造器
 * 基于鸿蒙默认的http框架实现
 */
class HttpBuilder {
  httpClient: http.HttpRequest
  config: HttpRequestConfig

  constructor(options: HttpRequestConfig) {
    this.httpClient = http.createHttp()
    this.config = options
    this.setupInterceptor()
  }
  /**
   * 配置属性拦截器
   */
  setupInterceptor() {

  }

  request<T = any>(config: HttpRequestConfig): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.httpClient.request(
        config.url,
        config,
        (error, data) => {
          if (!error) {
            resolve(data.result as T);
          } else {
            reject(error)
          }
          // 当该请求使用完毕时,调用destroy方法主动销毁x
          this.httpClient.destroy()
        }
      )
    })
  }

  get<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: http.RequestMethod.GET })
  }

  post<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: http.RequestMethod.POST })
  }
}

export default HttpBuilder

官方http模块封装使用

import http from '@ohos.net.http';
import showToast from '../utils/ToastUtils';
import HttpBuilder from './http';

//接口发送超时
const READ_TIMEOUT = 100000
//接口读取超时
const CONNECT_TIMEOUT = 100000

let baseUrl = "https://www.wanandroid.com/"

const httpClient = new HttpBuilder({
  readTimeout: READ_TIMEOUT,
  connectTimeout: CONNECT_TIMEOUT
})

//返回数据结构定义
interface HomeModelIssueList {
  releaseTime: number;
  type: string;
  date: number;
  publishTime: number;
  count: number;
}

interface HomeModel {
  issueList: HomeModelIssueList[];
  itemList: HomeModelIssueList[];
  nextPageUrl: string;
  nextPublishTime: number;
  newestIssueType: string;
}

/**
 * 请求数据--系统http请求
 * @param date
 * @returns
 */
export function getHomeList(date: string = "") {
  return httpClient.get<HomeModel>({
    url: baseUrl + "api/v2/feed",
    extraData: { "date": date }
  })
}

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注博主,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新VIP学习资料,请关注猫哥公众号【猫青年】,回复“鸿蒙”获取

其他资源

太硬核!为什么一定要学习鸿蒙HarmonyOS系统开发?-CSDN博客

鸿蒙Harmony应用开发,一起来写一个“遥遥领先”的开眼App_开眼api-CSDN博客

鸿蒙HarmonyOS开发框架—学习ArkTS语言(状态管理 一)_鸿蒙os appstorage @storageprop @watch-CSDN博客

文档中心--HTTP数据请求

yang/ohos_axios

网络组件axios可以在OpenHarmony上使用了_@ohos/axios-CSDN博客

OpenHarmony-SIG/ohos_axios

TypeScript利用TS封装Axios实战_编程网

ts 封装 axios 技巧:充分利用类型检查与提示 - 掘金


http://www.niftyadmin.cn/n/5349856.html

相关文章

AAC解码算法原理

关于更多音视频开发内容&#xff0c;请参考专栏音视频开发 AAC&#xff08;Advanced Audio Coding&#xff09;是一种高级音频编码标准&#xff0c;它是一种十分流行的音频压缩格式&#xff0c;通常用于存储和传输音频数据。AAC提供了高音质和高压缩效率&#xff0c;广泛应用于…

初识人工智能,一文读懂机器学习之逻辑回归知识文集(6)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

【从浅到深的算法技巧】初级排序算法 上

5.排序 5.1 初级排序算法 作为对排序算法领域的第一次探索&#xff0c; 我们将学习两种初级的排序算法以及其中种的一个变体。深入学习这些相对简单的算法的原因在于:第一,我们将通过它们熟悉些术语和简单的技巧 第二&#xff0c;这些简单的算法在某些情况下比我们之后将会讨论…

信息系统安全(第二章)

第二章 信息系统安全认定 2.1概述 2.1.1基本概念 在网络开放环境中&#xff0c;信息系统易遭受各种各样的攻击&#xff0c;例如消息窃听&#xff0c;身份伪装&#xff0c;消息伪造与篡 改&#xff0c;消息重放等。这种入侵行为的实施相当一部分建立在入侵者获得已经存在的通…

MQ面试题之Kafka

前言 前文介绍了消息队列相关知识&#xff0c;并未针对某个具体的产品&#xff0c;所以略显抽象。本人毕业到现在使用的都是公司内部产品&#xff0c;对于通用产品无实际经验&#xff0c;但是各种消息中间件大差不差&#xff0c;故而本次选择一个相对较熟悉的Kafka进行详细介绍…

OpenCV 0 - VS2019配置OpenCV

1 配置好环境变量 根据自己的opencv的安装目录配置 2 新建一个空项目 3 打开 视图->工具栏->属性管理器 4 添加新项目属性表 右键项目名(我这是opencvdemo)添加新项目属性表,如果有配置好了的属性表选添加现有属性表 5 双击选中Debug|x64的刚添加的属性表 6 (重点)添…

【JAVA语言-第15话】集合框架(二)——List、ArrayList、LinkedList、Vector集合

目录 List集合 1.1 概述 1.2 特点 1.3 常用方法 1.4 ArrayList集合 1.4.1 概述 1.4.2 练习 1.5 LinkedList集合 1.5.1 概述 1.5.2 特点 1.5.3 常用方法 1.5.4 练习 1.6 Vector类 1.6.1 概述 1.6.2 练习 1.7 List实现类的异同点 List集合 1.1 概述 java.util…

力扣(LeetCode)227. 基本计算器 II

给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。 注意&#xff1a;不允许使用任何将字符串作为数学表达式计算的内置函数&#…