Java基础(十)——内存分析(入门了解)和异常
一、内存分析
1、初步了解
一个简单的Java文件,从编译运行开始,到中间的main方法执行,实例化对象,赋值等等一系列操作,中间内存都发生了什么变化呢?先举个简单的例子:
看左边的代码,下面的都是普通的定义,get、set方法以及toString。上面都是实例化对象,赋值,以及输出语句。
右边的图中,分三个区域,分别是栈、堆、方法区。栈:放了栈帧和局部变量,其中 main 方法在栈里面有额外的一个区来存储,叫栈帧。堆中主要负责:实例化对象。方法区中主要:常量池,静态化和字节码文件。
接着就是代码运行时,内存中的一些操作:
其中要注意的是:
1、局部变量的一个特点,用完即销毁。
2、基本数据类型是直接赋值。
3、引用数据类型是指向或者引用。
方法不调用的时候是不存在的,需要用到方法的时候根据字节码文件再创造出来,方法的主体准确来说是放在栈里面,虽然用完了下次再用还是同一个方法名,但是已经不一样了;可以说即是一样又是不一样。
只要记住,堆中对象只存放属性。
2、例题1(对象)
思考:这里打印出来的结果会是什么?
先上分析:
a、分析:属性部分
代码从上到下运行,所以(上图懒得画属性写在方法区中的常量池):
第一行代码,堆中实例化对象 new Student ("zs"18),接着栈帧中Student stu1指向堆中 zs 所在的对象;第二行代码同理。
接着就是栈帧中 int age = 18:
b、分析:第一个方法
接着就到了方法1,stu1传递到了方法中的形参,stu1指向的是zs,所以方法中的 stu 指向的是zs,然后执行 setAge()方法,把 age 改成了10,:
接着 栈中下面的 Student stu 用完即销毁:
c、分析:第二个方法(重点)
方法二中的形参,接收的是 ls 对象,所以这里的 Student stu 指向的是 ls。但是,里面的代码又 new 了一个对象,所以堆中要开辟一个新的空间,原本的 stu 指向的是 ls ,这里重新指向了 ww 。然后就到了 setAge()方法,这里因为指向的是 ww ,所以修改的是 ww 的年龄;然后修改完后就销毁。
d、分析:第三个方法
这里形参接收的是全局的 age ,所以是 18,接着方法里面给 age 赋值30,这里赋值的是方法里面的局部变量,赋值完毕后整体销毁。所以这里全局变量 age ,还是18。
3、例题2(数组对象)
左右两边代码只有部分不一样。思考打印出来的结果是什么?
左右两边的代码唯一不同的地方在于,for 循环内外的 new Student 这一块。
a、分析左图代码
核心在于:new Student 在 for 循环里面这一块,for 循环三次,会开辟三个新的空间,原本的三个数组分别指向了三个新的对象。这时候数组里面的三个对象内容都是 name:z6,age:18.。接着 for 循环结束后,执行名字重新赋值,数组里第一个对象名字改为 张三,其他都不变:
b、分析右图代码
这一部分的代码,new Student 在 for 循环的外面,意味着三次循环都不会开辟新的空间。通过 for 循环,让数组里面三个对象,都统一指向了 stu 这个对象:
这时候关键就来了,for循环结束后,stus[0].setName(“张三”),这语句把名字修改成张三,但是因为这时候数组里三个对象都指向了 stu 这个对象,所以修改的是 stu 里面的名字,所以三个对象的名字都同时修改了。
4、总结例题2
1、数组里3个引用不同的对象。
2、数组里3个引用相同的对象。
二、异常
1、异常初了解
每一个程序员不可避免的就是程序报错,也就是程序出现异常。异常种类多种多样
Throwable:异常和错误的父类
Error:错误
Exception:异常
当递归无限调用自己,就会报错异常,这种叫栈溢出错误:
2、常见异常
异常一般分为两种:
1、受检性异常(一般异常):编译时期出现异常(一般语法有问题,或者有异常需要你去处理的这种是受检性异常)
2、非受检性异常(运行时异常):运行时期出现的异常
a、数组索引越界异常
举例子:
下标超出了,出现错异常:
不懂英文可以查看翻译:
b、空指针异常
equal()方法是对比字符串是否一样的方法。这里面括号内的空,跟 null 不是同一个东西。
养成良好习惯,把对象写括号里面,这样就算 str 为null,也不会报错:
c、类型转换异常
还有个前面讲过的,动物类和猫类狗类,猫变狗会报错:
d、算术异常
0不能作为分母这种算术异常:
三、异常处理
1、try…catch捕获异常
a、了解使用
捕获异常语法:
以往程序出现异常,到了出错部分往往就停止了,后面有代码也不会再运行。当有了捕获异常后,就算有异常,后面的代码还能继续运行:
但是原本的异常信息没有了,这里还是可以打印:
b、try块出现异常后续代码不再执行
c、何时使用捕获异常
当知道如何处理异常的时候使用 try catch
2、throws—抛出(声明)异常
抛出异常归抛出异常,但是异常的后续代码一样不会执行。
3、RuntimeException—自定义异常
需要先继承RuntimeException这个类。
上图可以看到,MyException 是自己写的异常,出现异常可以根据自己写的内容显现出来。但是后面的代码,也同样没有执行,程序同样会终止。
自定义异常:throw
抛出异常对象,会使程序中止。
4、finally——一定会执行的代码块
finally 是无论出现什么异常,一定都会执行里面的代码块的功能。
其中,这里面不一定要跟着 catch ,可以直接 try…finally:
这个代码执行的是 -1。正常来说到 return 就结束了,但是因为有 finally,所以会执行到 finally 部分再结束。