销售网站建设赚钱吗,网站推广案例分析,wordpress熊掌号出图改造,山西省吕梁市天气文章目录开端通过引用创建对象Java的数据存储方式基本类型包装类和高精度数字操作符自动递增和自动递减老生常谈的问题#xff1a;和equals()如何重写equals方法#xff1f;短路字面量科学计数法位运算类型转换初始化和清理方法的重载方法的重写无参构造器this与构造器垃圾收…
文章目录开端通过引用创建对象Java的数据存储方式基本类型包装类和高精度数字操作符自动递增和自动递减老生常谈的问题和equals()如何重写equals方法短路字面量科学计数法位运算类型转换初始化和清理方法的重载方法的重写无参构造器this与构造器垃圾收集器资源清理数组你一定要知道的细节声明与初始化数组内存分析Arrays工具类的使用本文适合有一定基础的Java学习者可以加深你对Java的理解助你应对考试或者面试 开端
通过引用创建对象
在Java中我们总是听见过一句话叫万事万物皆对象但实际上我们在程序中操作的其实是对象的引用(reference)。那么究竟什么是引用呢 我们有时候也会将对象的引用叫做对象的句柄 举个例子我们通常会用下面这一行代码来创建一个对象
Person person new Person(张三);有人会说这里的person是一个对象是Person类的一个实例。
也有人会说这里的person并不是真正的对象而是指向所创建的对象的引用。
到底哪种说法是对的我们先不急着纠结哪种说法是对的再看两行代码
Person person;person new Person(张三);这两行代码实现的功能和上面的一行代码是完全一样的。大家都知道在Java中new是用来在堆上创建对象用的如果person是一个对象的话那么第二行为何还要通过new来创建对象呢由此可见person并不是所创建的对象是什么 “操纵的标识符实际是指向一个对象的引用” --《Java编程思想》 也就是说person是一个引用是指向一个可以指向Person类的对象的引用。真正创建对象的语句是右边的new Person(“张三”);
我们再说白一点看下面的图
我们操作的person处在左边的堆中它就是我们对象的引用而栈中的才是我们真正的对象。 引用的本质是内存地址 Java的数据存储方式
当程序运行时能够可视化其内容的排布方式是十分有帮助的对于内存管理来说尤其如此。我们平常所熟知的数据存储方式就是栈和堆在这里我们来一个系统的了解
寄存器这是速度最快的数据存储方式因为它保存数据的位置在中央处理器CPU里。寄存器的数量是有限的所以只能按需分配。此外我们不能直接控制寄存器的分配。栈位于RAM当中通过栈指针可以从处理器获得直接支持。栈指针向下移动则分配新的内存向上移动则释放那些内存。这种存储方式速度仅次于寄存器。 常用于存放对象引用和基本数据类型而不用于存储对象Java系统在创建应用程序的就明确栈上的所有对象的生命周期这种限制约束了程序的灵活性但是提高了效率 堆一种通用的内存池也位于RAM当中用于存放所有Java对象。其中存放的数据由JVM自动进行管理。 编译器不需要知道存储的数据在堆里存活多长。当需要一个对象时使用new写一行代码当执行这行代码时会自动在堆里进行存储分配。同时因为以上原因用堆进行数据的存储分配和清理需要花费更多的时间。(一句话说就是提高了灵活性但是效率会降低) 常量存储我们一般也叫做常量池。常量(字符串常量和基本类型常量、static final 关键字修饰的变量值)通常直接存储在程序代码内部(常量池)。这样做是安全的因为它们的值在初始化时就已经被确定并不会被改变。常量池在java用于保存在编译期已确定的已编译的class文件中的一份数据。它包括了关于类方法接口等中的常量。 为了提供重用性、节约内存空间JVM会将所有的字符串存放在常量池中如果使用的字符串在常量池已存在则会直接返回不需要重复创建。 非RAM存储如果数据完全存活于程序之外那么它可以不受程序的任何控制在程序没有运行时也可以存在。其中两个基本的例子是序列化对象和持久化对象。 序列化对象它指的是可以转化为字节流并可以发送至其他机器的对象持久化对象它指的是保存在磁盘上的对象特点将对象转化为其他形式来保存在其他媒介中。然后在需要的时候重新转化回常规的RAM对象。
为了便于理解我们放上一段代码然后画出它的数据存储示意图
public class number{final static int i1 1;int i22;int i32;String str1 abc;String str2 abc;String str3 abc;String str4 new String(abc);String str5 new String(abc);}顺便提一嘴RAM和ROM很多人把他们弄混我们可以这么去理解 RAM就是我们平时所说的CPU缓存、电脑和手机的(运行)内存ROM就是固态硬盘、U盘还有我们平时买手机说的32G、64G的存储空间 基本类型
Java分为以下8种基本数据类型 整数型byteshortintlong 浮点型floatdouble 布尔型boolean 字符型char
既然是基本数据类型那也就意味着我们不需要通过new来创建而是直接创建一个自动变量注意不是引用。也就是说变量会直接在栈上保存它的值因此运行效率也非常高。
在Java中定义了每一种基本类型所占用的空间大小。要知道Java是一种跨平台的语言即使是在不同的机器上这些基本类型所占用的空间也是保持一致的。这种一致性让Java的可移植性比其它语言更好。 注意 boolean类型的空间大小没有明确标出其对象只能被赋值为true和false 最后我们还要关注一下基本类型的默认值
注意
当基本数据类型被声明为非类变量时JVM不会为相应的数据类型提供默认值此时需要显示设置初始值才能正常使用基本类型否则会提示The local variable * may not have been initialized2.Java将数据类型分为基本数据类型和引用类型在作为类变量使用时未显式初始化的引用类型类变量也会被初始化为默认值(nul)3.Java为8中基本数据类型都提供了相应的包装类(Byte、Short、nteger、 Long、 Float、Double以及Character、Boolean)并且基本数据类型对应的包装类为引用类型JVM会为其赋值默认值(null)。 一定要注意 不是任何时候变量都会被赋予默认值是只有作为成员变量的时候 包装类和高精度数字
各数据类型对应的包装类
包装类设计出来的意义在于
Java是一个面向对象的语言基本类型并不具有对象的性质。将基本类型包装成对象以后扩大了基本类型所具有的操作更是JAVA面向对象的体现在泛型中传入的参数只能是类类型不能使用基本数据类型包装类中有的方法是静态方法不能通过对象去调用需要通过类来访问。
说到包装类我们就不得不提到自动拆箱与自动装箱他们的概念我们都知道但这里我说说它们的本质
自动拆箱的本质调用对应类的XXValue()方法自动装箱的本质调用对应类的XX.valueOf()方法
在包装类方面还有一个注意点包装类的缓存设计(也叫做享元设计) 就是创建一个缓存区有限定大小的来将重复使用的数据放进去从而达到少占用内存空间的效果。在区域之内的直接拿来用一旦超过缓存空间则系统会在堆内存中new一个新的对象出来。 (面试爱考喜欢在这些重用对象的地址身上出题) Byte (缓存范围[-128,127])Short (缓存范围[-128,127])Long (缓存范围[-128,127])Integer (缓存范围[-128,127])Character (缓存范围[0,127])Boolean (全部缓存)Float (没有缓存)Doulbe (没有缓存) Java提供了两个支持高精度计算的类分别是
BigInteger 可以支持任意精度的整数不用担心丢失精度 BigDecimal 用于任意精度的定点数例如你可以用于货币计算
虽然这两个类大致也可以归为包装类但是他们其实并没有对应的基本类型。
这两个类都提供了一些方法来模拟基本类型的各种操作。也就是说你能对int和float做什么就能对BigInteger和BigDecimal做什么区别只是你用方法代替了运算符而已。此外由于涉及更多的计算量导致的结果就是相关操作的效率有所降低。也就是速度换精度。 相关方法参考JDK文档 操作符
自动递增和自动递减
递减操作符--表示减少一个单位递增操作符表示增加一个单位
注意这两个操作符都有前缀式和后缀式
前缀式表示程序会先执行运算然后返回生成的结果后缀式表示程序会先返回变量的值然后再执行计算
接下来我们看几个例子体会一下
public class AutoInc {public static void main(String[] args) {int i 1;System.out.println(i: i);System.out.println(i: i); // Pre-incrementSystem.out.println(i: i); // Post-incrementSystem.out.println(i: i);System.out.println(--i: --i); // Pre-decrementSystem.out.println(i--: i--); // Post-decrementSystem.out.println(i: i);}
}
/* Output:
i: 1
i: 2
i: 2
i: 3
--i: 2
i--: 2
i: 1
*/老生常谈的问题和equals()
之所以在这里还要提一嘴实在是因为这个问题太过经典经典到基本学过java的人都会被提醒到这是一个坑以后要注意。
当创建一个新类的时候它会自动继承Object类。如果该类没有重写equals方法那么它使用的就是Object中的equals方法而这个方法其实默认比较的就是内存地址也就是和的作用一样。 大多数标准库会重写equals方法 如何重写equals方法
一个适当的equals方法必须满足以下五个条件
自反性对于任何非空引用值 xx.equals(x) 都应返回 true。对称性对于任何非空引用值 x 和 y当且仅当 y.equals(x) 返回 true 时x.equals(y) 才应返回 true。传递性对于任何非空引用值 x、y 和 z如果 x.equals(y) 返回 true并且 y.equals(z) 返回 true那么 x.equals(z) 应返回 true。一致性对于任何非空引用值 x 和 y多次调用 x.equals(y) 始终返回 true 或始终返回 false前提是对象上 equals 比较中所用的信息没有被修改。对于任何非空引用值 xx.equals(null) 都应返回 false。
通过上面这几点我们可以总结出以下的重写思路 我们将要比较的对象命名为a 如果a为null则两个对象不相等如果a为this对象(即自己与自己比较)则两个对象相等如果a不是this对象所属的类或子类则两个对象不相等接下来我们再来比较两个对象的实际值
我们再来看看String源码中equals是怎么写的 public boolean equals(Object anObject) {if (this anObject) {return true;}if (anObject instanceof String) {String anotherString (String)anObject;int n value.length;if (n anotherString.value.length) {char v1[] value;char v2[] anotherString.value;int i 0;while (n-- ! 0) {if (v1[i] ! v2[i])return false;i;}return true;}}return false;}我们可以看到在String中equals的逻辑如下
先看内存地址是否相同在看是否为同类或子类如果是则强转之后比较长度如果长度还一样则比较相应位置的字符是否一样 关于String的equals方法源码面试有时候也会考到 短路
逻辑操作符支持一种称为短路的现象一旦表达式当前部分的计算结果能够明确无误的确定整个表达式的值表达式余下部分就不会执行了。
我们可以看看下面的例子
public class ShortCircuit {static boolean test1(int val) {System.out.println(test1( val ));System.out.println(result: (val 1));return val 1;}static boolean test2(int val) {System.out.println(test2( val ));System.out.println(result: (val 2));return val 2;}static boolean test3(int val) {System.out.println(test3( val ));System.out.println(result: (val 3));return val 3;}public static void main(String[] args) {boolean b test1(0) test2(2) test3(2);System.out.println(expression is b);}
}
/* Output:
test1(0)
result: true
test2(2)
result: false
expression is false
*/当然我们把换成可以避免这种短路情况。 字面量
一般来说如果程序里使用了一个字面量 (literal value )则编译器能准确地知道它是什么类型的。不过当类型模棱两可的时候你就必须使用与该字面量相关的一些字符以此添加额外信息来引导编译器。
下面这段代码展示了这些字符
public class Literals {public static void main(String[] args) {int i1 0x2f; // 十六进制 (小写)System.out.println(i1: Integer.toBinaryString(i1));int i2 0X2F; // 十六进制 (大写)System.out.println(i2: Integer.toBinaryString(i2));int i3 0177; // 八进制 (前置0)System.out.println(i3: Integer.toBinaryString(i3));char c 0xffff; // char类型的最大十六进制值System.out.println(c: Integer.toBinaryString(c));byte b 0x7f; // byte类型的最大十六进制值System.out.println(b: Integer.toBinaryString(b));short s 0x7fff; // short类型的最大十六进制值System.out.println(s: Integer.toBinaryString(s));long n1 200L; // long类型后缀(大写)long n2 200l; // long类型后缀(小写)long n3 200;// Java 7 的二进制字面量byte blb (byte)0b00110101;System.out.println(blb: Integer.toBinaryString(blb));short bls (short)0B0010111110101111;System.out.println(bls: Integer.toBinaryString(bls));int bli 0b00101111101011111010111110101111;System.out.println(bli: Integer.toBinaryString(bli));long bll 0b00101111101011111010111110101111;System.out.println(bll: Long.toBinaryString(bll));float f1 1;float f2 1F; // float类型后缀(大写)float f3 1f; // float类型后缀(小写)double d1 1d; // double类型后缀(大写)double d2 1D; // double类型后缀(小写)// (十六进制和八进制也能作为long类型使用)}
}
/* Output:
i1: 101111
i2: 101111
i3: 1111111
c: 1111111111111111
b: 1111111
s: 111111111111111
blb: 110101
bls: 10111110101111
bli: 101111101011111010111110101111
bll: 101111101011111010111110101111
*/注意
这里的十六进制数、八进制数、二进制数适用于所有整数类型我们在前面已经说过char、byte和short所能表示的最大十六进制值。如果超出范围,编译器会自动将其转换成int型并告诉你这次赋值需要进行“窄化转型”
字面量里的下划线
Java 7 中有一个十分有用的新增功能:可以在数字字面量里使用下划线这样更易于阅读。这对在大数值里分组数字特别有帮助
科学计数法
在java中e代表10的幂次并且这个e大写小写均可 注意 编译器一般会把指数当作double类型处理 位运算
在位运算中有两套操作符
按位操作符移位操作符
按位操作符
用来操作整数基本数据类型中的单个二进制位。按位操作符会对两个参数中对应的二进制位执行布尔代数运算。
与操作符|或操作符^异或操作符~非操作符
前三个都是二元操作符最后一个是一元操作符。所以前三个都可以和等号联合使用、|、^ 你可以对布尔类型使用与、或、异或运算但不能使用非运算(大概是为了避免与逻辑操作符!混淆) 看一段易错代码
int a 3;
System.out.println(Integer.toBinaryString(a));
System.out.println(Integer.toBinaryString(~a));结果 移位操作符
移位操作符也操作二进制位他们只能用来处理基本类型里的整数类型。
左移位低位补0num 1,相当于num乘以2有符号的右移位操作符num 1,相当于num除以2 符号为正高位插0符号为负高位差1 无符号的右移位操作符 无论符号正负都在高位插入0
移位操作符可以与等号结合使用
示例代码 //Integer.toBinaryString()是将数字用二进制格式显示int i -10;System.out.println(Integer.toBinaryString(i)); //左移两位int j -102;System.out.println(Integer.toBinaryString(j));//右移两位int m -102;System.out.println(Integer.toBinaryString(m));//无符号右移int n -102;System.out.println(Integer.toBinaryString(n));结果
11111111111111111111111111110110
11111111111111111111111111011000
11111111111111111111111111111101
111111111111111111111111111101省略了首位两个0计算机中数字以补码存储首位为符号位 类型转换
Java中有两种数据转换
宽化转型不必显式的进行类型转换因为新类型可以容纳比原来类型更多的信息而不会造成任何信息的丢失。当然在有的时候可能需要显式的类型转换才能正常编译窄化转型将容纳更多信息的数据类型转化为无法容纳那么多信息的数据类型有可能面临信息丢失的危险。 Java可以把任何基本类型转化成别的基本类型但boolean除外他不允许进行任何类型的转换 在执行窄化转型的时候要注意截尾与舍入的问题 我们将float或者double转化为整型值时总是对该数值进行截尾而不是四舍五入(要用round方法)。 说到这里就不得不提类型提升:
如果对小于 int 类型的基本数据类型(即char、byte 或者 shot)执行算术运算或按位运算运算执行前这些值就会被自动提升为 int结果也是 int 类型。如果要把结果赋值给较小的类型就必须使用强制类型转换(由于把值赋给了较小的类型可能会出现信息丢失)。
通常表达式里出现的最大的数据类型决定了表达式最终结果的数据类型。如果将一个 float 类型的值与一个 double 类型的值相乘结果就是 double 类型。如果将-个int 值和一个 long值相加则结果为 long 类型。
初始化和清理
方法的重载
方法重载指同一个类中定义的多个方法之间的关系满足下列条件的多个方法相互构成重载
多个方法在同一个类中多个方法具有相同的方法名多个方法的参数不相同类型不同或者数量不同 不能通过返回值确定方法是否重载 注意
参数即使名字相同、类型相同、数量相同但是如果顺序不同也可以构成重载
public class OverloadingOrder {static void f(String s, int i) {System.out.println(String: s , int: i);}static void f(int i, String s) {System.out.println(int: i , String: s);}public static void main(String[] args) {f(String first, 11);f(99, Int first);}
}
/* Output:
String: String first, int: 11
int: 99, String: Int first
*/方法的重写
说到重载那就不得不提到重写
方法重写的要求 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表。 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型。例如Student Person。 注意如果返回值类型是基本数据类型和void那么必须是相同 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限。public protected 缺省 private 注意① 父类私有方法不能重写 ② 跨包的父类缺省的方法也不能重写 子类方法抛出的异常不能大于父类被重写方法的异常
此外子类与父类中同名同参数的方法必须同时声明为非static的(即为重写)或者同时声明为static的不是重写。因为static方法是属于类的子类无法覆盖父类的方法。
无参构造器
这是一个小的注意点知道即可
当我们新建一个对象没有创建无参构造器编译器时会自动帮你创建的。但是只要你自己写了构造器(假设是一个有参数的构造器)编译器就不会再帮你创建构造器如果你此时再去通过无参构造器创建对象编译器会提示找不到相应的构造器
this与构造器
this表示对当前对象的引用也就是我们常说的这个对象和当前对象。 this关键字只能在非静态方法中使用 我们在java中很少使用到this因为编译器会自动帮我们添加当然你自己写也是可以的。
当需要明确指出当前对象的引用时才使用this关键字。下面我列举出三种使用this的场景
①当你的方法要返回的是当前对象的引用 increment方法通过this关键字返回了当前对象的引用所以可以很容易的对同一个对象执行多次操作。
②用来将当前对象传递给另外一个方法
class Person {public void eat(Apple apple) {Apple peeled apple.getPeeled();System.out.println(Yummy);}
}class Peeler {static Apple peel(Apple apple) {// ... 削皮return apple; // 削皮后的}
}class Apple {Apple getPeeled() { return Peeler.peel(this); }}public class PassingThis {public static void main(String[] args) {new Person().eat(new Apple());}
}
/* Output:
Yummy
*/也就是在当前类中我们要将对象传递给一个外部方法的时候可以用this
③在构造器中调用构造器
当一个类中有多个构造器时有时会希望一个构造器里调用另外一个构造器以避免重复的代码。这里就可以使用this。
在构造器中如果在this后面加了参数列表那么就有了不同的含义他会显示调用与该参数列表匹配的构造器。 注意 不能同时用this调用两个构造器并且构造器调用必须出现在方法的最开始部分 ④当参数与成员变量的名字相同而产生冲突的时候
我们把第三点和第四点结合起来看一个例子
public class Flower {int petalCount 0;String s initial value;Flower(int petals) {petalCount petals;System.out.println(Constructor w/ int arg only, petalCount petalCount);}Flower(String ss) {System.out.println(Constructor w/ String arg only, s ss);s ss;}Flower(String s, int petals) {this(petals);//- this(s); // 不能同时调用两个构造器this.s s; // 成员变量与参数冲突System.out.println(String int args);}Flower() {this(hi, 47);System.out.println(Zero-argument constructor);}void printPetalCount() {//- this(11); // 不能在非构造器里System.out.println(petalCount petalCount s s);}
}垃圾收集器
垃圾收集是Java虚拟机(JVM)垃圾收集器Garbage Collector提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。
在java中垃圾收集器具有以下四个主要特征
停止-复制stop-and-copy标记-清除mark-and-sweep分代generation自适应adaption
停止-复制算法
程序首先停止然后将所有存活对象从一个堆复制到另一个堆剩下的就都是垃圾。当一个对象从一个地方移动到另一个地方时所有指向该对象的引用都必须修改。
该算法的问题
你需要有两个堆然后在这两个独立的堆之间来回复制内存这比实际需要多了一倍内存。一旦程序变得稳定它可能很少产生垃圾甚至没有。尽管如此复制收集器仍会将所有内存从一个地方复制到另一个地方这是一种浪费。
标记-清除算法
该算法从栈和静态存储开始遍历所有引用以查找存活对象。每当它找到一个存活对象就会给该对象设置一个标志。此时尚未开始收集只有在标记过程完成后才会进行清除。在清除过程中没有标记的对象被释放但不会发生复制。
该算法的特征
对于一般用途“标记 – 清除”算法相当慢在垃圾很少或没有的时候它的速度就很快了。
分代 为了解决这两个算法各有利弊的情况所以就有了垃圾收集器的第三个特点分代 在老年代中的对象变成垃圾的可能性更大新生代中的对象因为刚刚被分配所以变成垃圾的可能性小很多。 创建一个对象的时候总是在Eden区操作当这个区满了那么就会触发一次Young GC也就是年轻代的垃圾回收。当Eden区再次被用完就再触发一次Young GC此时会将Eden区与From区还在被使用的对象复制到To区。在下一次Young GC的时候则是将Eden区与To区中的还在被使用的对象复制到From区。若干次Young GC后有些对象在From与To之间来回游荡一旦超出阈值就将它们复制到老年代。如果老年代被用完则执行Full GC。 这里的Young GC就用的是标记-清除算法Full GC用的是停止-复制算法 其策略就是让执行效率高的多执行让执行效率低的少执行。 自适应
JVM会监控垃圾收集的效率如果所有对象都很稳定垃圾收集器效率很低的话它会切换到“标记 – 清除”算法。同样JVM会跟踪标记和清除的效果如果堆里开始出现很多碎片它会切换回“停止 – 复制”算法。
资源清理
C vs. Java 垃圾回收不是C中的析构. 两者不是对应关系, 因为垃圾回收的发生是不确定的, 而C中析构函数是由程序员控制(delete) 或者离开器作用域时自动调用发生, 是在确定的时间对对象进行销毁并释放其所占用的内存. 那么我们就有了一个疑问Java已经有了垃圾收集器Garbage Collector还需要额外针对创建的资源做清理工作吗
答案是需要。因为假设你的对象在不使用 new 的情况下分配了一块“特殊”内存。垃圾收集器只知道如何释放由 new 分配的内存所以它不知道如何释放对象的这块“特殊”内存。 未使用 Java 的new分配内存(比如文件操作的句柄, 数据库的连接等等)即采用了类似 C 语言的机制在Java中就是使用本地方法来实现例如通过调用 C 的 malloc() 系列函数来分配存储空间。此时应该在finalize()方法中调用C的free()函数。 Java 允许定义这样的方法它在对象被垃圾收集器析构(回收)之前调用这个方法叫做 finalize( )它用来清除回收对象。
finalize()方法在java中是由Object类提供的 使用Deprecated注解表示他从JDK9之后就被作废了 finalize()虽然看起来用于清理资源但它不能等同于C的析构函数它通常会被GC自动调用由于垃圾收集不一定会执行因此finalize()方法也不一定会执行。所以finalize() 的使用仅限于一种特殊情况对象以某种方式分配存储空间而不是通过创建对象来分配。(事实上这也是一种无奈之举因为确实是不能保障他能被执行) 垃圾收集不一定会执行是因为垃圾收集的一个策略 能不回收就不会回收.只要程序的内存没有达到即将用完的地步, 对象占用的空间就不会被释放.因为如果程序正常结束了,而且垃圾回收器没有释放申请的内存, 那么随着程序的正常退出, 申请的内存会自动交还给操作系统; 而且垃圾回收本身就需要付出代价, 是有一定开销的, 如果不使用,就不会存在这一部分的开销. 《 Effective Java 》的作者——Joshua Bloch 认为“Java语言规范不仅不保证finalize()方法会被及时地执行而且根本就不保证它们会被执行。” 因此普遍的建议是“永远不要直接调用 finalize()方法” 为什么——假设用finalize()方法关闭已经打开的文件由于finalize()方法有可能永远不执行也可能被延迟执行就可能导致该文件迟迟未能关闭甚至永远没有关闭最终导致大量的文件会保留在打开状态。积累到一定程度程序就可能再也无法打开文件导致运行失败 我们总结一下
在Java中始终使用new创建对象此时垃圾收集器会自动释放存储空间如果在Java中通过其他机制创建了本地对象则使用finalize()管理内存空间的释放但它仍然通过垃圾收集器自动调用如果需要终止对象封装的资源如文件或线程请提供一个显式的终止方法如显式定义close()方法 这里讲述的只是一点皮毛如果想要更加详细的了解垃圾收集器以及资源清理的相关内容可以参考我的另一篇文章 JVM从跨平台到跨专业 Ⅱ-- 垃圾回收 数组
你一定要知道的细节 数组本身是引用数据类型而数组中的元素可以是任何数据类型包括基本数据类型和引用数据类型。创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小取决于数组的长度和数组中元素的类型。数组中的元素在内存中是依次紧密排列的有序的。数组一旦初始化完成其长度就是确定的。数组的长度一旦确定就不能修改。我们可以直接通过下标(或索引)的方式调用指定位置的元素速度很快。数组名中引用的是这块连续空间的首地址。
声明与初始化
数组的声明需要明确 数组的维度在Java中数组的符号是[][]表示一维[][]表示二维。 数组的元素类型即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如int、String、Student等。 数组名就是代表某个数组的标识符数组名其实也是变量名按照变量的命名规范来命名。数组名是个引用数据类型的变量因为它代表一组数据。
int[] arr;
int arr1[];
double[] arr2;
String[] arr3; //引用类型变量数组注意 Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如 int a[5]; //非法 而初始化分为两种
如果数组变量的初始化和数组元素的赋值操作同时进行那就称为静态初始化。静态初始化本质是用静态数据编译时已知为数组初始化。此时数组的长度由静态数据的个数决定。数据类型[] 数组名 new 数据类型[]{元素1,元素2,元素3,...};
数据类型[] 数组名 {元素1,元素2,元素3...};//必须在一个语句中完成不能分成两个语句写数组变量的初始化和数组元素的赋值操作分开进行即为动态初始化。动态初始化中只确定了元素的个数即数组的长度而元素值此时只是默认值还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。数组存储的元素的数据类型[] 数组名字 new 数组存储的元素的数据类型[长度];我们来看看动态初始化里面的默认值 对于基本数据类型而言默认初始化值各有不同。 对于引用数据类型而言默认初始化值为null注意与0不同)
还要提一下二维数组的初始化。静态初始化与前面基本一样而动态初始化要注意其使用场景是如果二维数组的每一个数据甚至是每一行的列数需要后期单独确定。
这里就要分为两种情况
规则二维表每一行的列数是相同的//1确定行数和列数
元素的数据类型[][] 二维数组名 new 元素的数据类型[m][n];
//其中m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
//其中n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格
//此时创建完数组行数、列数确定而且元素也都有默认值//2再为元素赋新值
二维数组名[行下标][列下标] 值;不规则二维表每一行的列数不一样//1先确定总行数
元素的数据类型[][] 二维数组名 new 元素的数据类型[总行数][];//此时只是确定了总行数每一行里面现在是null//2再确定每一行的列数创建每一行的一维数组
二维数组名[行下标] new 元素的数据类型[该行的总列数];//此时已经new完的行的元素就有默认值了没有new的行还是null//(3)再为元素赋值
二维数组名[行下标][列下标] 值;数组内存分析
先浅谈一下java虚拟机
为了提高运算效率就对空间进行了不同区域的划分因为每一片区域都有特定的处理数据方式和内存管理方式。 区域名称作用虚拟机栈用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用方法执行完自动释放。堆内存存储对象包括数组对象new来创建的都存储在堆内存。方法区存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。本地方法栈当程序中调用了native的本地方法时本地方法执行期间的内存区域程序计数器程序计数器是CPU中的寄存器它包含每一个线程下一条要执行的指令的地址
一个一维数组内存图
public static void main(String[] args) {int[] arr new int[3];System.out.println(arr);//[I5f150435
} 注意 我们上面打印出来的地址[I5f150435是一个虚拟地址并不是数据真实存在的地址Java并不像C/C一样存在指针而是对其进行了隐藏。我们下面的图中举出的例子使用的是真实地址进行演示。 数组下标为什么是0开始
因为第一个元素距离数组首地址间隔0个单元格。
两个一维数组内存图
两个数组独立
public static void main(String[] args) {int[] arr new int[3];int[] arr2 new int[2];System.out.println(arr);System.out.println(arr2);
} 两个变量指向一个一维数组
两个数组变量本质上代表同一个数组。
public static void main(String[] args) {// 定义数组存储3个元素int[] arr new int[3];//数组索引进行赋值arr[0] 5;arr[1] 6;arr[2] 7;//输出3个索引上的元素值System.out.println(arr[0]);System.out.println(arr[1]);System.out.println(arr[2]);//定义数组变量arr2将arr的地址赋值给arr2int[] arr2 arr;arr2[1] 9;System.out.println(arr[1]);
}我们再来看看二维数组的内存分析
二维数组本质上是元素类型是一维数组的一维数组。
int[][] arr {{1},{2,2},{3,3,3},{4,4,4,4},{5,5,5,5,5}
};//1、声明二维数组并确定行数和列数
int[][] arr new int[4][5];//2、确定元素的值
for (int i 0; i arr.length; i) {for (int j 0; j arr.length; j) {arr[i][j] i 1;}
}//1、声明一个二维数组并且确定行数
//因为每一行的列数不同这里无法直接确定列数
int[][] arr new int[5][];//2、确定每一行的列数
for(int i0; iarr.length; i){/*arr[0] 的列数是1arr[1] 的列数是2arr[2] 的列数是3arr[3] 的列数是4arr[4] 的列数是5*/arr[i] new int[i1];
}//3、确定元素的值
for(int i0; iarr.length; i){for(int j0; jarr[i].length; j){arr[i][j] i1;}
}Arrays工具类的使用
java.util.Arrays类即为操作数组的工具类包含了用来操作数组比如排序和搜索的各种方法。 比如
数组元素拼接 static String toString(int[] a) 字符串表示形式由数组的元素列表组成括在方括号“[]”中。相邻元素用字符 , 逗号加空格分隔。形式为[元素1元素2元素3。。。]static String toString(Object[] a) 字符串表示形式由数组的元素列表组成括在方括号“[]”中。相邻元素用字符 , 逗号加空格分隔。元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接如果没有重写则返回类型hash值如果重写则按重写返回的字符串进行拼接。 数组排序 static void sort(int[] a) 将a数组按照从小到大进行排序static void sort(int[] a, int fromIndex, int toIndex) 将a数组的[fromIndex, toIndex)部分按照升序排列static void sort(Object[] a) 根据元素的自然顺序对指定对象数组按升序进行排序。static void sort(T[] a, Comparator? super T c) 根据指定比较器产生的顺序对指定对象数组进行排序。 数组元素的二分查找 static int binarySearch(int[] a, int key) 、static int binarySearch(Object[] a, Object key) 要求数组有序在数组中查找key是否存在如果存在返回第一次找到的下标不存在返回负数。 数组的复制 static int[] copyOf(int[] original, int newLength) 根据original原数组复制一个长度为newLength的新数组并返回新数组static T[] copyOf(T[] original,int newLength)根据original原数组复制一个长度为newLength的新数组并返回新数组static int[] copyOfRange(int[] original, int from, int to) 复制original原数组的[from,to)构成新数组并返回新数组static T[] copyOfRange(T[] original,int from,int to)复制original原数组的[from,to)构成新数组并返回新数组 比较两个数组是否相等 static boolean equals(int[] a, int[] a2) 比较两个数组的长度、元素是否完全相同static boolean equals(Object[] a,Object[] a2)比较两个数组的长度、元素是否完全相同 填充数组 static void fill(int[] a, int val) 用val值填充整个a数组static void fill(Object[] a,Object val)用val对象填充整个a数组static void fill(int[] a, int fromIndex, int toIndex, int val)将a数组[fromIndex,toIndex)部分填充为val值static void fill(Object[] a, int fromIndex, int toIndex, Object val) 将a数组[fromIndex,toIndex)部分填充为val对象 文章转载自: http://www.morning.yrddl.cn.gov.cn.yrddl.cn http://www.morning.bylzr.cn.gov.cn.bylzr.cn http://www.morning.rwrn.cn.gov.cn.rwrn.cn http://www.morning.pwwdp.cn.gov.cn.pwwdp.cn http://www.morning.wpmqq.cn.gov.cn.wpmqq.cn http://www.morning.cnqff.cn.gov.cn.cnqff.cn http://www.morning.rgksz.cn.gov.cn.rgksz.cn http://www.morning.jggr.cn.gov.cn.jggr.cn http://www.morning.kaylyea.com.gov.cn.kaylyea.com http://www.morning.wngpq.cn.gov.cn.wngpq.cn http://www.morning.tgdys.cn.gov.cn.tgdys.cn http://www.morning.cttti.com.gov.cn.cttti.com http://www.morning.fwkq.cn.gov.cn.fwkq.cn http://www.morning.bmlcy.cn.gov.cn.bmlcy.cn http://www.morning.xqffq.cn.gov.cn.xqffq.cn http://www.morning.bmqls.cn.gov.cn.bmqls.cn http://www.morning.tndxg.cn.gov.cn.tndxg.cn http://www.morning.nqbs.cn.gov.cn.nqbs.cn http://www.morning.cwlxs.cn.gov.cn.cwlxs.cn http://www.morning.fpkpz.cn.gov.cn.fpkpz.cn http://www.morning.wypyl.cn.gov.cn.wypyl.cn http://www.morning.lpnpn.cn.gov.cn.lpnpn.cn http://www.morning.hxwrs.cn.gov.cn.hxwrs.cn http://www.morning.phechi.com.gov.cn.phechi.com http://www.morning.ndrzq.cn.gov.cn.ndrzq.cn http://www.morning.lxhny.cn.gov.cn.lxhny.cn http://www.morning.nhzzn.cn.gov.cn.nhzzn.cn http://www.morning.kgxyd.cn.gov.cn.kgxyd.cn http://www.morning.pbmkh.cn.gov.cn.pbmkh.cn http://www.morning.duckgpt.cn.gov.cn.duckgpt.cn http://www.morning.cwqrj.cn.gov.cn.cwqrj.cn http://www.morning.zffn.cn.gov.cn.zffn.cn http://www.morning.ckfyp.cn.gov.cn.ckfyp.cn http://www.morning.btwrj.cn.gov.cn.btwrj.cn http://www.morning.iiunion.com.gov.cn.iiunion.com http://www.morning.jqhrk.cn.gov.cn.jqhrk.cn http://www.morning.fcpjq.cn.gov.cn.fcpjq.cn http://www.morning.nrmyj.cn.gov.cn.nrmyj.cn http://www.morning.ntgsg.cn.gov.cn.ntgsg.cn http://www.morning.brbmf.cn.gov.cn.brbmf.cn http://www.morning.dmwck.cn.gov.cn.dmwck.cn http://www.morning.tdgwg.cn.gov.cn.tdgwg.cn http://www.morning.sjgsh.cn.gov.cn.sjgsh.cn http://www.morning.pbmg.cn.gov.cn.pbmg.cn http://www.morning.rrdch.cn.gov.cn.rrdch.cn http://www.morning.ckctj.cn.gov.cn.ckctj.cn http://www.morning.tmrjb.cn.gov.cn.tmrjb.cn http://www.morning.kdjtt.cn.gov.cn.kdjtt.cn http://www.morning.wdwfm.cn.gov.cn.wdwfm.cn http://www.morning.gidmag.com.gov.cn.gidmag.com http://www.morning.lanyee.com.cn.gov.cn.lanyee.com.cn http://www.morning.dpppx.cn.gov.cn.dpppx.cn http://www.morning.wskn.cn.gov.cn.wskn.cn http://www.morning.ysdwq.cn.gov.cn.ysdwq.cn http://www.morning.cwskn.cn.gov.cn.cwskn.cn http://www.morning.dnbhd.cn.gov.cn.dnbhd.cn http://www.morning.zxznh.cn.gov.cn.zxznh.cn http://www.morning.frtt.cn.gov.cn.frtt.cn http://www.morning.sqfrg.cn.gov.cn.sqfrg.cn http://www.morning.dgwrz.cn.gov.cn.dgwrz.cn http://www.morning.tsmcc.cn.gov.cn.tsmcc.cn http://www.morning.xqkcs.cn.gov.cn.xqkcs.cn http://www.morning.wdprz.cn.gov.cn.wdprz.cn http://www.morning.cwgn.cn.gov.cn.cwgn.cn http://www.morning.tfei69.cn.gov.cn.tfei69.cn http://www.morning.rtlrz.cn.gov.cn.rtlrz.cn http://www.morning.ttshf.cn.gov.cn.ttshf.cn http://www.morning.rhmpk.cn.gov.cn.rhmpk.cn http://www.morning.ttdxn.cn.gov.cn.ttdxn.cn http://www.morning.drfrm.cn.gov.cn.drfrm.cn http://www.morning.mhrzd.cn.gov.cn.mhrzd.cn http://www.morning.rtkz.cn.gov.cn.rtkz.cn http://www.morning.rxfgh.cn.gov.cn.rxfgh.cn http://www.morning.ghkgl.cn.gov.cn.ghkgl.cn http://www.morning.kcbml.cn.gov.cn.kcbml.cn http://www.morning.twgzq.cn.gov.cn.twgzq.cn http://www.morning.ttfh.cn.gov.cn.ttfh.cn http://www.morning.mjzgg.cn.gov.cn.mjzgg.cn http://www.morning.jqrhz.cn.gov.cn.jqrhz.cn http://www.morning.rfpb.cn.gov.cn.rfpb.cn