重识java11
来源:互联网 发布:json remove key 编辑:程序博客网 时间:2024/05/01 22:41
面向对象的一些知识暂时告一段落,从本文开始,进入java语法的重学阶段~
初识数组
什么是数组
数组:相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。
数组的定义及初始化
定义及动态初始化
- 方式一,java推荐用法: type [] 变量名 = new type[数组中元素的个数];
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 方式二,c语言用法(不推荐): type 变量名 [] = new type[数组中元素的个数];
- 1
- 2
- 3
- 4
以上两种方式都叫做动态初始化,也就是说,只有当程序运行以后,你才能知道数组里到底存了哪些数据。方式二的命名方式c和c++程序员比较熟悉,但是java官方推荐使用第一种,一看就能知道,这是一个int型的数组,叫a。
静态初始化
- 1
在定义数组的时候直接初始化,大括号里的值就是数组的值。
隐式初始化
- 1
可以不写new,直接使用大括号初始化,但是本质上还是调用了new的,只是可以不写出来而已,所以叫隐式初始化。
最后,我们回过头来仔细的研究一下下面这一句代码:
- 1
这句代码做了哪些事呢?
- int[] a: 定义了一个int型数组的引用,名字叫做a,存放在栈中。
- new int[10]:初始化一个长度为10的int型数组,在堆中开辟相应大小的内存。
- int[] a = new int[10]:将堆中开辟的数组的内存地址赋给数组引用a。
这样就可以通过a这个变量,来操作这个数组了。
是不是觉得这个过程很熟悉?没错!我们创建一个对象的过程也是这样的!那这是不是证明,数组其实是一个对象呢?我们后面会详细分析。
数组的使用
数组自身的使用
数组是使用方式大家应该都很清楚了,我这里简单的提一下。
数组的遍历
- 方式一:for循环
- 1
- 2
- 3
- 方式二:foreach循环
- 1
- 2
- 3
数组长度
- 1
java中的每个数组都有一个名为length的属性,表示数组的长度。
length属性我们后面会详细分析。
数组元素不为基本数据类型
数组是可以存放任意类型的数据的,不一定非得是基本数据类型。数组元素不为基本原生数据类型时,存放的是引用类型,而不是对象本身。当生成对象之后,引用才指向对象,否则引用为null。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
数组作为方法的参数
- 1
- 2
- 3
- 4
- 5
数组作为方法的返回值
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
数组内容的输出
首先,这样写是不对的。
- 1
- 2
- 3
- 4
- 5
这输出的是什么奇怪的东西?我们先不管,后面会详细说。那怎么输出数组呢?
方式一:
- 1
- 2
- 3
- 4
- 5
- 6
方式二:
- 1
- 2
- 3
- 4
数组内容的比较
数组内容的比较可以使用equals()方法吗?
看代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
所以证明不能直接用equals()方法比较数组内容,因为没有override Object中的实现,所以仍采用其实现,即采用==实现equals()方法,比较是否为同一个对象。
Object类中的equals方法默认使用==实现的,至于为什么数组也能使用equals方法,我们后面再分析。
怎么比较呢?一种解决方案是自己写代码,另一种方法是利用java.util.Arrays。
java.util.Arrays中的方法全是static的。其中包括了equals()方法的各种重载版本。
代码如下:
- 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
Arrays类的使用
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
具有以下功能:
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
- …….
我觉得这些大家也都知道,我就不细说了,重点在后面。
数组的高级应用
二维数组
二维数组是数组的数组。其实java只有一维数组,但是由于数组可以存放任意类型的数据,当然也就可以存放数组了,这个时候,就可以模拟多维数组了。
基本的定义方式同样有两种,如:
- 1
- 2
- 3
变长的二维数组
二维数组的每个元素都是一个一维数组,这些数组不一定都是等长的。
声明二维数组的时候可以只指定第一维大小,空缺出第二维大小,之后再指定不同长度的数组。但是注意,第一维大小不能空缺(不能只指定列数不指定行数)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
二维数组也可以在定义的时候初始化,使用花括号的嵌套完成,这时候不指定两个维数的大小,并且根据初始化值的个数不同,可以生成不同长度的数组元素。
- 1
可变参数
有的时候,你需要一个方法,但是你在调用它之前不知道要传递几个参数给他,这个时候你就需要可变参数了。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
那个奇怪的int ...args
就是可变参数,这样你就可以传递任意个你想传递的数据了。
java把可变参数当做数组处理。
注意:可变参数必须位于最后一项。当可变参数个数多余一个时,必将有一个不是最后一项,所以只支持有一个可变参数。因为参数个数不定,所以当其后边还有相同类型参数时,java无法区分传入的参数属于前一个可变参数还是后边的参数,所以只能让可变参数位于最后一项。
可变参数实质上是一个数组,所以下面这样重载是不可以的!
- 1
- 2
- 3
- 4
尽管在背地里,编译器会把能匹配不确定个实参的形参,转化为数组形参;而且也可以用数组包了实参,再传递给实参个数可变的方法;但是,这并不表示“能匹配不确定个实参的形参”和“数组形参”完全没有差异。
一个明显的差异是,如果按照调用实参个数可变的方法的形式,来调用一个最后一个形参是数组形参的方法,只会导致一个“cannot be applied to”的编译错误。
比如:
- 1
- 2
- 3
- 4
- 5
- 6
这样是不行的。
除此之外,可变参数是不可以使用泛型的,关于泛型,我们下一篇文章会详细讲解。
可变参数还有许多其他的坑,感兴趣的可以详细了解一下,我就不多说了。毕竟。。打字好累啊。
数组复制
- 1
- 2
- 3
这个时候a[1]也变成了5,为什么会这样?就不用我多说了吧,所以,要拷贝一个数组,还是需要些技巧的:
方式一:System.arraycopy的用法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
方式二:Arrays.copyOf的用法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
方式三:Arrays.copyOfRange的用法
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
数组到底是什么
说了那么多,那么,数组究竟是个什么东西呢?
我们来看看数组有没有什么可以用的方法:
哟,还真有?怎么看着这么像Object类里那几个方法啊!这其中,必有蹊跷。
来看这段代码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
从上面示例可以看出,数组的是Object的直接子类,它属于“第一类对象”,但是它又与普通的java对象存在很大的不同,从它的类名就可以看出:[I,这是什么东东??
我们再看如下示例:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
通过这个实例我们知道:[代表了数组的维度,一个[表示一维,两个[表示二维。可以简单的说数组的类名由若干个’[‘和数组元素类型的内部名称组成。不清楚我们再看:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
从这个实例我们可以看出数组的“庐山真面目”。同时也可以看出数组和普通的Java类是不同的,普通的java类是以全限定路径名+类名来作为自己的唯一标示的,而数组则是以若干个[+L+数组元素类全限定路径+类来最为唯一标示的。这个不同也许在某种程度上说明了数组也普通java类在实现上存在很大的区别,也许可以利用这个区别来使得JVM在处理数组和普通java类时作出区分。
我们在jdk中并没有找到一个可以代表数组的类,但是数组的的确确是Object类的一个子类,那么,它究竟是从哪冒出来的呢?
数组是对象
首先,数组是对象!
但是这个数组对象并不是从某个类实例化来的,而是由JVM直接创建的,因此查看类名的时候会发现是很奇怪的样子,这个直接创建的对象的父类就是Object,所以可以调用Object中的所有方法,包括你用到的toString()。
所以我们之前的输出问题就很明显了,因为调用的toString()方法是来自于Object的,这个方法的实现是
- 1
- 2
- 3
所以就打出了类似于[I@61bbe9ba这样的稀奇古怪的数字。
如果要输出“{1,9}”这样的内容,可以写一个循环逐个输出,或者使用Arrays.toString()输出。
数组的length属性也是jvm添加的,数组一初始化,jvm就会给它一个固定的length【属性】,在它的生命周期中不可变。
数组的协变
java中数组为什么要设计为协变的?
比如:
- 1
- 2
这样的语句可以通过编译,而在运行时会错误。
那为何不禁止数组协变,在编译期间就指出错误呢?
因为SE5之前还没有泛型,但很多代码迫切需要泛型来解决问题。
举个例子,比较两个数组是否“值相等“的Arrays.equals( )方法。因为底层实现调用的是Object.equals( )方法,和数组中元素的具体类型无关。
- 1
- 2
- 3
- 4
- 5
- 6
所以不想让每个类型都要重新定义Arrays.equals( )方法。而是”泛化“地接受任何元素类型的数组为参数,就像现在这样:
- 1
- 2
- 3
要让Object[]能接受所有数组类型,那个时候又没有泛型,最简单的办法就是让数组接受协变,把String[],Integer[]都定义成Object[]的派生类,然后多态就起作用了。
但为什么数组设计成”协变“不会有大问题呢?这是基于数组的一个独有特性:
数组记得它内部元素的具体类型,并且会在运行时做类型检查。
这就是上面的代码能通过编译,但运行时报错的原因:
- 1
- 2
num变量记得它内部元素是Integer。所以运行时给它插入double型的时候不让执行。
这反而是数组的优点,也是当初”敢于“把数组设计成协变的原因。虽然向上转型以后,编译期类型检查放松了,但因为数组运行时对内部元素类型看得紧,不匹配的类型还是插不进去的。
这也是为什么容器Collection不能设计成协变的原因。Collection不做运行时类型检查,比较耿直。还是题主Number的例子,如果Collection接受”协变“,List的引用能传给List:
- 1
- 2
这时候我想往List里插入一个Double。它不会像数组这样”坚贞“,它将”安静“地接受。
- 1
然后当我们从原先的integerList里面取东西,才会发现出问题了。虽然看上去从integerList里取Integer,我们的操作无可指责。但取出来的却是Double型。
- 1
于其到拿出来之后才发现不对,那还不如当初就不让插入。这就是数组的好处。
而且,在引入了通配符(Wildcard)之后,协变的功能也已经被实现了。而且配合通配符的”上界“和”下界“一起用,容器内元素的类型还是受到严格控制的,虽然有点复杂。
- 1
所以总的来说,虽然数组的协变不是一个完美的设计,但也不能算非常烂。起码还能用,没有捅出大篓子。而且数组又不支持泛型,底层类库到处是Object[],现在也不可能改了。
数组不支持泛型
比如:
- 1
会报错,无法编译通过
根本的原因是:数组在创建的时候必须知道内部元素的类型,而且一直都会记得这个类型信息,每次往数组里添加元素,都会做类型检查。
但因为Java泛型是用擦除(Erasure)实现的,运行时类型参数会被擦掉。所以对于泛型数组,编译器看不到泛型的String类型参数。数组由于无法确定所持有元素的类型,所以不允许初始化。
具体我们会在下一篇《泛型》中详细说明。
内存中的数组
数组的内存模型
一维数组:
int arr[] = new int[3];
二维数组:
- 1
- 2
- 3
- 4
总结
- 数组的定义推荐使用
int[] a
方式。 - 数组的长度是不可变的。
- 数组是特殊的对象,父类是Object类。
- java不支持泛型数组。
- 数组是协变的。
- 数组中可以保存任意类型的数据,从而可以创建多维数组。
- 重识java11
- Java11
- java11
- java11
- JAVA11
- java11-15
- 学习Java11
- ImageLoadAsyncTask.java11
- StreamTools.java11
- HealthInfo.java11
- MyBaseAdapter.java11
- 重拾Java回忆录(十一):Java11内部类的使用
- java11个第三方类库
- 要点Java11 对象
- java11月13日
- Java11.11/11.12作业
- JAVA11.6作业
- JAVA11.16作业
- Font Awesome(六)
- hrbust 2354 An Easy Geometry Problem
- 分治---线性时间选择
- retrain tensorflow中InceptionV3模型
- 如何让opencv成为MATLAB中的一个工具箱
- 重识java11
- python-基础知识-20171219
- Gainlo 面试指南 翻译完成
- 嵌入式名词以及简略说明
- 【Luogu2711】小行星(网络流,最大流)
- 最大值的选取
- Even Parity UVA
- 数据结构-树
- 顺序栈(C语言)