惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
博客园 - 【当耐特】
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
Last Week in AI
Last Week in AI
IT之家
IT之家
V
V2EX
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
博客园 - 叶小钗
Martin Fowler
Martin Fowler
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
M
MIT News - Artificial intelligence
博客园 - 聂微东
MyScale Blog
MyScale Blog
云风的 BLOG
云风的 BLOG
The Cloudflare Blog
WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
阮一峰的网络日志
阮一峰的网络日志
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 三生石上(FineUI控件)
Cyberwarzone
Cyberwarzone
Scott Helme
Scott Helme
H
Help Net Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The GitHub Blog
The GitHub Blog
Cisco Talos Blog
Cisco Talos Blog
D
DataBreaches.Net
P
Palo Alto Networks Blog
T
Threatpost
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 最新话题
Know Your Adversary
Know Your Adversary
C
CXSECURITY Database RSS Feed - CXSecurity.com
B
Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cloudbric
Cloudbric
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
N
News and Events Feed by Topic
T
The Blog of Author Tim Ferriss
L
Lohrmann on Cybersecurity
量子位
Security Latest
Security Latest
Google DeepMind News
Google DeepMind News
Y
Y Combinator Blog
aimingoo的专栏
aimingoo的专栏
Forbes - Security
Forbes - Security
T
Tailwind CSS Blog

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
OOP2:数组
Fish4174 · 2026-05-13 · via 博客园_首页

OOP2:数组

总结了定义数组的方式、访问数组元素和数组长度的方法、用 for 循环遍历数组的方式、数组作为方法参数和方法返回值的用法、数组作为成员变量如何构造、如何复制数组、如何对数组进行排序和查找、Arrays 类和二维数组的相关知识点。

一、数组的定义

使用数组一般需要两个步骤:①声明数组:向编译器声明数组名称和元素的数据类型。②创建数组:为数组元素分配存储空间。

1. 声明数组

java 语言的数组是一种引用数据类型,即数组是对象,而数组名就是对象名(或引用名,即对象的引用,是一个引用变量)。数组的声明实际上是声明一个引用变量,形如:

元素类型 []数组名;
元素类型 数组名[];
元素类型[] 数组名;//三种形式等价,推荐用第一种

下面代码声明了两个数组:

double []marks;
Account []accounts;

数组元素类型可以是基本数据类型(如 int 或 double 类型),也可以是引用数据类型(如 String 或 Account 类型)。如果数组元素为引用类型,则该数组称为对象数组。上面的 accounts 就是对象数组。声明数组不能指定数组元素的个数

2. 创建数组

声明数组仅仅声明一个数组对象的引用,而创建数组才会为数组的每个元素分配存储空间。创建数组用 new 运算符,形如:

数组名 = new 元素类型[size];//size 指定大小

下面代码创建了 marks 数组和 accounts 数组:

marks = new double[5];
accounts = new Account[3];

Java 数组可以动态创建,也就是它的大小可以在运行时指定,可以以变量作为数组的大小

数组的声明与创建可以写在一个语句中,如下所示:

double[] marks = new double[5];
Account[] accounts = new Account[3];

创建数组时会为每个元素指定默认值,对于引用类型数组(对象数组),它的每个元素初值为 null ,因此,还需要创建数组元素对象。

accounts[0] = new Account(101, "Fish Yu", 3000.0);
accounts[1] = new Account(102, "Tina Wang", 5000.0);
accounts[2] = new Account(103, "Tear", 8000.0);

也可以用下面这种形式完成对象数组的声明-创建-初始化:

Account[] accounts = new Account[]
{
	new Account(101, "Fish Yu", 3000.0);
	new Account(102, "Tina Wang", 5000.0);
	new Account(103, "Tear", 8000.0);
}

3. 数组初始化器

初始化器是在一对花括号中给出数组的每个元素值,这种方式称为静态初始化。用这种方法创建数组不能指定大小,系统会根据元素个数确定数组大小。如下所示:

double[] marks = {11, 45, 14, 1919, 810};
Account[] accounts = {new Account(101, "Fish Yu", 3000.0), new Account(102, "Tina Wang", 5000.0), new Account(103, "Tear", 8000.0)};

可以用 var 类型推断符声明数组,减少代码冗余:

var marks = {11, 45, 14, 1919, 810};
var accounts = {new Account(101, "Fish Yu", 3000.0), new Account(102, "Tina Wang", 5000.0), new Account(103, "Tear", 8000.0)};

“声明-创建-初始化”的多种形式

下面两种”声明-创建“写法均合法:

double[] marks = new double[5];
var marks = new double[5];

下面三种”声明-创建-初始化“写法均合法:

double[] marks = {11, 45, 14, 1919, 810};
double[] marks = new double[]{11, 45, 14, 1919, 810};
var marks = new double[]{11, 45, 14, 1919, 810};

下面的写法不合法,因为没有充分的依据进行类型推断:

var marks = {11, 45, 14, 1919, 810};

二、访问数组

1. 访问数组元素

数组元素的使用方式如下:

数组名[下标]

数组元素下标从0开始,到数组长度减1。数组一经创建,大小不能改变。Java 运行时会对数组元素的范围进行越界检查,若数组元素的下标超出范围,会抛出 ArrayIndexOutOfBoundsException 运行时异常。

2. 访问数组长度

数组作为对象提供一个 length 成员变量,它的值是数组元素的个数,访问方法如下:

数组名.length

3. 增强的 for 循环

增强的 for 循环能按顺序访问数组元素,可以用来迭代数组和集合对象的每个元素。因此这种循环也称为 for each 循环。形如:

for(type identifier : expression)
{
	//循环体
}

type 是元素类型,expression 必须是一个数组或集合对象,identifier 是元素本身,后面不需要再加中括号和下标

下面代码求数组 marks 中各元素的和:

double sum = 0;
for(var score : marks)
{
	sum = sum + score;
}
System.out.println("总成绩 = " + sum);

注意:增强的 for 循环只能按顺序访问数组元素,并且只能使用元素而不能对元素进行修改,不能获取当前元素的索引(下标)。

三、数组的应用

1. 数组元素的复制

数组元素的复制,是将一个数组中的所有元素复制到另一个同类型的数组中,得到的是两个彼此独立但又一模一样的数组。设有一个数组 source ,其中有4个元素,现在定义一个数组 target ,与原来数组的类型相同,元素个数相同。

区分:元素复制引用赋值

类似于对象名是对象的引用,数组名也是数组的引用。如果直接用赋值号连接两个数组名,实际上是使前者指向后者所引用的数组对象,即令两个数组引用指向同一个数组对象。如下所示:

int[] source = {10, 30, 20, 40};
int[] target = source;

上述代码无法将数组 source 中的每个元素复制到 target 数组中。

数组元素复制的方法

方法一:使用循环结构,将数组元素一个一个复制到目标数组中。

int[] source = {10, 30, 20, 40};
int[] target = new int[source.length];
for(var i = 0; i < source.length; i++)
	target[i] = source[i];

方法二:使用 System 类的 arraycopy() 方法,格式如下:

public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

src 为原数组,srcPos 为原数组的起始下标,dest 为目标数组,destPos 为目标数组的下标,length 为复制的数组元素个数。下面代码实现将 source 中每个元素复制到数组 target 中:

int[] source = {10, 30, 20, 40};
int[] target = new int[source.length];
System.arraycopy(source, 0, target, 0, 4);

使用 arraycopy() 方法也可以将源数组的一部分复制到目标数组中。注意,如果目标数组不足以容纳原数组元素,会抛出异常。

2. 数组冒泡排序

算法与写法和 C 语言中大同小异,直接展示代码:

public static void bubbleSort(int[] array)
    {
        for(int i = array.length - 1; i > 0; i--)
        {
            boolean flag = true;
            for(int j = 0; j < i; j++)
            {
                if(array[j] > array[j + 1])
                {
                    int t = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = t;
                    flag = false;
                }
            }
            if(flag) break;
        }
    }

将该静态方法和主方法放在同一个类,然后即可在主方法中调用该方法。

3. java.util.Arrays 类

java.util.Arrays 类定义了若干静态方法对数组操作,下面介绍实用的几个。

  • public static int binarySearch(int[] a, int key)

    对于一个已升序排序的数组,通过二分查找快速定位 key 值位置。找到则返回下标,找不到则返回(插入点-1)。插入点为指定的值在数组中应该插入的位置。

  • public static void sort(int[] a)

    对数组 a 按升序排序。

  • public static void sort(int[] a, int fromIndex, int toIndex)

    对数组 a 中从起始下标 fromIndex (包含)到终止下标 toIndex (不包含)之间的元素排序。

  • public static void fill(int[] a, int val)

    用指定的 val 值填充数组 a 中的每个元素。

  • public static double[] copyOf(double[] original, int newLength)

    对原数组 original 进行复制,返回值为接收复制的新数组。新数组的长度可用参数 newLength 定义,若不想改变原数组长度,可填成 original.length。

4. 数组与方法

(1)数组作方法参数

数组对象可以作用参数传递给方法,下面代码定义了一个求数组元素和的方法:

public static double sumArray(double array[])
{
	var sum = 0;
	for(var i = 0; i < array.length; i++)
		sum += array[i];
	return sum;
}

注意:由于数组是对象,因此将其传递给方法是按引用传递。如果在方法体中修改了数组元素的值,则该修改反映到返回的数组对象。

(2)方法返回数组

一个方法可以返回一个数组对象,下面的方法返回参数数组的元素反转后的一个数组:

public static int[] reverse(int[] list)
{
	var result = new int[list.length];
	for(int i = 0, j = result.length - 1; i < list.length; i++, j--)
		result[j] = list[i];
	return result;
}

有了上述方法,可以使用如下语句实现数组的反转:

int[] list = {6, 7, 8, 9, 10};
int[] list2 = reverse(list);

(3)可变参数方法

Java 允许定义方法(包括构造方法)带可变数量的参数,这种方法称为可变参数方法。具体做法是在方法参数列表的最后一个参数的类型名之后、参数名之前使用省略号,如下所示:

public static int average(int ... array)
{
	//方法体
}

带可变参数的方法中也可以有一般的参数,但可变参数必须是方法的最后一个参数。如下所示:

public static int average(String name, int ... array)
{
	//方法体
}

这里,参数 values 被声明为一个 double 型值的序列,其中参数的类型可以是引用类型。对可变参数的方法,调用时可以为其传递任意数量的指定类型的实际参数

在方法体中,编译器将为可变参数创建一个数组,并将传递来的实际参数值作为数组元素的值,相当于为方法传递一个指定类型、长度不定的数组。

下面程序使用可变参数方法求和,加数个数不定。

程序 2-1 VarargsDemo.java

public class VarargsDemo
{
    //可变参数方法,可变参数类型定义成:类型...argName,它必须是方法的最后一个参数
    public static int getSum(int ... array)
    {
        var sum = 0;
        for(var i : array)
        {
            sum += i;
        }
        return sum;
    }
    public static void main(String[] args)
    {
        int result = getSum(2, 3, 4);
        System.out.println("2+3+4 = " + result);
        result = getSum(3, 5, 8, 9, 10);
        System.out.println("3+5+8+9+10 = " + result);

        int[] a = {1,2,3,4};
        result = getSum(a);
        System.out.println("a数组元素之和为 " + result);
    }
}

运行结果如下:

该程序调用了 getSum()方法并为其传递若干 int 型数,也可以传递一个 int 型数组。

5. 数组作成员变量

(1)设计构造方法

现有类 MyArray,带有私有成员数组 data。在类中进行代码设计。

public class MyArray
{
	private int []data;
	//构造方法
}
  • 浅拷贝(直接引用)
public MyArray(int[] inputArray)
{
	this.data = inputArray
}

通过传递的参数 inputArray 为 data 赋值,但由于是引用赋值,若 inputArray 改变,则对象内的 data 也会随之改变。

  • 深拷贝(创建副本)
public MyArray(int[] inputArray)
{
	this.data = Arrays.copyOf(intputArray, inputArray.length);
}

上述代码为传来的参数 inputArray 创建了副本,再赋值给 data ,因此 data 不会被外部篡改。这种写法无疑安全性更高,故下面主要使用这种写法。

(2)getter / setter

//getter 返回副本
public int[] getData()
{
	return Arrays.copyOf(data, data.length);
}
//setter 接收副本
public void setData(int[] newData)
{
	this.data = Arrays.copyOf(newData, newData.length);
}

(3)使用对象

在主方法中创建对象,并获取/修改对象成员的值。

  • 创建对象
//写法一:先另定义数组,再将其作为参数写入构造方法
int[] source = {1, 2, 3};
MyArray array1 = new MyArray(source)
//写法二:直接在构造方法参数列表中定义数组
MyArray array2 = new MyArray(new int[]{1, 2, 3});
  • 获取/修改对象
//获取数组
int[] got = array1.getData();
//修改数组
array1.setData(new int[]{4, 5, 6});

四、二维数组

Java 语言中数组元素还可以是一个数组,这样的数组称为数组的数组二维数组

1. 二维数组的定义

二维数组的使用也分声明、创建两个步骤。

(1)二维数组的声明

二维数组有下面3种等价的声明格式:

元素类型 [][]数组名;//推荐使用第一种格式
元素类型 数组名[][];
元素类型 []数组名[];

元素类型可以是基本类型,也可以是引用类型,示例:

int [][]matrix;
String [][]cities;

(2)二维数组的创建

创建二维数组即为每个元素分配存储空间。系统先为高维分配引用空间,然后顺次为低维分配空间。

  • 写法一:直接为每一维分配空间:
var matrix = new int[2][3];

适用于数组的低维具有相同个数的数组元素

二维数组是数组的数组,即数组元素也是一个数组。二维数组 matrix 有两个元素,matrix[0] 和 matrix[1] ,它们又都是数组,各有3个元素。matrix、matrix[0]、matrix[1]都是对象。每个元素在创建时会被指定默认值。

  • 写法二:先为第一维分配空间,再为第二维分配空间(反之不可):
var matrix = new int[2][];
matrix[0] = new int[3];
matrix[1] = new int[4];

适用于低维数组元素个数不同的情况。

第一维的每个元素(matrix[0]、matrix[1])可以指定不同的大小

对于引用类型的数组,除了为数组分配空间外,还要为每个数组元素分配空间:

cities[0][0] = new String("成都");
cities[0][1] = new String("绵阳");
cities[0][2] = new String("雅安");
cities[1][0] = new String("沈阳");
cities[1][1] = new String("锦州");

(3)数组初始化器

二维数组可使用初始化器在声明数组的同时为数组元素初始化,如下所示:

int[][] matrix = {{15, 56, 20, -2}, {10, 80, -9, 31}, {76, -3, 99, 21}};

2. 数组元素的使用

用下面的形式访问二维数组的元素:

数组名[下标1][下标2]

下标1和下标2可以是整型常数或表达式。每一维的下标是从0到该维长度减1.

  • 访问数组的长度

若有数组 matrix[3][4] ,则说明 matrix 数组是一个3行4列的数组。当我们讨论 matrix 的长度时,我们其实指的是它第一维的长度。多维数组每一维都有一个 length 成员表示数组的长度。matrix.length 的值是3,matrix[0].length 的值是4.